Skip to content

Commit

Permalink
Provide better error hint when UndefVarError results from name clas…
Browse files Browse the repository at this point in the history
…hes (JuliaLang#53469)

We can detect this since we set the `usingfailed` bit when the clash
occurs (to avoid printing the `WARNING` multiple times). In this case,
typos or missing imports (the current message) isn't quite as clear as
it could be, because in fact the name is probably spelled totally right,
it's just that there is a missing explicit import or the name should be
qualified.

This code will stop working if we change the flags in `Core.Binding`,
but the test I added should catch that. However if REPL is supposed to
be independent of Base and not depend on internals there, there could be
an issue. In that case we should probably add an API to Base to inspect
this `usingfailed` bit so we can use it in the REPL.

---------

Co-authored-by: Jameson Nash <[email protected]>
Co-authored-by: Alex Arslan <[email protected]>
  • Loading branch information
3 people authored Mar 3, 2024
1 parent e7734ea commit 0f902bf
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 1 deletion.
11 changes: 10 additions & 1 deletion stdlib/REPL/src/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,16 @@ function UndefVarError_hint(io::IO, ex::UndefVarError)
if C_NULL == owner
# No global of this name exists in this module.
# This is the common case, so do not print that information.
print(io, "\nSuggestion: check for spelling errors or missing imports.")
# It could be the binding was exported by two modules, which we can detect
# by the `usingfailed` flag in the binding:
if isdefined(bnd, :flags) && Bool(bnd.flags >> 4 & 1) # magic location of the `usingfailed` flag
print(io, "\nHint: It looks like two or more modules export different ",
"bindings with this name, resulting in ambiguity. Try explicitly ",
"importing it from a particular module, or qualifying the name ",
"with the module it should come from.")
else
print(io, "\nSuggestion: check for spelling errors or missing imports.")
end
owner = bnd
else
owner = unsafe_pointer_to_objref(owner)::Core.Binding
Expand Down
29 changes: 29 additions & 0 deletions stdlib/REPL/test/repl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1696,6 +1696,35 @@ finally
empty!(Base.Experimental._hint_handlers)
end

try # test the functionality of `UndefVarError_hint` against import clashes
@assert isempty(Base.Experimental._hint_handlers)
Base.Experimental.register_error_hint(REPL.UndefVarError_hint, UndefVarError)

@eval module X

module A
export x
x = 1
end # A

module B
export x
x = 2
end # B

using .A, .B

end # X

expected_message = string("\nHint: It looks like two or more modules export different ",
"bindings with this name, resulting in ambiguity. Try explicitly ",
"importing it from a particular module, or qualifying the name ",
"with the module it should come from.")
@test_throws expected_message X.x
finally
empty!(Base.Experimental._hint_handlers)
end

# Hints for tab completes

fake_repl() do stdin_write, stdout_read, repl
Expand Down

0 comments on commit 0f902bf

Please sign in to comment.