diff --git a/base/replutil.jl b/base/replutil.jl index 39d6563ccb560..c1461b7faf652 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -123,6 +123,16 @@ function showerror(io::IO, ex::MethodError) ft = typeof(f) name = ft.name.mt.name f_is_function = false + kwargs = Dict{Any,Any}() + if startswith(string(ft.name), "#kw#") + f = ex.args[2] + ft = typeof(f) + name = ft.name.mt.name + arg_types_param = arg_types_param[3:end] + temp = ex.args[1] + kwargs = [(temp[i*2-1], temp[i*2]) for i in 1:(length(temp) รท 2)] + ex = MethodError(f, ex.args[3:end]) + end if f == Base.convert && length(arg_types_param) == 2 && !is_arg_types f_is_function = true # See #13033 @@ -150,6 +160,13 @@ function showerror(io::IO, ex::MethodError) print(io, "::$typ") i == length(arg_types_param) || print(io, ", ") end + if 0 < length(kwargs) + print(io, "; ") + for (i ,(k, v)) in enumerate(kwargs) + print(io, k, "=", v) + i == length(kwargs) || print(io, ", ") + end + end print(io, ")") end if ft <: AbstractArray @@ -189,7 +206,7 @@ function showerror(io::IO, ex::MethodError) "\nsince type constructors fall back to convert methods.") end try - show_method_candidates(io, ex) + show_method_candidates(io, ex, kwargs) catch warn(io, "Error showing method candidates, aborted") end @@ -222,7 +239,7 @@ function showerror_nostdio(err, msg::AbstractString) ccall(:jl_printf, Cint, (Ptr{Void},Cstring), stderr_stream, "\n") end -function show_method_candidates(io::IO, ex::MethodError) +function show_method_candidates(io::IO, ex::MethodError, kwargs=Tuple{Symbol, Any}[]) is_arg_types = isa(ex.args, DataType) arg_types = is_arg_types ? ex.args : typesof(ex.args...) arg_types_param = Any[arg_types.parameters...] @@ -233,7 +250,7 @@ function show_method_candidates(io::IO, ex::MethodError) else f = ex.f end - + ft = typeof(f) lines = [] # These functions are special cased to only show if first argument is matched. special = f in [convert, getindex, setindex!] @@ -262,7 +279,6 @@ function show_method_candidates(io::IO, ex::MethodError) use_constructor_syntax = isa(func, Type) print(buf, use_constructor_syntax ? func : typeof(func).name.mt.name) end - right_matches = 0 tv = method.tvars if !isa(tv,SimpleVector) tv = Any[tv] @@ -318,17 +334,19 @@ function show_method_candidates(io::IO, ex::MethodError) end end - if right_matches > 0 + if right_matches > 0 || length(ex.args) < 2 if length(t_i) < length(sig) # If the methods args is longer than input then the method # arguments is printed as not a match - for sigtype in sig[length(t_i)+1:end] + for (k, sigtype) in enumerate(sig[length(t_i)+1:end]) if Base.isvarargtype(sigtype) sigstr = string(sigtype.parameters[1], "...") else sigstr = string(sigtype) end - print(buf, ", ") + if !((min(length(t_i), length(sig)) == 0) && k==1) + print(buf, ", ") + end if Base.have_color Base.with_output_color(:red, buf) do buf print(buf, "::$sigstr") @@ -338,7 +356,28 @@ function show_method_candidates(io::IO, ex::MethodError) end end end + kwords = Symbol[] + if isdefined(ft.name.mt, :kwsorter) + kwsorter_t = typeof(ft.name.mt.kwsorter) + kwords = kwarg_decl(method.sig, kwsorter_t) + length(kwords) > 0 && print(buf, "; ", join(kwords, ", ")) + end print(buf, ")") + if 0 < length(kwargs) + unexpected = Symbol[] + if isempty(kwords) || !(any(endswith(string(kword), "...") for kword in kwords)) + for (k, v) in kwargs + if !(k in kwords) + push!(unexpected, k) + end + end + end + if 0 < length(unexpected) + Base.with_output_color(:red, buf) do buf + print(buf, " got an unrecognized keyword argument \"", join(unexpected, "\", \""), "\"") + end + end + end push!(lines, (buf, right_matches)) end end diff --git a/test/replutil.jl b/test/replutil.jl index 136c024fcf99e..05500b7b83e2a 100644 --- a/test/replutil.jl +++ b/test/replutil.jl @@ -52,16 +52,19 @@ no_color = no_color = "\nClosest candidates are:\n method_c3(::Float64, !Matche test_have_color(buf, color, no_color) # Test for the method error in issue #8651 -Base.show_method_candidates(buf, MethodError(readline,("",))) -test_have_color(buf, "\e[0m\nClosest candidates are:\n readline(::AbstractString)\e[0m", "\nClosest candidates are:\n readline(::AbstractString)") +method_c4() = true +method_c4(x::AbstractString) = false +Base.show_method_candidates(buf, MethodError(method_c4,("",))) +test_have_color(buf, "\e[0m\nClosest candidates are:\n method_c4(::AbstractString)\n method_c4()\e[0m", "\nClosest candidates are:\n method_c4(::AbstractString)\n method_c4()") -method_c4(::Type{Float64}) = true -Base.show_method_candidates(buf, MethodError(method_c4,(Float64,))) -test_have_color(buf, "\e[0m\nClosest candidates are:\n method_c4(::Type{Float64})\e[0m", - "\nClosest candidates are:\n method_c4(::Type{Float64})") +method_c5(::Type{Float64}) = true +Base.show_method_candidates(buf, MethodError(method_c5,(Float64,))) +test_have_color(buf, "\e[0m\nClosest candidates are:\n method_c5(::Type{Float64})\e[0m", + "\nClosest candidates are:\n method_c5(::Type{Float64})") -Base.show_method_candidates(buf, MethodError(method_c4,(Int32,))) -test_have_color(buf, "", "") +Base.show_method_candidates(buf, MethodError(method_c5,(Int32,))) +test_have_color(buf, "\e[0m\nClosest candidates are:\n method_c5(\e[1m\e[31m::Type{Float64}\e[0m)\e[0m", + "\nClosest candidates are:\n method_c5(!Matched::Type{Float64})") type Test_type end test_type = Test_type() @@ -83,6 +86,37 @@ Base.show_method_candidates(buf, MethodError(PR16155,(Int64(3), 2.0, Int64(3)))) test_have_color(buf, "\e[0m\nClosest candidates are:\n PR16155(::Int64, ::Any)\n PR16155(::Any, ::Any)\n PR16155{T}(::Any)\e[0m", "\nClosest candidates are:\n PR16155(::Int64, ::Any)\n PR16155(::Any, ::Any)\n PR16155{T}(::Any)") +method_c6(; x=1) = x +method_c6(a; y=1) = y +m_error = try method_c6(y=1) catch e; e; end +showerror(buf, m_error) +error_out = takebuf_string(buf) +m_error = try method_c6(1, x=1) catch e; e; end +showerror(buf, m_error) +error_out1 = takebuf_string(buf) + +if Base.have_color + @test contains(error_out, "method_c6(; x)\e[1m\e[31m got an unrecognized keyword argument \"y\"\e[0m") + @test contains(error_out, "method_c6(\e[1m\e[31m::Any\e[0m; y)") + @test contains(error_out1, "method_c6(::Any; y)\e[1m\e[31m got an unrecognized keyword argument \"x\"\e[0m") +else + @test contains(error_out, "method_c6(; x) got an unrecognized keyword argument \"y\"") + @test contains(error_out, "method_c6(!Matched::Any; y)") + @test contains(error_out1, "method_c6(::Any; y) got an unrecognized keyword argument \"x\"") +end + +method_c7(a, b; kargs...) = a +Base.show_method_candidates(buf, MethodError(method_c7, (1, 1)), [(:x, 1), (:y, 2)]) +test_have_color(buf, "\e[0m\nClosest candidates are:\n method_c7(::Any, ::Any; kargs...)\e[0m", + "\nClosest candidates are:\n method_c7(::Any, ::Any; kargs...)") + +addConstraint_15639(c::Int32) = c +addConstraint_15639(c::Int64; uncset=nothing) = addConstraint_15639(Int32(c), uncset=uncset) + +Base.show_method_candidates(buf, MethodError(addConstraint_15639, (Int32(1),)), [(:uncset, nothing)]) +test_have_color(buf, "\e[0m\nClosest candidates are:\n addConstraint_15639(::Int32)\e[1m\e[31m got an unrecognized keyword argument \"uncset\"\e[0m\n addConstraint_15639(\e[1m\e[31m::Int64\e[0m; uncset)\e[0m", + "\nClosest candidates are:\n addConstraint_15639(::Int32) got an unrecognized keyword argument \"uncset\"\n addConstraint_15639(!Matched::Int64; uncset)") + macro except_str(expr, err_type) return quote let err = nothing