From e7ce049327d53e2d20e575a8a7eefa1d5f41e6ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8egh?= Date: Fri, 19 Dec 2014 16:28:30 +0100 Subject: [PATCH] Added so #7714 also takes care of args... when considering a match and removed the requirement of equal lengths of args. The match also takes into account that parametric methods args needs to have the same type for one tvar using typeintersect. There is also added test of `show_method_candidates`. --- base/replutil.jl | 66 ++++++++++++++++++++++++++++++++++++------------ test/replutil.jl | 41 ++++++++++++++++++++++++++++++ test/runtests.jl | 2 +- 3 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 test/replutil.jl 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..49557fe4bcb29b --- /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 + f(x::Int, s::AbstractString...) = pass + buf = IOBuffer() + Base.show_method_candidates(buf, Base.MethodError(f,(1.,1,""))) + no_color = "Closest candidates are:\n f(::Int64, ::AbstractString...)" + test_have_color(buf, + "\e[0m\nClosest candidates are:\n f(\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 f(\e[1m\e[31m::Int64\e[0m, ::AbstractString...)\n\e[0m", + no_color) + + # should match + Base.show_method_candidates(buf, Base.MethodError(f,(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(f,(1.,1,1))) + test_have_color(buf, "", "") + + g(x::Int, args...) = pass + g(x::Int, y::Float64 ,args...) = pass + g(x::Int, y::Float64) = pass + g{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 g(\e[1m\e[31m::Int64\e[0m, ::Float64, ::Any...)\n g(\e[1m\e[31m::Int64\e[0m, ::Any...)\n g{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 g(::Int64, ::Float64, ::Any...)\n g(::Int64, ::Any...)\n g{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" ]