diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 6c3f4bd4ba73a..24f10c5b756ee 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -55,37 +55,67 @@ function UndefVarError_hint(io::IO, ex::UndefVarError) 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) + if scope !== Base + warned = _UndefVarError_warnfor(io, [Base], var) + + if !warned + modules_to_check = (m for m in Base.loaded_modules_order + if m !== Core && m !== Base && m !== Main && m !== scope) + warned |= _UndefVarError_warnfor(io, modules_to_check, var) end - warned || - _UndefVarError_warnfor(io, Core, var) || - _UndefVarError_warnfor(io, Main, var) + + warned || _UndefVarError_warnfor(io, [Core, 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 +function _UndefVarError_warnfor(io::IO, modules, var::Symbol) active_mod = Base.active_module() - print(io, "\nHint: ") - if isdefined(active_mod, Symbol(m)) - print(io, "a global variable of this name also exists in $m.") - else - if Symbol(m) == var - print(io, "$m is loaded but not imported in the active module $active_mod.") + + warned = false + # collect modules which export or make public the variable by + # the parentmodule in which the variable is defined + to_warn_about = Dict{Module, Vector{Module}}() + for m in modules + # only warn if exported or public TODO why did this used to check isbindingresolved? + if !Base.isexported(m, var) && !Base.ispublic(m, var) + continue + end + warned = true + + # handle case where the undefined variable is the name of a loaded module + if Symbol(m) == var && !isdefined(active_mod, var) + print(io, "\nHint: $m is loaded but not imported in the active module $active_mod.") + continue + end + + parent_m = parentmodule(getproperty(m, var)) + if !haskey(to_warn_about, parent_m) + to_warn_about[parent_m] = [m] else - print(io, "a global variable of this name may be made accessible by importing $m in the current active module $active_mod") + push!(to_warn_about[parent_m], m) + end + end + + if !isempty(to_warn_about) + for (parent_m, modules) in pairs(to_warn_about) + print(io, "\nHint: a global variable of this name also exists in ", parent_m) + for m in modules + m == parent_m && continue + how_available = if Base.isexported(m, var) + "exported by" + elseif Base.ispublic(m, var) + "made available as public by" + end + print(io, "\n - Also $how_available $m") + if !isdefined(active_mod, Symbol(m)) + print(io, ", which would need to be imported in $active_mod") + end + print(io, ".") + end end end - return true + return warned end function __init__()