From a6844bf72533cda1fa1704aefc4ece63e7bbc641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 10 Jul 2025 17:30:36 +0200 Subject: [PATCH] Add tests for allow_locals option --- lib/elixir/lib/macro/env.ex | 8 ++++---- lib/elixir/src/elixir_dispatch.erl | 4 +--- lib/elixir/test/elixir/macro/env_test.exs | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/elixir/lib/macro/env.ex b/lib/elixir/lib/macro/env.ex index edae8a4cc5..af8d79b779 100644 --- a/lib/elixir/lib/macro/env.ex +++ b/lib/elixir/lib/macro/env.ex @@ -558,13 +558,13 @@ defmodule Macro.Env do * `:allow_locals` - controls how local macros are resolved. Defaults to `true`. - + - When `false`, does not attempt to capture local macros defined in the current module in `env` - When `true`, uses a default resolver that looks for public macros in the current module - - When a function, uses the function as a custom local resolver. The function - must have the signature: `(meta, name, arity, env) -> function() | false` + - When a function, it will be invoked to lazily compute a local function + (or return false). It has signature `(-> function() | false)` * `:check_deprecations` - when set to `false`, does not check for deprecations when expanding macros @@ -591,7 +591,7 @@ defmodule Macro.Env do # When allow_locals is a callback, we don't need to pass module macros as extra # because the callback will handle local macro resolution extra = - if is_function(allow_locals, 4) do + if is_function(allow_locals, 0) do [] else case allow_locals and function_exported?(module, :__info__, 1) do diff --git a/lib/elixir/src/elixir_dispatch.erl b/lib/elixir/src/elixir_dispatch.erl index ea52a61df9..346097e9a7 100644 --- a/lib/elixir/src/elixir_dispatch.erl +++ b/lib/elixir/src/elixir_dispatch.erl @@ -175,9 +175,7 @@ expand_import(Meta, Name, Arity, E, Extra, AllowLocals, Trace) -> Local = case AllowLocals of false -> false; true -> elixir_def:local_for(Meta, Name, Arity, [defmacro, defmacrop], E); - Fun when is_function(Fun, 4) -> - %% If we have a custom local resolver, use it. - Fun(Meta, Name, Arity, E) + Fun when is_function(Fun, 0) -> Fun() end, case Dispatch of diff --git a/lib/elixir/test/elixir/macro/env_test.exs b/lib/elixir/test/elixir/macro/env_test.exs index 3138f54251..07e6a5b954 100644 --- a/lib/elixir/test/elixir/macro/env_test.exs +++ b/lib/elixir/test/elixir/macro/env_test.exs @@ -207,6 +207,25 @@ defmodule Macro.EnvTest do assert fun.([generated: true], [quote(do: hello())]) == quote(generated: true, do: hello()) end + defmacro allow_locals_example, do: :ok + + test "allow_locals" do + {:macro, Macro.EnvTest, fun} = + expand_import(env(), meta(), :allow_locals_example, 0) + + assert fun.([], []) == :ok + + assert expand_import(env(), meta(), :allow_locals_example, 0, allow_locals: false) == + {:error, :not_found} + + assert expand_import(env(), meta(), :allow_locals_example, 0, + allow_locals: fn -> send(self(), false) end + ) == + {:error, :not_found} + + assert_received false + end + test "with tracing and deprecations" do message = "MacroEnvMacros.my_deprecated_macro/1 is deprecated"