From 01149a324879b55129845d3ad8c198a0daedb2b9 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 | 67 ++++++++++++++++++++++++++++++++++++------------ test/replutil.jl | 39 ++++++++++++++++++++++++++++ test/runtests.jl | 2 +- 3 files changed, 91 insertions(+), 17 deletions(-) create mode 100644 test/replutil.jl 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..1128ceaaf307af --- /dev/null +++ b/test/replutil.jl @@ -0,0 +1,39 @@ +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::Int, s::AbstractString...) = pass +buf = IOBuffer() +Base.show_method_candidates(buf, Base.MethodError(fe,(1.,1,""))) +no_color = "\nClosest candidates are:\n fe(::Int64, ::AbstractString...)\n" +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(fe,(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 fe(::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(ge,(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 = "\nClosest candidates are:\n ge(::Int64, ::Float64, ::Any...)\n ge(::Int64, ::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" ]