Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
4 changes: 2 additions & 2 deletions lib/elixir/lib/kernel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5185,7 +5185,7 @@ defmodule Kernel do
quote(do: Kernel.LexicalTracker.read_cache(unquote(pid), unquote(integer)))

%{} ->
:elixir_quote.escape(block, :none, false)
:elixir_quote.escape(block, :escape, false)
end

versioned_vars = env.versioned_vars
Expand Down Expand Up @@ -5465,7 +5465,7 @@ defmodule Kernel do
store =
case unquoted_expr or unquoted_call do
true ->
:elixir_quote.escape({call, expr}, :none, true)
:elixir_quote.escape({call, expr}, :escape, true)

false ->
key = :erlang.unique_integer()
Expand Down
4 changes: 2 additions & 2 deletions lib/elixir/lib/kernel/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ defmodule Kernel.Utils do
key == :__struct__ and raise(ArgumentError, "cannot set :__struct__ in struct definition")

try do
:elixir_quote.escape(val, :none, false)
:elixir_quote.escape(val, :escape, false)
rescue
e in [ArgumentError] ->
raise ArgumentError, "invalid value for struct field #{key}, " <> Exception.message(e)
Expand Down Expand Up @@ -171,7 +171,7 @@ defmodule Kernel.Utils do

:lists.foreach(foreach, enforce_keys)
struct = :maps.from_list([__struct__: module] ++ fields)
escaped_struct = :elixir_quote.escape(struct, :none, false)
escaped_struct = :elixir_quote.escape(struct, :escape, false)

body =
case bootstrapped? do
Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/macro.ex
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,7 @@ defmodule Macro do
@spec escape(term, escape_opts) :: t()
def escape(expr, opts \\ []) do
unquote = Keyword.get(opts, :unquote, false)
kind = if Keyword.get(opts, :prune_metadata, false), do: :prune_metadata, else: :none
kind = if Keyword.get(opts, :prune_metadata, false), do: :escape_and_prune, else: :escape
generated = Keyword.get(opts, :generated, false)

case :elixir_quote.escape(expr, kind, unquote) do
Expand Down
36 changes: 21 additions & 15 deletions lib/elixir/src/elixir_quote.erl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
line=false,
file=nil,
context=nil,
op=none, % none | prune_metadata | add_context
op=escape, % escape | escape_and_prune | quote
aliases_hygiene=nil,
imports_hygiene=nil,
unquote=true,
Expand Down Expand Up @@ -145,21 +145,27 @@ do_tuple_linify(Fun, Meta, Left, Right, Var, Gen) ->
%% Escapes the given expression. It is similar to quote, but
%% lines are kept and hygiene mechanisms are disabled.
escape(Expr, Op, Unquote) ->
do_quote(Expr, #elixir_quote{
Q = #elixir_quote{
line=true,
file=nil,
op=Op,
unquote=Unquote
}).
},
case Unquote of
true -> do_quote(Expr, Q);
false -> do_escape(Expr, Q)
end.

do_escape({Left, Meta, Right}, #elixir_quote{op=prune_metadata} = Q) when is_list(Meta) ->
do_escape({Left, Meta, Right}, #elixir_quote{op=escape_and_prune} = Q) when is_list(Meta) ->
TM = [{K, V} || {K, V} <- Meta, (K == no_parens) orelse (K == line) orelse (K == delimiter)],
TL = do_quote(Left, Q),
TR = do_quote(Right, Q),
TL = do_escape(Left, Q),
TR = do_escape(Right, Q),
{'{}', [], [TL, TM, TR]};

do_escape({Left, Right}, Q) ->
{do_escape(Left, Q), do_escape(Right, Q)};
do_escape(Tuple, Q) when is_tuple(Tuple) ->
TT = do_quote(tuple_to_list(Tuple), Q),
TT = do_escape(tuple_to_list(Tuple), Q),
{'{}', [], TT};

do_escape(BitString, _) when is_bitstring(BitString) ->
Expand Down Expand Up @@ -193,7 +199,7 @@ do_escape([], _) ->
[];

do_escape([H | T], #elixir_quote{unquote=false} = Q) ->
do_quote_simple_list(T, do_quote(H, Q), Q);
do_quote_simple_list(T, do_escape(H, Q), Q);

do_escape([H | T], Q) ->
%% The improper case is inefficient, but improper lists are rare.
Expand All @@ -203,7 +209,7 @@ do_escape([H | T], Q) ->
_:_ ->
{L, R} = reverse_improper(T, [H]),
TL = do_quote_splice(L, Q, [], []),
TR = do_quote(R, Q),
TR = do_escape(R, Q),
update_last(TL, fun(X) -> {'|', [], [X, TR]} end)
end;

Expand Down Expand Up @@ -232,7 +238,7 @@ escape_map_key_value(K, V, Map, Q) ->
('Elixir.Kernel':inspect(MaybeRef, []))/binary, ") and therefore it cannot be escaped ",
"(it must be defined within a function instead). ", (bad_escape_hint())/binary>>);
true ->
{do_quote(K, Q), do_quote(V, Q)}
{do_escape(K, Q), do_escape(V, Q)}
end.

find_tuple_ref(Tuple, Index) when Index > tuple_size(Tuple) -> nil;
Expand Down Expand Up @@ -261,7 +267,7 @@ build(Meta, Line, File, Context, Unquote, Generated, E) ->
validate_runtime(generated, Generated),

Q = #elixir_quote{
op=add_context,
op=quote,
aliases_hygiene=E,
imports_hygiene=E,
line=VLine,
Expand Down Expand Up @@ -341,7 +347,7 @@ do_quote({quote, Meta, [Arg]}, Q) when is_list(Meta) ->
TArg = do_quote(Arg, Q#elixir_quote{unquote=false}),

NewMeta = case Q of
#elixir_quote{op=add_context, context=Context} -> keystore(context, Meta, Context);
#elixir_quote{op=quote, context=Context} -> keystore(context, Meta, Context);
_ -> Meta
end,

Expand All @@ -352,7 +358,7 @@ do_quote({quote, Meta, [Opts, Arg]}, Q) when is_list(Meta) ->
TArg = do_quote(Arg, Q#elixir_quote{unquote=false}),

NewMeta = case Q of
#elixir_quote{op=add_context, context=Context} -> keystore(context, Meta, Context);
#elixir_quote{op=quote, context=Context} -> keystore(context, Meta, Context);
_ -> Meta
end,

Expand All @@ -379,7 +385,7 @@ do_quote({'__aliases__', Meta, [H | T]}, #elixir_quote{aliases_hygiene=(#{}=E)}

%% Vars

do_quote({Name, Meta, nil}, #elixir_quote{op=add_context} = Q)
do_quote({Name, Meta, nil}, #elixir_quote{op=quote} = Q)
when is_atom(Name), is_list(Meta) ->
ImportMeta = case Q#elixir_quote.imports_hygiene of
nil -> Meta;
Expand Down Expand Up @@ -435,7 +441,7 @@ do_quote({Left, Right}, Q) ->

%% Everything else

do_quote(Other, #elixir_quote{op=Op} = Q) when Op =/= add_context ->
do_quote(Other, #elixir_quote{op=Op} = Q) when Op =/= quote ->
do_escape(Other, Q);

do_quote({_, _, _} = Tuple, Q) ->
Expand Down
5 changes: 5 additions & 0 deletions lib/elixir/test/elixir/macro_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ defmodule MacroTest do
assert Macro.escape({:quote, [], [[do: :foo]]}) == {:{}, [], [:quote, [], [[do: :foo]]]}
end

test "escapes the content of :quote tuples" do
assert Macro.escape({:quote, [%{}], [{}]}) ==
{:{}, [], [:quote, [{:%{}, [], []}], [{:{}, [], []}]]}
end

test "escape container when a reference cannot be escaped" do
assert_raise ArgumentError, ~r"contains a reference", fn ->
Macro.escape(%{re_pattern: {:re_pattern, 0, 0, 0, make_ref()}})
Expand Down