Skip to content
Closed
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
2 changes: 1 addition & 1 deletion lib/elixir/lib/macro/env.ex
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ defmodule Macro.Env do
:elixir_dispatch.check_deprecated(:macro, meta, receiver, name, arity, env)
end

quoted = expander.(args, env)
quoted = expander.(:elixir_dispatch.stop_generated_propagation(args), env)
next = :elixir_module.next_counter(env.module)
:elixir_quote.linify_with_context_counter(expansion_meta, {receiver, next}, quoted)
end
Expand Down
11 changes: 9 additions & 2 deletions lib/elixir/src/elixir_dispatch.erl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
require_function/5, import_function/4,
expand_import/7, expand_require/6, check_deprecated/6,
default_functions/0, default_macros/0, default_requires/0,
find_import/4, find_imports/3, format_error/1]).
find_import/4, find_imports/3, format_error/1, stop_generated_propagation/1]).
-include("elixir.hrl").
-import(ordsets, [is_element/2]).
-define(kernel, 'Elixir.Kernel').
Expand Down Expand Up @@ -119,7 +119,7 @@ dispatch_import(Meta, Name, Args, S, E, Callback) ->
{macro, Receiver, Expander} ->
check_deprecated(macro, Meta, Receiver, Name, Arity, E),
Caller = {?line(Meta), S, E},
expand_quoted(Meta, Receiver, Name, Arity, Expander(Args, Caller), S, E);
expand_quoted(Meta, Receiver, Name, Arity, Expander(stop_generated_propagation(Args), Caller), S, E);
{function, Receiver, NewName} ->
case elixir_rewrite:inline(Receiver, NewName, Arity) of
{AR, AN} ->
Expand All @@ -134,6 +134,13 @@ dispatch_import(Meta, Name, Args, S, E, Callback) ->
elixir_errors:file_error(Meta, E, ?MODULE, {import, Error, Name, Arity})
end.

stop_generated_propagation(Args) ->
lists:map(fun
% we add generated: false but do not overwrite any potential generated: true if present
({Call, Meta, Ctx}) when is_list(Meta) -> {Call, [{generated, false} | Meta], Ctx};
(Other) -> Other
end, Args).

dispatch_require(Meta, Receiver, Name, Args, S, E, Callback) when is_atom(Receiver) ->
Arity = length(Args),

Expand Down
57 changes: 31 additions & 26 deletions lib/elixir/src/elixir_quote.erl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
-include("elixir.hrl").
-define(defs(Kind), Kind == def; Kind == defp; Kind == defmacro; Kind == defmacrop; Kind == '@').
-define(lexical(Kind), Kind == import; Kind == alias; Kind == require).
-compile({inline, [keyfind/2, keystore/3, keydelete/2, keynew/3, do_tuple_linify/5]}).
-compile({inline, [keyfind/2, keystore/3, keydelete/2, keynew/3, do_tuple_linify/6]}).

-record(elixir_quote, {
line=false,
Expand Down Expand Up @@ -84,53 +84,58 @@ linify(Line, Key, Exprs) when is_integer(Line) ->
end
end,

do_linify(Fun, Exprs, nil).
do_linify(Fun, Exprs, nil, false).

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

Fun =
case lists:keyfind(generated, 1, ContextMeta) of
{generated, true} when Line =:= 0 -> fun elixir_utils:generated/1;
{generated, true} -> fun(Meta) -> elixir_utils:generated(keynew(line, Meta, Line)) end;
_ when Line =:= 0 -> fun(Meta) -> Meta end;
_ -> fun(Meta) -> keynew(line, Meta, Line) end
end,
Generated = keyfind(generated, ContextMeta) == {generated, true},

Fun = if
Line =:= 0 -> fun(Meta) -> Meta end;
true -> fun(Meta) -> keynew(line, Meta, Line) end
end,

do_linify(Fun, Exprs, Var).
do_linify(Fun, Exprs, Var, Generated).

do_linify(Fun, {quote, Meta, [_ | _] = Args}, {Receiver, Counter} = Var)
do_linify(Fun, {quote, Meta, [_ | _] = Args}, {Receiver, Counter} = Var, Gen)
when is_list(Meta) ->
NewMeta =
case keyfind(context, Meta) == {context, Receiver} of
true -> keynew(counter, Meta, Counter);
false -> Meta
end,
do_tuple_linify(Fun, NewMeta, quote, Args, Var);
do_tuple_linify(Fun, NewMeta, quote, Args, Var, Gen);

do_linify(Fun, {Left, Meta, Receiver}, {Receiver, Counter} = Var)
do_linify(Fun, {Left, Meta, Receiver}, {Receiver, Counter} = Var, Gen)
when is_atom(Left), is_list(Meta), Left /= '_' ->
do_tuple_linify(Fun, keynew(counter, Meta, Counter), Left, Receiver, Var);
do_tuple_linify(Fun, keynew(counter, Meta, Counter), Left, Receiver, Var, Gen);

do_linify(Fun, {Lexical, Meta, [_ | _] = Args}, {_, Counter} = Var)
do_linify(Fun, {Lexical, Meta, [_ | _] = Args}, {_, Counter} = Var, Gen)
when ?lexical(Lexical); Lexical == '__aliases__' ->
do_tuple_linify(Fun, keynew(counter, Meta, Counter), Lexical, Args, Var);
do_tuple_linify(Fun, keynew(counter, Meta, Counter), Lexical, Args, Var, Gen);

do_linify(Fun, {Left, Meta, Right}, Var, Gen) when is_list(Meta) ->
do_tuple_linify(Fun, Meta, Left, Right, Var, Gen);

do_linify(Fun, {Left, Meta, Right}, Var) when is_list(Meta) ->
do_tuple_linify(Fun, Meta, Left, Right, Var);
do_linify(Fun, {Left, Right}, Var, Gen) ->
{do_linify(Fun, Left, Var, Gen), do_linify(Fun, Right, Var, Gen)};

do_linify(Fun, {Left, Right}, Var) ->
{do_linify(Fun, Left, Var), do_linify(Fun, Right, Var)};
do_linify(Fun, List, Var, Gen) when is_list(List) ->
[do_linify(Fun, X, Var, Gen) || X <- List];

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

do_linify(_, Else, _) -> Else.
do_tuple_linify(Fun, Meta, Left, Right, Var, Gen) ->
{NewMeta, NewGen} =
case keyfind(generated, Meta) of
{generated, false} -> {keydelete(generated, Meta), false};
false when Gen -> {[{generated, true} | Meta], Gen};
_ -> {Meta, Gen}
end,

-compile({inline, do_tuple_linify/5}).
do_tuple_linify(Fun, Meta, Left, Right, Var) ->
{do_linify(Fun, Left, Var), Fun(Meta), do_linify(Fun, Right, Var)}.
{do_linify(Fun, Left, Var, NewGen), Fun(NewMeta), do_linify(Fun, Right, Var, NewGen)}.

%% Escaping

Expand Down
28 changes: 21 additions & 7 deletions lib/elixir/test/elixir/macro/env_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
Code.require_file("../test_helper.exs", __DIR__)

defmodule MacroEnvMacros do
defmacro my_macro(arg), do: arg
defmacro my_macro(arg) do
quote do: foo(unquote(arg))
end

@deprecated "this is deprecated"
defmacro my_deprecated_macro(arg), do: arg
Expand Down Expand Up @@ -154,9 +156,15 @@ defmodule Macro.EnvTest do

test "expands with argument" do
{:macro, MacroEnvMacros, fun} = expand_require(env(), meta(), MacroEnvMacros, :my_macro, 1)
assert fun.([], [quote(do: hello())]) == quote(do: hello())
assert fun.([line: 789], [quote(do: hello())]) == quote(line: 789, do: hello())
assert fun.([generated: true], [quote(do: hello())]) == quote(generated: true, do: hello())
assert fun.([], [quote(do: hello())]) == quote(do: foo(hello()))
assert fun.([line: 789], [quote(do: hello())]) == quote(line: 789, do: foo(hello()))

# do not propagate generated: true to arguments
assert {:foo, outer_meta, [{:hello, inner_meta, []}]} =
fun.([generated: true], [quote(do: hello())])

assert outer_meta[:generated]
refute inner_meta[:generated]
end

test "with tracing and deprecations" do
Expand Down Expand Up @@ -202,9 +210,15 @@ defmodule Macro.EnvTest do

test "expands with argument" do
{:macro, MacroEnvMacros, fun} = expand_import(env(), meta(), :my_macro, 1)
assert fun.([], [quote(do: hello())]) == quote(do: hello())
assert fun.([line: 789], [quote(do: hello())]) == quote(line: 789, do: hello())
assert fun.([generated: true], [quote(do: hello())]) == quote(generated: true, do: hello())
assert fun.([], [quote(do: hello())]) == quote(do: foo(hello()))
assert fun.([line: 789], [quote(do: hello())]) == quote(line: 789, do: foo(hello()))

# do not propagate generated: true to arguments
assert {:foo, outer_meta, [{:hello, inner_meta, []}]} =
fun.([generated: true], [quote(do: hello())])

assert outer_meta[:generated]
refute inner_meta[:generated]
end

defmacro allow_locals_example, do: :ok
Expand Down
37 changes: 36 additions & 1 deletion lib/elixir/test/elixir/macro_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ defmodule MacroTest do
assert Macro.expand_once(expr, __ENV__) == expr
end

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

Expand All @@ -274,6 +274,41 @@ defmodule MacroTest do
assert meta[:generated]
end

test "does not propagate :generated to unquoted" do
non_generated = quote do: foo()

assert {:||, outer_meta, [{:foo, inner_meta, []}, false]} =
Macro.expand_once(
quote generated: true do
oror(unquote(non_generated), false)
end,
__ENV__
)

assert outer_meta[:generated]
refute inner_meta[:generated]
end

defmacro foo_bar(x) do
y = quote do: bar(unquote(x))

quote do: foo(unquote(y))
end

test "propagates :generated to unquote within macro" do
non_generated = quote do: baz()

assert {:foo, foo_meta, [{:bar, bar_meta, [{:baz, baz_meta, []}]}]} =
Macro.expand_once(
quote(generated: true, do: foo_bar(unquote(non_generated))),
__ENV__
)

assert foo_meta[:generated]
assert bar_meta[:generated]
refute baz_meta[:generated]
end

test "does not expand module attributes" do
message =
"could not call Module.get_attribute/2 because the module #{inspect(__MODULE__)} " <>
Expand Down
13 changes: 8 additions & 5 deletions lib/ex_unit/lib/ex_unit/assertions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ defmodule ExUnit.Assertions do

"""
defmacro assert({:=, meta, [left, right]} = assertion) do
code = escape_quoted(:assert, meta, assertion)
code = escape_quoted(:assert, meta, mark_as_generated(assertion))

check =
quote generated: true do
Expand All @@ -150,7 +150,7 @@ defmodule ExUnit.Assertions do
end

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

left = __expand_pattern__(left, __CALLER__)
Expand Down Expand Up @@ -727,10 +727,13 @@ defmodule ExUnit.Assertions do

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

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

defp mark_as_generated({name, meta, context}), do: {name, [generated: true] ++ meta, context}
defp mark_as_generated(other), do: other

@doc false
def __expand_pattern__({:when, meta, [left, right]}, caller) do
left = expand_pattern(left, Macro.Env.to_match(caller))
Expand Down Expand Up @@ -952,7 +955,7 @@ defmodule ExUnit.Assertions do
defp do_catch(kind, expr) do
quote do
try do
_ = unquote(expr)
_ = unquote(mark_as_generated(expr))
flunk("Expected to catch #{unquote(kind)}, got nothing")
rescue
e in [ExUnit.AssertionError] ->
Expand Down
Loading