Skip to content

Commit 4cb4c96

Browse files
committed
Do not add runtime deps for alias references in patterns and guards
1 parent 008126b commit 4cb4c96

File tree

5 files changed

+77
-5
lines changed

5 files changed

+77
-5
lines changed

lib/elixir/lib/code.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ defmodule Code do
135135
136136
* `{:require, meta, module, opts}` - traced whenever `module` is required.
137137
`meta` is the require AST metadata and `opts` are the require options.
138-
If the `meta` option contains the `:from_macro`, then `require` was called
138+
If the `meta` option contains the `:from_macro`, then module was called
139139
from within a macro and therefore must be treated as a compile-time dependency.
140140
141141
* `{:struct_expansion, meta, module, keys}` - traced whenever `module`'s struct

lib/elixir/src/elixir_lexical.erl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,13 @@ trace({struct_expansion, _Meta, Module, _Keys}, #{lexical_tracker := Pid}) ->
5656
?tracker:add_export(Pid, Module),
5757
ok;
5858
trace({alias_reference, _Meta, Module}, #{lexical_tracker := Pid} = E) ->
59-
?tracker:remote_dispatch(Pid, Module, mode(E)),
59+
case E of
60+
%% Alias references inside patterns and guards in functions are not
61+
%% compile time dependencies.
62+
#{function := nil} -> ?tracker:remote_dispatch(Pid, Module, compile);
63+
#{context := nil} -> ?tracker:remote_dispatch(Pid, Module, runtime);
64+
#{} -> ok
65+
end,
6066
ok;
6167
trace({remote_function, _Meta, Module, _Function, _Arity}, #{lexical_tracker := Pid} = E) ->
6268
?tracker:remote_dispatch(Pid, Module, mode(E)),

lib/elixir/test/elixir/kernel/lexical_tracker_test.exs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,44 @@ defmodule Kernel.LexicalTrackerTest do
390390
refute URI in runtime
391391
end
392392

393+
test "aliases in patterns and guards inside functions do not add runtime dependency" do
394+
{{compile, exports, runtime, _}, _binding} =
395+
Code.eval_string("""
396+
defmodule Kernel.LexicalTrackerTest.PatternGuardsRuntime do
397+
def is_uri_atom(URI), do: true
398+
def is_range_struct(range) when is_struct(range, Range), do: true
399+
Kernel.LexicalTracker.references(__ENV__.lexical_tracker)
400+
end |> elem(3)
401+
""")
402+
403+
refute URI in compile
404+
refute URI in exports
405+
refute URI in runtime
406+
refute Range in compile
407+
refute Range in exports
408+
refute Range in runtime
409+
end
410+
411+
test "aliases in patterns and guards outside functions do add compile dependency" do
412+
{{compile, exports, runtime, _}, _binding} =
413+
Code.eval_string("""
414+
defmodule Kernel.LexicalTrackerTest.PatternGuardsCompile do
415+
%URI{} = URI.parse("/")
416+
case 1..3 do
417+
range when is_struct(range, Range) -> :ok
418+
end
419+
Kernel.LexicalTracker.references(__ENV__.lexical_tracker)
420+
end |> elem(3)
421+
""")
422+
423+
assert URI in compile
424+
assert URI in exports
425+
refute URI in runtime
426+
assert Range in compile
427+
refute Range in exports
428+
refute Range in runtime
429+
end
430+
393431
test "compile_env! does not add a compile dependency" do
394432
{{compile, exports, runtime, _}, _binding} =
395433
Code.eval_string("""

lib/mix/lib/mix/tasks/xref.ex

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -517,15 +517,20 @@ defmodule Mix.Tasks.Xref do
517517
## Trace
518518

519519
@doc false
520+
def trace({:alias_reference, meta, module}, env) when env.module != module do
521+
case env do
522+
%{function: nil} -> add_trace(:compile, :alias, module, module, meta, env)
523+
%{context: nil} -> add_trace(:runtime, :alias, module, module, meta, env)
524+
%{} -> :ok
525+
end
526+
end
527+
520528
def trace({:require, meta, module, _opts}, env),
521529
do: add_trace(require_mode(meta), :require, module, module, meta, env)
522530

523531
def trace({:struct_expansion, meta, module, _keys}, env),
524532
do: add_trace(:export, :struct, module, module, meta, env)
525533

526-
def trace({:alias_reference, meta, module}, env) when env.module != module,
527-
do: add_trace(mode(env), :alias, module, module, meta, env)
528-
529534
def trace({:remote_function, meta, module, function, arity}, env),
530535
do: add_trace(mode(env), :call, module, {module, function, arity}, meta, env)
531536

lib/mix/test/mix/tasks/xref_test.exs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,29 @@ defmodule Mix.Tasks.XrefTest do
268268
assert_trace("lib/b.ex", files, output)
269269
end
270270

271+
test "ignores dependencies from patterns and guards" do
272+
files = %{
273+
"lib/a.ex" => ~S"""
274+
defmodule A do
275+
defstruct [:foo, :bar]
276+
end
277+
""",
278+
"lib/b.ex" => ~S"""
279+
defmodule B do
280+
def pattern(A), do: true
281+
def guard(a) when is_struct(a, A), do: true
282+
end
283+
"""
284+
}
285+
286+
output = """
287+
Compiling 2 files (.ex)
288+
Generated sample app
289+
"""
290+
291+
assert_trace("lib/b.ex", files, output)
292+
end
293+
271294
test "shows traces for module callbacks" do
272295
files = %{
273296
"lib/a.ex" => ~S"""

0 commit comments

Comments
 (0)