Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keyword display improvements #16669

Merged
merged 4 commits into from
Jun 28, 2016
Merged
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
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 @@ -397,7 +397,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 @@ -492,7 +493,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_cell_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 @@ -325,10 +325,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