aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2022-03-11 12:02:26 +0100
committerEelco Dolstra <edolstra@gmail.com>2022-03-11 12:02:26 +0100
commitaee56e0f895e7b9890d1ec948b24c75478f9e88e (patch)
treeb3d1f693cf8d068978c2f0f62abd4d4551d779b2
parent31a392dfe2c89a3cb5880eea9e4070ece4e14acb (diff)
parentf6078e474d5fc41c8a7f683865d60490bf0c7040 (diff)
Merge remote-tracking branch 'origin/eval-suggestions'
-rw-r--r--src/libexpr/eval.cc45
-rw-r--r--tests/suggestions.sh8
2 files changed, 48 insertions, 5 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 0207603c3..c65ef9738 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -729,9 +729,18 @@ 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(ErrorInfo {
+ .msg = hintfmt(s, s2),
+ .errPos = pos,
+ .suggestions = suggestions,
+ });
+}
+
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2))
{
- throw EvalError({
+ throw EvalError(ErrorInfo {
.msg = hintfmt(s, s2),
.errPos = pos
});
@@ -775,6 +784,16 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
});
}
+LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol & s2))
+{
+ throw TypeError(ErrorInfo {
+ .msg = hintfmt(s, fun.showNamePos(), s2),
+ .errPos = pos,
+ .suggestions = suggestions,
+ });
+}
+
+
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
{
throw TypeError(s, showType(v));
@@ -1247,8 +1266,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;
@@ -1364,8 +1390,17 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
/* Nope, so show the first unexpected argument to the
user. */
for (auto & i : *args[0]->attrs)
- if (!lambda.formals->has(i.name))
- throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name);
+ if (!lambda.formals->has(i.name)) {
+ std::set<std::string> formalNames;
+ for (auto & formal : lambda.formals->formals)
+ formalNames.insert(formal.name);
+ throwTypeError(
+ pos,
+ Suggestions::bestMatches(formalNames, i.name),
+ "%1% called with unexpected argument '%2%'",
+ lambda,
+ i.name);
+ }
abort(); // can't happen
}
}
diff --git a/tests/suggestions.sh b/tests/suggestions.sh
index 16a5a7004..f18fefef9 100644
--- a/tests/suggestions.sh
+++ b/tests/suggestions.sh
@@ -34,3 +34,11 @@ 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" =~ "Did you mean" ]] || \
fail "The nix build stderr shouldn’t suggest anything if there’s 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"
+
+NIX_EVAL_STDERR_WITH_SUGGESTIONS=$(! nix build --impure --expr '({ foo }: foo) { foo = 1; fob = 2; }' 2>&1 1>/dev/null)
+[[ "$NIX_EVAL_STDERR_WITH_SUGGESTIONS" =~ "Did you mean foo?" ]] || \
+ fail "The evaluator should suggest the three closest possiblities"