Good line numbers in macro expansions

My macros often have poor line numbers. For instance, given this simple macro,

macro add_hi(def)
       @capture(def, function f_(args__) body__ end)
       esc(:(function $f($(args...)) println(hi); $body end))
end

at the REPL:

julia> @add_hi function g() 2+2 end
g (generic function with 1 method)

julia> methods(g)
# 1 method for generic function "g" from Main:
 [1] g()
     @ ~/Utils/src/Utils.jl:1559

Clearly, the function was defined at the REPL, not in Utils.jl. The culprit is the usage of code quotation, that peppered line numbers from Utils.jl all over the place.

julia> @macroexpand @add_hi function g() 2+2 end
:(function g()
      #= /Utils/src/Utils.jl:1559 =#
      #= /Utils/src/Utils.jl:1559 =#
      println(hi)
      #= /Utils/src/Utils.jl:1559 =#
      Any[:(2 + 2)]
  end)

I kinda wish it didn’t do that? I’ve had some limited success using MacroTools.@q and __source__, but sometimes it just doesn’t work. In particular, for a macro like

macro add_noinline(def)
    esc(:(@noinline $def))
end

the macroexpansion becomes

julia> @macroexpand1 @add_hi2 f(x) = 3
:(#= Utils.jl:1565 =# @noinline f(x) = begin
              #= REPL[16]:1 =#
              3
          end)

so the __source__ passed to @noinline is again not what I want; I’d like it to be the REPL of course. And I can’t seem to be able to get it to be REPL without painfully building the Expr(:macrocall, ...) expression.

Incidentally, looking at macro code from Base, like @noinline, I see that Base doesn’t use code quoting! Is code quoting basically broken for line numbering? How do people deal with these issues?

1 Like

I found a solution, proposed in this PR: Define @qq by cstjean · Pull Request #200 · FluxML/MacroTools.jl · GitHub. Reviews welcome.

Incidentally, I also found Meta.replace_sourceloc!, but A) it doesn’t preserve the line numbers of interpolated expressions, and B) it doesn’t seem to work.

1 Like

Your example uses @capture. Where is this macro defined?

It comes from MacroTools.jl:

1 Like

The link is dead so I can’t tell if add_hi is defined in a Utils.jl file, but if it is, then a line number at the macro site would make some sense in the general case because the add_hi macro is what pieces together the evaluated method expression. In an extreme case of a macro with 0 arguments that returns a fixed method expression, the call site gives no information about the method. It’s just that in this case, you pass in a nearly identical method expression that would be more informative. Maybe it would be better to report call sites generally because we could at least search for the macro afterward, which is not possible the other way around.

1 Like

Yeah, I don’t deny that pointing to the macro expansion code makes sense in some cases. In the case of a macro invocation inside of a function definition, then the call stack usually provides the surrounding function call, so it’s fine for the macro expansion to point to the macro definition. But for top-level definitions like @defequation ... , @kwdef ... or @with_parameters ... etc defining a method like equation_value(...) = ... or a type, having equation_value(::...) in the stacktrace pointing at the macro def_equation() code is rarely helpful.

Yes, exactly.

I get why quote behaves the way it does. It’s already magical enough as it is, so changing it to implicitly use __source__ like the proposed @qq does, that might be too much.

1 Like