I’m having trouble getting the source location of a method definition to be correct. The method is defined by a macro. I would like for the source location of the method to be the location of the macro call, rather than the line in the macro definition that starts with function.
I’m hoping someone can explain to me how source locations travel from the parser, through macro expansion and compilation to the line and file fields of a Method.
I’m seeing something that is totally confusing. This is in Julia 1.9.3.
Here’s the macro and an invocation:
macro unify_equal(typ, op)
r =
quote
function Unification.unify(continuation,
thing1::$typ, thing2::$typ,
bindings::AbstractBindings)::Nothing
if $op(thing1, thing2)
return continuation(bindings)
end
#=
@unification_failure(thing1, thing2,
_file = $(__source__.file),
_line = $(__source__.line),
_module = $__module__)
=#
end
end
# Replace the LineNumberNode that preceeds the function with
# __source__. There should be an easier way.
function sourcify(e)
if isexpr(e, :block)
for i in 1:(length(e.args))
if isexpr(e.args[i], :function)
@assert e.args[i - 1] isa LineNumberNode
e.args[i - 1] = __source__
end
end
return
elseif e isa Expr
for a in e.args
sourcify(a)
end
end
end
sourcify(r)
r = esc(r)
Meta.dump(r; maxdepth=100)
r
end
@unify_equal(AbstractString, ==)
Here’s the dump:
Expr
head: Symbol escape
args: Array{Any}((1,))
1: Expr
head: Symbol block
args: Array{Any}((2,))
1: LineNumberNode
line: Int64 235
file: Symbol c:\Users\Mark Nahabedian\.julia\dev\U2\src\unify.jl
2: Expr
head: Symbol function
args: Array{Any}((2,))
1: Expr
head: Symbol ::
args: Array{Any}((2,))
1: Expr
head: Symbol call
args: Array{Any}((5,))
1: Expr
head: Symbol .
args: Array{Any}((2,))
1: Symbol Unification
2: QuoteNode
value: Symbol unify
2: Symbol continuation
3: Expr
head: Symbol ::
args: Array{Any}((2,))
1: Symbol thing1
2: Symbol AbstractString
4: Expr
head: Symbol ::
args: Array{Any}((2,))
1: Symbol thing2
2: Symbol AbstractString
5: Expr
head: Symbol ::
args: Array{Any}((2,))
1: Symbol bindings
2: Symbol AbstractBindings
2: Symbol Nothing
2: Expr
head: Symbol block
args: Array{Any}((3,))
1: LineNumberNode
line: Int64 193
file: Symbol c:\Users\Mark Nahabedian\.julia\dev\U2\src\unify.jl
2: LineNumberNode
line: Int64 196
file: Symbol c:\Users\Mark Nahabedian\.julia\dev\U2\src\unify.jl
3: Expr
head: Symbol if
args: Array{Any}((2,))
1: Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol ==
2: Symbol thing1
3: Symbol thing2
2: Expr
head: Symbol block
args: Array{Any}((2,))
1: LineNumberNode
line: Int64 197
file: Symbol c:\Users\Mark Nahabedian\.julia\dev\U2\src\unify.jl
2: Expr
head: Symbol return
args: Array{Any}((1,))
1: Expr
head: Symbol call
args: Array{Any}((2,))
1: Symbol continuation
2: Symbol bindings
The above is correct. Line 233 is where the macro invocation is located.
If I list the methods though, I get
[9] unify(continuation, thing1::AbstractString, thing2::AbstractString, bindings::AbstractBindings)
@ c:\Users\Mark Nahabedian\.julia\dev\U2\src\unify.jl:193
Line 193 is this line of the macro though
function Unification.unify(continuation,
Why is the line number what I want in the dump but not on the actual method’s meta-data?
Also, why is the file path coming out a symbol and not a string?
Poking around in the Julia source, I found Meta.replace_sourceloc! but that only changes the first argument of a nested macrocall Expr. It doesn’t appear to be used anywhere.
Thanks.