Skip to content

Commit 8de98a8

Browse files
committed
Raise nice error message when escaping maps/structs with references, closes #14497
1 parent 1f9433f commit 8de98a8

File tree

2 files changed

+22
-4
lines changed

2 files changed

+22
-4
lines changed

lib/elixir/src/elixir_quote.erl

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,19 @@ do_escape(BitString, _) when is_bitstring(BitString) ->
164164
end;
165165

166166
do_escape(Map, Q) when is_map(Map) ->
167-
TT = do_quote(lists:sort(maps:to_list(Map)), Q),
167+
TT =
168+
[if
169+
is_reference(V) ->
170+
argument_error(<<('Elixir.Kernel':inspect(Map, []))/binary, " contains a reference (",
171+
('Elixir.Kernel':inspect(V, []))/binary, ") and therefore it cannot be escaped ",
172+
"(it must be defined within a function instead). ", (bad_escape_hint())/binary>>);
173+
true ->
174+
{do_quote(K, Q), do_quote(V, Q)}
175+
end || {K, V} <- lists:sort(maps:to_list(Map))],
168176
{'%{}', [], TT};
169177

170-
do_escape([], _) -> [];
178+
do_escape([], _) ->
179+
[];
171180

172181
do_escape([H | T], #elixir_quote{unquote=false} = Q) ->
173182
do_quote_simple_list(T, do_quote(H, Q), Q);
@@ -199,8 +208,11 @@ do_escape(Other, _) ->
199208

200209
bad_escape(Arg) ->
201210
argument_error(<<"cannot escape ", ('Elixir.Kernel':inspect(Arg, []))/binary, ". ",
202-
"The supported values are: lists, tuples, maps, atoms, numbers, bitstrings, ",
203-
"PIDs and remote functions in the format &Mod.fun/arity">>).
211+
(bad_escape_hint())/binary>>).
212+
213+
bad_escape_hint() ->
214+
<<"The supported values are: lists, tuples, maps, atoms, numbers, bitstrings, ",
215+
"PIDs and remote functions in the format &Mod.fun/arity">>.
204216

205217
%% Quote entry points
206218

lib/elixir/test/elixir/macro_test.exs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,12 @@ defmodule MacroTest do
140140
test "does not add context to quote" do
141141
assert Macro.escape({:quote, [], [[do: :foo]]}) == {:{}, [], [:quote, [], [[do: :foo]]]}
142142
end
143+
144+
test "inspects container when a reference cannot be escaped" do
145+
assert_raise ArgumentError, ~r"~r/foo/ contains a reference", fn ->
146+
Macro.escape(%{~r/foo/ | re_pattern: make_ref()})
147+
end
148+
end
143149
end
144150

145151
describe "expand_once/2" do

0 commit comments

Comments
 (0)