From 4d290542bc7f780ec3133f0e415e91c6f5fa5ca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 7 Jun 2025 08:48:21 +0200 Subject: [PATCH 1/3] Gate is_reference check on OTP_RELEASE version --- lib/elixir/src/elixir_quote.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/elixir/src/elixir_quote.erl b/lib/elixir/src/elixir_quote.erl index d55a90314b..ca5134a8bb 100644 --- a/lib/elixir/src/elixir_quote.erl +++ b/lib/elixir/src/elixir_quote.erl @@ -162,7 +162,7 @@ do_escape(BitString, _) when is_bitstring(BitString) -> do_escape(Map, Q) when is_map(Map) -> TT = [if - is_reference(V) -> + ?OTP_RELEASE >= 28, is_reference(V) -> argument_error(<<('Elixir.Kernel':inspect(Map, []))/binary, " contains a reference (", ('Elixir.Kernel':inspect(V, []))/binary, ") and therefore it cannot be escaped ", "(it must be defined within a function instead). ", (bad_escape_hint())/binary>>); From f76dc86ab94befc65550b6665905c7ba0c797734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 7 Jun 2025 12:50:36 +0200 Subject: [PATCH 2/3] Remove outdated test --- lib/elixir/test/elixir/macro_test.exs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/elixir/test/elixir/macro_test.exs b/lib/elixir/test/elixir/macro_test.exs index 851dbf16f0..297e46f9f1 100644 --- a/lib/elixir/test/elixir/macro_test.exs +++ b/lib/elixir/test/elixir/macro_test.exs @@ -136,12 +136,6 @@ defmodule MacroTest do test "does not add context to quote" do assert Macro.escape({:quote, [], [[do: :foo]]}) == {:{}, [], [:quote, [], [[do: :foo]]]} end - - test "inspects container when a reference cannot be escaped" do - assert_raise ArgumentError, ~r"~r/foo/ contains a reference", fn -> - Macro.escape(%{~r/foo/ | re_pattern: make_ref()}) - end - end end describe "expand_once/2" do From ae160d71692f3abd101ec18883bd66b388623f74 Mon Sep 17 00:00:00 2001 From: Jean Klingler Date: Sat, 7 Jun 2025 14:56:10 +0200 Subject: [PATCH 3/3] More precise error message when escaping a regex with a ref (#14560) --- lib/elixir/src/elixir_quote.erl | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/lib/elixir/src/elixir_quote.erl b/lib/elixir/src/elixir_quote.erl index ca5134a8bb..ed9e05cc85 100644 --- a/lib/elixir/src/elixir_quote.erl +++ b/lib/elixir/src/elixir_quote.erl @@ -160,15 +160,7 @@ do_escape(BitString, _) when is_bitstring(BitString) -> end; do_escape(Map, Q) when is_map(Map) -> - TT = - [if - ?OTP_RELEASE >= 28, is_reference(V) -> - argument_error(<<('Elixir.Kernel':inspect(Map, []))/binary, " contains a reference (", - ('Elixir.Kernel':inspect(V, []))/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)} - end || {K, V} <- lists:sort(maps:to_list(Map))], + TT = [escape_map_key_value(K, V, Map, Q) || {K, V} <- lists:sort(maps:to_list(Map))], {'%{}', [], TT}; do_escape([], _) -> @@ -202,6 +194,29 @@ do_escape(Fun, _) when is_function(Fun) -> do_escape(Other, _) -> bad_escape(Other). +escape_map_key_value(K, V, Map, Q) -> + MaybeRef = if + ?OTP_RELEASE < 28 -> nil; + is_reference(V) -> V; + is_tuple(V) -> find_tuple_ref(V, 1); + true -> nil + end, + if + is_reference(MaybeRef) -> + argument_error(<<('Elixir.Kernel':inspect(Map, []))/binary, " contains a reference (", + ('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)} + end. + +find_tuple_ref(Tuple, Index) when Index > tuple_size(Tuple) -> nil; +find_tuple_ref(Tuple, Index) -> + case element(Index, Tuple) of + Ref when is_reference(Ref) -> Ref; + _ -> find_tuple_ref(Tuple, Index + 1) + end. + bad_escape(Arg) -> argument_error(<<"cannot escape ", ('Elixir.Kernel':inspect(Arg, []))/binary, ". ", (bad_escape_hint())/binary>>).