diff --git a/base/replutil.jl b/base/replutil.jl index 207f7dd037e584..89d226e2ff17c8 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -165,27 +165,62 @@ function showerror(io::IO, ex::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, ex) +end + +function show_method_candidates(io::IO, ex::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) + name = isgeneric(ex.f) ? ex.f.env.name : :anonymous for method in methods(ex.f) - n = length(ex.args) - if n != length(method.sig) - continue - end buf = IOBuffer() - print(buf, " $(ex.f.env.name)(") - first = true + print(buf, " $name") right_matches = 0 - for (arg, sigtype) in Zip2{Any,Any}(ex.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(ex.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(ex.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 @@ -196,7 +231,7 @@ function showerror(io::IO, ex::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..05fbbbc1a5b764 --- /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 + +fe(x::Float64, s::AbstractString...) = true +ge(x::Int32, args...) = true +ge(x::Int32, y::Float64 ,args...) = true +ge(x::Int32, y::Float64) = true +ge{T<:Real}(x::T, y::T, z::T) = true + +buf = IOBuffer() +Base.show_method_candidates(buf, Base.MethodError(fe,(1, 1, ""))) +no_color = "\nClosest candidates are:\n fe(::Float64, ::AbstractString...)\n" +test_have_color(buf, + "\e[0m\nClosest candidates are:\n fe(\e[1m\e[31m::Float64\e[0m, \e[1m\e[31m::AbstractString...\e[0m)\n\e[0m", + no_color) + +Base.show_method_candidates(buf, Base.MethodError(fe,(1, "", ""))) +test_have_color(buf, + "\e[0m\nClosest candidates are:\n fe(\e[1m\e[31m::Float64\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 fe(::Float64, ::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, "", "") + + +Base.show_method_candidates(buf, Base.MethodError(ge,(1., 1., 2))) +color = "\e[0m\nClosest candidates are:\n ge(\e[1m\e[31m::Int32\e[0m, ::Float64, ::Any...)\n ge(\e[1m\e[31m::Int32\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 = "\nClosest candidates are:\n ge(::Int32, ::Float64, ::Any...)\n ge(::Int32, ::Any...)\n ge{T<:Real}(::T<:Real, ::T<:Real, ::T<:Real)\n ...\n" +test_have_color(buf, color, no_color) \ 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" ]