Skip to content

Commit e848e87

Browse files
author
José Valim
committed
Do not load modules for xref purposes, instead use BEAM info
Signed-off-by: José Valim <[email protected]>
1 parent bcadd96 commit e848e87

File tree

2 files changed

+48
-10
lines changed

2 files changed

+48
-10
lines changed

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

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,28 +160,45 @@ defmodule Mix.Tasks.Xref do
160160

161161
defp unreachable(pair_fun) do
162162
excludes = excludes()
163-
164163
each_source_entries(&source_warnings(&1, excludes), pair_fun)
165164
end
166165

167166
defp source_warnings(source, excludes) do
168167
source(runtime_dispatches: runtime_dispatches) = source
169168

170169
for {module, func_arity_lines} <- runtime_dispatches,
170+
exports = load_exports(module),
171171
{{func, arity}, lines} <- func_arity_lines,
172-
warning = unreachable_mfa(module, func, arity, lines, excludes),
172+
warning = unreachable_mfa(exports, module, func, arity, lines, excludes),
173173
do: warning
174174
end
175175

176-
defp unreachable_mfa(module, func, arity, lines, excludes) do
176+
defp load_exports(module) do
177+
if :code.is_loaded(module) do
178+
# If the module is loaded, we will use the faster function_exported?/3 check
179+
module
180+
else
181+
# Otherwise we get all exports from :beam_lib to avoid loading modules
182+
with file when is_list(file) <- :code.which(module),
183+
{:ok, {^module, [exports: exports]}} <- :beam_lib.chunks(file, [:exports]) do
184+
exports
185+
else
186+
_ -> :unknown_module
187+
end
188+
end
189+
end
190+
191+
defp unreachable_mfa(exports, module, func, arity, lines, excludes) do
177192
cond do
178193
excluded?(module, func, arity, excludes) ->
179194
nil
180195
skip?(module, func, arity) ->
181196
nil
182-
not Code.ensure_loaded?(module) ->
197+
exports == :unknown_module ->
183198
{Enum.sort(lines), :unknown_module, module, func, arity}
184-
not function_exported?(module, func, arity) ->
199+
is_atom(exports) and not function_exported?(module, func, arity) ->
200+
{Enum.sort(lines), :unknown_function, module, func, arity}
201+
is_list(exports) and not {func, arity} in exports ->
185202
{Enum.sort(lines), :unknown_function, module, func, arity}
186203
true ->
187204
nil
@@ -276,7 +293,7 @@ defmodule Mix.Tasks.Xref do
276293
defp source_calls_for_filter(source, filter) do
277294
runtime_dispatches = source(source, :runtime_dispatches)
278295
compile_dispatches = source(source, :compile_dispatches)
279-
dispatches = Stream.concat(runtime_dispatches, compile_dispatches)
296+
dispatches = runtime_dispatches ++ compile_dispatches
280297

281298
calls =
282299
for {module, func_arity_lines} <- dispatches,

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

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,25 +129,46 @@ defmodule Mix.Tasks.XrefTest do
129129

130130
test "warnings: handles multiple modules in one file" do
131131
assert_warnings """
132-
defmodule A do
132+
defmodule A1 do
133133
def a, do: A2.no_func
134134
def b, do: A2.a
135135
end
136136
137137
defmodule A2 do
138-
def a, do: A.no_func
139-
def b, do: A.b
138+
def a, do: A1.no_func
139+
def b, do: A1.b
140140
end
141141
""", """
142142
warning: function A2.no_func/0 is undefined or private
143143
lib/a.ex:2
144144
145-
warning: function A.no_func/0 is undefined or private
145+
warning: function A1.no_func/0 is undefined or private
146146
lib/a.ex:7
147147
148148
"""
149149
end
150150

151+
test "warnings: doesn't load unloaded modules" do
152+
assert_warnings """
153+
defmodule A1 do
154+
@compile {:autoload, false}
155+
@on_load :init
156+
def init do
157+
raise "oops"
158+
end
159+
end
160+
161+
defmodule A2 do
162+
def a, do: A1.no_func
163+
def b, do: A1.init
164+
end
165+
""", """
166+
warning: function A1.no_func/0 is undefined or private
167+
lib/a.ex:10
168+
169+
"""
170+
end
171+
151172
test "warnings: groups multiple warnings in one file" do
152173
assert_warnings """
153174
defmodule A do

0 commit comments

Comments
 (0)