Skip to content

Commit c8f8d02

Browse files
committed
Fix dialyzer opaqueness warnings on module attrs in OTP28 (#14755)
* Mark module attributes as generated in case they contain opaque terms * Add and use Macro.escape(ast, generated: true)
1 parent d0634c9 commit c8f8d02

File tree

4 files changed

+29
-3
lines changed

4 files changed

+29
-3
lines changed

lib/elixir/lib/kernel.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3859,7 +3859,9 @@ defmodule Kernel do
38593859

38603860
defp do_at_escape(name, value) do
38613861
try do
3862-
:elixir_quote.escape(value, :none, false)
3862+
# mark module attrs as shallow-generated since the ast for their representation
3863+
# might contain opaque terms
3864+
Macro.escape(value, generated: true)
38633865
rescue
38643866
ex in [ArgumentError] ->
38653867
raise ArgumentError,

lib/elixir/lib/macro.ex

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,10 @@ defmodule Macro do
808808
nodes. Note this option changes the semantics of escaped code and
809809
it should only be used when escaping ASTs. Defaults to `false`.
810810
811+
* `:generated` - (since v1.19.0) Whether the AST should be considered as generated
812+
by the compiler or not. This means the compiler and tools like Dialyzer may not
813+
emit certain warnings.
814+
811815
As an example for `:prune_metadata`, `ExUnit` stores the AST of every
812816
assertion, so when an assertion fails we can show code snippets to users.
813817
Without this option, each time the test module is compiled, we would get a
@@ -908,7 +912,17 @@ defmodule Macro do
908912
def escape(expr, opts \\ []) do
909913
unquote = Keyword.get(opts, :unquote, false)
910914
kind = if Keyword.get(opts, :prune_metadata, false), do: :prune_metadata, else: :none
911-
:elixir_quote.escape(expr, kind, unquote)
915+
generated = Keyword.get(opts, :generated, false)
916+
917+
case :elixir_quote.escape(expr, kind, unquote) do
918+
# mark module attrs as shallow-generated since the ast for their representation
919+
# might contain opaque terms
920+
{caller, meta, args} when generated and is_list(meta) ->
921+
{caller, [generated: true] ++ meta, args}
922+
923+
ast ->
924+
ast
925+
end
912926
end
913927

914928
# TODO: Deprecate me on Elixir v1.22

lib/elixir/test/elixir/fixtures/dialyzer/opaqueness.ex

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@ defmodule Dialyzer.Opaqueness do
44
set
55
end
66

7-
def foo() do
7+
def inlined do
88
# inlining of literals should not violate opaqueness check
99
bar(MapSet.new([1, 2, 3]))
1010
end
1111

12+
@my_set MapSet.new([1, 2, 3])
13+
def module_attr do
14+
bar(@my_set)
15+
end
16+
1217
# Task.Supervisor returns a Task.t() containing an opaque Task.ref()
1318
@spec run_task() :: Task.t()
1419
def run_task do

lib/elixir/test/elixir/macro_test.exs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ defmodule MacroTest do
8484
assert Macro.escape(contents, unquote: true) == {:x, [], MacroTest}
8585
end
8686

87+
test "with generated" do
88+
assert Macro.escape(%{a: {}}, generated: true) ==
89+
{:%{}, [generated: true], [a: {:{}, [], []}]}
90+
end
91+
8792
defp eval_escaped(contents) do
8893
{eval, []} = Code.eval_quoted(Macro.escape(contents, unquote: true))
8994
eval

0 commit comments

Comments
 (0)