Skip to content

Commit 458fcaf

Browse files
authored
Improve UndefinedFunctionError for mis-cased module (#12839)
1 parent 37bcb19 commit 458fcaf

File tree

2 files changed

+38
-2
lines changed

2 files changed

+38
-2
lines changed

lib/elixir/lib/exception.ex

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,8 +1342,33 @@ defmodule UndefinedFunctionError do
13421342
hint_for_loaded_module(module, function, arity, nil)
13431343
end
13441344

1345-
defp hint(_module, _function, _arity, _loaded?) do
1346-
""
1345+
defp hint(module, function, arity, _loaded?) do
1346+
downcased_module = downcase_module_name(module)
1347+
1348+
candidate =
1349+
Enum.find(:code.all_available(), fn {name, _, _} ->
1350+
downcase_module_name(name) == downcased_module
1351+
end)
1352+
1353+
with {_, _, _} <- candidate,
1354+
{:module, module} <- load_module(candidate),
1355+
true <- function_exported?(module, function, arity) do
1356+
". Did you mean:\n\n * #{Exception.format_mfa(module, function, arity)}\n"
1357+
else
1358+
_ -> ""
1359+
end
1360+
end
1361+
1362+
defp load_module({name, _path, _loaded?}) do
1363+
name
1364+
|> List.to_atom()
1365+
|> Code.ensure_loaded()
1366+
end
1367+
1368+
defp downcase_module_name(module) do
1369+
module
1370+
|> to_string()
1371+
|> String.downcase(:ascii)
13471372
end
13481373

13491374
@doc false

lib/elixir/test/elixir/exception_test.exs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,17 @@ defmodule ExceptionTest do
595595
assert message =~ "* set_cookie/2"
596596
end
597597

598+
test "annotates undefined function error with module suggestions" do
599+
assert blame_message(ENUM, & &1.map(&1, 1)) == """
600+
function ENUM.map/2 is undefined (module ENUM is not available). Did you mean:
601+
602+
* Enum.map/2
603+
"""
604+
605+
assert blame_message(ENUM, & &1.not_a_function(&1, 1)) ==
606+
"function ENUM.not_a_function/2 is undefined (module ENUM is not available)"
607+
end
608+
598609
test "annotates undefined function clause error with macro hints" do
599610
assert blame_message(Integer, & &1.is_odd(1)) ==
600611
"function Integer.is_odd/1 is undefined or private. However, there is " <>

0 commit comments

Comments
 (0)