Skip to content
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

move REPL.REPLCompletions.UndefVarError_hint to REPL module #52990

Merged
merged 1 commit into from
Jan 22, 2024
Merged
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
76 changes: 73 additions & 3 deletions stdlib/REPL/src/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,83 @@ REPL.run_repl(repl)
"""
module REPL

Base.Experimental.@optlevel 1
Base.Experimental.@max_methods 1

function UndefVarError_hint(io::IO, ex::UndefVarError)
var = ex.var
if var === :or
print(io, "\nSuggestion: Use `||` for short-circuiting boolean OR.")
elseif var === :and
print(io, "\nSuggestion: Use `&&` for short-circuiting boolean AND.")
elseif var === :help
println(io)
# Show friendly help message when user types help or help() and help is undefined
show(io, MIME("text/plain"), Base.Docs.parsedoc(Base.Docs.keywords[:help]))
elseif var === :quit
print(io, "\nSuggestion: To exit Julia, use Ctrl-D, or type exit() and press enter.")
end
if isdefined(ex, :scope)
scope = ex.scope
if scope isa Module
bnd = ccall(:jl_get_module_binding, Any, (Any, Any, Cint), scope, var, true)::Core.Binding
if isdefined(bnd, :owner)
owner = bnd.owner
if owner === bnd
print(io, "\nSuggestion: add an appropriate import or assignment. This global was declared but not assigned.")
end
else
owner = ccall(:jl_binding_owner, Ptr{Cvoid}, (Any, Any), scope, var)
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.")
owner = bnd
else
owner = unsafe_pointer_to_objref(owner)::Core.Binding
end
end
if owner !== bnd
# this could use jl_binding_dbgmodule for the exported location in the message too
print(io, "\nSuggestion: this global was defined as `$(owner.globalref)` but not assigned a value.")
end
elseif scope === :static_parameter
print(io, "\nSuggestion: run Test.detect_unbound_args to detect method arguments that do not fully constrain a type parameter.")
elseif scope === :local
print(io, "\nSuggestion: check for an assignment to a local variable that shadows a global of the same name.")
end
else
scope = undef
end
if scope !== Base && !_UndefVarError_warnfor(io, Base, var)
warned = false
for m in Base.loaded_modules_order
m === Core && continue
m === Base && continue
m === Main && continue
m === scope && continue
warned |= _UndefVarError_warnfor(io, m, var)
end
warned ||
_UndefVarError_warnfor(io, Core, var) ||
_UndefVarError_warnfor(io, Main, var)
end
return nothing
end

function _UndefVarError_warnfor(io::IO, m::Module, var::Symbol)
Base.isbindingresolved(m, var) || return false
(Base.isexported(m, var) || Base.ispublic(m, var)) || return false
print(io, "\nHint: a global variable of this name also exists in $m.")
return true
end

function __init__()
Base.REPL_MODULE_REF[] = REPL
Base.Experimental.register_error_hint(UndefVarError_hint, UndefVarError)
return nothing
end

Base.Experimental.@optlevel 1
Base.Experimental.@max_methods 1

using Base.Meta, Sockets
import InteractiveUtils

Expand Down
71 changes: 1 addition & 70 deletions stdlib/REPL/src/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1473,78 +1473,9 @@ function shell_completions(string, pos)
return Completion[], 0:-1, false
end

function UndefVarError_hint(io::IO, ex::UndefVarError)
var = ex.var
if var === :or
print(io, "\nSuggestion: Use `||` for short-circuiting boolean OR.")
elseif var === :and
print(io, "\nSuggestion: Use `&&` for short-circuiting boolean AND.")
elseif var === :help
println(io)
# Show friendly help message when user types help or help() and help is undefined
show(io, MIME("text/plain"), Base.Docs.parsedoc(Base.Docs.keywords[:help]))
elseif var === :quit
print(io, "\nSuggestion: To exit Julia, use Ctrl-D, or type exit() and press enter.")
end
if isdefined(ex, :scope)
scope = ex.scope
if scope isa Module
bnd = ccall(:jl_get_module_binding, Any, (Any, Any, Cint), scope, var, true)::Core.Binding
if isdefined(bnd, :owner)
owner = bnd.owner
if owner === bnd
print(io, "\nSuggestion: add an appropriate import or assignment. This global was declared but not assigned.")
end
else
owner = ccall(:jl_binding_owner, Ptr{Cvoid}, (Any, Any), scope, var)
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.")
owner = bnd
else
owner = unsafe_pointer_to_objref(owner)::Core.Binding
end
end
if owner !== bnd
# this could use jl_binding_dbgmodule for the exported location in the message too
print(io, "\nSuggestion: this global was defined as `$(owner.globalref)` but not assigned a value.")
end
elseif scope === :static_parameter
print(io, "\nSuggestion: run Test.detect_unbound_args to detect method arguments that do not fully constrain a type parameter.")
elseif scope === :local
print(io, "\nSuggestion: check for an assignment to a local variable that shadows a global of the same name.")
end
else
scope = undef
end
if scope !== Base && !_UndefVarError_warnfor(io, Base, var)
warned = false
for m in Base.loaded_modules_order
m === Core && continue
m === Base && continue
m === Main && continue
m === scope && continue
warned |= _UndefVarError_warnfor(io, m, var)
end
warned ||
_UndefVarError_warnfor(io, Core, var) ||
_UndefVarError_warnfor(io, Main, var)
end
nothing
end

function _UndefVarError_warnfor(io::IO, m::Module, var::Symbol)
Base.isbindingresolved(m, var) || return false
(Base.isexported(m, var) || Base.ispublic(m, var)) || return false
print(io, "\nHint: a global variable of this name also exists in $m.")
return true
end

function __init__()
Base.Experimental.register_error_hint(UndefVarError_hint, UndefVarError)
COMPLETION_WORLD[] = Base.get_world_counter()
nothing
return nothing
end

end # module
2 changes: 1 addition & 1 deletion stdlib/REPL/test/repl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1677,7 +1677,7 @@ end

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

# check the requirement to trigger the hint via `UndefVarError_hint`
@test !isdefined(Main, :remove_linenums!) && Base.ispublic(Base, :remove_linenums!)
Expand Down