Skip to content

Commit 0e88732

Browse files
ericmjJosé Valim
authored andcommitted
Do not warn for functions generated by Erlang compiler (#9713)
1 parent ba02eb9 commit 0e88732

File tree

5 files changed

+70
-6
lines changed

5 files changed

+70
-6
lines changed

lib/elixir/lib/module/checker.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ defmodule Module.Checker do
216216
# TODO: Do not warn inside guards
217217
# TODO: Properly handle protocols
218218
defp warn_undefined?(_module, :__impl__, 1, _state), do: false
219+
defp warn_undefined?(_module, :module_info, 0, _state), do: false
220+
defp warn_undefined?(_module, :module_info, 1, _state), do: false
219221
defp warn_undefined?(:erlang, :orelse, 2, _state), do: false
220222
defp warn_undefined?(:erlang, :andalso, 2, _state), do: false
221223

lib/elixir/lib/module/parallel_checker.ex

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,11 @@ defmodule Module.ParallelChecker do
209209
end
210210

211211
defp cache_from_module_map(ets, map) do
212-
exports = [{{:__info__, 1}, :def} | definitions_to_exports(map.definitions)]
212+
exports =
213+
[{{:__info__, 1}, :def}] ++
214+
behaviour_exports(map) ++
215+
definitions_to_exports(map.definitions)
216+
213217
deprecated = Map.new(map.deprecated)
214218
cache_info(ets, map.module, exports, deprecated)
215219
end
@@ -227,6 +231,7 @@ defmodule Module.ParallelChecker do
227231
defp info_exports(module) do
228232
Map.new(
229233
[{{:__info__, 1}, :def}] ++
234+
behaviour_exports(module) ++
230235
Enum.map(module.__info__(:macros), &{&1, :defmacro}) ++
231236
Enum.map(module.__info__(:functions), &{&1, :def})
232237
)
@@ -268,6 +273,17 @@ defmodule Module.ParallelChecker do
268273
:ets.insert(ets, {{:cached, module}, true})
269274
end
270275

276+
defp behaviour_exports(%{is_behaviour: true}), do: [{{:behaviour_info, 1}, :def}]
277+
defp behaviour_exports(%{is_behaviour: false}), do: []
278+
279+
defp behaviour_exports(module) when is_atom(module) do
280+
if {:behaviour_info, 1} in module.module_info(:functions) do
281+
[{{:behaviour_info, 1}, :def}]
282+
else
283+
[]
284+
end
285+
end
286+
271287
defp definitions_to_exports(definitions) do
272288
Enum.flat_map(definitions, fn {function, kind, _meta, _clauses} ->
273289
if kind in [:def, :defmacro] do

lib/elixir/src/elixir_erl.erl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ signature_to_binary(Module, '__struct__', []) ->
549549
signature_to_binary(_, Name, Signature) ->
550550
'Elixir.Macro':to_string({Name, [], Signature}).
551551

552-
checker_chunk(#{definitions := Definitions, deprecated := Deprecated}, NoWarnUndefined) ->
552+
checker_chunk(#{definitions := Definitions, deprecated := Deprecated, is_behaviour := IsBehaviour}, NoWarnUndefined) ->
553553
DeprecatedMap = maps:from_list(Deprecated),
554554

555555
Exports =
@@ -564,12 +564,15 @@ checker_chunk(#{definitions := Definitions, deprecated := Deprecated}, NoWarnUnd
564564
end, [], Definitions),
565565

566566
Contents = #{
567-
exports => lists:sort(Exports),
567+
exports => lists:sort(behaviour_info_exports(IsBehaviour) ++ Exports),
568568
no_warn_undefined => NoWarnUndefined
569569
},
570570

571571
[{<<"ExCk">>, erlang:term_to_binary({elixir_checker_v1, Contents})}].
572572

573+
behaviour_info_exports(true) -> [{{behaviour_info, 1}, #{kind => def, deprecated_reason => nil}}];
574+
behaviour_info_exports(false) -> [].
575+
573576
split_no_warn_undefined([{no_warn_undefined, NoWarnUndefined} | CompileOpts], AccNWU, AccCO) ->
574577
split_no_warn_undefined(CompileOpts, list_wrap(NoWarnUndefined) ++ AccNWU, AccCO);
575578
split_no_warn_undefined([Opt | CompileOpts], AccNWU, AccCO) ->

lib/elixir/src/elixir_module.erl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ compile(Line, Module, Block, Vars, E) ->
133133
definitions => AllDefinitions,
134134
unreachable => Unreachable,
135135
compile_opts => CompileOpts,
136-
deprecated => get_deprecated(DataBag)
136+
deprecated => get_deprecated(DataBag),
137+
is_behaviour => is_behaviour(DataBag)
137138
},
138139

139140
Binary = elixir_erl:compile(ModuleMap),
@@ -199,6 +200,9 @@ validate_on_load_attribute({on_load, Def}, Defs, File, Line) ->
199200
end;
200201
validate_on_load_attribute(false, _Defs, _File, _Line) -> ok.
201202

203+
is_behaviour(DataBag) ->
204+
ets:member(DataBag, {accumulate, callback}) orelse ets:member(DataBag, {accumulate, macrocallback}).
205+
202206
%% An undef error for a function in the module being compiled might result in an
203207
%% exception message suggesting the current module is not loaded. This is
204208
%% misleading so use a custom reason.

lib/elixir/test/elixir/module/checker_test.exs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,34 @@ defmodule Module.CheckerTest do
2323
@deprecated "oops"
2424
def e, do: :ok
2525
end
26+
""",
27+
"b.ex" => """
28+
defmodule B do
29+
@callback f() :: :ok
30+
end
31+
""",
32+
"c.ex" => """
33+
defmodule C do
34+
@macrocallback g() :: :ok
35+
end
2636
"""
2737
}
2838

2939
modules = compile(files)
30-
contents = read_chunk(modules[A])
3140

32-
assert contents.exports == [
41+
assert read_chunk(modules[A]).exports == [
3342
{{:c, 0}, %{deprecated_reason: nil, kind: :def}},
3443
{{:d, 0}, %{deprecated_reason: nil, kind: :defmacro}},
3544
{{:e, 0}, %{deprecated_reason: "oops", kind: :def}}
3645
]
46+
47+
assert read_chunk(modules[B]).exports == [
48+
{{:behaviour_info, 1}, %{deprecated_reason: nil, kind: :def}}
49+
]
50+
51+
assert read_chunk(modules[C]).exports == [
52+
{{:behaviour_info, 1}, %{deprecated_reason: nil, kind: :def}}
53+
]
3754
end
3855
end
3956

@@ -60,6 +77,28 @@ defmodule Module.CheckerTest do
6077
assert_warnings(files, warning)
6178
end
6279

80+
test "handles built in functions" do
81+
files = %{
82+
"a.ex" => """
83+
defmodule A do
84+
def a, do: Kernel.module_info()
85+
def b, do: Kernel.module_info(:functions)
86+
def c, do: Kernel.__info__(:functions)
87+
def d, do: GenServer.behaviour_info(:callbacks)
88+
def e, do: Kernel.behaviour_info(:callbacks)
89+
end
90+
"""
91+
}
92+
93+
warning = """
94+
warning: Kernel.behaviour_info/1 is undefined or private
95+
a.ex:6: A.e/0
96+
97+
"""
98+
99+
assert_warnings(files, warning)
100+
end
101+
63102
test "handles module body conditionals" do
64103
files = %{
65104
"a.ex" => """

0 commit comments

Comments
 (0)