libexpr: Deprecate or in non-keyword positions

Test cases courtesy of rhenrdic

Co-authored-by: Commentator2.0 <lix@crystal-cavern.systems>
Change-Id: Id8df684ddd00d07b49e1c9e68bf41ee6c0e6887c
This commit is contained in:
piegames
2026-01-31 15:32:28 +01:00
co-authored by Commentator2.0
parent af166146ff
commit 17f1bcfd2c
33 changed files with 432 additions and 2 deletions
+2 -1
View File
@@ -1,7 +1,7 @@
---
synopsis: 'more deprecated features'
issues: []
cls: [2092, 2310, 2311, 4638, 4652]
cls: [2092, 2310, 2311, 4638, 4652, 4764]
category: Breaking Changes
credits: [piegames, commentator2.0]
---
@@ -13,3 +13,4 @@ You can opt in into the old behavior with `--extra-deprecated-features` or any e
- `floating-without-zero` so far, one was able to declare a float using something like `.123`. This can cause confusion about accessing attributes. Floating point numbers must now always include the leading zero, i.e. `0.123`
- `rec-set-merges` Attribute sets like `{ foo = {}; foo.bar = 42;}` implicitly merge at parse time, however if one of them is marked as recursive but not the others then the recursive attribute may get lost (order-dependent). Therefore, merging attrs with mixed-`rec` is now forbidden.
- `rec-set-dynamic-attrs` Dynamic attributes have weird semantics in the presence of recursive attrsets (they evaluate *after* the rest of the set). This is now forbidden.
- `or-as-identifier` `or` as an identifier has always been weird since the `or` (almost-)keyword has been introduced. We are deprecating the backcompat hacks from the early days of Nix in favor of making `or` a full and proper keyword.
+9 -1
View File
@@ -324,7 +324,11 @@ struct AttrState : SubexprState {
template<> struct BuildAST<grammar::v1::attr::simple> {
static void apply(const auto & in, auto & s, State & ps) {
s.pushAttr(ps.symbols.create(in.string_view()), ps.at(in));
auto symbol = ps.symbols.create(in.string_view());
if (!ps.featureSettings.isEnabled(Dep::OrAsIdentifier) && symbol == ps.symbols.sym_or) {
ps.orIdentifierFound(ps.at(in));
}
s.pushAttr(symbol, ps.at(in));
}
};
@@ -956,6 +960,10 @@ template<> struct BuildAST<grammar::v1::expr::select::attr_or> {
template<> struct BuildAST<grammar::v1::expr::select::as_app_or> {
static void apply(const auto & in, SelectState & s, State & ps) {
if (!ps.featureSettings.isEnabled(Dep::OrAsIdentifier)) {
ps.orArgumentFound(ps.at(in));
}
std::vector<std::unique_ptr<Expr>> args(1);
args[0] = std::make_unique<ExprVar>(ps.at(in), ps.symbols.sym_or);
s->emplaceExpr<ExprCall>(s.pos, s->popExprOnly(), std::move(args));
+30
View File
@@ -42,6 +42,8 @@ struct State
void nulFound(const PosIdx pos);
void recSetMergeFound(const AttrPath & attrPath, const PosIdx pos);
void recSetDynamicAttrFound(const PosIdx pos);
void orIdentifierFound(const PosIdx pos);
void orArgumentFound(const PosIdx pos);
void addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_ptr<Expr> e, const PosIdx pos);
void mergeAttrs(AttrPath & attrPath, ExprSet * source, ExprSet * target);
void validateLambdaAttrs(AttrsPattern & pattern, PosIdx pos = noPos);
@@ -198,6 +200,34 @@ inline void State::recSetDynamicAttrFound(const PosIdx pos)
});
}
// Added 2026-01-30
inline void State::orIdentifierFound(const PosIdx pos)
{
logWarning({
.msg = HintFmt(
"using %s as an identifier is deprecated because it cannot be used in most places (try "
"%s). Use %s to disable this warning.",
"or",
"let or = 1; in or",
"--extra-deprecated-features or-as-identifier"
),
.pos = positions[pos],
});
}
// Added 2026-01-30
inline void State::orArgumentFound(const PosIdx pos)
{
logWarning({
.msg = HintFmt(
"using %s as an argument is deprecated because it is parsed with the wrong precedence "
"which may cause unexpected behavior. Use %s to disable this warning.",
"or",
"--extra-deprecated-features or-as-identifier"
),
.pos = positions[pos],
});
}
inline void State::addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_ptr<Expr> e, const PosIdx pos)
{
AttrPath::iterator i;
@@ -0,0 +1,15 @@
---
name: or-as-identifier
internalName: OrAsIdentifier
timeline:
- date: 2026-01-30
release: 2.95.0
cls: [4764]
message: Introduced as a warning.
---
Back when the `or` operator was introduced, instead of making it a proper keyword, the syntax was adapted in attempt of making it a context-sensitive keyword without disrupting existing code.
This attempt has backfired, because it causes glitches in the operator precedence when a variable is called `or`:
`let or = 1; in [ (x: x) or ]` evaluates to a list with one element, but `let nor = 1; in [ (x: x) nor ]` evaluates to a list with two elements.
These old backwards compatibility hacks are deprecated in favor of treating `or` as a full language keyword.
To fix this, rename affected attributes or put the attribute name in quotes (`"or"`).
+1
View File
@@ -181,6 +181,7 @@ deprecated_feature_definitions = files(
'deprecated-features/floating-without-zero.md',
'deprecated-features/nix-path-shadow.md',
'deprecated-features/nul-bytes.md',
'deprecated-features/or-as-identifier.md',
'deprecated-features/rec-set-dynamic-attrs.md',
'deprecated-features/rec-set-merges.md',
'deprecated-features/rec-set-overrides.md',
+1
View File
@@ -0,0 +1 @@
let or = 1; in 1 or
+1
View File
@@ -0,0 +1 @@
let or = 1; in [ (x: x) or ]
+8
View File
@@ -0,0 +1,8 @@
let
x = 2;
or = 2;
in
[
builtins.add 1 x
builtins.add 1 or
]
+8
View File
@@ -0,0 +1,8 @@
let
x = 2;
or = 2;
in
[
[ 1 x ]
[ 1 or ]
]
+1
View File
@@ -0,0 +1 @@
let or = 1; in { a = 2; }.a or (x: x) or
@@ -0,0 +1,15 @@
_type: ExprLet
attrs:
or:
_type: ExprLiteral
value: 1
valueType: Int
body:
_type: ExprCall
args:
- _type: ExprVar
value: or
fun:
_type: ExprLiteral
value: 1
valueType: Int
@@ -0,0 +1,19 @@
_type: ExprLet
attrs:
or:
_type: ExprLiteral
value: 1
valueType: Int
body:
_type: ExprList
elems:
- _type: ExprCall
args:
- _type: ExprVar
value: or
fun:
_type: ExprLambda
arg: x
body:
_type: ExprVar
value: x
@@ -0,0 +1,38 @@
_type: ExprLet
attrs:
or:
_type: ExprLiteral
value: 2
valueType: Int
x:
_type: ExprLiteral
value: 2
valueType: Int
body:
_type: ExprList
elems:
- _type: ExprSelect
attrs:
- add
e:
_type: ExprVar
value: builtins
- _type: ExprLiteral
value: 1
valueType: Int
- _type: ExprVar
value: x
- _type: ExprSelect
attrs:
- add
e:
_type: ExprVar
value: builtins
- _type: ExprCall
args:
- _type: ExprVar
value: or
fun:
_type: ExprLiteral
value: 1
valueType: Int
@@ -0,0 +1,30 @@
_type: ExprLet
attrs:
or:
_type: ExprLiteral
value: 2
valueType: Int
x:
_type: ExprLiteral
value: 2
valueType: Int
body:
_type: ExprList
elems:
- _type: ExprList
elems:
- _type: ExprLiteral
value: 1
valueType: Int
- _type: ExprVar
value: x
- _type: ExprList
elems:
- _type: ExprCall
args:
- _type: ExprVar
value: or
fun:
_type: ExprLiteral
value: 1
valueType: Int
@@ -0,0 +1,29 @@
_type: ExprLet
attrs:
or:
_type: ExprLiteral
value: 1
valueType: Int
body:
_type: ExprSelect
attrs:
- a
default:
_type: ExprCall
args:
- _type: ExprVar
value: or
fun:
_type: ExprLambda
arg: x
body:
_type: ExprVar
value: x
e:
_type: ExprSet
attrs:
a:
_type: ExprLiteral
value: 2
valueType: Int
recursive: false
@@ -0,0 +1,9 @@
[[test]]
runner = "parse-okay"
flags = ["--extra-deprecated-features", "or-as-identifier"]
matrix = true
[[test]]
name = "warning"
runner = "parse-okay"
matrix = true
@@ -0,0 +1,10 @@
warning: using or as an identifier is deprecated because it cannot be used in most places (try let or = 1; in or). Use --extra-deprecated-features or-as-identifier to disable this warning.
at /pwd/in.nix:1:5:
1| let or = 1; in 1 or
| ^
2|
warning: using or as an argument is deprecated because it is parsed with the wrong precedence which may cause unexpected behavior. Use --extra-deprecated-features or-as-identifier to disable this warning.
at /pwd/in.nix:1:18:
1| let or = 1; in 1 or
| ^
2|
@@ -0,0 +1,15 @@
_type: ExprLet
attrs:
or:
_type: ExprLiteral
value: 1
valueType: Int
body:
_type: ExprCall
args:
- _type: ExprVar
value: or
fun:
_type: ExprLiteral
value: 1
valueType: Int
@@ -0,0 +1,10 @@
warning: using or as an identifier is deprecated because it cannot be used in most places (try let or = 1; in or). Use --extra-deprecated-features or-as-identifier to disable this warning.
at /pwd/in.nix:1:5:
1| let or = 1; in [ (x: x) or ]
| ^
2|
warning: using or as an argument is deprecated because it is parsed with the wrong precedence which may cause unexpected behavior. Use --extra-deprecated-features or-as-identifier to disable this warning.
at /pwd/in.nix:1:25:
1| let or = 1; in [ (x: x) or ]
| ^
2|
@@ -0,0 +1,19 @@
_type: ExprLet
attrs:
or:
_type: ExprLiteral
value: 1
valueType: Int
body:
_type: ExprList
elems:
- _type: ExprCall
args:
- _type: ExprVar
value: or
fun:
_type: ExprLambda
arg: x
body:
_type: ExprVar
value: x
@@ -0,0 +1,12 @@
warning: using or as an identifier is deprecated because it cannot be used in most places (try let or = 1; in or). Use --extra-deprecated-features or-as-identifier to disable this warning.
at /pwd/in.nix:3:5:
2| x = 2;
3| or = 2;
| ^
4| in
warning: using or as an argument is deprecated because it is parsed with the wrong precedence which may cause unexpected behavior. Use --extra-deprecated-features or-as-identifier to disable this warning.
at /pwd/in.nix:7:24:
6| builtins.add 1 x
7| builtins.add 1 or
| ^
8| ]
@@ -0,0 +1,38 @@
_type: ExprLet
attrs:
or:
_type: ExprLiteral
value: 2
valueType: Int
x:
_type: ExprLiteral
value: 2
valueType: Int
body:
_type: ExprList
elems:
- _type: ExprSelect
attrs:
- add
e:
_type: ExprVar
value: builtins
- _type: ExprLiteral
value: 1
valueType: Int
- _type: ExprVar
value: x
- _type: ExprSelect
attrs:
- add
e:
_type: ExprVar
value: builtins
- _type: ExprCall
args:
- _type: ExprVar
value: or
fun:
_type: ExprLiteral
value: 1
valueType: Int
@@ -0,0 +1,12 @@
warning: using or as an identifier is deprecated because it cannot be used in most places (try let or = 1; in or). Use --extra-deprecated-features or-as-identifier to disable this warning.
at /pwd/in.nix:3:5:
2| x = 2;
3| or = 2;
| ^
4| in
warning: using or as an argument is deprecated because it is parsed with the wrong precedence which may cause unexpected behavior. Use --extra-deprecated-features or-as-identifier to disable this warning.
at /pwd/in.nix:7:13:
6| [ 1 x ]
7| [ 1 or ]
| ^
8| ]
@@ -0,0 +1,30 @@
_type: ExprLet
attrs:
or:
_type: ExprLiteral
value: 2
valueType: Int
x:
_type: ExprLiteral
value: 2
valueType: Int
body:
_type: ExprList
elems:
- _type: ExprList
elems:
- _type: ExprLiteral
value: 1
valueType: Int
- _type: ExprVar
value: x
- _type: ExprList
elems:
- _type: ExprCall
args:
- _type: ExprVar
value: or
fun:
_type: ExprLiteral
value: 1
valueType: Int
@@ -0,0 +1,10 @@
warning: using or as an identifier is deprecated because it cannot be used in most places (try let or = 1; in or). Use --extra-deprecated-features or-as-identifier to disable this warning.
at /pwd/in.nix:1:5:
1| let or = 1; in { a = 2; }.a or (x: x) or
| ^
2|
warning: using or as an argument is deprecated because it is parsed with the wrong precedence which may cause unexpected behavior. Use --extra-deprecated-features or-as-identifier to disable this warning.
at /pwd/in.nix:1:39:
1| let or = 1; in { a = 2; }.a or (x: x) or
| ^
2|
@@ -0,0 +1,29 @@
_type: ExprLet
attrs:
or:
_type: ExprLiteral
value: 1
valueType: Int
body:
_type: ExprSelect
attrs:
- a
default:
_type: ExprCall
args:
- _type: ExprVar
value: or
fun:
_type: ExprLambda
arg: x
body:
_type: ExprVar
value: x
e:
_type: ExprSet
attrs:
a:
_type: ExprLiteral
value: 2
valueType: Int
recursive: false
@@ -0,0 +1,12 @@
warning: using or as an identifier is deprecated because it cannot be used in most places (try let or = 1; in or). Use --extra-deprecated-features or-as-identifier to disable this warning.
at /pwd/in.nix:9:3:
8|
9| or = x: y: x || y;
| ^
10|
warning: using or as an argument is deprecated because it is parsed with the wrong precedence which may cause unexpected behavior. Use --extra-deprecated-features or-as-identifier to disable this warning.
at /pwd/in.nix:23:11:
22| # it short-circuits and never runs into the type error
23| (fold or [] [true false false])
| ^
24| ]
+1
View File
@@ -0,0 +1 @@
with {}; or
+1
View File
@@ -0,0 +1 @@
let or = 1; in or
@@ -0,0 +1,5 @@
error: syntax error, expecting expression
at /pwd/in.nix:1:10:
1| with {}; or
| ^
2|
@@ -0,0 +1,10 @@
warning: using or as an identifier is deprecated because it cannot be used in most places (try let or = 1; in or). Use --extra-deprecated-features or-as-identifier to disable this warning.
at /pwd/in.nix:1:5:
1| let or = 1; in or
| ^
2|
error: syntax error, expecting expression
at /pwd/in.nix:1:16:
1| let or = 1; in or
| ^
2|
+1
View File
@@ -15,6 +15,7 @@ let
"broken-string-escape"
"rec-set-merges"
"rec-set-dynamic-attrs"
"or-as-identifier"
];
in
+1
View File
@@ -24,6 +24,7 @@ let
"broken-string-escape"
"rec-set-merges"
"rec-set-dynamic-attrs"
"or-as-identifier"
];
env.NIX_CONFIG = "extra-deprecated-features = ${concatStringsSep " " deprecatedFeatures}";