Skip to content

Commit 67042e8

Browse files
authored
Stop propagating generated on macro arguments (#14696)
* Stop propagating generated on macro arguments * Explicitly mark assert as generated
1 parent 04f1b7c commit 67042e8

File tree

6 files changed

+105
-42
lines changed

6 files changed

+105
-42
lines changed

lib/elixir/lib/macro/env.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,7 @@ defmodule Macro.Env do
657657
:elixir_dispatch.check_deprecated(:macro, meta, receiver, name, arity, env)
658658
end
659659

660-
quoted = expander.(args, env)
660+
quoted = expander.(:elixir_dispatch.stop_generated(args), env)
661661
next = :elixir_module.next_counter(env.module)
662662
:elixir_quote.linify_with_context_counter(expansion_meta, {receiver, next}, quoted)
663663
end

lib/elixir/src/elixir_dispatch.erl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
require_function/5, import_function/4,
1111
expand_import/7, expand_require/6, check_deprecated/6,
1212
default_functions/0, default_macros/0, default_requires/0,
13-
find_import/4, find_imports/3, format_error/1]).
13+
find_import/4, find_imports/3, format_error/1, stop_generated/1]).
1414
-include("elixir.hrl").
1515
-import(ordsets, [is_element/2]).
1616
-define(kernel, 'Elixir.Kernel').
@@ -119,7 +119,7 @@ dispatch_import(Meta, Name, Args, S, E, Callback) ->
119119
{macro, Receiver, Expander} ->
120120
check_deprecated(macro, Meta, Receiver, Name, Arity, E),
121121
Caller = {?line(Meta), S, E},
122-
expand_quoted(Meta, Receiver, Name, Arity, Expander(Args, Caller), S, E);
122+
expand_quoted(Meta, Receiver, Name, Arity, Expander(stop_generated(Args), Caller), S, E);
123123
{function, Receiver, NewName} ->
124124
case elixir_rewrite:inline(Receiver, NewName, Arity) of
125125
{AR, AN} ->
@@ -134,6 +134,12 @@ dispatch_import(Meta, Name, Args, S, E, Callback) ->
134134
elixir_errors:file_error(Meta, E, ?MODULE, {import, Error, Name, Arity})
135135
end.
136136

137+
stop_generated(Args) ->
138+
lists:map(fun
139+
({Call, Meta, Ctx}) when is_list(Meta) -> {Call, [{stop_generated, true} | Meta], Ctx};
140+
(Other) -> Other
141+
end, Args).
142+
137143
dispatch_require(Meta, Receiver, Name, Args, S, E, Callback) when is_atom(Receiver) ->
138144
Arity = length(Args),
139145

lib/elixir/src/elixir_quote.erl

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
-include("elixir.hrl").
1010
-define(defs(Kind), Kind == def; Kind == defp; Kind == defmacro; Kind == defmacrop; Kind == '@').
1111
-define(lexical(Kind), Kind == import; Kind == alias; Kind == require).
12-
-compile({inline, [keyfind/2, keystore/3, keydelete/2, keynew/3, do_tuple_linify/5]}).
12+
-compile({inline, [keyfind/2, keystore/3, keydelete/2, keynew/3, do_tuple_linify/6]}).
1313

1414
-record(elixir_quote, {
1515
line=false,
@@ -84,53 +84,58 @@ linify(Line, Key, Exprs) when is_integer(Line) ->
8484
end
8585
end,
8686

87-
do_linify(Fun, Exprs, nil).
87+
do_linify(Fun, Exprs, nil, false).
8888

8989
%% Same as linify but also considers the context counter and generated.
9090
linify_with_context_counter(ContextMeta, Var, Exprs) when is_list(ContextMeta) ->
9191
Line = ?line(ContextMeta),
9292

93-
Fun =
94-
case lists:keyfind(generated, 1, ContextMeta) of
95-
{generated, true} when Line =:= 0 -> fun elixir_utils:generated/1;
96-
{generated, true} -> fun(Meta) -> elixir_utils:generated(keynew(line, Meta, Line)) end;
97-
_ when Line =:= 0 -> fun(Meta) -> Meta end;
98-
_ -> fun(Meta) -> keynew(line, Meta, Line) end
99-
end,
93+
Generated = keyfind(generated, ContextMeta) == {generated, true},
94+
95+
Fun = if
96+
Line =:= 0 -> fun(Meta) -> Meta end;
97+
true -> fun(Meta) -> keynew(line, Meta, Line) end
98+
end,
10099

101-
do_linify(Fun, Exprs, Var).
100+
do_linify(Fun, Exprs, Var, Generated).
102101

103-
do_linify(Fun, {quote, Meta, [_ | _] = Args}, {Receiver, Counter} = Var)
102+
do_linify(Fun, {quote, Meta, [_ | _] = Args}, {Receiver, Counter} = Var, Gen)
104103
when is_list(Meta) ->
105104
NewMeta =
106105
case keyfind(context, Meta) == {context, Receiver} of
107106
true -> keynew(counter, Meta, Counter);
108107
false -> Meta
109108
end,
110-
do_tuple_linify(Fun, NewMeta, quote, Args, Var);
109+
do_tuple_linify(Fun, NewMeta, quote, Args, Var, Gen);
111110

112-
do_linify(Fun, {Left, Meta, Receiver}, {Receiver, Counter} = Var)
111+
do_linify(Fun, {Left, Meta, Receiver}, {Receiver, Counter} = Var, Gen)
113112
when is_atom(Left), is_list(Meta), Left /= '_' ->
114-
do_tuple_linify(Fun, keynew(counter, Meta, Counter), Left, Receiver, Var);
113+
do_tuple_linify(Fun, keynew(counter, Meta, Counter), Left, Receiver, Var, Gen);
115114

116-
do_linify(Fun, {Lexical, Meta, [_ | _] = Args}, {_, Counter} = Var)
115+
do_linify(Fun, {Lexical, Meta, [_ | _] = Args}, {_, Counter} = Var, Gen)
117116
when ?lexical(Lexical); Lexical == '__aliases__' ->
118-
do_tuple_linify(Fun, keynew(counter, Meta, Counter), Lexical, Args, Var);
117+
do_tuple_linify(Fun, keynew(counter, Meta, Counter), Lexical, Args, Var, Gen);
118+
119+
do_linify(Fun, {Left, Meta, Right}, Var, Gen) when is_list(Meta) ->
120+
do_tuple_linify(Fun, Meta, Left, Right, Var, Gen);
119121

120-
do_linify(Fun, {Left, Meta, Right}, Var) when is_list(Meta) ->
121-
do_tuple_linify(Fun, Meta, Left, Right, Var);
122+
do_linify(Fun, {Left, Right}, Var, Gen) ->
123+
{do_linify(Fun, Left, Var, Gen), do_linify(Fun, Right, Var, Gen)};
122124

123-
do_linify(Fun, {Left, Right}, Var) ->
124-
{do_linify(Fun, Left, Var), do_linify(Fun, Right, Var)};
125+
do_linify(Fun, List, Var, Gen) when is_list(List) ->
126+
[do_linify(Fun, X, Var, Gen) || X <- List];
125127

126-
do_linify(Fun, List, Var) when is_list(List) ->
127-
[do_linify(Fun, X, Var) || X <- List];
128+
do_linify(_, Else, _, _Gen) -> Else.
128129

129-
do_linify(_, Else, _) -> Else.
130+
do_tuple_linify(Fun, Meta, Left, Right, Var, Gen) ->
131+
{NewMeta, NewGen} =
132+
case keyfind(stop_generated, Meta) of
133+
{stop_generated, true} -> {keydelete(stop_generated, Meta), false};
134+
_ when Gen -> {elixir_utils:generated(Meta), Gen};
135+
_ -> {Meta, Gen}
136+
end,
130137

131-
-compile({inline, do_tuple_linify/5}).
132-
do_tuple_linify(Fun, Meta, Left, Right, Var) ->
133-
{do_linify(Fun, Left, Var), Fun(Meta), do_linify(Fun, Right, Var)}.
138+
{do_linify(Fun, Left, Var, NewGen), Fun(NewMeta), do_linify(Fun, Right, Var, NewGen)}.
134139

135140
%% Escaping
136141

lib/elixir/test/elixir/macro/env_test.exs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
Code.require_file("../test_helper.exs", __DIR__)
55

66
defmodule MacroEnvMacros do
7-
defmacro my_macro(arg), do: arg
7+
defmacro my_macro(arg) do
8+
quote do: foo(unquote(arg))
9+
end
810

911
@deprecated "this is deprecated"
1012
defmacro my_deprecated_macro(arg), do: arg
@@ -154,9 +156,15 @@ defmodule Macro.EnvTest do
154156

155157
test "expands with argument" do
156158
{:macro, MacroEnvMacros, fun} = expand_require(env(), meta(), MacroEnvMacros, :my_macro, 1)
157-
assert fun.([], [quote(do: hello())]) == quote(do: hello())
158-
assert fun.([line: 789], [quote(do: hello())]) == quote(line: 789, do: hello())
159-
assert fun.([generated: true], [quote(do: hello())]) == quote(generated: true, do: hello())
159+
assert fun.([], [quote(do: hello())]) == quote(do: foo(hello()))
160+
assert fun.([line: 789], [quote(do: hello())]) == quote(line: 789, do: foo(hello()))
161+
162+
# do not propagate generated: true to arguments
163+
assert {:foo, outer_meta, [{:hello, inner_meta, []}]} =
164+
fun.([generated: true], [quote(do: hello())])
165+
166+
assert outer_meta[:generated]
167+
refute inner_meta[:generated]
160168
end
161169

162170
test "with tracing and deprecations" do
@@ -202,9 +210,15 @@ defmodule Macro.EnvTest do
202210

203211
test "expands with argument" do
204212
{:macro, MacroEnvMacros, fun} = expand_import(env(), meta(), :my_macro, 1)
205-
assert fun.([], [quote(do: hello())]) == quote(do: hello())
206-
assert fun.([line: 789], [quote(do: hello())]) == quote(line: 789, do: hello())
207-
assert fun.([generated: true], [quote(do: hello())]) == quote(generated: true, do: hello())
213+
assert fun.([], [quote(do: hello())]) == quote(do: foo(hello()))
214+
assert fun.([line: 789], [quote(do: hello())]) == quote(line: 789, do: foo(hello()))
215+
216+
# do not propagate generated: true to arguments
217+
assert {:foo, outer_meta, [{:hello, inner_meta, []}]} =
218+
fun.([generated: true], [quote(do: hello())])
219+
220+
assert outer_meta[:generated]
221+
refute inner_meta[:generated]
208222
end
209223

210224
defmacro allow_locals_example, do: :ok

lib/elixir/test/elixir/macro_test.exs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ defmodule MacroTest do
264264
assert Macro.expand_once(expr, __ENV__) == expr
265265
end
266266

267-
test "propagates generated" do
267+
test "propagates :generated" do
268268
assert {:||, meta, [1, false]} = Macro.expand_once(quote(do: oror(1, false)), __ENV__)
269269
refute meta[:generated]
270270

@@ -274,6 +274,41 @@ defmodule MacroTest do
274274
assert meta[:generated]
275275
end
276276

277+
test "does not propagate :generated to unquoted" do
278+
non_generated = quote do: foo()
279+
280+
assert {:||, outer_meta, [{:foo, inner_meta, []}, false]} =
281+
Macro.expand_once(
282+
quote generated: true do
283+
oror(unquote(non_generated), false)
284+
end,
285+
__ENV__
286+
)
287+
288+
assert outer_meta[:generated]
289+
refute inner_meta[:generated]
290+
end
291+
292+
defmacro foo_bar(x) do
293+
y = quote do: bar(unquote(x))
294+
295+
quote do: foo(unquote(y))
296+
end
297+
298+
test "propagates :generated to unquote within macro" do
299+
non_generated = quote do: baz()
300+
301+
assert {:foo, foo_meta, [{:bar, bar_meta, [{:baz, baz_meta, []}]}]} =
302+
Macro.expand_once(
303+
quote(generated: true, do: foo_bar(unquote(non_generated))),
304+
__ENV__
305+
)
306+
307+
assert foo_meta[:generated]
308+
assert bar_meta[:generated]
309+
refute baz_meta[:generated]
310+
end
311+
277312
test "does not expand module attributes" do
278313
message =
279314
"could not call Module.get_attribute/2 because the module #{inspect(__MODULE__)} " <>

lib/ex_unit/lib/ex_unit/assertions.ex

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ defmodule ExUnit.Assertions do
133133
134134
"""
135135
defmacro assert({:=, meta, [left, right]} = assertion) do
136-
code = escape_quoted(:assert, meta, assertion)
136+
code = escape_quoted(:assert, meta, mark_as_generated(assertion))
137137

138138
check =
139139
quote generated: true do
@@ -150,7 +150,7 @@ defmodule ExUnit.Assertions do
150150
end
151151

152152
defmacro assert({:match?, meta, [left, right]} = assertion) do
153-
code = escape_quoted(:assert, meta, assertion)
153+
code = escape_quoted(:assert, meta, mark_as_generated(assertion))
154154
match? = {:match?, meta, [left, Macro.var(:right, __MODULE__)]}
155155

156156
left = __expand_pattern__(left, __CALLER__)
@@ -727,10 +727,13 @@ defmodule ExUnit.Assertions do
727727

728728
defp has_var?(pattern, name, context), do: Enum.any?(pattern, &match?({^name, _, ^context}, &1))
729729

730-
defp mark_as_generated(vars) do
731-
for {name, meta, context} <- vars, do: {name, [generated: true] ++ meta, context}
730+
defp mark_as_generated(vars) when is_list(vars) do
731+
Enum.map(vars, fn {name, meta, context} -> {name, [generated: true] ++ meta, context} end)
732732
end
733733

734+
defp mark_as_generated({name, meta, context}), do: {name, [generated: true] ++ meta, context}
735+
defp mark_as_generated(other), do: other
736+
734737
@doc false
735738
def __expand_pattern__({:when, meta, [left, right]}, caller) do
736739
left = expand_pattern(left, Macro.Env.to_match(caller))
@@ -952,7 +955,7 @@ defmodule ExUnit.Assertions do
952955
defp do_catch(kind, expr) do
953956
quote do
954957
try do
955-
_ = unquote(expr)
958+
_ = unquote(mark_as_generated(expr))
956959
flunk("Expected to catch #{unquote(kind)}, got nothing")
957960
rescue
958961
e in [ExUnit.AssertionError] ->

0 commit comments

Comments
 (0)