Add some suggestions to the evaluator

Make the evaluator show some suggestions when trying to access an
invalid field from an attrset.

```console
$ nix eval --expr '{ foo = 1; }.foa'
error: attribute 'foa' missing

       at «string»:1:1:

            1| { foo = 1; }.foa
             | ^
       Did you mean foo?
```
This commit is contained in:
regnat 2022-03-08 06:16:51 +01:00
parent 92b8d4d886
commit 0c6e46e349
2 changed files with 22 additions and 2 deletions

View file

@ -727,6 +727,15 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2
throw EvalError(s, s2); throw EvalError(s, s2);
} }
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & suggestions, const char * s, const std::string & s2))
{
throw EvalError({
.msg = hintfmt(s, s2),
.errPos = pos,
.suggestions = suggestions,
});
}
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2)) LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2))
{ {
throw EvalError({ throw EvalError({
@ -1281,8 +1290,15 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
} }
} else { } else {
state.forceAttrs(*vAttrs, pos); state.forceAttrs(*vAttrs, pos);
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
throwEvalError(pos, "attribute '%1%' missing", name); std::set<std::string> allAttrNames;
for (auto & attr : *vAttrs->attrs)
allAttrNames.insert(attr.name);
throwEvalError(
pos,
Suggestions::bestMatches(allAttrNames, name),
"attribute '%1%' missing", name);
}
} }
vAttrs = j->value; vAttrs = j->value;
pos2 = j->pos; pos2 = j->pos;

View file

@ -34,3 +34,7 @@ NIX_BUILD_STDERR_WITH_SUGGESTIONS=$(! nix build .\#fob 2>&1 1>/dev/null)
NIX_BUILD_STDERR_WITH_NO_CLOSE_SUGGESTION=$(! nix build .\#bar 2>&1 1>/dev/null) NIX_BUILD_STDERR_WITH_NO_CLOSE_SUGGESTION=$(! nix build .\#bar 2>&1 1>/dev/null)
[[ ! "$NIX_BUILD_STDERR_WITH_NO_CLOSE_SUGGESTION" =~ "Did you mean" ]] || \ [[ ! "$NIX_BUILD_STDERR_WITH_NO_CLOSE_SUGGESTION" =~ "Did you mean" ]] || \
fail "The nix build stderr shouldnt suggest anything if theres nothing relevant to suggest" fail "The nix build stderr shouldnt suggest anything if theres nothing relevant to suggest"
NIX_EVAL_STDERR_WITH_SUGGESTIONS=$(! nix build --impure --expr '(builtins.getFlake (builtins.toPath ./.)).packages.'$system'.fob' 2>&1 1>/dev/null)
[[ "$NIX_EVAL_STDERR_WITH_SUGGESTIONS" =~ "Did you mean one of fo1, fo2, foo or fooo?" ]] || \
fail "The evaluator should suggest the three closest possiblities"