-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
compiler: Improve error messages for using a function with wrong arity #9094
compiler: Improve error messages for using a function with wrong arity #9094
Conversation
CT Test Results 2 files 96 suites 1h 8m 53s ⏱️ For more details on these failures, see this check. Results for commit ab85bb7. ♻️ This comment has been updated with latest results. To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass. See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally. Artifacts// Erlang/OTP Github Action Bot |
lib/stdlib/src/erl_lint.erl
Outdated
end, St, Fs). | ||
func_location_error(Type, [{F,Anno}|Fs], St0, FAList) -> | ||
{Name, Arity} = F, | ||
PossibleAs = [A || {FName, A} <- FAList, FName =:= Name], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick:
PossibleAs = [A || {FName, A} <- FAList, FName =:= Name], | |
PossibleAs = [A || {FName, A} <:- FAList, FName =:= Name], |
Perhaps we should also try to pick the closest arity rather than the first one that pops up, e.g. PossibleAs = sort(fun(A, B) -> abs(A - Arity) =< abs(B - Arity) end, PossibleAs0)
, or by making a most_possible_function
variant of most_possible_string
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why just suggest one alternative, if there are multiple functions with the same name, isn't it equally likely that that the the intended function was arity+1 as arity-1? For example, both g++ and clang++, give you a list of candidates when there are multiple "sane" choices.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"did you mean foo/3,4,5?
" sounds good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
If we start guessing and proposing things, we might do as the C-compilers and have the compiler produce errors, warnings and notes. The undefined function would be the error, and each suggested function would be a note. That way you can jump to the proposed function by just a click/keystroke in your editor/IDE. But this is outside the scope of this MR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good to me. I have a few suggestions for improvements.
lib/stdlib/src/erl_lint.erl
Outdated
PossibleAs = [A || {F, A} <- DefFunctions, F =:= Name], | ||
case PossibleAs of | ||
[] -> | ||
case most_possible_string(Name, PossibleFs) of | ||
[] -> | ||
add_error(Anno, {undefined_function,FA}, St); | ||
GuessF -> | ||
add_error(Anno, {undefined_function,FA,GuessF,Arity}, St) | ||
end; | ||
[GuessA|_] -> | ||
add_error(Anno, {undefined_function,FA,Name,GuessA}, St) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The following change will eliminate duplicated code and overlong lines:
PossibleAs = [A || {F, A} <- DefFunctions, F =:= Name], | |
case PossibleAs of | |
[] -> | |
case most_possible_string(Name, PossibleFs) of | |
[] -> | |
add_error(Anno, {undefined_function,FA}, St); | |
GuessF -> | |
add_error(Anno, {undefined_function,FA,GuessF,Arity}, St) | |
end; | |
[GuessA|_] -> | |
add_error(Anno, {undefined_function,FA,Name,GuessA}, St) | |
func_location_error(undefined_function, | |
[{FA,Anno}], | |
St, | |
DefFunctions) |
lib/stdlib/src/erl_lint.erl
Outdated
[] -> | ||
add_error(Anno, {Type,F}, St0); | ||
GuessF -> | ||
add_error(Anno, {Type,F,GuessF,Arity}, St0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For symmetry, I suggest the following change:
add_error(Anno, {Type,F,GuessF,Arity}, St0) | |
add_error(Anno, {Type,F,{GuessF,Arity}}, St0) |
You must also do a similar change in line 1699, in format_error/1
, and in the test suite.
lib/stdlib/test/erl_lint_SUITE.erl
Outdated
{error,[{{1,22},erl_lint,{bad_inline,{ba,1},ba,2}}, | ||
{{1,22},erl_lint,{bad_inline,{foo,1},"foa",1}}], | ||
[{{2,15},erl_lint,{unused_function,{foa,1}}}, | ||
{{3,15},erl_lint,{unused_function,{ba,2}}}]}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There does not seem to be any tests of a bad dialyzer option with a suggestion.
ee1dd42
to
9135d37
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new messages look good. However, there is an inconsistency in the internal representation of the message:
[{{1,22},erl_lint,{undefined_function,{bar,1},{"baz",1}}},
{{1,22},erl_lint,{undefined_function,{foo,2},{foo,[1,3]}}}]
That is, function names are lists if they originated from a misspelling, but atoms if they originated from wrong arity.
I think they should always be represented as atoms. That is also consistent with how functions are represented in other places.
The easiest way to achieve this is to change the end of most_possible_string/2
like so:
case MaxSim > SufficientlySimilar of
true -> list_to_atom(GuessName);
false -> []
end
Also, I think that for consistency the arity should always be in a list:
[{{1,22},erl_lint,{undefined_function,{bar,1},{baz,[1]}}},
{{1,22},erl_lint,{undefined_function,{foo,2},{foo,[1,3]}}}]
lib/stdlib/src/erl_lint.erl
Outdated
format_fa({F, A}) when is_integer(A) -> | ||
atom_to_list(F) ++ "/" ++ integer_to_list(A). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This clause is not covered, and I don't think it is possible to cover it.
9135d37
to
4b67a69
Compare
When a function is used with wrong arity, the compiler will try to suggest all defined functions with the same name but different arities. For example, given the following module: -module(typos). -export([t/0]). bar(A) -> A. bar(A,A,A) -> A. bar(A,A,A,A) -> A. t() -> bar(0, 0). The compiler will emit the following message: typo.erl:6:12: function bar/2 undefined, did you mean bar/1,3,4? % 6| t() -> bar(0, 0). % | ^ Error types that are extended by this change: `bad_inline`, `undefined_nif`, `bad_nowarn_unused_function`, `bad_nowarn_bif_clash`, `undefined_function`. Using a function with wrong arity has higher precedence than having a typo in the function name. If the compiler can find a defined function with the same name but a different arity, it will not suggest a defined function with a close-enough name, regardless of arity.
4b67a69
to
ab85bb7
Compare
When a function is used with wrong arity, the compiler will try to suggest a defined function with the same name but a different arity. For example, given the following module:
The compiler will emit the following message:
Error types that are extended by this change:
bad_inline
,undefined_nif
,bad_nowarn_unused_function
,bad_nowarn_bif_clash
,undefined_function
.Using a function with wrong arity has higher precedence than having a typo in the function name. If the compiler can find a defined function with the same name but a different arity, it will not suggest a defined function with a close-enough name, regardless of arity.