Skip to content

Commit 5f13cd2

Browse files
ajwheelerararslanIanButterworthvtjnash
authored
Group import hints (JuliaLang#56753)
This is my attempt to resolve JuliaLang#53000. ``` julia> solve ERROR: UndefVarError: `solve` not defined in `Main` Suggestion: check for spelling errors or missing imports. Hint: a global variable of this name also exists in CommonSolve. - Also exported by SciMLBase. - Also exported by DiffEqBase. - Also exported by JumpProcesses. - Also exported by LinearSolve. - Also exported by BracketingNonlinearSolve (loaded but not imported in Main). - Also exported by SimpleNonlinearSolve. - Also exported by NonlinearSolve. - Also exported by OrdinaryDiffEqLowStorageRK (loaded but not imported in Main). - Also exported by OrdinaryDiffEqSSPRK (loaded but not imported in Main). - Also exported by OrdinaryDiffEqVerner (loaded but not imported in Main). - Also exported by OrdinaryDiffEqBDF (loaded but not imported in Main). - Also exported by OrdinaryDiffEqTsit5 (loaded but not imported in Main). - Also exported by OrdinaryDiffEqRosenbrock (loaded but not imported in Main). - Also exported by OrdinaryDiffEqDefault (loaded but not imported in Main). - Also exported by Sundials. ``` I would have beefed up the test case, but can't follow how it works. --------- Co-authored-by: Alex Arslan <ararslan@comcast.net> Co-authored-by: Ian Butterworth <i.r.butterworth@gmail.com> Co-authored-by: Jameson Nash <vtjnash@gmail.com>
1 parent 8f00a51 commit 5f13cd2

File tree

2 files changed

+86
-28
lines changed

2 files changed

+86
-28
lines changed

stdlib/REPL/src/REPL.jl

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -55,36 +55,65 @@ function UndefVarError_hint(io::IO, ex::UndefVarError)
5555
else
5656
scope = undef
5757
end
58-
if scope !== Base && !_UndefVarError_warnfor(io, Base, var)
59-
warned = false
60-
for m in Base.loaded_modules_order
61-
m === Core && continue
62-
m === Base && continue
63-
m === Main && continue
64-
m === scope && continue
65-
warned |= _UndefVarError_warnfor(io, m, var)
58+
if scope !== Base
59+
warned = _UndefVarError_warnfor(io, [Base], var)
60+
61+
if !warned
62+
modules_to_check = (m for m in Base.loaded_modules_order
63+
if m !== Core && m !== Base && m !== Main && m !== scope)
64+
warned |= _UndefVarError_warnfor(io, modules_to_check, var)
6665
end
67-
warned ||
68-
_UndefVarError_warnfor(io, Core, var) ||
69-
_UndefVarError_warnfor(io, Main, var)
66+
67+
warned || _UndefVarError_warnfor(io, [Core, Main], var)
7068
end
7169
return nothing
7270
end
7371

74-
function _UndefVarError_warnfor(io::IO, m::Module, var::Symbol)
75-
(Base.isexported(m, var) || Base.ispublic(m, var)) || return false
72+
function _UndefVarError_warnfor(io::IO, modules, var::Symbol)
7673
active_mod = Base.active_module()
77-
print(io, "\nHint: ")
78-
if isdefined(active_mod, Symbol(m))
79-
print(io, "a global variable of this name also exists in $m.")
80-
else
81-
if Symbol(m) == var
82-
print(io, "$m is loaded but not imported in the active module $active_mod.")
74+
75+
warned = false
76+
# collect modules which export or make public the variable by
77+
# the module in which the variable is defined
78+
to_warn_about = Dict{Module, Vector{Module}}()
79+
for m in modules
80+
# only include in info if binding has a value and is exported or public
81+
if !Base.isdefined(m, var) || (!Base.isexported(m, var) && !Base.ispublic(m, var))
82+
continue
83+
end
84+
warned = true
85+
86+
# handle case where the undefined variable is the name of a loaded module
87+
if Symbol(m) == var && !isdefined(active_mod, var)
88+
print(io, "\nHint: $m is loaded but not imported in the active module $active_mod.")
89+
continue
90+
end
91+
92+
binding_m = Base.binding_module(m, var)
93+
if !haskey(to_warn_about, binding_m)
94+
to_warn_about[binding_m] = [m]
8395
else
84-
print(io, "a global variable of this name may be made accessible by importing $m in the current active module $active_mod")
96+
push!(to_warn_about[binding_m], m)
97+
end
98+
end
99+
100+
for (binding_m, modules) in pairs(to_warn_about)
101+
print(io, "\nHint: a global variable of this name also exists in ", binding_m, ".")
102+
for m in modules
103+
m == binding_m && continue
104+
how_available = if Base.isexported(m, var)
105+
"exported by"
106+
elseif Base.ispublic(m, var)
107+
"declared public in"
108+
end
109+
print(io, "\n - Also $how_available $m")
110+
if !isdefined(active_mod, nameof(m)) || (getproperty(active_mod, nameof(m)) !== m)
111+
print(io, " (loaded but not imported in $active_mod)")
112+
end
113+
print(io, ".")
85114
end
86115
end
87-
return true
116+
return warned
88117
end
89118

90119
function __init__()

stdlib/REPL/test/repl.jl

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1833,23 +1833,52 @@ fake_repl() do stdin_write, stdout_read, repl
18331833
@test contains(txt, "Some type information was truncated. Use `show(err)` to see complete types.")
18341834
end
18351835

1836-
try # test the functionality of `UndefVarError_hint` against `Base.remove_linenums!`
1836+
try # test the functionality of `UndefVarError_hint`
18371837
@assert isempty(Base.Experimental._hint_handlers)
18381838
Base.Experimental.register_error_hint(REPL.UndefVarError_hint, UndefVarError)
18391839

1840-
# check the requirement to trigger the hint via `UndefVarError_hint`
1841-
@test !isdefined(Main, :remove_linenums!) && Base.ispublic(Base, :remove_linenums!)
1842-
18431840
fake_repl() do stdin_write, stdout_read, repl
18441841
backend = REPL.REPLBackend()
18451842
repltask = @async REPL.run_repl(repl; backend)
1846-
write(stdin_write,
1847-
"remove_linenums!\n\"ZZZZZ\"\n")
1843+
write(stdin_write, """
1844+
module A53000
1845+
export f
1846+
f() = 0.0
1847+
end
1848+
1849+
module C_outer_53000
1850+
import ..A53000: f
1851+
public f
1852+
1853+
module C_inner_53000
1854+
import ..C_outer_53000: f
1855+
export f
1856+
end
1857+
end
1858+
1859+
module D_53000
1860+
public f
1861+
f() = 1.0
1862+
end
1863+
1864+
C_inner_53000 = "I'm a decoy with the same name as C_inner_53000!"
1865+
1866+
append!(Base.loaded_modules_order, [A53000, C_outer_53000, C_outer_53000.C_inner_53000, D_53000])
1867+
f
1868+
"""
1869+
)
1870+
write(stdin_write, "\nZZZZZ\n")
18481871
txt = readuntil(stdout_read, "ZZZZZ")
18491872
write(stdin_write, '\x04')
18501873
wait(repltask)
1851-
@test occursin("Hint: a global variable of this name also exists in Base.", txt)
1874+
@test occursin("Hint: a global variable of this name also exists in Main.A53000.", txt)
1875+
@test occursin("Hint: a global variable of this name also exists in Main.D_53000.", txt)
1876+
@test occursin("- Also declared public in Main.C_outer_53000.", txt)
1877+
@test occursin("- Also exported by Main.C_outer_53000.C_inner_53000 (loaded but not imported in Main).", txt)
18521878
end
1879+
catch e
1880+
# fail test if error
1881+
@test false
18531882
finally
18541883
empty!(Base.Experimental._hint_handlers)
18551884
end

0 commit comments

Comments
 (0)