diff --git a/lib/elixir/lib/regex.ex b/lib/elixir/lib/regex.ex index d07f4479c2..5f308f5a43 100644 --- a/lib/elixir/lib/regex.ex +++ b/lib/elixir/lib/regex.ex @@ -1008,6 +1008,9 @@ defmodule Regex do pattern_ast = cond do + is_nil(regex.re_pattern) -> + nil + # TODO: Remove this when we require Erlang/OTP 28+ # Before OTP 28.0, patterns did not contain any refs and could be safely be escaped :erlang.system_info(:otp_release) < [?2, ?8] -> @@ -1018,8 +1021,10 @@ defmodule Regex do {:ok, exported} = :re.compile(regex.source, [:export] ++ regex.opts) quote do - :re.import(unquote(Macro.escape(exported))) + Regex.__import_pattern__(unquote(Macro.escape(exported))) end + # we now that the Regex module is defined at this stage, so this macro can be safely called + |> Macro.update_meta(&([required: true] ++ &1)) # TODO: Remove this when we require Erlang/OTP 28.1+ # OTP 28.0 works in degraded mode performance-wise, we need to recompile from the source @@ -1041,4 +1046,15 @@ defmodule Regex do } end end + + @doc false + defmacro __import_pattern__(pattern) do + if __CALLER__.context in [:match, :guard] do + raise ArgumentError, "escaped Regex structs are not allowed in match or guards" + end + + quote do + :re.import(unquote(pattern)) + end + end end diff --git a/lib/elixir/test/elixir/macro_test.exs b/lib/elixir/test/elixir/macro_test.exs index ba9889d6b2..516a6df62f 100644 --- a/lib/elixir/test/elixir/macro_test.exs +++ b/lib/elixir/test/elixir/macro_test.exs @@ -168,7 +168,8 @@ defmodule MacroTest do [ __struct__: Regex, re_pattern: - {{:., [], [:re, :import]}, [], [{:{}, [], [:re_exported_pattern | _]}]}, + {{:., [], [{:__aliases__, _, [:Regex]}, :__import_pattern__]}, + [required: true], [{:{}, [], [:re_exported_pattern | _]}]}, source: "foo", opts: [] ] diff --git a/lib/elixir/test/elixir/regex_test.exs b/lib/elixir/test/elixir/regex_test.exs index 166a328136..1145b0d0a3 100644 --- a/lib/elixir/test/elixir/regex_test.exs +++ b/lib/elixir/test/elixir/regex_test.exs @@ -28,6 +28,25 @@ defmodule RegexTest do end end + @tag :re_import + test "module attribute in match context" do + assert_raise( + ArgumentError, + ~r/escaped Regex structs are not allowed in match or guards/, + fn -> + Code.eval_quoted( + quote do + defmodule ModAttrGuard do + @regex ~r/example/ + def example?(@regex), do: true + def example?(_), do: false + end + end + ) + end + ) + end + test "multiline" do refute Regex.match?(~r/^b$/, "a\nb\nc") assert Regex.match?(~r/^b$/m, "a\nb\nc")