Skip to content

Commit cd51c80

Browse files
author
José Valim
committed
Tidy up and clean up quote API, closes #814
1 parent a1a576e commit cd51c80

File tree

11 files changed

+58
-53
lines changed

11 files changed

+58
-53
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* [Mix] Allow Mix projects to be generated with just one letter
1010

1111
* deprecations
12+
* [Kernel] Tidy up and clean `quote` API
1213
* [Kernel] Old `:local.(args)` syntax is deprecated
1314

1415
# v0.8.0 (2013-01-28)

lib/elixir/include/elixir.hrl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@
3333

3434
-record(elixir_quote, {
3535
line=0,
36-
var_context=nil,
37-
expand_aliases=true,
38-
expand_imports=true,
36+
vars_hygiene=nil,
37+
aliases_hygiene=true,
38+
imports_hygiene=true,
3939
unquote=true
4040
}).
4141

lib/elixir/lib/kernel/special_forms.ex

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -355,15 +355,12 @@ defmodule Kernel.SpecialForms do
355355
356356
## Options
357357
358-
* `:hygiene` - When false, disables hygiene for variables, aliases and imports;
359358
* `:unquote` - When false, disables unquoting. Useful when you have a quote
360359
inside another quote and want to control which quote is
361360
able to unquote;
362361
* `:location` - When set to `:keep`, keeps the current line and file on quotes.
363362
Read the Stacktrace information section below for more information;
364-
* `:expand_aliases` - When false, do not expand aliases;
365-
* `:expand_imports` - When false, do not expand imports;
366-
* `:var_context` - The context for quoted variables. Defaults to the current module;
363+
* `:hygiene` - Allows a developer to disable hygiene selectively;
367364
368365
## Macro literals
369366
@@ -379,17 +376,15 @@ defmodule Kernel.SpecialForms do
379376
380377
## Hygiene
381378
382-
Elixir macros are hygienic. This means aliases and imports
383-
defined inside the quoted often refer to the context that
384-
defined the macro.
379+
Elixir macros are hygienic via means of deferred resolution.
385380
386-
Furthermore, variables inside quote are also hygienic. That
387-
said, a variable defined in a macro cannot affect the scope
388-
where the macro is included.
381+
This means aliases and imports defined inside the quoted refer
382+
to the context that defined the macro and not the context
383+
where the macro is expanded.
389384
390-
The option `hygiene` can turn off all the hygiene mechanisms
391-
defined below. However, they can also be changed in a one by
392-
one basis.
385+
Furthermore, variables inside quote are also hygienic: a
386+
variable defined in a macro cannot affect the variables where
387+
the macro is expanded.
393388
394389
### Hygiene in variables
395390
@@ -451,10 +446,9 @@ defmodule Kernel.SpecialForms do
451446
# Access the variable a from Hygiene1
452447
quote do: var!(a, Hygiene1) = 2
453448
454-
Another option is to set the `var_context` option, affecting
455-
all variables in the block:
449+
Hygiene for variables can be disabled overall as:
456450
457-
quote var_context: Hygiene1, do: a = 2
451+
quote hygiene: [vars: false], do: x
458452
459453
### Hygiene in aliases
460454
@@ -476,9 +470,9 @@ defmodule Kernel.SpecialForms do
476470
in the context the macro is expanded, the code above works
477471
because `D` was expanded when the quote was generated.
478472
479-
There are two ways to disable this behaviour. By giving
480-
the `expand_aliases` equals to false to quote or by using
481-
the `alias!` macro inside the quote:
473+
There are two ways to disable this behaviour. By disabling
474+
hygiene for aliases or by using the `alias!` macro inside
475+
the quote:
482476
483477
defmodule NoHygiene do
484478
defmacro interference do
@@ -536,7 +530,7 @@ defmodule Kernel.SpecialForms do
536530
Lazy.return_size #=> 2
537531
538532
As in aliases, imports expansion can be explicitly disabled
539-
via the `expand_imports` option.
533+
via the `hygiene: [imports: false]` option.
540534
541535
## Stacktrace information
542536

lib/elixir/lib/kernel/typespec.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ defmodule Kernel.Typespec do
583583
# Handle access macro
584584
defp typespec({{:., meta, [Kernel, :access]}, meta1, [target, args]}, vars, caller) do
585585
access = {{:., meta, [Kernel, :access]}, meta1,
586-
[target, args ++ [_: (quote hygiene: false, do: any)]]}
586+
[target, args ++ [_: { :any, [], [] }]]}
587587
typespec(Macro.expand(access, caller), vars, caller)
588588
end
589589

lib/elixir/src/elixir_errors.erl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ form_error(Meta, File, Module, Desc) ->
7777

7878
%% Shows a deprecation message
7979

80-
deprecation(Line, File, Message) -> deprecation(Line, File, Message, []).
80+
deprecation(Meta, File, Message) -> deprecation(Meta, File, Message, []).
8181

82-
deprecation(Line, File, Message, Args) ->
83-
io:format(file_format(Line, File, io_lib:format(Message, Args))).
82+
deprecation(Meta, File, Message, Args) ->
83+
io:format(file_format(?line(Meta), File, io_lib:format(Message, Args))).
8484

8585
%% Handle warnings and errors (called during module compilation)
8686

lib/elixir/src/elixir_quote.erl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ do_quote({ unquote, _Meta, [Expr] }, #elixir_quote{unquote=true}, S) ->
5252
elixir_translator:translate_each(Expr, S);
5353

5454
do_quote({ 'alias!', _Meta, [Expr] }, Q, S) ->
55-
do_quote(Expr, Q#elixir_quote{expand_aliases=false}, S);
55+
do_quote(Expr, Q#elixir_quote{aliases_hygiene=false}, S);
5656

57-
% do_quote({ '__aliases__', Meta, [H|_] = Aliases }, #elixir_quote{expand_aliases=true} = Q, S) when is_atom(H) and H /= 'Elixir' ->
57+
% do_quote({ '__aliases__', Meta, [H|_] = Aliases }, #elixir_quote{aliases_hygiene=true} = Q, S) when is_atom(H) and H /= 'Elixir' ->
5858
% Line = ?line(Meta),
5959
% { TAliases, SA } = do_quote(['Elixir'|Aliases], Q, S),
6060
%
@@ -79,11 +79,11 @@ do_quote({ Left, Meta, nil }, Q, S) when is_atom(Left) ->
7979
Tuple = { tuple, Line, [
8080
{ atom, Line, Left },
8181
meta(Meta, Q),
82-
{ atom, Line, Q#elixir_quote.var_context }
82+
{ atom, Line, Q#elixir_quote.vars_hygiene }
8383
] },
8484
{ Tuple, S };
8585

86-
do_quote({ Name, Meta, ArgsOrAtom } = Tuple, #elixir_quote{expand_imports=true} = Q, S) when is_atom(Name) ->
86+
do_quote({ Name, Meta, ArgsOrAtom } = Tuple, #elixir_quote{imports_hygiene=true} = Q, S) when is_atom(Name) ->
8787
Arity = case is_atom(ArgsOrAtom) of
8888
true -> 0;
8989
false -> length(ArgsOrAtom)

lib/elixir/src/elixir_translator.erl

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -244,13 +244,25 @@ translate_each({ quote, Meta, [T] }, S) when is_list(T) ->
244244
syntax_error(Meta, S#elixir_scope.file, "invalid args for quote")
245245
end,
246246

247-
Context = case lists:keyfind(var_context, 1, T) of
247+
Hygiene = case lists:keyfind(hygiene, 1, T) of
248+
{ hygiene, true } ->
249+
elixir_errors:deprecation(Meta, S#elixir_scope.file, "hygiene: true is deprecated, please set it to [] instead", []),
250+
[];
251+
{ hygiene, List } when is_list(List) ->
252+
List;
253+
{ hygiene, false } ->
254+
elixir_errors:deprecation(Meta, S#elixir_scope.file, "hygiene: false is deprecated, please set it to [vars: false] instead", []),
255+
[{vars,false}, {imports,false}, {aliases,false}];
256+
false ->
257+
[]
258+
end,
259+
260+
Vars = case lists:keyfind(var_context, 1, T) of
248261
{ var_context, VarContext } ->
249-
expand_var_context(Meta, VarContext,
250-
"invalid argument given for var_context in quote", S);
262+
expand_var_context(Meta, VarContext, "invalid argument given for var_context in quote", S);
251263
false ->
252-
case lists:keyfind(hygiene, 1, T) of
253-
{ hygiene, false } -> nil;
264+
case lists:keyfind(vars, 1, Hygiene) of
265+
{ vars, false } -> nil;
254266
_ ->
255267
case S#elixir_scope.module of
256268
nil -> 'Elixir';
@@ -259,20 +271,18 @@ translate_each({ quote, Meta, [T] }, S) when is_list(T) ->
259271
end
260272
end,
261273

262-
GetExpansion = fun(Key) ->
274+
GetExpansion = fun(Key, New) ->
263275
case lists:keyfind(Key, 1, T) of
264276
{ Key, Bool } when is_boolean(Bool) ->
277+
elixir_errors:deprecation(Meta, S#elixir_scope.file, "the ~s option for quote is deprecated, please use hygiene: [~s: ~s] instead", [Key, New, Bool]),
265278
Bool;
266279
false ->
267-
case lists:keyfind(hygiene, 1, T) of
268-
{ hygiene, Bool } when is_boolean(Bool) -> Bool;
269-
false -> true
270-
end
280+
not(lists:keyfind(New, 1, Hygiene) == { New, false })
271281
end
272282
end,
273283

274-
Aliases = GetExpansion(expand_aliases),
275-
Imports = GetExpansion(expand_imports),
284+
Aliases = GetExpansion(aliases_hygiene, aliases),
285+
Imports = GetExpansion(imports_hygiene, imports),
276286

277287
{ DefaultLine, DefaultFile } = case lists:keyfind(location, 1, T) of
278288
{ location, keep } -> { keep, keep };
@@ -314,8 +324,8 @@ translate_each({ quote, Meta, [T] }, S) when is_list(T) ->
314324
_ -> true
315325
end,
316326

317-
Q = #elixir_quote{var_context=Context, line=TLine, unquote=Unquote,
318-
expand_aliases=Aliases, expand_imports=Imports},
327+
Q = #elixir_quote{vars_hygiene=Vars, line=TLine, unquote=Unquote,
328+
aliases_hygiene=Aliases, imports_hygiene=Imports},
319329

320330
elixir_quote:quote(TExprs, Q, SL);
321331

@@ -468,11 +478,11 @@ translate_each({ { '.', _, [Left, Right] }, Meta, Args } = Original, S) when is_
468478

469479
%% Anonymous function calls
470480

471-
translate_each({ { '.', Line, [Expr] }, Meta, Args } = Original, S) ->
481+
translate_each({ { '.', _, [Expr] }, Meta, Args } = Original, S) ->
472482
{ TExpr, SE } = translate_each(Expr, S),
473483
case TExpr of
474484
{ atom, _, Atom } ->
475-
elixir_errors:deprecation(?line(Line), S#elixir_scope.file, "the :~s.() syntax is deprecated, please use ~s() instead", [Atom, Atom]),
485+
elixir_errors:deprecation(Meta, S#elixir_scope.file, "the :~s.() syntax is deprecated, please use ~s() instead", [Atom, Atom]),
476486
translate_each({ Atom, Meta, Args }, S);
477487
_ ->
478488
case elixir_partials:handle(Original, S) of

lib/elixir/test/elixir/code_test.exs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ defmodule CodeTest do
6161
end
6262

6363
test :string_to_ast do
64-
assert Code.string_to_ast("1 + 2") == { :ok, quote hygiene: false, line: 1, do: 1 + 2 }
65-
assert Code.string_to_ast("1 + 2; 3 + 4") == { :ok, quote hygiene: false, line: 1, do: (1 + 2; 3 + 4) }
64+
assert Code.string_to_ast("1 + 2") == { :ok, quote hygiene: [imports: false], line: 1, do: 1 + 2 }
65+
assert Code.string_to_ast("1 + 2; 3 + 4") == { :ok, quote hygiene: [imports: false], line: 1, do: (1 + 2; 3 + 4) }
6666
assert { :error, _ } = Code.string_to_ast("a.1")
6767
end
6868

@@ -72,7 +72,7 @@ defmodule CodeTest do
7272
end
7373

7474
test :string_to_ast! do
75-
assert Code.string_to_ast!("1 + 2") == quote hygiene: false, line: 1, do: 1 + 2
75+
assert Code.string_to_ast!("1 + 2") == quote hygiene: [imports: false], line: 1, do: 1 + 2
7676

7777
assert_raise SyntaxError, fn ->
7878
Code.string_to_ast!("a.1")

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ defmodule Kernel.QuoteTest.VarHygiene do
9090
end
9191

9292
defmacro no_hygiene do
93-
quote [hygiene: false] do
93+
quote [hygiene: [vars: false]] do
9494
a = 1
9595
end
9696
end

lib/elixir/test/elixir/module_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ defmodule ModuleTest do
5757
Module.eval_quoted __MODULE__, contents, [], file: "sample.ex", line: 13
5858

5959
defmacrop in_module(block) do
60-
quote expand_aliases: false do
60+
quote do
6161
defmodule Temp, unquote(block)
6262
:code.purge(Temp)
6363
:code.delete(Temp)

0 commit comments

Comments
 (0)