Skip to content

Commit

Permalink
Merge pull request #16669 from dhoegh/kword
Browse files Browse the repository at this point in the history
Keyword display improvements
  • Loading branch information
vtjnash authored Jun 28, 2016
2 parents b44ec98 + d4e8792 commit c775fc5
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 27 deletions.
11 changes: 8 additions & 3 deletions base/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion base/error.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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 ##

Expand Down
8 changes: 7 additions & 1 deletion base/methodshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
54 changes: 47 additions & 7 deletions base/replutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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...]
Expand All @@ -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!]
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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")
Expand All @@ -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
Expand Down
6 changes: 4 additions & 2 deletions src/julia-syntax.scm
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions test/keywordargs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions test/replcompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down
54 changes: 46 additions & 8 deletions test/replutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand Down
8 changes: 6 additions & 2 deletions test/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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...; <i>z</i>)")
@test contains(repr, "f5971(x, y...; <i>z, w...</i>)")
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...; <i>z, w, q...</i>)")
end

if isempty(Base.GIT_VERSION_INFO.commit)
Expand Down

0 comments on commit c775fc5

Please sign in to comment.