Skip to content

Commit

Permalink
Add a keyword name checking method to hasmethod
Browse files Browse the repository at this point in the history
With this change, `hasmethod` now accepts a tuple of symbols
corresponding to keyword argument names to check for when finding a
matching method.
  • Loading branch information
ararslan committed Jan 18, 2019
1 parent 1135a72 commit f7ece4d
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 1 deletion.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ New language features

* The `extrema` function now accepts a function argument in the same manner as `minimum` and
`maximum` ([#30323]).
* `hasmethod` can now check for matching keyword argument names ([#30712]).

Multi-threading changes
-----------------------
Expand Down
34 changes: 33 additions & 1 deletion base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1084,17 +1084,37 @@ function parentmodule(@nospecialize(f), @nospecialize(types))
end

"""
hasmethod(f, Tuple type; world = typemax(UInt)) -> Bool
hasmethod(f, t::Type{<:Tuple}[, kwnames]; world=typemax(UInt)) -> Bool
Determine whether the given generic function has a method matching the given
`Tuple` of argument types with the upper bound of world age given by `world`.
If a tuple of keyword argument names `kwnames` is provided, this also checks
whether the method of `f` matching `t` has the given keyword argument names.
If the matching method accepts a variable number of keyword arguments, e.g.
with `kwargs...`, any names given in `kwnames` are considered valid. Otherwise
the provided names must be a subset of the method's keyword arguments.
See also [`applicable`](@ref).
!!! compat "Julia 1.2"
Providing keyword argument names requires Julia 1.2 or later.
# Examples
```jldoctest
julia> hasmethod(length, Tuple{Array})
true
julia> hasmethod(sum, Tuple{Function, Array}, (:dims,))
true
julia> hasmethod(sum, Tuple{Function, Array}, (:apples, :bananas))
false
julia> g(; xs...) = 4;
julia> hasmethod(g, Tuple{}, (:a, :b, :c, :d)) # g accepts arbitrary kwargs
true
```
"""
function hasmethod(@nospecialize(f), @nospecialize(t); world = typemax(UInt))
Expand All @@ -1103,6 +1123,18 @@ function hasmethod(@nospecialize(f), @nospecialize(t); world = typemax(UInt))
return ccall(:jl_method_exists, Cint, (Any, Any, UInt), typeof(f).name.mt, t, world) != 0
end

function hasmethod(@nospecialize(f), @nospecialize(t), kwnames::Tuple{Vararg{Symbol}}; world=typemax(UInt))
hasmethod(f, t, world=world) || return false
isempty(kwnames) && return true
m = which(f, t)
max_world(m) <= world || return false
kws = kwarg_decl(m, Core.kwftype(typeof(f)))
for kw in kws
endswith(String(kw), "...") && return true
end
issubset(kwnames, kws)
end

"""
isambiguous(m1, m2; ambiguous_bottom=false) -> Bool
Expand Down
21 changes: 21 additions & 0 deletions test/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,27 @@ Base.delete_method(m)

end

module HasmethodKwargs
using Test
f(x::Int; y=3) = x + y
@test hasmethod(f, Tuple{Int})
@test hasmethod(f, Tuple{Int}, ())
@test hasmethod(f, Tuple{Int}, (:y,))
@test !hasmethod(f, Tuple{Int}, (:jeff,))
@test !hasmethod(f, Tuple{Int}, (:y,), world=typemin(UInt))
g(; b, c, a) = a + b + c
h(; kwargs...) = 4
for gh = (g, h)
@test hasmethod(gh, Tuple{})
@test hasmethod(gh, Tuple{}, ())
@test hasmethod(gh, Tuple{}, (:a,))
@test hasmethod(gh, Tuple{}, (:a, :b))
@test hasmethod(gh, Tuple{}, (:a, :b, :c))
end
@test !hasmethod(g, Tuple{}, (:a, :b, :c, :d))
@test hasmethod(h, Tuple{}, (:a, :b, :c, :d))
end

# issue #26267
module M26267
import Test
Expand Down

0 comments on commit f7ece4d

Please sign in to comment.