Skip to content

Commit bfebc1e

Browse files
committed
Fix Dialyzer failures
1 parent 60bd0ae commit bfebc1e

File tree

3 files changed

+50
-41
lines changed

3 files changed

+50
-41
lines changed

lib/elixir/lib/module/types.ex

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,8 @@ defmodule Module.Types do
3333
@no_infer [__protocol__: 1, behaviour_info: 1]
3434

3535
@doc false
36-
def infer(module, file, defs, private, defmacrop, env) do
36+
def infer(module, file, defs, private, used_private, env) do
3737
infer_signatures? = :elixir_config.get(:infer_signatures)
38-
defmacrop = Map.from_keys(defmacrop, [])
3938

4039
finder =
4140
fn fun_arity ->
@@ -63,9 +62,9 @@ defmodule Module.Types do
6362

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

66-
{types, %{local_sigs: local_sigs} = context} =
65+
{types, %{local_sigs: reachable_sigs} = context} =
6766
for {fun_arity, kind, meta, _clauses} = def <- defs,
68-
kind in [:def, :defmacro] or (kind == :defmacrop and is_map_key(defmacrop, fun_arity)),
67+
kind in [:def, :defmacro],
6968
reduce: {[], context()} do
7069
{types, context} ->
7170
finder = fn _ -> {infer_mode(kind, infer_signatures?), def} end
@@ -78,20 +77,32 @@ defmodule Module.Types do
7877
end
7978
end
8079

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
80+
# Now traverse all used privates to find any other private that have been used by them.
81+
context =
82+
%{local_sigs: used_sigs} =
83+
for fun_arity <- used_private, reduce: context do
84+
context ->
85+
{_kind, _inferred, context} = local_handler([], fun_arity, stack, context, finder)
86+
context
87+
end
8988

90-
unreachable =
91-
for {fun_arity, _kind, _meta, _defaults} = info <- private,
92-
warn_unused_def(info, local_sigs, defmacrop, env),
93-
not is_map_key(local_sigs, fun_arity),
94-
do: fun_arity
89+
{unreachable, _context} =
90+
Enum.reduce(private, {[], context}, fn
91+
{fun_arity, kind, _meta, _defaults} = info, {unreachable, context} ->
92+
warn_unused_def(info, used_sigs, env)
93+
94+
# Find anything undefined within unused functions
95+
{_kind, _inferred, context} = local_handler([], fun_arity, stack, context, finder)
96+
97+
# defp is reachable if used, defmacrop only if directly invoked
98+
private_sigs = if kind == :defp, do: used_sigs, else: reachable_sigs
99+
100+
if is_map_key(private_sigs, fun_arity) do
101+
{unreachable, context}
102+
else
103+
{[fun_arity | unreachable], context}
104+
end
105+
end)
95106

96107
{Map.new(types), unreachable}
97108
end
@@ -106,25 +117,25 @@ defmodule Module.Types do
106117
:elixir_errors.module_error(Helpers.with_span(meta, fun), env, __MODULE__, tuple)
107118
end
108119

109-
defp warn_unused_def({_fun_arity, _kind, false, _}, _reachable, _used, _env) do
120+
defp warn_unused_def({_fun_arity, _kind, false, _}, _used, _env) do
110121
:ok
111122
end
112123

113-
defp warn_unused_def({fun_arity, kind, meta, 0}, reachable, used, env) do
114-
case is_map_key(reachable, fun_arity) or is_map_key(used, fun_arity) do
124+
defp warn_unused_def({fun_arity, kind, meta, 0}, used, env) do
125+
case is_map_key(used, fun_arity) do
115126
true -> :ok
116127
false -> :elixir_errors.file_warn(meta, env, __MODULE__, {:unused_def, fun_arity, kind})
117128
end
118129

119130
:ok
120131
end
121132

122-
defp warn_unused_def({tuple, kind, meta, default}, reachable, used, env) when default > 0 do
133+
defp warn_unused_def({tuple, kind, meta, default}, used, env) when default > 0 do
123134
{name, arity} = tuple
124135
min = arity - default
125136
max = arity
126137

127-
case min_reachable_default(max, min, :none, name, reachable, used) do
138+
case min_reachable_default(max, min, :none, name, used) do
128139
:none -> :elixir_errors.file_warn(meta, env, __MODULE__, {:unused_def, tuple, kind})
129140
^min -> :ok
130141
^max -> :elixir_errors.file_warn(meta, env, __MODULE__, {:unused_args, tuple})
@@ -134,16 +145,16 @@ defmodule Module.Types do
134145
:ok
135146
end
136147

137-
defp min_reachable_default(max, min, last, name, reachable, used) when max >= min do
148+
defp min_reachable_default(max, min, last, name, used) when max >= min do
138149
fun_arity = {name, max}
139150

140-
case is_map_key(reachable, fun_arity) or is_map_key(used, fun_arity) do
141-
true -> min_reachable_default(max - 1, min, max, name, reachable, used)
142-
false -> min_reachable_default(max - 1, min, last, name, reachable, used)
151+
case is_map_key(used, fun_arity) do
152+
true -> min_reachable_default(max - 1, min, max, name, used)
153+
false -> min_reachable_default(max - 1, min, last, name, used)
143154
end
144155
end
145156

146-
defp min_reachable_default(_max, _min, last, _name, _reachable, _used) do
157+
defp min_reachable_default(_max, _min, last, _name, _used) do
147158
last
148159
end
149160

lib/elixir/src/elixir_def.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ invoke_local(Meta, Module, ErlName, Args, External) ->
6363

6464
track_defmacrop(Module, FunArity) ->
6565
{_, Bag} = elixir_module:data_tables(Module),
66-
ets:insert(Bag, {defmacrop_calls, FunArity}).
66+
ets:insert(Bag, {used_private, FunArity}).
6767

6868
invoke_external(Meta, Mod, Name, Args, E) ->
6969
is_map(E) andalso elixir_env:trace({require, Meta, Mod, []}, E),

lib/elixir/src/elixir_module.erl

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ compile(Meta, Module, ModuleAsCharlist, Block, Vars, Prune, E) ->
163163
{AllDefinitions, Private} = elixir_def:fetch_definitions(Module, E),
164164

165165
OnLoadAttribute = lists:keyfind(on_load, 1, Attributes),
166-
NewPrivate = validate_on_load_attribute(OnLoadAttribute, AllDefinitions, Private, Line, E),
166+
validate_on_load_attribute(OnLoadAttribute, AllDefinitions, DataBag, Line, E),
167167

168168
DialyzerAttribute = lists:keyfind(dialyzer, 1, Attributes),
169169
validate_dialyzer_attribute(DialyzerAttribute, AllDefinitions, Line, E),
@@ -187,8 +187,8 @@ compile(Meta, Module, ModuleAsCharlist, Block, Vars, Prune, E) ->
187187
case elixir_config:is_bootstrap() of
188188
true -> {#{}, []};
189189
false ->
190-
Defmacrop = bag_lookup_element(DataBag, defmacrop_calls, 2),
191-
'Elixir.Module.Types':infer(Module, File, AllDefinitions, NewPrivate, Defmacrop, E)
190+
UsedPrivate = bag_lookup_element(DataBag, used_private, 2),
191+
'Elixir.Module.Types':infer(Module, File, AllDefinitions, Private, UsedPrivate, E)
192192
end,
193193

194194
RawCompileOpts = bag_lookup_element(DataBag, {accumulate, compile}, 2),
@@ -278,7 +278,7 @@ validate_inlines([Inline | Inlines], Defs, Unreachable, Acc) ->
278278
case lists:keyfind(Inline, 1, Defs) of
279279
false ->
280280
{error, {undefined_function, {compile, inline}, Inline}};
281-
{_Def, Type, _Meta, _Clauses} when Type == defmacro; Type == defmacrop ->
281+
{_Def, Kind, _Meta, _Clauses} when Kind == defmacro; Kind == defmacrop ->
282282
{error, {bad_macro, {compile, inline}, Inline}};
283283
_ ->
284284
case lists:member(Inline, Unreachable) of
@@ -288,18 +288,16 @@ validate_inlines([Inline | Inlines], Defs, Unreachable, Acc) ->
288288
end;
289289
validate_inlines([], _Defs, _Unreachable, Acc) -> {ok, Acc}.
290290

291-
validate_on_load_attribute({on_load, Def}, Defs, Private, Line, E) ->
291+
validate_on_load_attribute({on_load, Def}, Defs, Bag, Line, E) ->
292292
case lists:keyfind(Def, 1, Defs) of
293293
false ->
294-
elixir_errors:module_error([{line, Line}], E, ?MODULE, {undefined_function, on_load, Def}),
295-
Private;
296-
{_Def, Type, _Meta, _Clauses} when Type == defmacro; Type == defmacrop ->
297-
elixir_errors:module_error([{line, Line}], E, ?MODULE, {bad_macro, on_load, Def}),
298-
Private;
299-
_ ->
300-
lists:keydelete(Def, 1, Private)
294+
elixir_errors:module_error([{line, Line}], E, ?MODULE, {undefined_function, on_load, Def});
295+
{_Def, Kind, _Meta, _Clauses} when Kind == defmacro; Kind == defmacrop ->
296+
elixir_errors:module_error([{line, Line}], E, ?MODULE, {bad_macro, on_load, Def});
297+
{_Def, Kind, _Meta, _Clauses} ->
298+
(Kind == defp) andalso ets:insert(Bag, {used_private, Def})
301299
end;
302-
validate_on_load_attribute(false, _Defs, Private, _Line, _E) -> Private.
300+
validate_on_load_attribute(false, _Defs, _Bag, _Line, _E) -> ok.
303301

304302
validate_dialyzer_attribute({dialyzer, Dialyzer}, Defs, Line, E) ->
305303
[validate_definition({dialyzer, Key}, Fun, Defs, Line, E)

0 commit comments

Comments
 (0)