Skip to content

Commit e424f02

Browse files
author
José Valim
committed
Ensure aliases defined in macros do not leak
1 parent 7dd9da1 commit e424f02

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

lib/elixir/src/elixir_dispatch.erl

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,15 +218,29 @@ translate_expansion(Meta, Receiver, Tree, S) ->
218218
New = S#elixir_scope.macro_counter + 1,
219219

220220
try
221-
elixir_translator:translate_each(
221+
{ TE, TS } = elixir_translator:translate_each(
222222
elixir_quote:linify_with_context_counter(Line, { Receiver, New }, Tree),
223223
S#elixir_scope{macro_counter=New}
224-
)
224+
),
225+
{ TE,
226+
TS#elixir_scope{
227+
macro_macros=S#elixir_scope.macro_macros,
228+
macro_aliases=merge_aliases(TS, S),
229+
macro_functions=S#elixir_scope.macro_functions
230+
} }
225231
catch
226232
Kind:Reason ->
227233
erlang:raise(Kind, Reason, prune_stacktrace(mfa(Line, S), erlang:get_stacktrace(), nil))
228234
end.
229235

236+
%% We only keep aliases from module definitions.
237+
%% All others are discarded straight-away.
238+
merge_aliases(#elixir_scope{context_modules=Context, macro_aliases=New},
239+
#elixir_scope{macro_aliases=Old}) ->
240+
ContextAliases = [{ N, O } || { N, O } <- New, lists:member(O, Context)],
241+
lists:foldl(fun({ N, O }, Acc) -> orddict:store(N, O, Acc) end,
242+
ContextAliases, Old).
243+
230244
mfa(Line, #elixir_scope{module=nil} = S) ->
231245
{ elixir_compiler, '__FILE__', 2, location(Line, S) };
232246

lib/elixir/test/elixir/kernel/alias_test.exs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,40 @@ defmodule Kernel.AliasMacroNestingTest do
9393
assert Parent.Child.b.message == "ok"
9494
end
9595
end
96+
97+
# Test case extracted from using records with aliases
98+
# and @before_compile. We are basically testing that
99+
# macro aliases are not leaking from the macro.
100+
101+
defmodule Macro.AliasTest.Definer do
102+
defmacro __using__(_options) do
103+
quote do
104+
@before_compile unquote(__MODULE__)
105+
end
106+
end
107+
108+
defmacro __before_compile__(_env) do
109+
quote do
110+
defrecord Record, [foo: :bar]
111+
end
112+
end
113+
end
114+
115+
defmodule Macro.AliasTest.Aliaser do
116+
defmacro __using__(_options) do
117+
quote do
118+
alias Some.Record
119+
end
120+
end
121+
end
122+
123+
defmodule Macro.AliasTest.User do
124+
use ExUnit.Case, async: true
125+
126+
use Macro.AliasTest.Definer
127+
use Macro.AliasTest.Aliaser
128+
129+
test "has a record defined from after compile" do
130+
assert is_tuple Macro.AliasTest.User.Record.new
131+
end
132+
end

0 commit comments

Comments
 (0)