Skip to content

Commit e18c160

Browse files
author
José Valim
committed
Only add quoted: true to required ast nodes
1 parent 32ae6d3 commit e18c160

File tree

11 files changed

+93
-90
lines changed

11 files changed

+93
-90
lines changed

lib/elixir/include/elixir.hrl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
mark=true
4545
}).
4646

47+
%% Introspection
48+
-define(defs(Kind), Kind == def; Kind == defp; Kind == defmacro; Kind == defmacrop).
49+
4750
%% Used in tokenization and interpolation
4851

4952
%% Numbers

lib/elixir/lib/kernel/special_forms.ex

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -359,24 +359,22 @@ defmodule Kernel.SpecialForms do
359359
## Examples
360360
361361
iex> quote do: sum(1, 2, 3)
362-
{ :sum, [quoted: true], [1, 2, 3] }
362+
{ :sum, [], [1, 2, 3] }
363363
364364
## Explanation
365365
366366
Any Elixir code can be represented using Elixir data structures.
367-
The building block of Elixir homoiconicity is a tuple with three
368-
elements, for example:
367+
The building block of Elixir macros is a tuple with three elements,
368+
for example:
369369
370-
{ :sum, , [1, 2, 3] }
370+
{ :sum, [], [1, 2, 3] }
371371
372372
The tuple above represents a function call to sum passing 1, 2 and
373373
3 as arguments. The tuple elements are:
374374
375375
* The first element of the tuple is always an atom or
376376
another tuple in the same representation;
377-
* The second element of the tuple represents metadata.
378-
For example, quoted expressions contain [quoted: true]
379-
to represent the element was generated from a quote.
377+
* The second element of the tuple represents metadata;
380378
* The third element of the tuple are the arguments for the
381379
function call. The third argument may be an atom, which is
382380
usually a variable (or a local call);
@@ -420,7 +418,7 @@ defmodule Kernel.SpecialForms do
420418
end
421419
422420
ContextSample.hello
423-
#=> {:world,[quoted: true],ContextSample}
421+
#=> {:world,[],ContextSample}
424422
425423
Notice how the third element of the returned tuple is the
426424
module name. This means that the variable is associated to the
@@ -714,7 +712,7 @@ defmodule Kernel.SpecialForms do
714712
and should not be invoked directly:
715713
716714
iex> quote do: (1; 2; 3)
717-
{ :__block__, [quoted: true], [1,2,3] }
715+
{ :__block__, [], [1,2,3] }
718716
719717
"""
720718
defmacro __block__(args)
@@ -737,13 +735,13 @@ defmodule Kernel.SpecialForms do
737735
It is usually compiled to an atom:
738736
739737
quote do: Foo.Bar #=>
740-
{ :__aliases__, [quoted: true], [:Foo,:Bar] }
738+
{ :__aliases__, [], [:Foo,:Bar] }
741739
742740
Elixir represents `Foo.Bar` as `__aliases__` so calls can be
743741
unambiguously identified by the operator `:.`. For example:
744742
745743
quote do: Foo.bar #=>
746-
{{:.,[quoted: true],[{:__aliases__,[quoted: true],[:Foo]},:bar]},[],[]}
744+
{{:.,[],[{:__aliases__,[],[:Foo]},:bar]},[],[]}
747745
748746
Whenever an expression iterator sees a `:.` as the tuple key,
749747
it can be sure that it represents a call and the second argument
@@ -760,7 +758,7 @@ defmodule Kernel.SpecialForms do
760758
4) When the head element of aliases is not an atom, it is expanded at runtime:
761759
762760
quote do: some_var.Foo
763-
{:__aliases__,[quoted: true],[{:some_var,[quoted: true],:quoted},:Bar]}
761+
{:__aliases__,[],[{:some_var,[],:quoted},:Bar]}
764762
765763
Since `some_var` is not available at compilation time, the compiler
766764
expands such expression to:

lib/elixir/src/elixir_aliases.erl

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
11
-module(elixir_aliases).
22
-export([nesting_alias/2, last/1, concat/1, safe_concat/1,
3-
format_error/1, ensure_loaded/3, expand/3]).
3+
format_error/1, ensure_loaded/3, expand/3, store/4]).
44
-include("elixir.hrl").
55
-compile({parse_transform, elixir_transform}).
66

7+
%% Store an alias in the given scope
8+
store(_Meta, New, New, S) -> S;
9+
store(Meta, New, Old, S) ->
10+
SA = S#elixir_scope{
11+
aliases=orddict:store(New, Old, S#elixir_scope.aliases)
12+
},
13+
14+
case lists:keymember(context, 1, Meta) of
15+
true ->
16+
SA#elixir_scope{
17+
macro_aliases=orddict:store(New, Old, S#elixir_scope.macro_aliases)
18+
};
19+
false ->
20+
SA
21+
end.
22+
723
%% Expand an alias. It returns an atom (meaning that there
824
%% was an expansion) or a list of atoms.
925

lib/elixir/src/elixir_import.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ record_warn(Meta, Ref, Opts, S) ->
3636
case keyfind(warn, Opts) of
3737
{ warn, false } -> false;
3838
{ warn, true } -> true;
39-
false -> lists:keyfind(quoted, 1, Meta) /= { quoted, true }
39+
false -> not lists:keymember(context, 1, Meta)
4040
end,
4141

4242
Warn andalso ets:insert(Table, { Ref, ?line(Meta) }).

lib/elixir/src/elixir_macros.erl

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@
66
-import(elixir_scope, [umergec/2]).
77
-import(elixir_errors, [syntax_error/3, syntax_error/4,
88
assert_no_function_scope/3, assert_module_scope/3, assert_no_match_or_guard_scope/3]).
9-
-include("elixir.hrl").
10-
11-
-define(FUNS(Kind), Kind == def; Kind == defp; Kind == defmacro; Kind == defmacrop).
12-
-define(IN_TYPES(Kind), Kind == atom orelse Kind == integer orelse Kind == float).
139

10+
-include("elixir.hrl").
11+
-define(opt_in_types(Kind), Kind == atom orelse Kind == integer orelse Kind == float).
1412
-compile({parse_transform, elixir_transform}).
1513

1614
%% Operators
@@ -201,12 +199,8 @@ translate({defmodule, Meta, [Ref, KV]}, S) ->
201199
FullModule = expand_module(Ref, Module, S),
202200

203201
RS = case elixir_aliases:nesting_alias(S#elixir_scope.module, FullModule) of
204-
{ New, Old } ->
205-
S#elixir_scope{
206-
aliases=orddict:store(New, Old, S#elixir_scope.aliases),
207-
macro_aliases=orddict:store(New, Old, S#elixir_scope.macro_aliases)};
208-
false ->
209-
S
202+
{ New, Old } -> elixir_aliases:store(Meta, New, Old, S);
203+
false -> S
210204
end,
211205

212206
{
@@ -220,19 +214,19 @@ translate({defmodule, Meta, [Ref, KV]}, S) ->
220214
MS = FS#elixir_scope{local=nil},
221215
{ elixir_module:translate(Meta, FRef, Block, MS), FS };
222216

223-
translate({Kind, Meta, [Call]}, S) when ?FUNS(Kind) ->
217+
translate({Kind, Meta, [Call]}, S) when ?defs(Kind) ->
224218
translate({Kind, Meta, [Call, nil]}, S);
225219

226-
translate({Kind, Meta, [Call, Expr]}, S) when ?FUNS(Kind) ->
220+
translate({Kind, Meta, [Call, Expr]}, S) when ?defs(Kind) ->
227221
assert_module_scope(Meta, Kind, S),
228222
assert_no_function_scope(Meta, Kind, S),
229223
{ TCall, QC, SC } = elixir_quote:erl_escape(Call, true, S),
230224
{ TExpr, QE, SE } = elixir_quote:erl_escape(Expr, true, SC),
231-
CheckClauses = (lists:keyfind(quoted, 1, Meta) /= { quoted, true }) andalso
225+
CheckClauses = (not lists:keymember(context, 1, Meta)) andalso
232226
(not QC#elixir_quote.unquoted) andalso (not QE#elixir_quote.unquoted),
233227
{ elixir_def:wrap_definition(Kind, Meta, TCall, TExpr, CheckClauses, SE), SE };
234228

235-
translate({Kind, Meta, [Name, Args, Guards, Expr]}, S) when ?FUNS(Kind) ->
229+
translate({Kind, Meta, [Name, Args, Guards, Expr]}, S) when ?defs(Kind) ->
236230
assert_module_scope(Meta, Kind, S),
237231
assert_no_function_scope(Meta, Kind, S),
238232
{ TName, SN } = translate_each(Name, S),
@@ -287,9 +281,9 @@ translate_in(Meta, Left, Right, S) ->
287281
{ Cache, Expr };
288282
{ tuple, _, [{ atom, _, 'Elixir.Range' }, Start, End] } ->
289283
Expr = case { Start, End } of
290-
{ { K1, _, StartInt }, { K2, _, EndInt } } when ?IN_TYPES(K1), ?IN_TYPES(K2), StartInt =< EndInt ->
284+
{ { K1, _, StartInt }, { K2, _, EndInt } } when ?opt_in_types(K1), ?opt_in_types(K2), StartInt =< EndInt ->
291285
increasing_compare(Line, Var, Start, End);
292-
{ { K1, _, _ }, { K2, _, _ } } when ?IN_TYPES(K1), ?IN_TYPES(K2) ->
286+
{ { K1, _, _ }, { K2, _, _ } } when ?opt_in_types(K1), ?opt_in_types(K2) ->
293287
decreasing_compare(Line, Var, Start, End);
294288
_ ->
295289
{ op, Line, 'orelse',

lib/elixir/src/elixir_quote.erl

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,17 @@ do_quote({ quote, _, Args } = Tuple, #elixir_quote{unquote=true} = Q, S) when le
8181
do_quote({ unquote, _Meta, [Expr] }, #elixir_quote{unquote=true} = Q, _) ->
8282
{ Expr, Q#elixir_quote{unquoted=true} };
8383

84-
do_quote({ function, Meta, [{ '/', _, [{F, _, C}, A]}] = Args },
85-
#elixir_quote{imports_hygiene=true} = Q, S) when is_atom(F), is_integer(A), is_atom(C) ->
86-
do_quote_fa(function, Meta, Args, F, A, Q, S);
84+
%% Context mark
8785

88-
do_quote({ { '.', _, [_, function] } = Target, Meta, [{ '/', _, [{F, _, C}, A]}] = Args },
89-
#elixir_quote{imports_hygiene=true} = Q, S) when is_atom(F), is_integer(A), is_atom(C) ->
90-
do_quote_fa(Target, Meta, Args, F, A, Q, S);
86+
do_quote({ Def, Meta, Args }, #elixir_quote{mark=true} = Q, S) when ?defs(Def); Def == defmodule; Def == alias; Def == import ->
87+
NewMeta = lists:keystore(context, 1, Meta, { context, Q#elixir_quote.context }),
88+
do_quote_tuple({ Def, NewMeta, Args }, Q, S);
89+
90+
do_quote({ { '.', _, [_, Def] } = Target, Meta, Args }, #elixir_quote{mark=true} = Q, S) when ?defs(Def); Def == defmodule; Def == alias; Def == import ->
91+
NewMeta = lists:keystore(context, 1, Meta, { context, Q#elixir_quote.context }),
92+
do_quote_tuple({ Target, NewMeta, Args }, Q, S);
93+
94+
%% Aliases
9195

9296
do_quote({ 'alias!', _Meta, [Expr] }, #elixir_quote{aliases_hygiene=true} = Q, S) ->
9397
{ TExpr, TQ } = do_quote(Expr, Q#elixir_quote{aliases_hygiene=false}, S),
@@ -98,21 +102,31 @@ do_quote({ '__aliases__', Meta, [H|T] } = Alias, #elixir_quote{aliases_hygiene=t
98102
Atom when is_atom(Atom) -> Atom;
99103
Aliases when is_list(Aliases) -> false
100104
end,
101-
NewMeta = lists:keystore(alias, 1, Meta, { alias, Annotation }),
102-
do_quote_tuple({ '__aliases__', NewMeta, [H|T] }, Q, S);
105+
AliasMeta = lists:keystore(alias, 1, Meta, { alias, Annotation }),
106+
do_quote_tuple({ '__aliases__', AliasMeta, [H|T] }, Q, S);
107+
108+
%% Vars
109+
110+
do_quote({ Left, Meta, nil }, #elixir_quote{vars_hygiene=true} = Q, S) when is_atom(Left) ->
111+
do_quote_tuple({ Left, Meta, Q#elixir_quote.context }, Q, S);
112+
113+
%% Unquote
103114

104115
do_quote({ { { '.', Meta, [Left, unquote] }, _, [Expr] }, _, Args }, #elixir_quote{unquote=true} = Q, S) ->
105116
do_quote_call(Left, Meta, Expr, Args, Q, S);
106117

107118
do_quote({ { '.', Meta, [Left, unquote] }, _, [Expr] }, #elixir_quote{unquote=true} = Q, S) ->
108119
do_quote_call(Left, Meta, Expr, nil, Q, S);
109120

110-
do_quote({ Left, Meta, nil }, #elixir_quote{vars_hygiene=true} = Q, S) when is_atom(Left) ->
111-
do_quote_tuple({ Left, Meta, Q#elixir_quote.context }, Q, S);
121+
%% Imports
122+
123+
do_quote({ function, Meta, [{ '/', _, [{F, _, C}, A]}] = Args },
124+
#elixir_quote{imports_hygiene=true} = Q, S) when is_atom(F), is_integer(A), is_atom(C) ->
125+
do_quote_fa(function, Meta, Args, F, A, Q, S);
112126

113-
do_quote({ import, Meta, Args }, #elixir_quote{imports_hygiene=true} = Q, S) ->
114-
ImportMeta = lists:keystore(import, 1, Meta, { context, Q#elixir_quote.context }),
115-
do_quote_tuple({ import, ImportMeta, Args }, Q, S);
127+
do_quote({ { '.', _, [_, function] } = Target, Meta, [{ '/', _, [{F, _, C}, A]}] = Args },
128+
#elixir_quote{imports_hygiene=true} = Q, S) when is_atom(F), is_integer(A), is_atom(C) ->
129+
do_quote_fa(Target, Meta, Args, F, A, Q, S);
116130

117131
do_quote({ Name, Meta, ArgsOrAtom } = Tuple, #elixir_quote{imports_hygiene=true} = Q, S) when is_atom(Name) ->
118132
Arity = case is_atom(ArgsOrAtom) of
@@ -133,6 +147,8 @@ do_quote({ Name, Meta, ArgsOrAtom } = Tuple, #elixir_quote{imports_hygiene=true}
133147
do_quote({ _, _, _ } = Tuple, Q, S) ->
134148
do_quote_tuple(Tuple, Q, S);
135149

150+
%% Literals
151+
136152
do_quote({ Left, Right }, #elixir_quote{unquote=true} = Q, S) when
137153
is_tuple(Left) andalso (element(1, Left) == unquote_splicing);
138154
is_tuple(Right) andalso (element(1, Right) == unquote_splicing) ->
@@ -174,18 +190,11 @@ do_quote_tuple({ Left, Meta, Right }, Q, S) ->
174190
{ TRight, RQ } = do_quote(Right, LQ, S),
175191
{ { '{}', [], [TLeft, meta(Meta, Q), TRight] }, RQ }.
176192

177-
meta(Meta, Q) -> mark_meta(line_meta(Meta, Q), Q).
178-
179-
mark_meta(Meta, #elixir_quote{mark=true}) ->
180-
lists:keystore(quoted, 1, Meta, { quoted, true });
181-
mark_meta(Meta, #elixir_quote{mark=false}) ->
182-
Meta.
183-
184-
line_meta(Meta, #elixir_quote{line=keep}) ->
193+
meta(Meta, #elixir_quote{line=keep}) ->
185194
Meta;
186-
line_meta(Meta, #elixir_quote{line=nil}) ->
195+
meta(Meta, #elixir_quote{line=nil}) ->
187196
lists:keydelete(line, 1, Meta);
188-
line_meta(Meta, #elixir_quote{line=Line}) ->
197+
meta(Meta, #elixir_quote{line=Line}) ->
189198
lists:keystore(line, 1, Meta, { line, Line }).
190199

191200
%% Quote splicing

lib/elixir/src/elixir_translator.erl

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -101,32 +101,15 @@ translate_each({ alias, Meta, [Ref, KV] }, S) ->
101101

102102
case TRef of
103103
{ atom, _, Old } ->
104-
{ New, SF } = expand_alias_as(Meta, Old, KV, SR),
104+
{ New, _ } = expand_alias_as(Meta, Old, KV, SR),
105105

106-
%% Avoid creating aliases if first == last
107-
%% unecessarily polluting the aliases dict
108-
case New == Old of
109-
true -> { { atom, ?line(Meta), nil }, SF };
110-
false ->
111-
case string:tokens(atom_to_list(New), "-") of
112-
[_,_] -> [];
113-
_ -> syntax_error(Meta, S#elixir_scope.file,
106+
case (New == Old) orelse (length(string:tokens(atom_to_list(New), "-")) == 2) of
107+
true -> ok;
108+
false -> syntax_error(Meta, S#elixir_scope.file,
114109
"invalid args for alias, cannot create nested alias ~s", [elixir_errors:inspect(New)])
115-
end,
116-
117-
SA = SF#elixir_scope{
118-
aliases=orddict:store(New, Old, S#elixir_scope.aliases)
119-
},
120-
121-
SM = case lists:keyfind(quoted, 1, Meta) of
122-
{ quoted, true } -> SA#elixir_scope{
123-
macro_aliases=orddict:store(New, Old, S#elixir_scope.macro_aliases)
124-
};
125-
_ -> SA
126-
end,
110+
end,
127111

128-
{ { atom, ?line(Meta), nil }, SM }
129-
end;
112+
{ { atom, ?line(Meta), nil }, elixir_aliases:store(Meta, New, Old, SR) };
130113
_ ->
131114
syntax_error(Meta, S#elixir_scope.file, "invalid args for alias, expected an atom or alias as argument")
132115
end;

lib/elixir/test/elixir/kernel/quote_test.exs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,29 +21,29 @@ defmodule Kernel.QuoteTest do
2121

2222
test :keep_line do
2323
## DO NOT MOVE THIS LINE
24-
assert quote(line: :keep, do: bar(1,2,3)) == { :bar, [line: 24, quoted: true], [1,2,3] }
24+
assert quote(line: :keep, do: bar(1,2,3)) == { :bar, [line: 24], [1,2,3] }
2525
end
2626

2727
test :fixed_line do
28-
assert quote(line: 3, do: bar(1,2,3)) == { :bar, [line: 3, quoted: true], [1,2,3] }
28+
assert quote(line: 3, do: bar(1,2,3)) == { :bar, [line: 3], [1,2,3] }
2929
end
3030

3131
test :keep_location do
3232
## DO NOT MOVE THIS LINE
3333
assert quote(location: :keep, do: bar(1,2,3)) == {
3434
:__scope__,
35-
[line: 33, quoted: true],
35+
[line: 33],
3636
[
3737
[file: __FILE__],
38-
[do: { :bar, [line: 33, quoted: true], [1,2,3] }]
38+
[do: { :bar, [line: 33], [1,2,3] }]
3939
]
4040
}
4141
end
4242

4343
test :quote_line_var do
4444
## DO NOT MOVE THIS LINE
4545
line = __ENV__.line
46-
assert quote(line: line, do: bar(1,2,3)) == { :bar, [line: 45, quoted: true], [1,2,3] }
46+
assert quote(line: line, do: bar(1,2,3)) == { :bar, [line: 45], [1,2,3] }
4747
end
4848

4949
test :unquote_call do
@@ -202,10 +202,10 @@ defmodule Kernel.QuoteTest.AliasHygieneTest do
202202
alias Dict, as: SuperDict
203203

204204
test :annotate_aliases do
205-
assert quote(do: Foo.Bar) == { :__aliases__, [alias: false, quoted: true], [:Foo, :Bar] }
206-
assert quote(do: Dict.Bar) == { :__aliases__, [alias: false, quoted: true], [:Dict, :Bar] }
207-
assert quote(do: SuperDict.Bar) == { :__aliases__, [alias: Dict.Bar, quoted: true], [:SuperDict, :Bar] }
208-
assert quote(do: alias!(SuperDict.Bar)) == { :__aliases__, [quoted: true], [:SuperDict, :Bar] }
205+
assert quote(do: Foo.Bar) == { :__aliases__, [alias: false], [:Foo, :Bar] }
206+
assert quote(do: Dict.Bar) == { :__aliases__, [alias: false], [:Dict, :Bar] }
207+
assert quote(do: SuperDict.Bar) == { :__aliases__, [alias: Dict.Bar], [:SuperDict, :Bar] }
208+
assert quote(do: alias!(SuperDict.Bar)) == { :__aliases__, [], [:SuperDict, :Bar] }
209209
end
210210

211211
test :expand_aliases do

lib/elixir/test/elixir/macro_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ defmodule MacroTest do
4141
assert Macro.escape_quoted(contents) == 1
4242

4343
contents = quote unquote: false, do: unquote(x)
44-
assert Macro.escape_quoted(contents) == { :x, [quoted: true], MacroTest }
44+
assert Macro.escape_quoted(contents) == { :x, [], MacroTest }
4545
end
4646

4747
defp eval_escaped(contents) do

lib/elixir/test/elixir/typespec_test.exs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,8 @@ defmodule Typespec.TypeTest do
377377
],
378378
[]}
379379
assert Kernel.Typespec.type_to_ast(record_type) ==
380-
{ :::,[quoted: true], [
381-
{ :my_record,[quoted: true],[] },
380+
{ :::,[], [
381+
{ :my_record,[],[] },
382382
{ :{},[], [:my_record,
383383
{ :::, [line: 0], [
384384
{:field1,0,nil},
@@ -395,7 +395,7 @@ defmodule Typespec.TypeTest do
395395
test "type_to_ast for paren_type" do
396396
type = {:my_type, {:paren_type, 0, [{:type, 0, :integer, []}]}, []}
397397
assert Kernel.Typespec.type_to_ast(type) ==
398-
{ :::, [quoted: true], [{:my_type,[quoted: true],[]}, {:integer,[line: 0],[]}] }
398+
{ :::, [], [{:my_type,[],[]}, {:integer,[line: 0],[]}] }
399399
end
400400

401401
test "spec_to_ast" do

0 commit comments

Comments
 (0)