Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,15 @@ function is_func_expr(@nospecialize(ex), meth::Method)
ex = get_func_expr(ex)
is_func_expr(ex) || return false
fname = nothing
if ex.head == :(->)
sigex = ex.args[1]
if ex.head == :function && get_call_expr(sigex) === nothing
# anonymous `function (args...) ... end`: match the argument tuple as for `->`
while isexpr(sigex, (:where, :(::)))
sigex = sigex.args[1]
end
isexpr(sigex, :tuple) || return false
exargs = sigex.args
elseif ex.head == :(->)
exargs = ex.args[1]
if isexpr(exargs, :tuple)
exargs = exargs.args
Expand Down Expand Up @@ -309,6 +317,18 @@ function kwmethod_basename(meth::Method)
end
false
end
if idx === nothing
# The kwbody of an anonymous closure is named after neither the closure nor its type,
# but like every kwbody it has its own name in place of `#self#`, and the
# (unnamed) closure object follows the keyword arguments.
margs = Base.method_argnames(meth)
if margs[1] !== Symbol("#self#")
params = Base.unwrap_unionall(meth.sig).parameters
idx = findfirst(eachindex(margs)) do i
i > 1 && margs[i] === Symbol("") && i <= length(params) && params[i] isa Type && params[i] <: Function
end
end
end
idx === nothing && return name_final, 0
return name_final, idx
end
Expand Down
17 changes: 17 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -505,3 +505,20 @@ end
CodeTracking.method_info[MethodInfoKey(method)] = [(lnn, ex)]
@test CodeTracking.definition(Expr, method) == ex
end

@testset "anonymous closures" begin
for (make, src) in ((make_anon_function, "function (x::Vector{Float32}, i)"),
(make_anon_function_kw, "function (x::Vector{Float32}, i; k = a)"),
(make_arrow_kw, "(x::Vector{Float32}, i; k = a) -> x[i] + k"))
f = make(1.0f0)
m = only(methods(f))
body, _ = CodeTracking.definition(String, m)
@test startswith(lstrip(body), src)
if !isempty(Base.kwarg_decl(m))
# the keyword-body method; its function type is the closure's last type parameter
kwbody = only(methods(typeof(f).parameters[end].instance))
body, _ = CodeTracking.definition(String, kwbody)
@test startswith(lstrip(body), src)
end
end
end
15 changes: 15 additions & 0 deletions test/script.jl
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,18 @@ end

# Single-line `eval(:(...))`: the method's line points at the `eval` call itself
eval(:(eval_oneline(x) = 2x + 1))

# Anonymous closures, each passed as a call argument on its own line
make_anon_function(a) = identity(
function (x::Vector{Float32}, i)
return x[i] + a
end
)
make_anon_function_kw(a) = identity(
function (x::Vector{Float32}, i; k = a)
return x[i] + k
end
)
make_arrow_kw(a) = identity(
(x::Vector{Float32}, i; k = a) -> x[i] + k
)