diff --git a/base/replutil.jl b/base/replutil.jl index 8138bb9df60ccb..aa25d25e457199 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -168,27 +168,61 @@ function showerror(io::IO, e::MethodError) print(io, "since type constructors fall back to convert methods in julia v0.4.") end - # Display up to three closest candidates + show_method_candidates(io, e) +end + +function show_method_candidates(io::IO, e::MethodError) + # Displays the closest candidates of the given function by looping over the + # functions methods and counting the number of matching arguments. lines = Array((IOBuffer, Int), 0) for method in methods(e.f) - n = length(e.args) - if n != length(method.sig) - continue - end buf = IOBuffer() - print(buf, " $(e.f.env.name)(") - first = true + print(buf, " $(e.f.env.name)") right_matches = 0 - for (arg, sigtype) in Zip2{Any,Any}(e.args, method.sig) - if first - first = false + tv = method.tvars + if !isa(tv,Tuple) + tv = (tv,) + end + if !isempty(tv) + show_delim_array(buf, tv, '{', ',', '}', false) + end + print(buf, "(") + t_i = Any[typeof(e.args)...] + right_matches = 0 + for i = 1 : min(length(t_i), length(method.sig)) + i != 1 && print(buf, ", ") + # If isvarargtype then it checks wether the rest of the input arguements matches + # the varargtype + j = Base.isvarargtype(method.sig[i]) ? length(t_i) : i + # checks if the type of arg 1:i of the input intersects with the current method + t_in = typeintersect(method.sig[1:i], tuple(t_i[1:j]...)) + if t_in == None + # If there is no typeintersect then the type signature from the method is + # inserted in t_i this insures if the type at the next i matches the type + # signature then there will be a type intersect + t_i[i] = method.sig[i] + Base.with_output_color(:red, buf) do buf + print(buf, "::$(method.sig[i])") + end else - print(buf, ", ") + right_matches += j==i ? 1 : 0 + print(buf, "::$(method.sig[i])") end - if typeof(arg) <: sigtype - right_matches += 1 - print(buf, "::$(sigtype)") - else + end + if length(t_i) > length(method.sig) && Base.isvarargtype(method.sig[end]) + # It insures that methods like f(a::AbstractString...) gets the correct + # number of right_matches + for t in typeof(e.args)[length(method.sig):end] + if t <: method.sig[end].parameters[1] + right_matches += 1 + end + end + end + if length(t_i) < length(method.sig) + # If the methods args is longer than input then the method + # arguments is printed as not a match + for sigtype in method.sig[length(t_i)+1:end] + print(buf, ", ") Base.with_output_color(:red, buf) do buf print(buf, "::$(sigtype)") end @@ -199,7 +233,7 @@ function showerror(io::IO, e::MethodError) push!(lines, (buf, right_matches)) end end - if length(lines) != 0 + if length(lines) != 0 # Display up to three closest candidates Base.with_output_color(:normal, io) do io println(io, "\nClosest candidates are:") sort!(lines, by = x -> -x[2]) diff --git a/test/replutil.jl b/test/replutil.jl new file mode 100644 index 00000000000000..64cd913e7a8857 --- /dev/null +++ b/test/replutil.jl @@ -0,0 +1,41 @@ +function test_have_color(buf, color, no_color) + if Base.have_color + @test takebuf_string(buf) == color + else + @test takebuf_string(buf) == no_color + end +end +let + fe(x::Int, s::AbstractString...) = pass + buf = IOBuffer() + Base.show_method_candidates(buf, Base.MethodError(fe,(1.,1,""))) + no_color = "Closest candidates are:\n f(::Int64, ::AbstractString...)" + test_have_color(buf, + "\e[0m\nClosest candidates are:\n fe(\e[1m\e[31m::Int64\e[0m, \e[1m\e[31m::AbstractString...\e[0m)\n\e[0m", + no_color) + + Base.show_method_candidates(buf, Base.MethodError(f,(1.,"",""))) + test_have_color(buf, + "\e[0m\nClosest candidates are:\n fe(\e[1m\e[31m::Int64\e[0m, ::AbstractString...)\n\e[0m", + no_color) + + # should match + Base.show_method_candidates(buf, Base.MethodError(fe,(1,"",""))) + test_have_color(buf, + "\e[0m\nClosest candidates are:\n f(::Int64, ::AbstractString...)\n\e[0m", + no_color) + + # Have no matches so should return empty + Base.show_method_candidates(buf, Base.MethodError(fe,(1.,1,1))) + test_have_color(buf, "", "") + + ge(x::Int, args...) = pass + ge(x::Int, y::Float64 ,args...) = pass + ge(x::Int, y::Float64) = pass + ge{T<:Real}(x::T, y::T, z::T) = pass + Base.show_method_candidates(buf, Base.MethodError(g,(1.,1.,2))) + color = "\e[0m\nClosest candidates are:\n ge(\e[1m\e[31m::Int64\e[0m, ::Float64, ::Any...)\n ge(\e[1m\e[31m::Int64\e[0m, ::Any...)\n ge{T<:Real}(::T<:Real, ::T<:Real, \e[1m\e[31m::T<:Real\e[0m)\n ...\n\e[0m" + no_color = no_color = "Closest candidates are:\n ge(::Int64, ::Float64, ::Any...)\n ge(::Int64, ::Any...)\n ge{T<:Real}(::T<:Real, ::T<:Real, ::T<:Real)\n ..." + test_have_color(buf, color, no_color) + +end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index e2d6ae4f427821..21cf7e366fb016 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,7 +9,7 @@ testnames = [ "resolve", "pollfd", "mpfr", "broadcast", "complex", "socket", "floatapprox", "readdlm", "reflection", "regex", "float16", "combinatorics", "sysinfo", "rounding", "ranges", "mod2pi", "euler", "show", - "lineedit", "replcompletions", "repl", "sets", "test", "goto", + "lineedit", "replcompletions", "repl", "replutil", "sets", "test", "goto", "llvmcall", "grisu", "nullable", "meta", "profile", "libgit2", "docs", "base64", "parser" ]