Skip to content
Closed
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
53 changes: 38 additions & 15 deletions stdlib/InteractiveUtils/src/InteractiveUtils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -122,24 +122,38 @@ end

# `methodswith` -- shows a list of methods using the type given
"""
methodswith(typ[, module or function]; supertypes::Bool=false])
methodswith(typ[, module or function]; supertypes::Bool=false, subtypes::Bool=false, all::Bool=false, submodules::Bool=false)

Return an array of methods with an argument of type `typ`.

The optional second argument restricts the search to a particular module or function
(the default is all top-level modules).
The optional second argument restricts the search to a particular module or function.
By default, only top-level modules and modules loaded into Main are examined.
Set `submodules=true` if you want to include inner modules.

If keyword `supertypes` is `true`, also return arguments with a parent type of `typ`,
excluding type `Any`.
If keyword `subtypes` is `true`, include methods with arguments that are subtypes of `typ`.
You may not set both to be true.

By default, the results include only exported functions.
Set `all=true` if you want to include non-exported functions.
"""
function methodswith(t::Type, f::Function, meths = Method[]; supertypes::Bool=false)
function methodswith(t::Type, f::Function, meths = Method[]; supertypes::Bool=false, subtypes::Bool=false)
supertypes && subtypes && throw(ArgumentError("supertypes and subtypes cannot both be true"))
for d in methods(f)
if any(function (x)
if subtypes
let x = unwrap_unionall(x)
isa(x, DataType) && return x <: t
return false
end
end
let x = rewrap_unionall(x, d.sig)
(type_close_enough(x, t) ||
(subtypes ? x <: t :
(supertypes ? (t <: x && (!isa(x,TypeVar) || x.ub != Any)) :
(isa(x,TypeVar) && x.ub != Any && t == x.ub)) &&
x != Any)
x != Any))
end
end,
unwrap_unionall(d.sig).parameters)
Expand All @@ -149,27 +163,36 @@ function methodswith(t::Type, f::Function, meths = Method[]; supertypes::Bool=fa
return meths
end

function _methodswith(t::Type, m::Module, supertypes::Bool)
meths = Method[]
for nm in names(m)
function methodswith!(meths, checking, t::Type, m::Module;
supertypes::Bool=false, subtypes::Bool=false,
all::Bool=false, submodules::Bool=false)
push!(checking, m) # block recursion with submodules (nameof(m) ∈ names(m))
for nm in names(m; all = all)
if isdefined(m, nm)
f = getfield(m, nm)
if isa(f, Function)
methodswith(t, f, meths; supertypes = supertypes)
methodswith(t, f, meths; supertypes = supertypes, subtypes = subtypes)
elseif submodules && isa(f, Module) && f ∉ checking
methodswith!(meths, checking, t, f; supertypes = supertypes,
subtypes = subtypes, all = all, submodules = true)
end
end
end
return unique(meths)
return meths
end

methodswith(t::Type, m::Module; supertypes::Bool=false) = _methodswith(t, m, supertypes)
function methodswith(t::Type, m::Module; kwargs...)
meths, checking = Set{Method}(), Set{Module}()
methodswith!(meths, checking, t, m; kwargs...)
return collect(meths)
end

function methodswith(t::Type; supertypes::Bool=false)
meths = Method[]
function methodswith(t::Type; kwargs...)
meths, checking = Set{Method}(), Set{Module}()
for mod in Base.loaded_modules_array()
append!(meths, _methodswith(t, mod, supertypes))
methodswith!(meths, checking, t, mod; kwargs...)
end
return unique(meths)
return collect(meths)
end

# subtypes
Expand Down
51 changes: 51 additions & 0 deletions stdlib/InteractiveUtils/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,57 @@ struct Type4Union end
func4union(::Union{Type4Union,Int}) = ()
@test !isempty(methodswith(Type4Union, @__MODULE__))

module MWM
export f, SubModule
struct C end
abstract type Super end
struct Sub <: Super end
abstract type SuperP{A<:AbstractArray} end
abstract type SubP{A<:AbstractVector} <: SuperP{A} end
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly, had I written SubP{A} <: SuperP{A} then the test would have failed. Presumably this is just #6383, but if there is a nice workaround I'd be happy to incorporate it. I am sure there's a lot of code out there that doesn't re-impose the same parameter bounds.

f(::C) = 1
f(::Super) = 2
f(::Sub) = 3
f(::SuperP) = 4
f(::SubP) = 5
f(::SubP{Vector{Int}}) = 6

f1(::Vector) = 1
f2(::Vector{Int}) = 1
f3(::Vector{I}) where I = 1
f4(::Vector{<:Integer}) = 1

g(::C) = 1

module SubModule
using ..MWM: C
export h
h(::C) = 1
end

end

@test methodswith(MWM.C, MWM) == [which(MWM.f, Tuple{MWM.C})]
@test Set(methodswith(MWM.C, MWM; all=true)) == Set([which(MWM.f, Tuple{MWM.C}),
which(MWM.g, Tuple{MWM.C})])
@test Set(methodswith(MWM.C, MWM; submodules=true)) == Set([which(MWM.f, Tuple{MWM.C}),
which(MWM.SubModule.h, Tuple{MWM.C})])
@test Set(methodswith(MWM.C, MWM; all=true, submodules=true)) ==
Set([which(MWM.f, Tuple{MWM.C}),
which(MWM.g, Tuple{MWM.C}),
which(MWM.SubModule.h, Tuple{MWM.C})])

@test methodswith(MWM.Super, MWM) == [which(MWM.f, Tuple{MWM.Super})]
@test Set(methodswith(MWM.Super, MWM; subtypes=true)) == Set(methods(MWM.f, Tuple{MWM.Super}))

@test Set(methodswith(MWM.SuperP, MWM; subtypes=true)) == Set(methods(MWM.f, Tuple{MWM.SuperP}))
@test Set(methodswith(MWM.SubP, MWM; subtypes=true)) == Set(methods(MWM.f, Tuple{MWM.SubP}))
@test Set(methodswith(MWM.SubP{Vector{Int}}, MWM; subtypes=true)) == Set(methods(MWM.f, Tuple{MWM.SubP{Vector{Int}}}))

for f in (MWM.f1, MWM.f2, MWM.f3, MWM.f4)
@test Set(methodswith(Vector, f; subtypes=true)) == Set(methods(f, Tuple{Vector}))
end


# PR #19964
@test isempty(subtypes(Float64))

Expand Down