1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-27 20:51:00 +01:00

Merge pull request #14646 from NixOS/fix-14642-2.32

[Backport 2.32-maintenance] Fix dynamic attributes that are simple string expressions
This commit is contained in:
Taeer Bar-Yam 2025-11-25 17:39:55 -05:00 committed by GitHub
commit ca5f35fcf7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 106 additions and 60 deletions

View file

@ -79,6 +79,8 @@
# Not supported by nixfmt # Not supported by nixfmt
''^tests/functional/lang/eval-okay-deprecate-cursed-or\.nix$'' ''^tests/functional/lang/eval-okay-deprecate-cursed-or\.nix$''
''^tests/functional/lang/eval-okay-attrs5\.nix$'' ''^tests/functional/lang/eval-okay-attrs5\.nix$''
''^tests/functional/lang/eval-fail-dynamic-attrs-inherit\.nix$''
''^tests/functional/lang/eval-fail-dynamic-attrs-inherit-2\.nix$''
# More syntax tests # More syntax tests
# These tests, or parts of them, should have been parse-* test cases. # These tests, or parts of them, should have been parse-* test cases.

View file

@ -185,28 +185,13 @@ struct ExprFloat : Expr
struct ExprString : Expr struct ExprString : Expr
{ {
std::string s;
Value v; Value v;
/** ExprString(std::string && s)
* This is only for strings already allocated in our polymorphic allocator, : s(std::move(s))
* or that live at least that long (e.g. c++ string literals)
*/
ExprString(const char * s)
{ {
v.mkStringNoCopy(s); v.mkStringNoCopy(this->s.data());
};
ExprString(std::pmr::polymorphic_allocator<char> & alloc, std::string_view sv)
{
auto len = sv.length();
if (len == 0) {
v.mkStringNoCopy("");
return;
}
char * s = alloc.allocate(len + 1);
sv.copy(s, len);
s[len] = '\0';
v.mkStringNoCopy(s);
}; };
Value * maybeThunk(EvalState & state, Env & env) override; Value * maybeThunk(EvalState & state, Env & env) override;

View file

@ -324,7 +324,7 @@ ParserState::stripIndentation(const PosIdx pos, std::vector<std::pair<PosIdx, st
// Ignore empty strings for a minor optimisation and AST simplification // Ignore empty strings for a minor optimisation and AST simplification
if (s2 != "") { if (s2 != "") {
es2->emplace_back(i->first, new ExprString(alloc, s2)); es2->emplace_back(i->first, new ExprString(std::move(s2)));
} }
}; };
for (; i != es.end(); ++i, --n) { for (; i != es.end(); ++i, --n) {

View file

@ -40,7 +40,7 @@ void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const
void ExprString::show(const SymbolTable & symbols, std::ostream & str) const void ExprString::show(const SymbolTable & symbols, std::ostream & str) const
{ {
printLiteralString(str, v.string_view()); printLiteralString(str, s);
} }
void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const

View file

@ -136,7 +136,6 @@ static Expr * makeCall(PosIdx pos, Expr * fn, Expr * arg) {
std::vector<nix::AttrName> * attrNames; std::vector<nix::AttrName> * attrNames;
std::vector<std::pair<nix::AttrName, nix::PosIdx>> * inheritAttrs; std::vector<std::pair<nix::AttrName, nix::PosIdx>> * inheritAttrs;
std::vector<std::pair<nix::PosIdx, nix::Expr *>> * string_parts; std::vector<std::pair<nix::PosIdx, nix::Expr *>> * string_parts;
std::variant<nix::Expr *, std::string_view> * to_be_string;
std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, nix::StringToken>>> * ind_string_parts; std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, nix::StringToken>>> * ind_string_parts;
} }
@ -151,8 +150,7 @@ static Expr * makeCall(PosIdx pos, Expr * fn, Expr * arg) {
%type <inheritAttrs> attrs %type <inheritAttrs> attrs
%type <string_parts> string_parts_interpolated %type <string_parts> string_parts_interpolated
%type <ind_string_parts> ind_string_parts %type <ind_string_parts> ind_string_parts
%type <e> path_start %type <e> path_start string_parts string_attr
%type <to_be_string> string_parts string_attr
%type <id> attr %type <id> attr
%token <id> ID %token <id> ID
%token <str> STR IND_STR %token <str> STR IND_STR
@ -307,13 +305,7 @@ expr_simple
} }
| INT_LIT { $$ = new ExprInt($1); } | INT_LIT { $$ = new ExprInt($1); }
| FLOAT_LIT { $$ = new ExprFloat($1); } | FLOAT_LIT { $$ = new ExprFloat($1); }
| '"' string_parts '"' { | '"' string_parts '"' { $$ = $2; }
std::visit(overloaded{
[&](std::string_view str) { $$ = new ExprString(state->alloc, str); },
[&](Expr * expr) { $$ = expr; }},
*$2);
delete $2;
}
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE { | IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
$$ = state->stripIndentation(CUR_POS, std::move(*$2)); $$ = state->stripIndentation(CUR_POS, std::move(*$2));
delete $2; delete $2;
@ -324,11 +316,11 @@ expr_simple
$$ = new ExprConcatStrings(CUR_POS, false, $2); $$ = new ExprConcatStrings(CUR_POS, false, $2);
} }
| SPATH { | SPATH {
std::string_view path($1.p + 1, $1.l - 2); std::string path($1.p + 1, $1.l - 2);
$$ = new ExprCall(CUR_POS, $$ = new ExprCall(CUR_POS,
new ExprVar(state->s.findFile), new ExprVar(state->s.findFile),
{new ExprVar(state->s.nixPath), {new ExprVar(state->s.nixPath),
new ExprString(state->alloc, path)}); new ExprString(std::move(path))});
} }
| URI { | URI {
static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals); static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals);
@ -337,7 +329,7 @@ expr_simple
.msg = HintFmt("URL literals are disabled"), .msg = HintFmt("URL literals are disabled"),
.pos = state->positions[CUR_POS] .pos = state->positions[CUR_POS]
}); });
$$ = new ExprString(state->alloc, $1); $$ = new ExprString(std::string($1));
} }
| '(' expr ')' { $$ = $2; } | '(' expr ')' { $$ = $2; }
/* Let expressions `let {..., body = ...}' are just desugared /* Let expressions `let {..., body = ...}' are just desugared
@ -354,19 +346,19 @@ expr_simple
; ;
string_parts string_parts
: STR { $$ = new std::variant<Expr *, std::string_view>($1); } : STR { $$ = new ExprString(std::string($1)); }
| string_parts_interpolated { $$ = new std::variant<Expr *, std::string_view>(new ExprConcatStrings(CUR_POS, true, $1)); } | string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); }
| { $$ = new std::variant<Expr *, std::string_view>(std::string_view()); } | { $$ = new ExprString(""); }
; ;
string_parts_interpolated string_parts_interpolated
: string_parts_interpolated STR : string_parts_interpolated STR
{ $$ = $1; $1->emplace_back(state->at(@2), new ExprString(state->alloc, $2)); } { $$ = $1; $1->emplace_back(state->at(@2), new ExprString(std::string($2))); }
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(state->at(@2), $3); } | string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(state->at(@2), $3); }
| DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<PosIdx, Expr *>>; $$->emplace_back(state->at(@1), $2); } | DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<PosIdx, Expr *>>; $$->emplace_back(state->at(@1), $2); }
| STR DOLLAR_CURLY expr '}' { | STR DOLLAR_CURLY expr '}' {
$$ = new std::vector<std::pair<PosIdx, Expr *>>; $$ = new std::vector<std::pair<PosIdx, Expr *>>;
$$->emplace_back(state->at(@1), new ExprString(state->alloc, $1)); $$->emplace_back(state->at(@1), new ExprString(std::string($1)));
$$->emplace_back(state->at(@2), $3); $$->emplace_back(state->at(@2), $3);
} }
; ;
@ -464,17 +456,16 @@ attrs
: attrs attr { $$ = $1; $1->emplace_back(AttrName(state->symbols.create($2)), state->at(@2)); } : attrs attr { $$ = $1; $1->emplace_back(AttrName(state->symbols.create($2)), state->at(@2)); }
| attrs string_attr | attrs string_attr
{ $$ = $1; { $$ = $1;
std::visit(overloaded { ExprString * str = dynamic_cast<ExprString *>($2);
[&](std::string_view str) { $$->emplace_back(AttrName(state->symbols.create(str)), state->at(@2)); }, if (str) {
[&](Expr * expr) { $$->emplace_back(AttrName(state->symbols.create(str->s)), state->at(@2));
delete str;
} else
throw ParseError({ throw ParseError({
.msg = HintFmt("dynamic attributes not allowed in inherit"), .msg = HintFmt("dynamic attributes not allowed in inherit"),
.pos = state->positions[state->at(@2)] .pos = state->positions[state->at(@2)]
}); });
} }
}, *$2);
delete $2;
}
| { $$ = new std::vector<std::pair<AttrName, PosIdx>>; } | { $$ = new std::vector<std::pair<AttrName, PosIdx>>; }
; ;
@ -482,20 +473,22 @@ attrpath
: attrpath '.' attr { $$ = $1; $1->push_back(AttrName(state->symbols.create($3))); } : attrpath '.' attr { $$ = $1; $1->push_back(AttrName(state->symbols.create($3))); }
| attrpath '.' string_attr | attrpath '.' string_attr
{ $$ = $1; { $$ = $1;
std::visit(overloaded { ExprString * str = dynamic_cast<ExprString *>($3);
[&](std::string_view str) { $$->push_back(AttrName(state->symbols.create(str))); }, if (str) {
[&](Expr * expr) { $$->push_back(AttrName(expr)); } $$->push_back(AttrName(state->symbols.create(str->s)));
}, *$3); delete str;
delete $3; } else
$$->push_back(AttrName($3));
} }
| attr { $$ = new std::vector<AttrName>; $$->push_back(AttrName(state->symbols.create($1))); } | attr { $$ = new std::vector<AttrName>; $$->push_back(AttrName(state->symbols.create($1))); }
| string_attr | string_attr
{ $$ = new std::vector<AttrName>; { $$ = new std::vector<AttrName>;
std::visit(overloaded { ExprString *str = dynamic_cast<ExprString *>($1);
[&](std::string_view str) { $$->push_back(AttrName(state->symbols.create(str))); }, if (str) {
[&](Expr * expr) { $$->push_back(AttrName(expr)); } $$->push_back(AttrName(state->symbols.create(str->s)));
}, *$1); delete str;
delete $1; } else
$$->push_back(AttrName($1));
} }
; ;
@ -506,7 +499,7 @@ attr
string_attr string_attr
: '"' string_parts '"' { $$ = $2; } : '"' string_parts '"' { $$ = $2; }
| DOLLAR_CURLY expr '}' { $$ = new std::variant<Expr *, std::string_view>($2); } | DOLLAR_CURLY expr '}' { $$ = $2; }
; ;
expr_list expr_list

View file

@ -0,0 +1,6 @@
error: dynamic attributes not allowed in inherit
at /pwd/lang/eval-fail-dynamic-attrs-inherit-2.nix:5:15:
4| {
5| inherit (a) ${"b" + ""};
| ^
6| }

View file

@ -0,0 +1,6 @@
let
a.b = 1;
in
{
inherit (a) ${"b" + ""};
}

View file

@ -0,0 +1,6 @@
error: dynamic attributes not allowed in inherit
at /pwd/lang/eval-fail-dynamic-attrs-inherit.nix:5:11:
4| {
5| inherit ${"a" + ""};
| ^
6| }

View file

@ -0,0 +1,6 @@
let
a = 1;
in
{
inherit ${"a" + ""};
}

View file

@ -0,0 +1,5 @@
error: dynamic attributes not allowed in let
at /pwd/lang/eval-fail-dynamic-attrs-let-2.nix:1:1:
1| let
| ^
2| ${"${"a"}"} = 1;

View file

@ -0,0 +1,4 @@
let
${"${"a"}"} = 1;
in
a

View file

@ -0,0 +1,5 @@
error: dynamic attributes not allowed in let
at /pwd/lang/eval-fail-dynamic-attrs-let-3.nix:1:1:
1| let
| ^
2| "${"a"}" = 1;

View file

@ -0,0 +1,4 @@
let
"${"a"}" = 1;
in
a

View file

@ -0,0 +1,5 @@
error: dynamic attributes not allowed in let
at /pwd/lang/eval-fail-dynamic-attrs-let.nix:1:1:
1| let
| ^
2| ${"a" + ""} = 1;

View file

@ -0,0 +1,4 @@
let
${"a" + ""} = 1;
in
a

View file

@ -0,0 +1 @@
{ a = 1; attrs = { b = 1; c = 1; d = 1; }; b = 1; c = 1; d = 1; }

View file

@ -0,0 +1,14 @@
# dynamic attrs are not generally allowed in `let`, and inherit, but they are if they only contain a string
let
${"a"} = 1;
attrs = rec {
b = c;
${"c"} = d;
d = a;
};
in
{
inherit ${"a"};
inherit attrs;
inherit (attrs) ${"b"} ${"c"} d;
}