Skip to content

Commit 0182f7d

Browse files
committed
Raise for undefined function within unused function
1 parent 26318eb commit 0182f7d

File tree

3 files changed

+32
-28
lines changed

3 files changed

+32
-28
lines changed

lib/elixir/lib/module/types.ex

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -63,37 +63,30 @@ defmodule Module.Types do
6363

6464
stack = stack(:infer, file, module, {:__info__, 1}, :all, env, handler)
6565

66-
{types, %{local_sigs: local_sigs}} =
67-
for {fun_arity, kind, meta, clauses} = def <- defs, reduce: {[], context()} do
66+
{types, %{local_sigs: local_sigs} = context} =
67+
for {fun_arity, kind, meta, _clauses} = def <- defs,
68+
kind in [:def, :defmacro] or (kind == :defmacrop and is_map_key(defmacrop, fun_arity)),
69+
reduce: {[], context()} do
6870
{types, context} ->
69-
cond do
70-
kind in [:def, :defmacro] ->
71-
finder = fn _ -> {infer_mode(kind, infer_signatures?), def} end
72-
{_kind, inferred, context} = local_handler(meta, fun_arity, stack, context, finder)
73-
74-
if infer_signatures? and kind == :def and fun_arity not in @no_infer do
75-
{[{fun_arity, inferred} | types], context}
76-
else
77-
{types, context}
78-
end
79-
80-
kind == :defmacrop and is_map_key(defmacrop, fun_arity) ->
81-
# Bypass the caching structure for defmacrop, that's because
82-
# we don't need them stored in the signatures when we perform
83-
# unreachable checks. This may cause defmacrop to be traversed
84-
# twice if it uses default arguments (which is the only way
85-
# to refer to another defmacrop in definitions) but that should
86-
# be cheap anyway.
87-
{_kind, _inferred, context} =
88-
local_handler(fun_arity, kind, meta, clauses, :traversal, stack, context)
89-
90-
{types, context}
91-
92-
true ->
93-
{types, context}
71+
finder = fn _ -> {infer_mode(kind, infer_signatures?), def} end
72+
{_kind, inferred, context} = local_handler(meta, fun_arity, stack, context, finder)
73+
74+
if infer_signatures? and kind == :def and fun_arity not in @no_infer do
75+
{[{fun_arity, inferred} | types], context}
76+
else
77+
{types, context}
9478
end
9579
end
9680

81+
for {fun_arity, kind, meta, _clauses} = def <- defs,
82+
kind in [:defp, :defmacrop],
83+
reduce: context do
84+
context ->
85+
finder = fn _ -> {:traversal, def} end
86+
{_kind, _inferred, context} = local_handler(meta, fun_arity, stack, context, finder)
87+
context
88+
end
89+
9790
unreachable =
9891
for {fun_arity, _kind, _meta, _defaults} = info <- private,
9992
warn_unused_def(info, local_sigs, defmacrop, env),

lib/elixir/src/elixir_expand.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,7 @@ assert_no_ambiguous_op(Name, Meta, [Arg], S, E) ->
887887
assert_no_ambiguous_op(_Atom, _Meta, _Args, _S, _E) ->
888888
ok.
889889

890-
expand_local(Meta, Name, Args, S, #{module := Module, function := Function, context := Context} = E)
890+
expand_local(Meta, Name, Args, S, #{function := Function, context := Context} = E)
891891
when Function /= nil ->
892892
%% In case we have the wrong context, we log a module error
893893
%% so we can print multiple entries at the same time.

lib/elixir/test/elixir/kernel/errors_test.exs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,17 @@ defmodule Kernel.ErrorsTest do
123123
)
124124
end
125125

126+
test "undefined function within unused function" do
127+
assert_compile_error(
128+
["nofile:2:8", "undefined function bar/0"],
129+
~c"""
130+
defmodule Sample do
131+
defp foo, do: bar()
132+
end
133+
"""
134+
)
135+
end
136+
126137
test "undefined non-local function" do
127138
assert_compile_error(
128139
["nofile:1:1", "undefined function call/2 (there is no such import)"],

0 commit comments

Comments
 (0)