Skip to content

Commit 2a2be0a

Browse files
committed
Track alias defined through require usage, closes #14789
1 parent 583737d commit 2a2be0a

5 files changed

Lines changed: 59 additions & 20 deletions

File tree

lib/elixir/lib/kernel/lexical_tracker.ex

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ defmodule Kernel.LexicalTracker do
2525
Invoked during module expansion to annotate a require
2626
must be warned if unused.
2727
"""
28-
def warn_require(pid, meta, module) do
29-
:gen_server.cast(pid, {:warn_require, module, meta})
28+
def warn_require(pid, meta, module, alias) do
29+
:gen_server.cast(pid, {:warn_require, module, meta, alias})
3030
module
3131
end
3232

@@ -171,9 +171,12 @@ defmodule Kernel.LexicalTracker do
171171
end
172172

173173
def handle_call(:unused_requires, _from, state) do
174+
%{references: references, aliases: aliases} = state
175+
174176
unused_requires =
175-
for {module, meta} <- state.requires, Map.get(state.references, module) != :compile do
176-
{module, meta}
177+
for {module, {meta, alias}} <- state.requires,
178+
Map.get(references, module) != :compile do
179+
{module, meta, alias, Map.get(aliases, alias) == :used}
177180
end
178181

179182
{:reply, Enum.sort(unused_requires), state}
@@ -274,8 +277,8 @@ defmodule Kernel.LexicalTracker do
274277
{:noreply, put_in(state.imports[module][@warn_key], true)}
275278
end
276279

277-
def handle_cast({:warn_require, module, meta}, state) do
278-
{:noreply, put_in(state.requires[module], meta)}
280+
def handle_cast({:warn_require, module, meta, alias}, state) do
281+
{:noreply, put_in(state.requires[module], {meta, alias})}
279282
end
280283

281284
@doc false

lib/elixir/src/elixir_expand.erl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,18 +121,18 @@ expand({require, Meta, [Ref, Opts]}, S, E) ->
121121
false when is_atom(ERef) ->
122122
elixir_aliases:ensure_loaded(Meta, ERef, ET),
123123
RE = elixir_aliases:require(Meta, ERef, EOpts, ET, true),
124-
{ok, _, EU} = alias(Meta, ERef, false, EOpts, RE),
124+
{ok, Alias, EU} = alias(Meta, ERef, false, EOpts, RE),
125125

126126
Quoted =
127127
case should_warn(Meta, EOpts, EU) of
128128
false ->
129129
ERef;
130130

131131
Pid when ?key(EU, function) /= nil ->
132-
?tracker:warn_require(Pid, Meta, ERef);
132+
?tracker:warn_require(Pid, Meta, ERef, Alias);
133133

134134
Pid ->
135-
{{'.', Meta, [?tracker, warn_require]}, Meta, [Pid, Meta, ERef]}
135+
{{'.', Meta, [?tracker, warn_require]}, Meta, [Pid, Meta, ERef, Alias]}
136136
end,
137137

138138
{Quoted, ST, EU};

lib/elixir/src/elixir_lexical.erl

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ warn_unused_imports(Pid, E) ->
9797
ok.
9898

9999
warn_unused_requires(Pid, E) ->
100-
[elixir_errors:file_warn(Meta, ?key(E, file), ?MODULE, {unused_require, Module})
101-
|| {Module, Meta} <- ?tracker:collect_unused_requires(Pid)],
100+
[elixir_errors:file_warn(Meta, ?key(E, file), ?MODULE, {unused_require, Module, Alias, AliasUsed})
101+
|| {Module, Meta, Alias, AliasUsed} <- ?tracker:collect_unused_requires(Pid)],
102102
ok.
103103

104104
unused_imports_for_module(Module, Imports) ->
@@ -118,5 +118,10 @@ format_error({unused_import, {Module, Function, Arity}}) ->
118118
io_lib:format("unused import ~ts.~ts/~w", [elixir_aliases:inspect(Module), Function, Arity]);
119119
format_error({unused_import, Module}) ->
120120
io_lib:format("unused import ~ts", [elixir_aliases:inspect(Module)]);
121-
format_error({unused_require, Module}) ->
122-
io_lib:format("unused require ~ts", [elixir_aliases:inspect(Module)]).
121+
format_error({unused_require, Module, Alias, AliasUsed}) ->
122+
Message = if
123+
Alias == false -> "unused require ~ts";
124+
AliasUsed -> "unused require ~ts (convert it to an alias instead)";
125+
true -> "unused require ~ts (the alias is also unused)"
126+
end,
127+
io_lib:format(Message, [elixir_aliases:inspect(Module)]).

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,16 +97,24 @@ defmodule Kernel.LexicalTrackerTest do
9797
end
9898

9999
test "unused requires", config do
100-
D.warn_require(config[:pid], [], String)
101-
D.warn_require(config[:pid], [], List)
100+
D.warn_require(config[:pid], [], String, false)
101+
D.warn_require(config[:pid], [], List, false)
102102
D.remote_dispatch(config[:pid], String, :compile)
103-
assert D.collect_unused_requires(config[:pid]) == [{List, []}]
103+
assert D.collect_unused_requires(config[:pid]) == [{List, [], false, false}]
104104
end
105105

106106
test "function calls do not count as macro usage", config do
107-
D.warn_require(config[:pid], [], String)
107+
D.warn_require(config[:pid], [], String, false)
108108
D.remote_dispatch(config[:pid], String, :runtime)
109-
assert D.collect_unused_requires(config[:pid]) == [{String, []}]
109+
assert D.collect_unused_requires(config[:pid]) == [{String, [], false, false}]
110+
end
111+
112+
test "track alias usage through require", config do
113+
D.warn_require(config[:pid], [], String, S)
114+
D.remote_dispatch(config[:pid], String, :runtime)
115+
assert D.collect_unused_requires(config[:pid]) == [{String, [], S, false}]
116+
D.alias_dispatch(config[:pid], S)
117+
assert D.collect_unused_requires(config[:pid]) == [{String, [], S, true}]
110118
end
111119

112120
test "unused aliases", config do

lib/elixir/test/elixir/kernel/warning_test.exs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2286,6 +2286,14 @@ defmodule Kernel.WarningTest do
22862286
end
22872287

22882288
test "unused require" do
2289+
assert_warn_compile(
2290+
["nofile:1:1", "unused require Logger"],
2291+
"""
2292+
require Logger
2293+
"""
2294+
)
2295+
2296+
# Within a module
22892297
assert_warn_compile(
22902298
["nofile:2:3", "unused require Application"],
22912299
"""
@@ -2296,10 +2304,25 @@ defmodule Kernel.WarningTest do
22962304
"""
22972305
)
22982306

2307+
# Unused require and alias
22992308
assert_warn_compile(
2300-
["nofile:1:1", "unused require Logger"],
2309+
["nofile:2:3", "unused require Application (the alias is also unused)"],
23012310
"""
2302-
require Logger
2311+
defmodule Sample do
2312+
require Application, as: A
2313+
def a, do: nil
2314+
end
2315+
"""
2316+
)
2317+
2318+
# Unused require but used alias
2319+
assert_warn_compile(
2320+
["nofile:2:3", "unused require Application (convert it to an alias instead)"],
2321+
"""
2322+
defmodule Sample do
2323+
require Application, as: A
2324+
def a, do: A.started_applications()
2325+
end
23032326
"""
23042327
)
23052328
after

0 commit comments

Comments
 (0)