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.