Skip to content

Improve error on module case mismatch #5665

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion lib/elixir/lib/exception.ex
Original file line number Diff line number Diff line change
Expand Up @@ -640,8 +640,26 @@ defmodule UndefinedFunctionError do
end

def message(%{reason: :"module could not be loaded", module: module, function: function, arity: arity}) do
suffix =
case :code.get_object_code(module) do
Copy link
Member

@josevalim josevalim Jan 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will probably be cleaner if you use the with construct. Also keep in mind we can pass the second argument from :code.get_object_code to :beam_lib. Here is one suggestion:

suffix =
  with {_, binary, _} <- :code.get_object_code(module),
       {:ok, {alt_module, _}} <- :beam_lib.chunks(binary, []) do
    exports = exports_for(alt_module)
    if ... do ... else ... end
  else
    _ -> ""
  end

{_, _, path} ->
case :beam_lib.info(path) do
info when is_list(info) ->
alt_module = Keyword.get(info, :module)
exports = exports_for(alt_module)
if {function, arity} in exports do
". Did you mean:\n\n * " <>
Exception.format_mfa(alt_module, function, arity) <> "\n"
else
". Did you mean #{inspect alt_module}?"
end
_ -> ""
end
_ -> ""
end
"function " <> Exception.format_mfa(module, function, arity) <>
" is undefined (module #{inspect module} is not available)"
" is undefined (module #{inspect module} is not available)" <>
suffix
end

def message(%{reason: :"function not exported", module: module, function: function, arity: arity, exports: exports}) do
Expand Down
7 changes: 7 additions & 0 deletions lib/elixir/test/elixir/exception_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,13 @@ defmodule ExceptionTest do
* get_cookie/0
* set_cookie/2
"""
assert %UndefinedFunctionError{module: Genserver, function: :start_link, arity: 3} |> message == """
function Genserver.start_link/3 is undefined (module Genserver is not available). Did you mean:

* GenServer.start_link/3
"""
assert %UndefinedFunctionError{module: Genserver, function: :start_link, arity: 1} |> message ==
"function Genserver.start_link/1 is undefined (module Genserver is not available). Did you mean GenServer?"
end

test "UndefinedFunctionError when the mfa is a macro but require wasn't called" do
Expand Down
8 changes: 6 additions & 2 deletions lib/mix/lib/mix/tasks/xref.ex
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,12 @@ defmodule Mix.Tasks.Xref do
end

defp format_warning(file, {lines, :unknown_module, module, function, arity, _}) do
["function ", Exception.format_mfa(module, function, arity),
" is undefined (module #{inspect module} is not available)\n" | format_file_lines(file, lines)]
message =
[module: module, function: function, arity: arity, reason: :"module could not be loaded"]
|> UndefinedFunctionError.exception()
|> Exception.message()

[message, "\n" | format_file_lines(file, lines)]
end

defp format_file_lines(file, [line]) do
Expand Down
15 changes: 15 additions & 0 deletions lib/mix/test/mix/tasks/xref_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ defmodule Mix.Tasks.XrefTest do
"""
end

test "warnings: reports module case mismatch" do
assert_warnings """
defmodule A do
def a, do: Genserver.start_link(A, [], [])
end
""", """
warning: function Genserver.start_link/3 is undefined (module Genserver is not available). Did you mean:

* GenServer.start_link/3

lib/a.ex:2

"""
end

test "warnings: reports missing captures" do
assert_warnings """
defmodule A do
Expand Down