Skip to content

Commit 14d406f

Browse files
committed
Raise nice error message when escaping maps/structs with references, closes #14497
1 parent 08a9d4b commit 14d406f

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
@@ -160,10 +160,19 @@ do_escape(BitString, _) when is_bitstring(BitString) ->
160160
end;
161161

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

166-
do_escape([], _) -> [];
174+
do_escape([], _) ->
175+
[];
167176

168177
do_escape([H | T], #elixir_quote{unquote=false} = Q) ->
169178
do_quote_simple_list(T, do_quote(H, Q), Q);
@@ -195,8 +204,11 @@ do_escape(Other, _) ->
195204

196205
bad_escape(Arg) ->
197206
argument_error(<<"cannot escape ", ('Elixir.Kernel':inspect(Arg, []))/binary, ". ",
198-
"The supported values are: lists, tuples, maps, atoms, numbers, bitstrings, ",
199-
"PIDs and remote functions in the format &Mod.fun/arity">>).
207+
(bad_escape_hint())/binary>>).
208+
209+
bad_escape_hint() ->
210+
<<"The supported values are: lists, tuples, maps, atoms, numbers, bitstrings, ",
211+
"PIDs and remote functions in the format &Mod.fun/arity">>.
200212

201213
%% Quote entry points
202214

lib/elixir/test/elixir/macro_test.exs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ defmodule MacroTest do
136136
test "does not add context to quote" do
137137
assert Macro.escape({:quote, [], [[do: :foo]]}) == {:{}, [], [:quote, [], [[do: :foo]]]}
138138
end
139+
140+
test "inspects container when a reference cannot be escaped" do
141+
assert_raise ArgumentError, ~r"~r/foo/ contains a reference", fn ->
142+
Macro.escape(%{~r/foo/ | re_pattern: make_ref()})
143+
end
144+
end
139145
end
140146

141147
describe "expand_once/2" do

0 commit comments

Comments
 (0)