Skip to content

Add MethodError hints for functions in other modules #58715

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
34 changes: 27 additions & 7 deletions base/errorshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -327,13 +327,33 @@ function showerror(io::IO, ex::MethodError)
if ft <: AbstractArray
print(io, "\nIn case you're trying to index into the array, use square brackets [] instead of parentheses ().")
end
# Check for local functions that shadow methods in Base
let name = ft.name.singletonname
if f_is_function && isdefined(Base, name)
basef = getfield(Base, name)
if basef !== f && hasmethod(basef, arg_types)
print(io, "\nYou may have intended to import ")
show_unquoted(io, Expr(:., :Base, QuoteNode(name)))
# Check for functions with the same name in other modules
if f_is_function && ex.world != typemax(UInt)
let name = ft.name.singletonname
modules_to_check = Set{Module}()
push!(modules_to_check, Base)
for T in san_arg_types_param
modulesof!(modules_to_check, T)
end

# Check all modules (sorted for consistency)
sorted_modules = sort!(collect(modules_to_check), by=nameof)
for mod in sorted_modules
if isdefined(mod, name)
candidate = getfield(mod, name)
if candidate !== f && hasmethod(candidate, arg_types; world=ex.world)
if mod === Base
print(io, "\nYou may have intended to import ")
show_unquoted(io, Expr(:., :Base, QuoteNode(name)))
else
print(io, "\nThe definition in ")
show_unquoted(io, mod)
print(io, " may have intended to extend ")
f_module = parentmodule(ft)
show_unquoted(io, Expr(:., f_module, QuoteNode(name)))
end
end
end
end
end
end
Expand Down
14 changes: 14 additions & 0 deletions test/errorshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,20 @@ for (func,str) in ((TestMethodShadow.:+,":+"), (TestMethodShadow.:(==),":(==)"),
@test occursin("You may have intended to import Base.$str", sprint(Base.showerror, ex))
end

# Test hint for functions in modules of argument types (issue #58682)
module TestModuleHint
struct Bar end
length(x::Bar) = 42
end
let ex = try
# Call Base.length on TestModuleHint.Bar - should suggest importing TestModuleHint.length
length(TestModuleHint.Bar())
catch e
e
end::MethodError
@test occursin("may have intended to extend", sprint(Base.showerror, ex))
end

# Test that implementation detail of include() is hidden from the user by default
let bt = try
@noinline include("testhelpers/include_error.jl")
Expand Down