From f6078e474d5fc41c8a7f683865d60490bf0c7040 Mon Sep 17 00:00:00 2001 From: regnat Date: Tue, 8 Mar 2022 16:20:01 +0100 Subject: [PATCH] Also display some suggestions for invalid formal arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ```console $ nix eval --expr '({ foo ? 1 }: foo) { fob = 2; }' error: anonymous function at (string):1:2 called with unexpected argument 'fob' at «string»:1:1: 1| ({ foo ? 1 }: foo) { fob = 2; } | ^ Did you mean foo? ``` Not that because Nix will first check for _missing_ arguments before checking for extra arguments, `({ foo }: foo) { fob = 1; }` will complain about the missing `foo` argument (rather than extra `fob`) and so won’t display a suggestion. --- src/libexpr/eval.cc | 23 +++++++++++++++++++++-- tests/suggestions.sh | 4 ++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 3bfb82b16..a5e9dc286 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -782,6 +782,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)); @@ -1414,8 +1424,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 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 29d5b364b..f18fefef9 100644 --- a/tests/suggestions.sh +++ b/tests/suggestions.sh @@ -38,3 +38,7 @@ NIX_BUILD_STDERR_WITH_NO_CLOSE_SUGGESTION=$(! nix build .\#bar 2>&1 1>/dev/null) 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"