Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/elixir/lib/kernel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3858,7 +3858,9 @@ defmodule Kernel do

defp do_at_escape(name, value) do
try do
:elixir_quote.escape(value, :none, false)
# mark module attrs as shallow-generated since the ast for their representation
# might contain opaque terms
Macro.escape(value, generated: true)
rescue
ex in [ArgumentError] ->
raise ArgumentError,
Expand Down
16 changes: 15 additions & 1 deletion lib/elixir/lib/macro.ex
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,10 @@ defmodule Macro do
nodes. Note this option changes the semantics of escaped code and
it should only be used when escaping ASTs. Defaults to `false`.
* `:generated` - (since v1.19.0) Whether the AST should be considered as generated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sabiwara the new options should be included in escape_opts type

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wooops, thank you for catching this one ! 💜
Will fix

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed and backported #14761

by the compiler or not. This means the compiler and tools like Dialyzer may not
emit certain warnings.
Comment on lines +811 to +813
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an example for `:prune_metadata`, `ExUnit` stores the AST of every
assertion, so when an assertion fails we can show code snippets to users.
Without this option, each time the test module is compiled, we would get a
Expand Down Expand Up @@ -908,7 +912,17 @@ defmodule Macro do
def escape(expr, opts \\ []) do
unquote = Keyword.get(opts, :unquote, false)
kind = if Keyword.get(opts, :prune_metadata, false), do: :prune_metadata, else: :none
:elixir_quote.escape(expr, kind, unquote)
generated = Keyword.get(opts, :generated, false)

case :elixir_quote.escape(expr, kind, unquote) do
# mark module attrs as shallow-generated since the ast for their representation
# might contain opaque terms
{caller, meta, args} when generated and is_list(meta) ->
{caller, [generated: true] ++ meta, args}
Comment on lines +920 to +921
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we wrap with a __block__ for other ASTs?
It feels a bit too much, we shouldn't need it for this use case and quote doesn't do it either.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is no need, yeah.


ast ->
ast
end
end

# TODO: Deprecate me on Elixir v1.22
Expand Down
7 changes: 6 additions & 1 deletion lib/elixir/test/elixir/fixtures/dialyzer/opaqueness.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ defmodule Dialyzer.Opaqueness do
set
end

def foo() do
def inlined do
# inlining of literals should not violate opaqueness check
bar(MapSet.new([1, 2, 3]))
end

@my_set MapSet.new([1, 2, 3])
def module_attr do
bar(@my_set)
end

# Task.Supervisor returns a Task.t() containing an opaque Task.ref()
@spec run_task() :: Task.t()
def run_task do
Expand Down
5 changes: 5 additions & 0 deletions lib/elixir/test/elixir/macro_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ defmodule MacroTest do
assert Macro.escape(contents, unquote: true) == {:x, [], MacroTest}
end

test "with generated" do
assert Macro.escape(%{a: {}}, generated: true) ==
{:%{}, [generated: true], [a: {:{}, [], []}]}
end

defp eval_escaped(contents) do
{eval, []} = Code.eval_quoted(Macro.escape(contents, unquote: true))
eval
Expand Down