Macros always receive AST and are always required to return AST. So what you are seeing is normal. As the guide says:
… However, as we learned in the previous chapter, the macro will receive quoted expressions, inject them into the quote, and finally return another quoted expression.
I typically find the following construct useful for dealing with this when I want to use the macro arguments at compile time (and with @before_compile delegate the production of the final AST to a regular function).
bind_quoted is recommended in most cases and takes care compile-time unquoting. If you are passing something like a map you will have to Macro.escape/1 it first. The example below is from ex_cldr so its just an example. But you can see it is setting a module attribute.
defmacro __using__(opts \\ []) do
quote bind_quoted: [opts: opts] do
@cldr_opts opts
@before_compile Cldr.Backend.Compiler
end
end






















