Skip to content

Commit c765a95

Browse files
author
José Valim
committed
Ensure default calls are connected in dispatch tracker
1 parent 8836405 commit c765a95

File tree

4 files changed

+74
-11
lines changed

4 files changed

+74
-11
lines changed

lib/elixir/lib/module/dispatch_tracker.ex

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,12 @@ defmodule Module.DispatchTracker do
180180
:gen_server.cast(pid, { :add_definition, kind, tuple })
181181
end
182182

183+
# Adds and tracks defaults for a definition into the tracker.
184+
@doc false
185+
def add_defaults(pid, kind, tuple, defaults) when kind in [:def, :defp, :defmacro, :defmacrop] do
186+
:gen_server.cast(pid, { :add_defaults, kind, tuple, defaults })
187+
end
188+
183189
# Adds a local dispatch to the given target.
184190
def add_local(pid, to) when is_tuple(to) do
185191
:gen_server.cast(pid, { :add_local, :local, to })
@@ -327,18 +333,17 @@ defmodule Module.DispatchTracker do
327333
end
328334

329335
def handle_cast({ :add_local, from, to }, d) do
330-
:digraph.add_vertex(d, to)
331-
replace_edge!(d, from, to)
336+
handle_add_local(d, from, to)
332337
{ :noreply, d }
333338
end
334339

335340
def handle_cast({ :add_remote, function, module, { name, arity } }, d) do
336-
record_import_or_remote(d, :remote, function, module, name, arity)
341+
handle_import_or_remote(d, :remote, function, module, name, arity)
337342
{ :noreply, d }
338343
end
339344

340345
def handle_cast({ :add_import, function, module, { name, arity } }, d) do
341-
record_import_or_remote(d, :import, function, module, name, arity)
346+
handle_import_or_remote(d, :import, function, module, name, arity)
342347
{ :noreply, d }
343348
end
344349

@@ -355,14 +360,16 @@ defmodule Module.DispatchTracker do
355360
{ :noreply, d }
356361
end
357362

358-
def handle_cast({ :add_definition, public, tuple }, d) when public in [:def, :defmacro] do
359-
:digraph.add_vertex(d, tuple)
360-
replace_edge!(d, :local, tuple)
363+
def handle_cast({ :add_definition, kind, tuple }, d) do
364+
handle_add_definition(d, kind, tuple)
361365
{ :noreply, d }
362366
end
363367

364-
def handle_cast({ :add_definition, private, tuple }, d) when private in [:defp, :defmacrop] do
365-
:digraph.add_vertex(d, tuple)
368+
def handle_cast({ :add_defaults, kind, { name, arity }, defaults }, d) do
369+
lc i inlist :lists.seq(arity - defaults, arity - 1) do
370+
handle_add_definition(d, kind, { name, i })
371+
handle_add_local(d, { name, i }, { name, i + 1 })
372+
end
366373
{ :noreply, d }
367374
end
368375

@@ -388,7 +395,7 @@ defmodule Module.DispatchTracker do
388395
{ :ok, d }
389396
end
390397

391-
defp record_import_or_remote(d, kind, function, module, name, arity) do
398+
defp handle_import_or_remote(d, kind, function, module, name, arity) do
392399
:digraph.add_vertex(d, module)
393400
replace_edge!(d, kind, module)
394401

@@ -401,6 +408,20 @@ defmodule Module.DispatchTracker do
401408
end
402409
end
403410

411+
defp handle_add_local(d, from, to) do
412+
:digraph.add_vertex(d, to)
413+
replace_edge!(d, from, to)
414+
end
415+
416+
defp handle_add_definition(d, public, tuple) when public in [:def, :defmacro] do
417+
:digraph.add_vertex(d, tuple)
418+
replace_edge!(d, :local, tuple)
419+
end
420+
421+
defp handle_add_definition(d, private, tuple) when private in [:defp, :defmacrop] do
422+
:digraph.add_vertex(d, tuple)
423+
end
424+
404425
defp replace_edge!(d, from, to) do
405426
unless :lists.member(to, :digraph.out_neighbours(d, from)) do
406427
[:"$e"|_] = :digraph.add_edge(d, from, to)

lib/elixir/src/elixir_def.erl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,16 @@ store_definition(Kind, Line, CheckClauses, Module, Name, Args, Guards, Body, #el
124124

125125
{ Function, Defaults, TS } = translate_definition(Kind, Line, Name, Args, Guards, Body, S),
126126

127+
DefaultsLength = length(Defaults),
128+
elixir_tracker:record_defaults(Tuple, Kind, Module, DefaultsLength),
129+
127130
File = TS#elixir_scope.file,
128131
Table = table(Module),
129132
CTable = clauses_table(Module),
130133

131134
compile_super(Module, TS),
132135
store_each(CheckClauses, Kind, File, Location,
133-
Table, CTable, length(Defaults), Function),
136+
Table, CTable, DefaultsLength, Function),
134137

135138
[store_each(false, Kind, File, Location, Table, CTable, 0,
136139
default_function_for(Kind, Name, Default)) || Default <- Defaults],

lib/elixir/src/elixir_tracker.erl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
record_local/2, record_local/3,
77
record_import/4, record_remote/4,
88
record_warn/4, record_definition/3,
9+
record_defaults/4,
910
ensure_no_import_conflict/4, ensure_all_imports_used/3,
1011
warn_unused_local/3, format_error/1
1112
]).
@@ -70,6 +71,14 @@ record_definition(Tuple, Kind, Module) ->
7071
true
7172
end).
7273

74+
record_defaults(_Tuple, _Kind, _Module, 0) ->
75+
true;
76+
record_defaults(Tuple, Kind, Module, Defaults) ->
77+
if_tracker(Module, fun(Pid) ->
78+
?tracker:add_defaults(Pid, Kind, Tuple, Defaults),
79+
true
80+
end).
81+
7382
%% HELPERS
7483

7584
if_tracker(Module, Callback) ->

lib/elixir/test/elixir/module/dispatch_tracker_test.exs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,36 @@ defmodule Module.DispatchTrackerTest do
103103
assert unused == []
104104
end
105105

106+
## Defaults
107+
108+
test "can add defaults", config do
109+
D.add_definition(config[:pid], :def, { :foo, 4 })
110+
D.add_defaults(config[:pid], :def, { :foo, 4 }, 2)
111+
end
112+
113+
test "defaults are reachable if public", config do
114+
D.add_definition(config[:pid], :def, { :foo, 4 })
115+
D.add_defaults(config[:pid], :def, { :foo, 4 }, 2)
116+
assert { :foo, 2 } in D.reachable(config[:pid])
117+
assert { :foo, 3 } in D.reachable(config[:pid])
118+
end
119+
120+
test "defaults are not reachable if private", config do
121+
D.add_definition(config[:pid], :defp, { :foo, 4 })
122+
D.add_defaults(config[:pid], :defp, { :foo, 4 }, 2)
123+
refute { :foo, 2 } in D.reachable(config[:pid])
124+
refute { :foo, 3 } in D.reachable(config[:pid])
125+
end
126+
127+
test "defaults are connected", config do
128+
D.add_definition(config[:pid], :defp, { :foo, 4 })
129+
D.add_defaults(config[:pid], :defp, { :foo, 4 }, 2)
130+
D.add_local(config[:pid], { :foo, 2 })
131+
assert { :foo, 2 } in D.reachable(config[:pid])
132+
assert { :foo, 3 } in D.reachable(config[:pid])
133+
assert { :foo, 4 } in D.reachable(config[:pid])
134+
end
135+
106136
## Imports
107137

108138
test "can add import", config do

0 commit comments

Comments
 (0)