Skip to content

Commit 188cc93

Browse files
authored
move REPL.REPLCompletions.UndefVarError_hint to REPL module (#52990)
Since `UndefVarError_hint` is an interactive feature and isn't directly related to completion, it seems more appropriate to place it within `REPL` module rather than its `REPLCompletions` submodule.
1 parent 9c78420 commit 188cc93

File tree

3 files changed

+75
-74
lines changed

3 files changed

+75
-74
lines changed

stdlib/REPL/src/REPL.jl

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,83 @@ REPL.run_repl(repl)
1414
"""
1515
module REPL
1616

17+
Base.Experimental.@optlevel 1
18+
Base.Experimental.@max_methods 1
19+
20+
function UndefVarError_hint(io::IO, ex::UndefVarError)
21+
var = ex.var
22+
if var === :or
23+
print(io, "\nSuggestion: Use `||` for short-circuiting boolean OR.")
24+
elseif var === :and
25+
print(io, "\nSuggestion: Use `&&` for short-circuiting boolean AND.")
26+
elseif var === :help
27+
println(io)
28+
# Show friendly help message when user types help or help() and help is undefined
29+
show(io, MIME("text/plain"), Base.Docs.parsedoc(Base.Docs.keywords[:help]))
30+
elseif var === :quit
31+
print(io, "\nSuggestion: To exit Julia, use Ctrl-D, or type exit() and press enter.")
32+
end
33+
if isdefined(ex, :scope)
34+
scope = ex.scope
35+
if scope isa Module
36+
bnd = ccall(:jl_get_module_binding, Any, (Any, Any, Cint), scope, var, true)::Core.Binding
37+
if isdefined(bnd, :owner)
38+
owner = bnd.owner
39+
if owner === bnd
40+
print(io, "\nSuggestion: add an appropriate import or assignment. This global was declared but not assigned.")
41+
end
42+
else
43+
owner = ccall(:jl_binding_owner, Ptr{Cvoid}, (Any, Any), scope, var)
44+
if C_NULL == owner
45+
# No global of this name exists in this module.
46+
# This is the common case, so do not print that information.
47+
print(io, "\nSuggestion: check for spelling errors or missing imports.")
48+
owner = bnd
49+
else
50+
owner = unsafe_pointer_to_objref(owner)::Core.Binding
51+
end
52+
end
53+
if owner !== bnd
54+
# this could use jl_binding_dbgmodule for the exported location in the message too
55+
print(io, "\nSuggestion: this global was defined as `$(owner.globalref)` but not assigned a value.")
56+
end
57+
elseif scope === :static_parameter
58+
print(io, "\nSuggestion: run Test.detect_unbound_args to detect method arguments that do not fully constrain a type parameter.")
59+
elseif scope === :local
60+
print(io, "\nSuggestion: check for an assignment to a local variable that shadows a global of the same name.")
61+
end
62+
else
63+
scope = undef
64+
end
65+
if scope !== Base && !_UndefVarError_warnfor(io, Base, var)
66+
warned = false
67+
for m in Base.loaded_modules_order
68+
m === Core && continue
69+
m === Base && continue
70+
m === Main && continue
71+
m === scope && continue
72+
warned |= _UndefVarError_warnfor(io, m, var)
73+
end
74+
warned ||
75+
_UndefVarError_warnfor(io, Core, var) ||
76+
_UndefVarError_warnfor(io, Main, var)
77+
end
78+
return nothing
79+
end
80+
81+
function _UndefVarError_warnfor(io::IO, m::Module, var::Symbol)
82+
Base.isbindingresolved(m, var) || return false
83+
(Base.isexported(m, var) || Base.ispublic(m, var)) || return false
84+
print(io, "\nHint: a global variable of this name also exists in $m.")
85+
return true
86+
end
87+
1788
function __init__()
1889
Base.REPL_MODULE_REF[] = REPL
90+
Base.Experimental.register_error_hint(UndefVarError_hint, UndefVarError)
91+
return nothing
1992
end
2093

21-
Base.Experimental.@optlevel 1
22-
Base.Experimental.@max_methods 1
23-
2494
using Base.Meta, Sockets
2595
import InteractiveUtils
2696

stdlib/REPL/src/REPLCompletions.jl

Lines changed: 1 addition & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,78 +1473,9 @@ function shell_completions(string, pos)
14731473
return Completion[], 0:-1, false
14741474
end
14751475

1476-
function UndefVarError_hint(io::IO, ex::UndefVarError)
1477-
var = ex.var
1478-
if var === :or
1479-
print(io, "\nSuggestion: Use `||` for short-circuiting boolean OR.")
1480-
elseif var === :and
1481-
print(io, "\nSuggestion: Use `&&` for short-circuiting boolean AND.")
1482-
elseif var === :help
1483-
println(io)
1484-
# Show friendly help message when user types help or help() and help is undefined
1485-
show(io, MIME("text/plain"), Base.Docs.parsedoc(Base.Docs.keywords[:help]))
1486-
elseif var === :quit
1487-
print(io, "\nSuggestion: To exit Julia, use Ctrl-D, or type exit() and press enter.")
1488-
end
1489-
if isdefined(ex, :scope)
1490-
scope = ex.scope
1491-
if scope isa Module
1492-
bnd = ccall(:jl_get_module_binding, Any, (Any, Any, Cint), scope, var, true)::Core.Binding
1493-
if isdefined(bnd, :owner)
1494-
owner = bnd.owner
1495-
if owner === bnd
1496-
print(io, "\nSuggestion: add an appropriate import or assignment. This global was declared but not assigned.")
1497-
end
1498-
else
1499-
owner = ccall(:jl_binding_owner, Ptr{Cvoid}, (Any, Any), scope, var)
1500-
if C_NULL == owner
1501-
# No global of this name exists in this module.
1502-
# This is the common case, so do not print that information.
1503-
print(io, "\nSuggestion: check for spelling errors or missing imports.")
1504-
owner = bnd
1505-
else
1506-
owner = unsafe_pointer_to_objref(owner)::Core.Binding
1507-
end
1508-
end
1509-
if owner !== bnd
1510-
# this could use jl_binding_dbgmodule for the exported location in the message too
1511-
print(io, "\nSuggestion: this global was defined as `$(owner.globalref)` but not assigned a value.")
1512-
end
1513-
elseif scope === :static_parameter
1514-
print(io, "\nSuggestion: run Test.detect_unbound_args to detect method arguments that do not fully constrain a type parameter.")
1515-
elseif scope === :local
1516-
print(io, "\nSuggestion: check for an assignment to a local variable that shadows a global of the same name.")
1517-
end
1518-
else
1519-
scope = undef
1520-
end
1521-
if scope !== Base && !_UndefVarError_warnfor(io, Base, var)
1522-
warned = false
1523-
for m in Base.loaded_modules_order
1524-
m === Core && continue
1525-
m === Base && continue
1526-
m === Main && continue
1527-
m === scope && continue
1528-
warned |= _UndefVarError_warnfor(io, m, var)
1529-
end
1530-
warned ||
1531-
_UndefVarError_warnfor(io, Core, var) ||
1532-
_UndefVarError_warnfor(io, Main, var)
1533-
end
1534-
nothing
1535-
end
1536-
1537-
function _UndefVarError_warnfor(io::IO, m::Module, var::Symbol)
1538-
Base.isbindingresolved(m, var) || return false
1539-
(Base.isexported(m, var) || Base.ispublic(m, var)) || return false
1540-
print(io, "\nHint: a global variable of this name also exists in $m.")
1541-
return true
1542-
end
1543-
15441476
function __init__()
1545-
Base.Experimental.register_error_hint(UndefVarError_hint, UndefVarError)
15461477
COMPLETION_WORLD[] = Base.get_world_counter()
1547-
nothing
1478+
return nothing
15481479
end
15491480

15501481
end # module

stdlib/REPL/test/repl.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1677,7 +1677,7 @@ end
16771677

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

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

0 commit comments

Comments
 (0)