Skip to content

Commit 7ea2c28

Browse files
authored
handle method signatures represented as GlobalRef/QuoteNode (#66)
Julia allows following method definitions, but LoweredCodeUtils (and thus Revise) can't handle them well, and it lead to the issue <timholy/Revise.jl#643>: ```julia function foogr end macro deffoogr() gr = GlobalRef(__module__, :foogr) # will be lowered to `GlobalRef` quote $gr(args...) = length(args) end end @deffoogr @show foogr(1,2,3) # => 3 function fooqn end macro deffooqn() sig = :($(GlobalRef(__module__, :fooqn))(args...)) # will be lowered to `QuoteNode` return Expr(:function, sig, Expr(:block, __source__, :(length(args)))) end @deffooqn @show fooqn(1,2,3) # => 3 ``` --- fixes timholy/Revise.jl#643
1 parent 4612349 commit 7ea2c28

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

src/signatures.jl

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,10 @@ function identify_framemethod_calls(frame)
152152
end
153153
end
154154
elseif ismethod1(stmt)
155-
key = stmt.args[1]::Symbol
155+
key = stmt.args[1]
156+
key = normalize_defsig(key, frame)
157+
key === missing && continue
158+
key = key::Symbol
156159
mi = get(methodinfos, key, nothing)
157160
if mi === nothing
158161
methodinfos[key] = MethodInfo(i)
@@ -161,6 +164,8 @@ function identify_framemethod_calls(frame)
161164
end
162165
elseif ismethod3(stmt)
163166
key = stmt.args[1]
167+
key = normalize_defsig(key, frame)
168+
key === missing && continue
164169
if key isa Symbol
165170
mi = methodinfos[key]
166171
mi.stop = i
@@ -208,6 +213,18 @@ function identify_framemethod_calls(frame)
208213
return methodinfos, selfcalls
209214
end
210215

216+
# try to normalize `def` to `Symbol` representation
217+
function normalize_defsig(@nospecialize(def), frame::Frame)
218+
if def isa QuoteNode
219+
parentmodule(def.value) === moduleof(frame) || return false
220+
def = nameof(def.value)
221+
elseif def isa GlobalRef
222+
def.mod === moduleof(frame) || return false
223+
def = def.name
224+
end
225+
return def
226+
end
227+
211228
function callchain(selfcalls)
212229
calledby = Dict{Symbol,Union{Symbol,Bool,Nothing}}()
213230
for sc in selfcalls
@@ -469,8 +486,11 @@ function methoddef!(@nospecialize(recurse), signatures, frame::Frame, @nospecial
469486
end
470487
ismethod1(stmt) || error("expected method opening, got ", stmt)
471488
name = stmt.args[1]
489+
name = normalize_defsig(name, frame)
472490
if isa(name, Bool)
473491
error("not valid for anonymous methods")
492+
elseif name === missing
493+
error("given invalid definition: $stmt")
474494
end
475495
name = name::Symbol
476496
while true # methods containing inner methods may need multiple trips through this loop

test/signatures.jl

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,3 +432,41 @@ bodymethtest5(x, y=Dict(1=>2)) = 5
432432
end
433433
end
434434
end
435+
436+
# https://github.com/timholy/Revise.jl/issues/643
437+
module Revise643
438+
439+
using LoweredCodeUtils, JuliaInterpreter, Test
440+
441+
# make sure to not define `foogr` before macro expansion,
442+
# otherwise it will be resolved as `QuoteNode`
443+
macro deffoogr()
444+
gr = GlobalRef(__module__, :foogr) # will be lowered to `GlobalRef`
445+
quote
446+
$gr(args...) = length(args) + 2
447+
end
448+
end
449+
let
450+
ex = quote
451+
@deffoogr
452+
@show foogr(1,2,3)
453+
end
454+
methranges = rename_framemethods!(Frame(@__MODULE__, ex))
455+
@test haskey(methranges, :foogr)
456+
end
457+
458+
function fooqn end
459+
macro deffooqn()
460+
sig = :($(GlobalRef(__module__, :fooqn))(args...)) # will be lowered to `QuoteNode`
461+
return Expr(:function, sig, Expr(:block, __source__, :(length(args))))
462+
end
463+
let
464+
ex = quote
465+
@deffooqn
466+
@show fooqn(1,2,3)
467+
end
468+
methranges = rename_framemethods!(Frame(@__MODULE__, ex))
469+
@test haskey(methranges, :fooqn)
470+
end
471+
472+
end

0 commit comments

Comments
 (0)