diff options
author | regnat <rg@regnat.ovh> | 2022-03-08 06:16:51 +0100 |
---|---|---|
committer | regnat <rg@regnat.ovh> | 2022-03-08 06:21:45 +0100 |
commit | 0c6e46e34965ba0db4e0b755ee473868a4fef21f (patch) | |
tree | 33e8a2ee4beb267a84819af46c1966427e351093 /src | |
parent | 92b8d4d8861b908a7ec500526a84155c597d6d2b (diff) |
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?
```
Diffstat (limited to 'src')
-rw-r--r-- | src/libexpr/eval.cc | 20 |
1 files changed, 18 insertions, 2 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 5bf161cc0..1d88e8709 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -727,6 +727,15 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & 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)) { throw EvalError({ @@ -1281,8 +1290,15 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) } } else { state.forceAttrs(*vAttrs, pos); - if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) - throwEvalError(pos, "attribute '%1%' missing", name); + if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) { + 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; pos2 = j->pos; |