Skip to content

Commit 463fd60

Browse files
author
José Valim
committed
Properly track unreachable defmacrops in locals tracker
1 parent f2719b6 commit 463fd60

File tree

7 files changed

+59
-20
lines changed

7 files changed

+59
-20
lines changed

lib/elixir/lib/module/locals_tracker.ex

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@ defmodule Module.LocalsTracker do
138138

139139
# Reattach a previously yanked node
140140
@doc false
141-
def reattach(pid, kind, tuple, neighbours) do
142-
:gen_server.cast(to_pid(pid), {:reattach, kind, tuple, neighbours})
141+
def reattach(pid, tuple, kind, function, neighbours) do
142+
:gen_server.cast(to_pid(pid), {:reattach, tuple, kind, function, neighbours})
143143
end
144144

145145
# Collecting all conflicting imports with the given functions
@@ -162,15 +162,25 @@ defmodule Module.LocalsTracker do
162162
def collect_unused_locals(ref, private) do
163163
d = :gen_server.call(to_pid(ref), :digraph, @timeout)
164164
reachable = reachable_from(d, :local)
165-
{unreachable(reachable, private), collect_warnings(reachable, private)}
165+
reattached = :digraph.out_neighbours(d, :reattach)
166+
{unreachable(reachable, reattached, private), collect_warnings(reachable, private)}
166167
end
167168

168-
defp unreachable(reachable, private) do
169-
for {tuple, _, _, _} <- private,
170-
not :sets.is_element(tuple, reachable),
169+
defp unreachable(reachable, reattached, private) do
170+
for {tuple, kind, _, _} <- private,
171+
not reachable?(tuple, kind, reachable, reattached),
171172
do: tuple
172173
end
173174

175+
defp reachable?(tuple, :defmacrop, reachable, reattached) do
176+
# All private micros are unreachable unless they have been
177+
# reattached and they are reachable.
178+
:lists.member(tuple, reattached) and :sets.is_element(tuple, reachable)
179+
end
180+
defp reachable?(tuple, :defp, reachable, _reattached) do
181+
:sets.is_element(tuple, reachable)
182+
end
183+
174184
defp collect_warnings(reachable, private) do
175185
:lists.foldl(&collect_warnings(&1, &2, reachable), [], private)
176186
end
@@ -221,6 +231,7 @@ defmodule Module.LocalsTracker do
221231
def init([]) do
222232
d = :digraph.new([:protected])
223233
:digraph.add_vertex(d, :local)
234+
:digraph.add_vertex(d, :reattach)
224235
{:ok, d}
225236
end
226237

@@ -262,17 +273,29 @@ defmodule Module.LocalsTracker do
262273
{:noreply, d}
263274
end
264275

265-
def handle_cast({:reattach, _kind, tuple, {in_neigh, out_neigh}}, d) do
276+
def handle_cast({:reattach, tuple, kind, function, {in_neigh, out_neigh}}, d) do
277+
# Reattach the old function
266278
for from <- in_neigh do
267279
:digraph.add_vertex(d, from)
268-
replace_edge!(d, from, tuple)
280+
replace_edge!(d, from, function)
269281
end
270282

271283
for to <- out_neigh do
272284
:digraph.add_vertex(d, to)
273-
replace_edge!(d, tuple, to)
285+
replace_edge!(d, function, to)
286+
end
287+
288+
# Add the new definition
289+
handle_add_definition(d, kind, tuple)
290+
291+
# Make a call from the old function to the new one
292+
if function != tuple do
293+
handle_add_local(d, function, tuple)
274294
end
275295

296+
# Finally marked the new one as reattached
297+
replace_edge!(d, :reattach, tuple)
298+
276299
{:noreply, d}
277300
end
278301

lib/elixir/src/elixir_def.erl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ fetch_definition([[Tuple] | T], File, Module, Table, All, Private) ->
5858

5959
try ets:lookup_element(Table, {clauses, Tuple}, 2) of
6060
Clauses ->
61-
Unwrapped =
62-
{Tuple, Kind, Meta, Clauses},
61+
NewAll =
62+
[{Tuple, Kind, Meta, Clauses} | All],
6363
NewPrivate =
6464
case (Kind == defp) orelse (Kind == defmacrop) of
6565
true ->
@@ -68,7 +68,7 @@ fetch_definition([[Tuple] | T], File, Module, Table, All, Private) ->
6868
false ->
6969
Private
7070
end,
71-
fetch_definition(T, File, Module, Table, [Unwrapped | All], NewPrivate)
71+
fetch_definition(T, File, Module, Table, NewAll, NewPrivate)
7272
catch
7373
error:badarg ->
7474
warn_bodyless_function(Check, Meta, File, Module, Kind, Tuple),

lib/elixir/src/elixir_locals.erl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
-export([
44
setup/1, cleanup/1, cache_env/1, cache_env/2, get_cached_env/1,
55
record_local/2, record_local/3, record_import/4,
6-
record_definition/3, record_defaults/4,
6+
record_definition/3, record_defaults/4, reattach/5,
77
ensure_no_import_conflict/3, warn_unused_local/3, format_error/1
88
]).
99

@@ -25,6 +25,9 @@ setup(Module) ->
2525
cleanup(Module) ->
2626
if_tracker(Module, fun(Pid) -> unlink(Pid), ?tracker:stop(Pid), ok end).
2727

28+
reattach(Tuple, Kind, Module, Function, Neighbours) ->
29+
if_tracker(Module, fun(Pid) -> ?tracker:reattach(Pid, Tuple, Kind, Function, Neighbours) end).
30+
2831
record_local(Tuple, Module) when is_atom(Module) ->
2932
if_tracker(Module, fun(Pid) -> ?tracker:add_local(Pid, Tuple), ok end).
3033
record_local(Tuple, _Module, Function)

lib/elixir/src/elixir_overridable.erl

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,9 @@ store(Module, Function, Hidden) ->
5252
case Overridden of
5353
false ->
5454
overridable(Module, maps:put(Function, {Count, Def, Neighbours, true}, Overridable)),
55-
(not elixir_compiler:get_opt(internal)) andalso
56-
'Elixir.Module.LocalsTracker':reattach(Module, Kind, Function, Neighbours),
5755
elixir_def:store_definition(false, FinalKind, Meta, FinalName, FinalArity,
5856
File, Module, Defaults, FinalClauses),
59-
elixir_locals:record_definition(Tuple, FinalKind, Module),
60-
elixir_locals:record_local(Tuple, Module, Function);
57+
elixir_locals:reattach(Tuple, FinalKind, Module, Function, Neighbours);
6158
true ->
6259
ok
6360
end,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
defmodule Dialyzer.Defmacrop do
2+
defmacrop good_macro(id) do
3+
quote do
4+
{:good, {:good_macro, unquote(id)}}
5+
end
6+
end
7+
8+
def run() do
9+
good_macro("Not So Bad")
10+
end
11+
end

lib/elixir/test/elixir/kernel/dialyzer_test.exs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ defmodule Kernel.DialyzerTest do
104104
end
105105
end
106106

107+
test "no warnings on defmacrop", context do
108+
copy_beam! context, Dialyzer.Defmacrop
109+
assert_dialyze_no_warnings! context
110+
end
111+
107112
defp copy_beam!(context, module) do
108113
name = "#{module}.beam"
109114
File.cp! Path.join(context[:base_dir], name),

lib/elixir/test/elixir/module/locals_tracker_test.exs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,11 @@ defmodule Module.LocalsTrackerTest do
6060
D.add_local(config[:pid], {:foo, 1}, {:bar, 1})
6161
D.add_definition(config[:pid], :defp, {:bar, 1})
6262

63-
{infoo, outfoo} = D.yank(config[:pid], {:foo, 1})
63+
{infoo, outfoo} = D.yank(config[:pid], {:foo, 1})
6464
{inbar, outbar} = D.yank(config[:pid], {:bar, 1})
6565

66-
D.reattach(config[:pid], :defp, {:bar, 1}, {inbar, outbar})
67-
D.reattach(config[:pid], :def, {:foo, 1}, {infoo, outfoo})
66+
D.reattach(config[:pid], {:bar, 1}, :defp, {:bar, 1}, {inbar, outbar})
67+
D.reattach(config[:pid], {:foo, 1}, :def, {:foo, 1}, {infoo, outfoo})
6868
assert {:bar, 1} in D.reachable(config[:pid])
6969
end
7070

0 commit comments

Comments
 (0)