Skip to content

Commit 3cb5b59

Browse files
committed
Do not check deeply, improve message, handle improper lists
1 parent 482fd5d commit 3cb5b59

File tree

2 files changed

+34
-24
lines changed

2 files changed

+34
-24
lines changed

lib/elixir/src/elixir_quote.erl

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -259,15 +259,23 @@ is_valid(unquote, Unquote) -> is_boolean(Unquote).
259259
shallow_validate_ast(Expr) ->
260260
case shallow_valid_ast(Expr) of
261261
true -> Expr;
262-
false -> argument_error(<<"tried to unquote invalid AST: ", ('Elixir.Kernel':inspect(Expr))/binary>>)
262+
false -> argument_error(
263+
<<"tried to unquote invalid AST: ", ('Elixir.Kernel':inspect(Expr))/binary,
264+
"\nDid you forget to escape term using Macro.escape/1?">>)
263265
end.
264266

265-
shallow_valid_ast(Expr) when is_list(Expr) -> lists:all(fun shallow_valid_ast/1, Expr);
266-
shallow_valid_ast(Expr) when is_atom(Expr); is_binary(Expr); is_number(Expr); is_pid(Expr) -> true;
267-
shallow_valid_ast({Left, Right}) -> shallow_valid_ast(Left) andalso shallow_valid_ast(Right);
268-
shallow_valid_ast({Atom, Meta, Args}) when is_atom(Atom), is_list(Meta), is_atom(Args) orelse is_list(Args) -> true;
269-
shallow_valid_ast({Call, Meta, Args}) when is_list(Meta), is_list(Args) -> shallow_valid_ast(Call);
270-
shallow_valid_ast(_Term) -> false.
267+
shallow_valid_ast(Expr) when is_list(Expr) -> valid_ast_list(Expr);
268+
shallow_valid_ast(Expr) -> valid_ast_elem(Expr).
269+
270+
valid_ast_list([]) -> true;
271+
valid_ast_list([Head | Tail]) -> valid_ast_elem(Head) andalso valid_ast_list(Tail);
272+
valid_ast_list(_Improper) -> false.
273+
274+
valid_ast_elem(Expr) when is_list(Expr); is_atom(Expr); is_binary(Expr); is_number(Expr); is_pid(Expr) -> true;
275+
valid_ast_elem({Left, Right}) -> valid_ast_elem(Left) andalso valid_ast_elem(Right);
276+
valid_ast_elem({Atom, Meta, Args}) when is_atom(Atom), is_list(Meta), is_atom(Args) orelse is_list(Args) -> true;
277+
valid_ast_elem({Call, Meta, Args}) when is_list(Meta), is_list(Args) -> shallow_valid_ast(Call);
278+
valid_ast_elem(_Term) -> false.
271279

272280
quote({unquote_splicing, _, [_]}, #elixir_quote{unquote=true}) ->
273281
argument_error(<<"unquote_splicing only works inside arguments and block contexts, "

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

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -791,24 +791,26 @@ defmodule Kernel.QuoteTest.HasUnquoteTest do
791791
end
792792

793793
test "unquote with invalid AST (shallow check)" do
794-
assert_raise ArgumentError, "tried to unquote invalid AST: %{unescaped: :map}", fn ->
795-
quote do: unquote(%{unescaped: :map})
796-
end
797-
798-
assert_raise ArgumentError, "tried to unquote invalid AST: {:bad_meta, nil, []}", fn ->
799-
quote do: unquote({:bad_meta, nil, []})
800-
end
801-
802-
assert_raise ArgumentError, "tried to unquote invalid AST: {:bad_arg, nil, 1}", fn ->
803-
quote do: unquote({:bad_arg, nil, 1})
804-
end
805-
806-
assert_raise ArgumentError, "tried to unquote invalid AST: {:bad_tuple}", fn ->
807-
quote do: unquote({:bad_tuple})
794+
for term <- [
795+
%{unescaped: :map},
796+
1..10,
797+
{:bad_meta, nil, []},
798+
{:bad_arg, nil, 1},
799+
{:bad_tuple},
800+
make_ref(),
801+
[:improper | :list],
802+
[nested: {}]
803+
] do
804+
message = """
805+
tried to unquote invalid AST: #{inspect(term)}
806+
Did you forget to escape term using Macro.escape/1?\
807+
"""
808+
809+
assert_raise ArgumentError, message, fn -> quote do: unquote(term) end
808810
end
811+
end
809812

810-
assert_raise ArgumentError, ~r/tried to unquote invalid AST: #Reference</, fn ->
811-
quote do: unquote(make_ref())
812-
end
813+
test "unquote with invalid AST is not checked deeply" do
814+
assert quote do: unquote(foo: [bar: %{}]) == [foo: [bar: %{}]]
813815
end
814816
end

0 commit comments

Comments
 (0)