diff --git a/src/utils.jl b/src/utils.jl index a151b606..266723c2 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -288,7 +288,15 @@ function linetable(arg) end return (arg::CodeInfo).linetable::Union{Vector{Core.LineInfoNode},Vector{Any}} # issue #264 end -linetable(arg, i::Integer) = linetable(arg)[i]::Union{Expr,LineTypes} +_linetable(list::Vector, i::Integer) = list[i]::Union{Expr,LineTypes} +function linetable(arg, i::Integer; macro_caller::Bool=false)::Union{Expr,LineTypes} + lt = linetable(arg) + lineinfo = _linetable(lt, i) + macro_caller && while lineinfo.method === Symbol("macro expansion") && lineinfo.inlined_at != 0 + lineinfo = _linetable(lt, lineinfo.inlined_at) + end + return lineinfo +end function codelocs(arg) if isa(arg, Frame) @@ -352,19 +360,23 @@ function firstline(ex::Expr) end """ - loc = whereis(frame, pc::Int=frame.pc) + loc = whereis(frame, pc::Int=frame.pc; macro_caller=false) Return the file and line number for `frame` at `pc`. If this cannot be determined, `loc == nothing`. Otherwise `loc == (filepath, line)`. + +By default, any statements expanded from a macro are attributed to the macro +definition, but with`macro_caller=true` you can obtain the location within the +method that issued the macro. """ -function CodeTracking.whereis(framecode::FrameCode, pc::Int) +function CodeTracking.whereis(framecode::FrameCode, pc::Int; kwargs...) codeloc = codelocation(framecode.src, pc) codeloc == 0 && return nothing - lineinfo = linetable(framecode, codeloc) + lineinfo = linetable(framecode, codeloc; kwargs...) m = framecode.scope return isa(m, Method) ? whereis(lineinfo, m) : (getfile(lineinfo), getline(lineinfo)) end -CodeTracking.whereis(frame::Frame, pc::Int=frame.pc) = whereis(frame.framecode, pc) +CodeTracking.whereis(frame::Frame, pc::Int=frame.pc; kwargs...) = whereis(frame.framecode, pc; kwargs...) """ line = linenumber(framecode, pc) diff --git a/test/limits.jl b/test/limits.jl index 74659c85..3d4c454a 100644 --- a/test/limits.jl +++ b/test/limits.jl @@ -1,3 +1,7 @@ +using JuliaInterpreter +using CodeTracking +using Test + # This is a test-for-tests, verifying the code in utils.jl. if !isdefined(@__MODULE__, :read_and_parse) include("utils.jl") @@ -38,11 +42,8 @@ end @test Aborted(frame, i).at.line == 9 # Check macro frame = Frame(modexs[5]...) - if VERSION < v"1.4.0-DEV.475" - @test Aborted(frame, 1).at.file == Symbol("util.jl") - else - @test Aborted(frame, 1).at.file == Symbol("fake.jl") - end + @test Aborted(frame, 1).at.file == Symbol("fake.jl") + @test whereis(frame, 1; macro_caller=true) == ("fake.jl", 11) end module EvalLimited end @@ -79,7 +80,7 @@ module EvalLimited end end """; filename="fake.jl") if length(ex.args) == 2 - # Sadly, parse_input_line doesn't insert line info at toplevel, so do it manually + # Sadly, on some Julia versions parse_input_line doesn't insert line info at toplevel, so do it manually insert!(ex.args, 2, LineNumberNode(2, Symbol("fake.jl"))) insert!(ex.args, 1, LineNumberNode(1, Symbol("fake.jl"))) end diff --git a/test/utils.jl b/test/utils.jl index cfee717e..a41e728a 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -38,7 +38,8 @@ end function Aborted(frame::Frame, pc) src = frame.framecode.src lineidx = src.codelocs[pc] - return Aborted(src.linetable[lineidx]) + lineinfo = JuliaInterpreter.linetable(frame, lineidx; macro_caller=true) + return Aborted(lineinfo) end """