From af0f425cf9a8dc5d8cb0ddb2f0c18c0c29a6094d Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 26 Feb 2021 14:57:38 -0500 Subject: [PATCH] simplify and improve printing of qualified names This removes repeated code, fixes some cases where quoting was not done correcly (e.g. `Mod.:+`), and checks identifier visibility recursively so only a minimal module path is printed. fixes #39834 --- base/Enums.jl | 11 +----- base/show.jl | 90 +++++++++++++++++++++---------------------------- test/show.jl | 14 +++++++- test/testenv.jl | 6 ++-- 4 files changed, 56 insertions(+), 65 deletions(-) diff --git a/base/Enums.jl b/base/Enums.jl index 06860402fbcb1..f64e7b2b320ce 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -28,16 +28,7 @@ Base.Symbol(x::Enum) = namemap(typeof(x))[Integer(x)]::Symbol Base.print(io::IO, x::Enum) = print(io, Symbol(x)) function Base.show(io::IO, x::Enum) - sym = Symbol(x) - if !(get(io, :compact, false)::Bool) - from = get(io, :module, Main) - def = typeof(x).name.module - if from === nothing || !Base.isvisible(sym, def, from) - show(io, def) - print(io, ".") - end - end - print(io, sym) + print_qualified_name(io, parentmodule(typeof(x)), Symbol(x)) end function Base.show(io::IO, ::MIME"text/plain", x::Enum) diff --git a/base/show.jl b/base/show.jl index b668296f875b4..e2dfafba5c799 100644 --- a/base/show.jl +++ b/base/show.jl @@ -429,20 +429,6 @@ function _show_default(io::IO, @nospecialize(x)) print(io,')') end -# Check if a particular symbol is exported from a standard library module -function is_exported_from_stdlib(name::Symbol, mod::Module) - !isdefined(mod, name) && return false - orig = getfield(mod, name) - while !(mod === Base || mod === Core) - parent = parentmodule(mod) - if mod === Main || mod === parent || parent === Main - return false - end - mod = parent - end - return isexported(mod, name) && isdefined(mod, name) && !isdeprecated(mod, name) && getfield(mod, name) === orig -end - function show_function(io::IO, f::Function, compact::Bool) ft = typeof(f) mt = ft.name.mt @@ -453,12 +439,7 @@ function show_function(io::IO, f::Function, compact::Bool) print(io, mt.name) elseif isdefined(mt, :module) && isdefined(mt.module, mt.name) && getfield(mt.module, mt.name) === f - if is_exported_from_stdlib(mt.name, mt.module) || mt.module === Main - show_sym(io, mt.name) - else - print(io, mt.module, ".") - show_sym(io, mt.name) - end + print_qualified_name(io, mt.module, mt.name) else show_default(io, f) end @@ -584,17 +565,8 @@ function make_typealias(@nospecialize(x::Type)) end function show_typealias(io::IO, name::GlobalRef, x::Type, env::SimpleVector, wheres::Vector) - if !(get(io, :compact, false)::Bool) - # Print module prefix unless alias is visible from module passed to - # IOContext. If :module is not set, default to Main. nothing can be used - # to force printing prefix. - from = get(io, :module, Main) - if (from === nothing || !isvisible(name.name, name.mod, from)) - show(io, name.mod) - print(io, ".") - end - end - print(io, name.name) + print_qualified_name(io, name.mod, name.name) + n = length(env) n == 0 && return @@ -873,6 +845,35 @@ function isvisible(sym::Symbol, parent::Module, from::Module) isdefined(from, sym) # if we're going to return true, force binding resolution end +function should_print_qualified(io::IO, m::Module, name::Symbol, from::Union{Module,Nothing} = get(io,:module,Main)) + return !isvisible(name, m, from) +end + +# Print `name` from module `m` in qualified form based on IO settings and identifier +# visibility. +# If :module is not set, default to Main. `nothing` can be used to force printing prefix. +# The optional `from` argument exists so that the Main default can be set in +# a central place and possibly phased out eventually. +function print_qualified_name(io::IO, m::Module, name::Symbol, from::Union{Module,Nothing} = get(io,:module,Main)) + quo = false + if !(get(io, :compact, false)::Bool) + if from === nothing || !isvisible(name, m, from) + show(IOContext(io, :module=>from), m) + print(io, ".") + if is_valid_identifier(name) && !is_id_start_char(first(string(name))) + print(io, ':') + if name in quoted_syms + print(io, '(') + quo = true + end + end + end + end + show_sym(io, name) + quo && print(io, ')') + nothing +end + function is_global_function(tn::Core.TypeName, globname::Union{Symbol,Nothing}) if globname !== nothing globname_str = string(globname::Symbol) @@ -895,26 +896,11 @@ function show_type_name(io::IO, tn::Core.TypeName) globfunc = is_global_function(tn, globname) sym = (globfunc ? globname : tn.name)::Symbol globfunc && print(io, "typeof(") - quo = false - if !(get(io, :compact, false)::Bool) - # Print module prefix unless type is visible from module passed to - # IOContext If :module is not set, default to Main. nothing can be used - # to force printing prefix - from = get(io, :module, Main) - if isdefined(tn, :module) && (from === nothing || !isvisible(sym, tn.module, from)) - show(io, tn.module) - print(io, ".") - if globfunc && !is_id_start_char(first(string(sym))) - print(io, ':') - if sym in quoted_syms - print(io, '(') - quo = true - end - end - end + if isdefined(tn, :module) + print_qualified_name(io, tn.module, sym) + else + show_sym(io, sym) end - show_sym(io, sym) - quo && print(io, ")") globfunc && print(io, ")") nothing end @@ -1023,7 +1009,7 @@ function show(io::IO, m::Module) if is_root_module(m) print(io, nameof(m)) else - print(io, join(fullname(m),".")) + print_qualified_name(io, parentmodule(m), nameof(m)) end end @@ -2216,7 +2202,7 @@ function show_signature_function(io::IO, @nospecialize(ft), demangle=false, farg if ft <: Function && isa(uw, DataType) && isempty(uw.parameters) && isdefined(uw.name.module, uw.name.mt.name) && ft == typeof(getfield(uw.name.module, uw.name.mt.name)) - if qualified && !is_exported_from_stdlib(uw.name.mt.name, uw.name.module) && uw.name.module !== Main + if qualified && should_print_qualified(uw.name.module, uw.name.mt.name) print_within_stacktrace(io, uw.name.module, '.', bold=true) end s = sprint(show_sym, (demangle ? demangle_function_name : identity)(uw.name.mt.name), context=io) diff --git a/test/show.jl b/test/show.jl index b9ddbc689efdb..06d0c3a78d575 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1628,7 +1628,7 @@ end b = IOBuffer() show(IOContext(b, :module => @__MODULE__), TestShowType.TypeA) - @test String(take!(b)) == "$(@__MODULE__).TestShowType.TypeA" + @test String(take!(b)) == "TestShowType.TypeA" b = IOBuffer() show(IOContext(b, :module => TestShowType), TestShowType.TypeA) @@ -2162,3 +2162,15 @@ end s = sprint(show, MIME("text/plain"), Function) @test s == "Function" end + +# issue #39834, minimal qualifying of module paths in printing +module M39834 +export A39834 +module A39834 +struct Foo end +end +struct (+) end +end +using .M39834 +@test sprint(show, M39834.A39834.Foo, context = :module => @__MODULE__) == "A39834.Foo" +@test sprint(show, M39834.:+, context = :module => @__MODULE__) == "M39834.:+" diff --git a/test/testenv.jl b/test/testenv.jl index 2aeee0f6dfc80..c21286499c4c6 100644 --- a/test/testenv.jl +++ b/test/testenv.jl @@ -41,8 +41,10 @@ if !@isdefined(testenv_defined) end const curmod = @__MODULE__ - const curmod_name = fullname(curmod) - const curmod_str = curmod === Main ? "Main" : join(curmod_name, ".") + const curmod_name = let fn = fullname(curmod) + fn[1] === :Main ? fn[2:end] : fn + end + const curmod_str = join(curmod_name, ".") const curmod_prefix = "$(["$m." for m in curmod_name]...)" # platforms that support cfunction with closures