Skip to content

Commit

Permalink
simplify and improve printing of qualified names
Browse files Browse the repository at this point in the history
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
  • Loading branch information
JeffBezanson committed Feb 26, 2021
1 parent 76698ea commit af0f425
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 65 deletions.
11 changes: 1 addition & 10 deletions base/Enums.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
90 changes: 38 additions & 52 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down
14 changes: 13 additions & 1 deletion test/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.:+"
6 changes: 4 additions & 2 deletions test/testenv.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit af0f425

Please sign in to comment.