libexpr: Soft-deprecate __overrides

Change-Id: I787e69e1dad6edc5ccdb747b74a9ccd6e8e13bb3
This commit is contained in:
piegames 2024-08-18 16:02:23 +02:00
parent ac6974777e
commit 7210ed1b87
20 changed files with 86 additions and 12 deletions

View file

@ -1,12 +1,16 @@
--- ---
synopsis: Deprecated URL literals synopsis: Deprecated language features
issues: [fj#437] issues: [fj#437]
cls: [1736, 1735, 1744] cls: [1785, 1736, 1735, 1744]
category: Breaking Changes category: Breaking Changes
credits: [piegames, horrors] credits: [piegames, horrors]
--- ---
URL literals have long been obsolete and discouraged of use, and now they are officially deprecated. A system for deprecation (and then the planned removal) of undesired language features has been put into place.
This means that all URLs must be properly put within quotes like all other strings. It is controlled via feature flags much like experimental features, except that the deprecations are enabled default,
and can be disabled via the flags for backwards compatibility (opt-out with `--extra-deprecated-features` or the Nix configuration file).
To ease migration, they can still be enabled with `--extra-deprecated-features url-literals` for now. - `url-literals`: **URL literals** have long been obsolete and discouraged of use, and now they are officially deprecated.
This means that all URLs must be properly put within quotes like all other strings.
- `rec-set-overrides`: **__overrides** is an old arcane syntax which has not been in use for more than a decade.
It is soft-deprecated with a warning only, with the plan to turn that into an error in a future release.

View file

@ -249,6 +249,7 @@ EvalState::EvalState(
.findFile = symbols.create("__findFile"), .findFile = symbols.create("__findFile"),
.nixPath = symbols.create("__nixPath"), .nixPath = symbols.create("__nixPath"),
.body = symbols.create("body"), .body = symbols.create("body"),
.overrides = symbols.create("__overrides"),
} }
, repair(NoRepair) , repair(NoRepair)
, emptyBindings(0) , emptyBindings(0)

View file

@ -48,7 +48,7 @@ protected:
public: public:
struct AstSymbols { struct AstSymbols {
Symbol sub, lessThan, mul, div, or_, findFile, nixPath, body; Symbol sub, lessThan, mul, div, or_, findFile, nixPath, body, overrides;
}; };

View file

@ -676,6 +676,12 @@ template<> struct BuildAST<grammar::expr::ancient_let> : change_head<BindingsSta
template<> struct BuildAST<grammar::expr::rec_set> : change_head<BindingsState> { template<> struct BuildAST<grammar::expr::rec_set> : change_head<BindingsState> {
static void success(const auto & in, BindingsState & b, ExprState & s, State & ps) { static void success(const auto & in, BindingsState & b, ExprState & s, State & ps) {
// Before inserting new attrs, check for __override and throw an error
// (the error will initially be a warning to ease migration)
if (!featureSettings.isEnabled(Dep::RecSetOverrides) && b.attrs.attrs.contains(ps.s.overrides)) {
ps.overridesFound(ps.at(in));
}
b.attrs.pos = ps.at(in); b.attrs.pos = ps.at(in);
b.attrs.recursive = true; b.attrs.recursive = true;
s.pushExpr<ExprAttrs>(b.attrs.pos, std::move(b.attrs)); s.pushExpr<ExprAttrs>(b.attrs.pos, std::move(b.attrs));

View file

@ -2,6 +2,7 @@
///@file ///@file
#include "eval.hh" #include "eval.hh"
#include "logging.hh"
namespace nix::parser { namespace nix::parser {
@ -23,6 +24,7 @@ struct State
void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos); void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos); void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
void overridesFound(const PosIdx pos);
void addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_ptr<Expr> e, const PosIdx pos); void addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_ptr<Expr> e, const PosIdx pos);
std::unique_ptr<Formals> validateFormals(std::unique_ptr<Formals> formals, PosIdx pos = noPos, Symbol arg = {}); std::unique_ptr<Formals> validateFormals(std::unique_ptr<Formals> formals, PosIdx pos = noPos, Symbol arg = {});
std::unique_ptr<Expr> stripIndentation(const PosIdx pos, std::unique_ptr<Expr> stripIndentation(const PosIdx pos,
@ -58,6 +60,17 @@ inline void State::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
}); });
} }
inline void State::overridesFound(const PosIdx pos) {
// Added 2024-09-18. Turn into an error at some point in the future.
// See the documentation on deprecated features for more details.
warn(
"%s found at %s. This feature is deprecated and will be removed in the future. Use %s to silence this warning.",
"__overrides",
positions[pos],
"--extra-deprecated-features rec-set-overrides"
);
}
inline void State::addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_ptr<Expr> e, const PosIdx pos) inline void State::addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_ptr<Expr> e, const PosIdx pos)
{ {
AttrPath::iterator i; AttrPath::iterator i;
@ -123,6 +136,12 @@ inline void State::addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_
dupAttr(attrPath, pos, j->second.pos); dupAttr(attrPath, pos, j->second.pos);
} }
} else { } else {
// Before inserting new attrs, check for __override and throw an error
// (the error will initially be a warning to ease migration)
if (attrs->recursive && !featureSettings.isEnabled(Dep::RecSetOverrides) && i->symbol == s.overrides) {
overridesFound(pos);
}
// This attr path is not defined. Let's create it. // This attr path is not defined. Let's create it.
e->setName(i->symbol); e->setName(i->symbol);
attrs->attrs.emplace(std::piecewise_construct, attrs->attrs.emplace(std::piecewise_construct,

View file

@ -27,6 +27,15 @@ struct DeprecatedFeatureDetails
constexpr size_t numDepFeatures = 1 + static_cast<size_t>(Dep::UrlLiterals); constexpr size_t numDepFeatures = 1 + static_cast<size_t>(Dep::UrlLiterals);
constexpr std::array<DeprecatedFeatureDetails, numDepFeatures> depFeatureDetails = {{ constexpr std::array<DeprecatedFeatureDetails, numDepFeatures> depFeatureDetails = {{
{
.tag = Dep::RecSetOverrides,
.name = "rec-set-overrides",
.description = R"(
Allow `__overrides` in recursive attribute sets.
Use fix point functions (e.g. `lib.fix` in Nixpkgs) instead.
)",
},
{ {
.tag = Dep::UrlLiterals, .tag = Dep::UrlLiterals,
.name = "url-literals", .name = "url-literals",

View file

@ -18,6 +18,7 @@ namespace nix {
*/ */
enum struct DeprecatedFeature enum struct DeprecatedFeature
{ {
RecSetOverrides,
UrlLiterals, UrlLiterals,
}; };

View file

@ -53,10 +53,11 @@ done
for i in lang/parse-okay-*.nix; do for i in lang/parse-okay-*.nix; do
echo "parsing $i (should succeed)"; echo "parsing $i (should succeed)";
i=$(basename "$i" .nix) i=$(basename "$i" .nix)
# Hard-code that these two files are allowed to use url literals (because they test them)
if [[ "$i" == "parse-okay-url" || "$i" == "parse-okay-regression-20041027" ]] if [ -e "lang/$i.flags" ]; then
then extraArgs="$(cat "lang/$i.flags")"
extraArgs="--extra-deprecated-features url-literals" else
extraArgs=""
fi fi
if if
expect 0 nix-instantiate --parse ${extraArgs-} - < "lang/$i.nix" \ expect 0 nix-instantiate --parse ${extraArgs-} - < "lang/$i.nix" \
@ -75,8 +76,14 @@ done
for i in lang/eval-fail-*.nix; do for i in lang/eval-fail-*.nix; do
echo "evaluating $i (should fail)"; echo "evaluating $i (should fail)";
i=$(basename "$i" .nix) i=$(basename "$i" .nix)
if [ -e "lang/$i.flags" ]; then
extraArgs="$(cat "lang/$i.flags")"
else
extraArgs=""
fi
if if
expectStderr 1 nix-instantiate --eval --strict --show-trace "lang/$i.nix" \ expectStderr 1 nix-instantiate --eval --strict --show-trace ${extraArgs-} "lang/$i.nix" \
| sed "s!$(pwd)!/pwd!g" > "lang/$i.err" | sed "s!$(pwd)!/pwd!g" > "lang/$i.err"
then then
diffAndAccept "$i" err err.exp diffAndAccept "$i" err err.exp
@ -90,8 +97,14 @@ for i in lang/eval-okay-*.nix; do
echo "evaluating $i (should succeed)"; echo "evaluating $i (should succeed)";
i=$(basename "$i" .nix) i=$(basename "$i" .nix)
if [ -e "lang/$i.flags" ]; then
extraArgs="$(cat "lang/$i.flags")"
else
extraArgs=""
fi
if test -e "lang/$i.exp.xml"; then if test -e "lang/$i.exp.xml"; then
if expect 0 nix-instantiate --eval --xml --no-location --strict \ if expect 0 nix-instantiate --eval --xml --no-location --strict ${extraArgs-} \
"lang/$i.nix" > "lang/$i.out.xml" "lang/$i.nix" > "lang/$i.out.xml"
then then
diffAndAccept "$i" out.xml exp.xml diffAndAccept "$i" out.xml exp.xml

View file

@ -0,0 +1 @@
--extra-deprecated-features rec-set-overrides

View file

@ -0,0 +1 @@
--extra-deprecated-features rec-set-overrides

View file

@ -0,0 +1 @@
--extra-deprecated-features rec-set-overrides

View file

@ -0,0 +1 @@
--extra-deprecated-features rec-set-overrides

View file

@ -0,0 +1 @@
parse-okay-rec-set-override-warning.exp

View file

@ -0,0 +1 @@
--extra-deprecated-features rec-set-overrides

View file

@ -0,0 +1 @@
parse-okay-rec-set-override-warning.nix

View file

@ -0,0 +1,2 @@
warning: __overrides found at «stdin»:3:16. This feature is deprecated and will be removed in the future. Use --extra-deprecated-features rec-set-overrides to silence this warning.
warning: __overrides found at «stdin»:4:2. This feature is deprecated and will be removed in the future. Use --extra-deprecated-features rec-set-overrides to silence this warning.

View file

@ -0,0 +1 @@
[ ({ a = rec { __overrides = { }; }; }) (rec { __overrides = { }; }) ({ __overrides = { }; }) (rec { "${("__overrides" + "")}" = { }; }) ]

View file

@ -0,0 +1,9 @@
[
# Should warn
{ a = rec {}; a.__overrides = {}; }
rec { __overrides = {}; }
# Should not warn: Not recursive
{ __overrides = {}; }
# Should not warn: Dynamic
rec { ${"__overrides" + ""} = {}; }
]

View file

@ -0,0 +1 @@
--extra-deprecated-features url-literals

View file

@ -0,0 +1 @@
--extra-deprecated-features url-literals