diff --git a/base/REPLCompletions.jl b/base/REPLCompletions.jl
index c056987ae25d9..207be534a0520 100644
--- a/base/REPLCompletions.jl
+++ b/base/REPLCompletions.jl
@@ -323,10 +323,15 @@ function complete_methods(ex_org::Expr)
out = String[]
t_in = Tuple{Core.Typeof(func), args_ex...} # Input types
na = length(args_ex)+1
- for method in methods(func)
+ ml = methods(func)
+ kwtype = isdefined(ml.mt, :kwsorter) ? Nullable{DataType}(typeof(ml.mt.kwsorter)) : Nullable{DataType}()
+ io = IOBuffer()
+ for method in ml
# Check if the method's type signature intersects the input types
- typeintersect(Tuple{method.sig.parameters[1 : min(na, end)]...}, t_in) != Union{} &&
- push!(out,string(method))
+ if typeintersect(Tuple{method.sig.parameters[1 : min(na, end)]...}, t_in) != Union{}
+ show(io, method, kwtype=kwtype)
+ push!(out, takebuf_string(io))
+ end
end
return out
end
diff --git a/base/error.jl b/base/error.jl
index 994dde48f23a1..70a771d754545 100644
--- a/base/error.jl
+++ b/base/error.jl
@@ -27,7 +27,7 @@ backtrace() = ccall(:jl_backtrace_from_here, Array{Ptr{Void},1}, (Int32,), false
catch_backtrace() = ccall(:jl_get_backtrace, Array{Ptr{Void},1}, ())
## keyword arg lowering generates calls to this ##
-kwerr(kw) = error("unrecognized keyword argument \"", kw, "\"")
+kwerr(kw, args...) = throw(MethodError(typeof(args[1]).name.mt.kwsorter, (kw,args...)))
## system error handling ##
diff --git a/base/methodshow.jl b/base/methodshow.jl
index 928c8b09fe70a..fd9aa090b1f0b 100644
--- a/base/methodshow.jl
+++ b/base/methodshow.jl
@@ -78,7 +78,13 @@ function kwarg_decl(sig::ANY, kwtype::DataType)
kwli = ccall(:jl_methtable_lookup, Any, (Any, Any), kwtype.name.mt, sig)
if kwli !== nothing
kwli = kwli::Method
- return filter(x->!('#' in string(x)), kwli.lambda_template.slotnames[kwli.lambda_template.nargs+1:end])
+ kws = filter(x->!('#' in string(x)), kwli.lambda_template.slotnames[kwli.lambda_template.nargs+1:end])
+ # ensure the kwarg... is always printed last. The order of the arguments are not
+ # necessarily the same as defined in the function
+ i = findfirst(x -> endswith(string(x), "..."), kws)
+ i==0 && return kws
+ push!(kws, kws[i])
+ return deleteat!(kws,i)
end
return ()
end
diff --git a/base/replutil.jl b/base/replutil.jl
index c1ed0f2543953..90260b515363c 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 = 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 = Any[(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,14 @@ function showerror(io::IO, ex::MethodError)
print(io, "::$typ")
i == length(arg_types_param) || print(io, ", ")
end
+ if !isempty(kwargs)
+ print(io, "; ")
+ for (i, (k, v)) in enumerate(kwargs)
+ print(io, k, "=")
+ show(IOContext(io, :limit=>true), v)
+ i == length(kwargs) || print(io, ", ")
+ end
+ end
print(io, ")")
end
if ft <: AbstractArray
@@ -189,7 +207,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 +240,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::Vector=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 +251,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 +280,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 +335,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 +357,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 !isempty(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 !isempty(unexpected)
+ Base.with_output_color(:red, buf) do buf
+ print(buf, " got an unsupported keyword argument \"", join(unexpected, "\", \""), "\"")
+ end
+ end
+ end
push!(lines, (buf, right_matches))
end
end
diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm
index 92a6fbcf1ad05..2a081e9e5a7bb 100644
--- a/src/julia-syntax.scm
+++ b/src/julia-syntax.scm
@@ -411,7 +411,8 @@
sparams))
(keyword-sparam-names
(map (lambda (s) (if (symbol? s) s (cadr s))) keyword-sparams)))
- (let ((kw (gensy)) (i (gensy)) (ii (gensy)) (elt (gensy)) (rkw (gensy))
+ (let ((kw (gensy)) (i (gensy)) (ii (gensy)) (elt (gensy))
+ (rkw (if (null? restkw) '() (symbol (string (car restkw) "..."))))
(mangled (symbol (string "#" (if name (undot-name name) 'call) "#"
(string (current-julia-module-counter)))))
(flags (map (lambda (x) (gensy)) vals)))
@@ -506,7 +507,8 @@
,else)))
(if (null? restkw)
;; if no rest kw, give error for unrecognized
- `(call (top kwerr) ,elt)
+ `(call (top kwerr) ,kw ,@(map arg-name pargl),@(if (null? vararg) '()
+ (list `(... ,(arg-name (car vararg))))))
;; otherwise add to rest keywords
`(ccall 'jl_array_ptr_1d_push Void (tuple Any Any)
,rkw (tuple ,elt
diff --git a/test/keywordargs.jl b/test/keywordargs.jl
index ce2c979f051af..eff8844ec7fa4 100644
--- a/test/keywordargs.jl
+++ b/test/keywordargs.jl
@@ -9,7 +9,7 @@ kwf1(ones; tens=0, hundreds=0) = ones + 10*tens + 100*hundreds
@test kwf1(3, tens=7, hundreds=2) == 273
@test_throws MethodError kwf1() # no method, too few args
-@test_throws ErrorException kwf1(1, z=0) # unsupported keyword
+@test_throws MethodError kwf1(1, z=0) # unsupported keyword
@test_throws MethodError kwf1(1, 2) # no method, too many positional args
# keyword args plus varargs
@@ -18,7 +18,7 @@ kwf2(x, rest...; y=1) = (x, y, rest)
@test isequal(kwf2(0,1,2), (0, 1, (1,2)))
@test isequal(kwf2(0,1,2,y=88), (0, 88, (1,2)))
@test isequal(kwf2(0,y=88,1,2), (0, 88, (1,2)))
-@test_throws ErrorException kwf2(0, z=1)
+@test_throws MethodError kwf2(0, z=1)
@test_throws MethodError kwf2(y=1)
# keyword arg with declared type
@@ -191,7 +191,7 @@ let f = (x;a=1,b=2)->(x, a, b)
@test f(0) === (0, 1, 2)
@test f(1,a=10,b=20) === (1,10,20)
@test f(0,b=88) === (0, 1, 88)
- @test_throws ErrorException f(0,z=1)
+ @test_throws MethodError f(0,z=1)
end
@test ((a=2)->10a)(3) == 30
@test ((a=2)->10a)() == 20
diff --git a/test/replcompletions.jl b/test/replcompletions.jl
index f7d9d88531b7f..0bc56bb4cc11b 100644
--- a/test/replcompletions.jl
+++ b/test/replcompletions.jl
@@ -43,6 +43,8 @@ module CompletionFoo
const a=x->x
test6()=[a, a]
+ kwtest(; x=1, y=2, w...) = pass
+
array = [1, 1]
varfloat = 0.1
@@ -325,6 +327,12 @@ c, r, res = test_complete(s)
@test length(c) == 2
#################################################################
+s = "CompletionFoo.kwtest( "
+c, r, res = test_complete(s)
+@test !res
+@test length(c) == 1
+@test contains(c[1], "x, y, w...")
+
# Test of inference based getfield completion
s = "\"\"."
c,r = test_complete(s)
diff --git a/test/replutil.jl b/test/replutil.jl
index 40d69ef76f15f..a8e0d2acead96 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,41 @@ 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 unsupported 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 unsupported keyword argument \"x\"\e[0m")
+else
+ @test contains(error_out, "method_c6(; x) got an unsupported keyword argument \"y\"")
+ @test contains(error_out, "method_c6(!Matched::Any; y)")
+ @test contains(error_out1, "method_c6(::Any; y) got an unsupported 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...)")
+method_c8(a, b; y=1, w=1) = a
+Base.show_method_candidates(buf, MethodError(method_c8, (1, 1)), [(:x, 1), (:y, 2), (:z, 1), (:w, 1)])
+test_have_color(buf, "\e[0m\nClosest candidates are:\n method_c8(::Any, ::Any; y, w)\e[1m\e[31m got an unsupported keyword argument \"x\", \"z\"\e[0m\e[0m",
+ "\nClosest candidates are:\n method_c8(::Any, ::Any; y, w) got an unsupported keyword argument \"x\", \"z\"")
+
+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 unsupported 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 unsupported keyword argument \"uncset\"\n addConstraint_15639(!Matched::Int64; uncset)")
+
macro except_str(expr, err_type)
return quote
let err = nothing
diff --git a/test/show.jl b/test/show.jl
index e9e8eccd60fba..0d0dfc61eaf25 100644
--- a/test/show.jl
+++ b/test/show.jl
@@ -358,10 +358,14 @@ end
f5971(x, y...; z=1, w...) = nothing
let repr = sprint(io -> show(io,"text/plain", methods(f5971)))
- @test contains(repr, "f5971(x, y...; z)")
+ @test contains(repr, "f5971(x, y...; z, w...)")
end
let repr = sprint(io -> show(io,"text/html", methods(f5971)))
- @test contains(repr, "f5971(x, y...; z)")
+ @test contains(repr, "f5971(x, y...; z, w...)")
+end
+f16580(x, y...; z=1, w=y+x, q...) = nothing
+let repr = sprint(io -> show(io,"text/html", methods(f16580)))
+ @test contains(repr, "f16580(x, y...; z, w, q...)")
end
if isempty(Base.GIT_VERSION_INFO.commit)