Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 20 additions & 11 deletions src/libexpr/eval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1450,7 +1450,10 @@ static std::string showAttrSelectionPath(EvalState & state, Env & env, std::span
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
{
Value vTmp;
PosIdx pos2;
// current or last *definition site* (the attr, not the select)
PosIdx attrPos;
const AttrName * unresolvedOrEnd = attrPathStart;
// cursor, result if successful
Value * vAttrs = &vTmp;

e->eval(state, env, vTmp);
Expand Down Expand Up @@ -1483,28 +1486,34 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
allAttrNames.insert(std::string(state.symbols[attr.name]));
auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]);
state.error<EvalError>("attribute '%1%' missing", state.symbols[name])
.atPos(pos)
.withSuggestions(suggestions)
.withFrame(env, *this)
.debugThrow();
}
}
vAttrs = j->value;
pos2 = j->pos;
attrPos = j->pos;
unresolvedOrEnd = &i + 1;
if (state.countCalls)
state.attrSelects->try_emplace_or_visit(pos2, 1, [](auto & i) { i.second++; });
state.attrSelects->try_emplace_or_visit(attrPos, 1, [](auto & i) { i.second++; });
}

state.forceValue(*vAttrs, (pos2 ? pos2 : this->pos));
state.forceValue(*vAttrs, (attrPos ? attrPos : this->pos));

} catch (Error & e) {
if (pos2) {
auto pos2r = state.positions[pos2];
auto origin = std::get_if<SourcePath>(&pos2r.origin);
if (!(origin && *origin == state.derivationInternal))
state.addErrorTrace(
e, pos2, "while evaluating the attribute '%1%'", showAttrSelectionPath(state, env, getAttrPath()));
// Traces are printed in reverse, so we add context before the main item.
if (attrPos) {
auto attrPosR = state.positions[attrPos];
auto origin = std::get_if<SourcePath>(&attrPosR.origin);
if (!(origin && *origin == state.derivationInternal)) {
auto successPath =
showAttrSelectionPath(state, env, std::span<const AttrName>(attrPathStart, unresolvedOrEnd));
state.addErrorTrace(e, attrPos, "from the definition of '%1%'", successPath);
}
}
// Add main item: the selection site itself (`a.b`), ie the actual access
state.addErrorTrace(
e, getPos(), "while evaluating the attribute '%1%'", showAttrSelectionPath(state, env, getAttrPath()));
throw;
}

Expand Down
3 changes: 3 additions & 0 deletions src/libexpr/include/nix/expr/nixexpr.hh
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,9 @@ struct ExprSelect : Expr
return pos;
}

/**
* @return `std::span<const AttrName>` starting at `attrPathStart`
*/
std::span<const AttrName> getAttrPath() const
{
return {attrPathStart, nAttrPath};
Expand Down
2 changes: 1 addition & 1 deletion src/libexpr/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ expr_simple
/* Let expressions `let {..., body = ...}' are just desugared
into `(rec {..., body = ...}).body'. */
| LET '{' binds '}'
{ $3->recursive = true; $3->pos = CUR_POS; $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, noPos, $3, state->s.body); }
{ $3->recursive = true; $3->pos = CUR_POS; $$ = state->exprs.add<ExprSelect>(state->exprs.alloc, CUR_POS, $3, state->s.body); }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would gladly leave let/body alone, but this improvement keeps
the rest of the code tidy.

| REC '{' binds '}'
{ $3->recursive = true; $3->pos = CUR_POS; $$ = $3; }
| '{' binds1 '}'
Expand Down
6 changes: 6 additions & 0 deletions tests/functional/lang/eval-fail-assert.err.exp
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
error:
… while evaluating the attribute 'body'
at /pwd/lang/eval-fail-assert.nix:1:1:
1| let {
| ^
2| x =

… from the definition of 'body'
at /pwd/lang/eval-fail-assert.nix:7:3:
6|
7| body = x "x";
Expand Down
7 changes: 7 additions & 0 deletions tests/functional/lang/eval-fail-attr-name-type.err.exp
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
error:
… while evaluating the attribute 'puppy."${key}"'
at /pwd/lang/eval-fail-attr-name-type.nix:7:1:
6| in
7| attrs.puppy.${key}
| ^
8|

… from the definition of 'puppy'
at /pwd/lang/eval-fail-attr-name-type.nix:3:5:
2| attrs = {
3| puppy.doggy = { };
Expand Down
6 changes: 6 additions & 0 deletions tests/functional/lang/eval-fail-blackhole.err.exp
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
error:
… while evaluating the attribute 'body'
at /pwd/lang/eval-fail-blackhole.nix:1:1:
1| let {
| ^
2| body = x;

… from the definition of 'body'
at /pwd/lang/eval-fail-blackhole.nix:2:3:
1| let {
2| body = x;
Expand Down
12 changes: 10 additions & 2 deletions tests/functional/lang/eval-fail-remove.err.exp
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
error:
… while evaluating the attribute 'body'
at /pwd/lang/eval-fail-remove.nix:1:1:
1| let {
| ^
2| attrs = {

… from the definition of 'body'
at /pwd/lang/eval-fail-remove.nix:7:3:
6|
7| body = (removeAttrs attrs [ "x" ]).x;
| ^
8| }

error: attribute 'x' missing
at /pwd/lang/eval-fail-remove.nix:7:10:
… while evaluating the attribute 'x'
at /pwd/lang/eval-fail-remove.nix:7:10:
6|
7| body = (removeAttrs attrs [ "x" ]).x;
| ^
8| }

error: attribute 'x' missing
Did you mean y?
6 changes: 6 additions & 0 deletions tests/functional/lang/eval-fail-scope-5.err.exp
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
error:
… while evaluating the attribute 'body'
at /pwd/lang/eval-fail-scope-5.nix:1:1:
1| let {
| ^
2|

… from the definition of 'body'
at /pwd/lang/eval-fail-scope-5.nix:13:3:
12|
13| body = f { };
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
error:
… while evaluating the attribute '"${key}"'
at /pwd/lang/eval-fail-using-set-as-attr-name.nix:7:1:
6| in
7| attr.${key}
| ^
8|

… while evaluating an attribute name
at /pwd/lang/eval-fail-using-set-as-attr-name.nix:7:8:
6| in
Expand Down
30 changes: 20 additions & 10 deletions tests/functional/repl/doc-constant.expected
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,18 @@ Unchangeably constant.
nix-repl> :doc lib.attr.undocument
error:
… while evaluating the attribute 'attr.undocument'
at «string»:1:1:
1| lib.attr.undocument
| ^

… from the definition of 'attr'
at /path/to/tests/functional/repl/doc-comments.nix:52:3:
51| */
52| lib.attr.empty = { };
| ^
53|

error: attribute 'undocument' missing
at «string»:1:1:
1| lib.attr.undocument
| ^
Did you mean undocumented?

nix-repl> :doc (import ./doc-comments.nix).constant
Expand Down Expand Up @@ -89,28 +91,36 @@ error: undefined variable 'missing'
| ^

nix-repl> :doc lib.missing
error: attribute 'missing' missing
at «string»:1:1:
error:
… while evaluating the attribute 'missing'
at «string»:1:1:
1| lib.missing
| ^

error: attribute 'missing' missing

nix-repl> :doc lib.missing.attr
error: attribute 'missing' missing
at «string»:1:1:
error:
… while evaluating the attribute 'missing'
at «string»:1:1:
1| lib.missing.attr
| ^

error: attribute 'missing' missing

nix-repl> :doc lib.attr.undocumental
error:
… while evaluating the attribute 'attr.undocumental'
at «string»:1:1:
1| lib.attr.undocumental
| ^

… from the definition of 'attr'
at /path/to/tests/functional/repl/doc-comments.nix:52:3:
51| */
52| lib.attr.empty = { };
| ^
53|

error: attribute 'undocumental' missing
at «string»:1:1:
1| lib.attr.undocumental
| ^
Did you mean undocumented?
7 changes: 5 additions & 2 deletions tests/functional/repl/inherit-missing-shows-pos.expected
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ nix-repl> a = { x = 1; }
nix-repl> inherit (a) y

nix-repl> y
error: attribute 'y' missing
at «string»:1:13:
error:
… while evaluating the attribute 'y'
at «string»:1:13:
1| inherit (a) y
| ^

error: attribute 'y' missing
Did you mean x?
Comment thread
xokdvium marked this conversation as resolved.
18 changes: 18 additions & 0 deletions tests/functional/repl/missing-attr-shows-def.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Nix <nix version>
Type :? for help.

nix-repl> s = { a = { }; }

nix-repl> s.a.b
error:
… while evaluating the attribute 'a.b'
at «string»:1:1:
1| s.a.b
| ^

… from the definition of 'a'
at «string»:1:7:
1| s = { a = { }; }
| ^

error: attribute 'b' missing
3 changes: 3 additions & 0 deletions tests/functional/repl/missing-attr-shows-def.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# COM: a missing nested attribute reports where the containing set is defined
s = { a = { }; }
s.a.b
Loading