From 8c5ff78e52bceace3022751f77d77172f990d15c Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 15 Apr 2020 12:49:27 -0500 Subject: [PATCH] Fix line numbering for Julia 1.5 https://github.com/JuliaLang/julia/pull/35138 changed it so that the method's line number is the declaration, not the first line of the body. This also reworks the line-number detection logic on older releases, and it should be more robust. --- .travis.yml | 1 + Project.toml | 4 +-- src/Revise.jl | 2 ++ src/lowered.jl | 91 ++++++++++++++++++++++++++++++++---------------- test/runtests.jl | 12 +++++-- 5 files changed, 75 insertions(+), 35 deletions(-) diff --git a/.travis.yml b/.travis.yml index eddac75d..7beaf820 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ julia: - 1.1 - 1.2 - 1.3 + - 1.4 - nightly notifications: email: false diff --git a/Project.toml b/Project.toml index a89d3b5e..0e0de3c8 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Revise" uuid = "295af30f-e4ad-537b-8983-00126c2a3abe" -version = "2.6.0" +version = "2.6.1" [deps] CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" @@ -16,7 +16,7 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" [compat] -CodeTracking = "~0.5.1" +CodeTracking = "0.5.9" JuliaInterpreter = "~0.7.1" LoweredCodeUtils = "0.4" OrderedCollections = "1" diff --git a/src/Revise.jl b/src/Revise.jl index c5b18a6e..071f4dcc 100644 --- a/src/Revise.jl +++ b/src/Revise.jl @@ -14,6 +14,8 @@ using LoweredCodeUtils: next_or_nothing!, isanonymous_typedef, define_anonymous export revise, includet, entr, MethodSummary +using CodeTracking: line_is_decl + """ Revise.watching_files[] diff --git a/src/lowered.jl b/src/lowered.jl index 9d0af1ca..a0a0d82e 100644 --- a/src/lowered.jl +++ b/src/lowered.jl @@ -156,47 +156,78 @@ function methods_by_execution!(@nospecialize(recurse), methodinfo, docexprs, fra pc, pc3 = ret # Get the line number from the body stmt3 = pc_expr(frame, pc3) - bodycode = stmt3.args[end] - if !isa(bodycode, CodeInfo) - bodycode = @lookup(frame, bodycode) + lnn = nothing + if line_is_decl + sigcode = @lookup(frame, stmt3.args[2]) + lnn = sigcode[end] + if !isa(lnn, LineNumberNode) + lnn = nothing + end end - if isa(bodycode, CodeInfo) - lnn = bodycode.linetable[1] - if lnn.line == 0 && lnn.file == :none && length(bodycode.code) > 1 - # This may be a kwarg method. Mimic LoweredCodeUtils.bodymethod, - # except without having a method - stmt = bodycode.code[end-1] - if isa(stmt, Expr) && length(stmt.args) > 1 - a = stmt.args[1] - hasself = any(i->LoweredCodeUtils.is_self_call(stmt, bodycode.slotnames, i), 2:length(stmt.args)) - if hasself && isa(a, Symbol) - f = getfield(mod, stmt.args[1]) - mths = methods(f) - if length(mths) == 1 - mth = first(mths) - lnn = LineNumberNode(Int(mth.line), mth.file) + if lnn === nothing + bodycode = stmt3.args[end] + if !isa(bodycode, CodeInfo) + bodycode = @lookup(frame, bodycode) + end + if isa(bodycode, CodeInfo) + lnn = bodycode.linetable[1] + if lnn.line == 0 && lnn.file == :none + lnn = nothing + if length(bodycode.code) > 1 + # This may be a kwarg method. Mimic LoweredCodeUtils.bodymethod, + # except without having a method + stmt = bodycode.code[end-1] + if isa(stmt, Expr) && length(stmt.args) > 1 + a = stmt.args[1] + hasself = any(i->LoweredCodeUtils.is_self_call(stmt, bodycode.slotnames, i), 2:length(stmt.args)) + if isa(a, Core.SlotNumber) + a = bodycode.slotnames[a.id] + end + if hasself && (isa(a, Symbol) || isa(a, GlobalRef)) + thismod, thisname = isa(a, Symbol) ? (mod, a) : (a.mod, a.name) + if isdefined(thismod, thisname) + f = getfield(thismod, thisname) + mths = methods(f) + if length(mths) == 1 + mth = first(mths) + lnn = LineNumberNode(Int(mth.line), mth.file) + end + end + end end end - else - # Just try to find *any* line number - for lnntmp in bodycode.linetable - if lnntmp.line != 0 || lnntmp.file != :none - lnn = lnntmp - break + if lnn === nothing + # Just try to find *any* line number + for lnntmp in bodycode.linetable + if lnntmp.line != 0 || lnntmp.file != :none + lnn = lnntmp + break + end end end end + elseif isexpr(bodycode, :lambda) + lnntmp = bodycode.args[end][1] + if lnntmp.line != 0 || lnntmp.file != :none + lnn = lnntmp + end end - for sig in signatures - add_signature!(methodinfo, sig, lnn) + end + if lnn === nothing + i = frame.framecode.src.codelocs[pc3] + while i > 0 + lnntmp = frame.framecode.src.linetable[i] + if lnntmp.line != 0 || lnntmp.file != :none + lnn = lnntmp + break + end + i -= 1 end - elseif isexpr(bodycode, :lambda) - lnn = bodycode.args[end][1] + end + if lnn !== nothing && (lnn.line != 0 || lnn.file != :none) for sig in signatures add_signature!(methodinfo, sig, lnn) end - else - error("unhandled bodycode ", bodycode) end end elseif stmt.head == :(=) && isa(stmt.args[1], Symbol) diff --git a/test/runtests.jl b/test/runtests.jl index 83b1aaf7..eaeacf57 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -10,6 +10,8 @@ using OrderedCollections: OrderedSet using Test: collect_test_logs using Base.CoreLogging: Debug,Info +using CodeTracking: line_is_decl + include("common.jl") throwing_function(bt) = bt[2] @@ -1263,13 +1265,16 @@ foo(y::Int) = y-51 triggered(true, false) @test false catch err - bt = throwing_function(Revise.update_stacktrace_lineno!(stacktrace(catch_backtrace()))) + st = stacktrace(catch_backtrace()) + Revise.update_stacktrace_lineno!(st) + bt = throwing_function(st) @test bt.file == Symbol(filename) && bt.line == 2 end io = IOBuffer() if isdefined(Base, :methodloc_callback) print(io, methods(triggered)) - @test occursin(filename * ":2", String(take!(io))) + mline = line_is_decl ? 1 : 2 + @test occursin(filename * ":$mline", String(take!(io))) end open(filename, "w") do io println(io, """ @@ -1306,7 +1311,8 @@ foo(y::Int) = y-51 @test occursin(targetstr, String(take!(io))) if isdefined(Base, :methodloc_callback) print(io, methods(triggered)) - @test occursin(filename * ":3", String(take!(io))) + mline = line_is_decl ? 2 : 3 + @test occursin(filename * ":$mline", String(take!(io))) end push!(to_remove, filename)