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 Mar 3, 2021
1 parent de7d695 commit 9d3850d
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 91 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)
Base.print_qualified_name(io, parentmodule(typeof(x)), Symbol(x))
end

function Base.show(io::IO, ::MIME"text/plain", x::Enum)
Expand Down
26 changes: 13 additions & 13 deletions base/atomics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Atomic objects can be accessed using the `[]` notation:
# Examples
```jldoctest
julia> x = Threads.Atomic{Int}(3)
Base.Threads.Atomic{Int64}(3)
Threads.Atomic{Int64}(3)
julia> x[] = 1
1
Expand Down Expand Up @@ -100,17 +100,17 @@ time.
# Examples
```jldoctest
julia> x = Threads.Atomic{Int}(3)
Base.Threads.Atomic{Int64}(3)
Threads.Atomic{Int64}(3)
julia> Threads.atomic_cas!(x, 4, 2);
julia> x
Base.Threads.Atomic{Int64}(3)
Threads.Atomic{Int64}(3)
julia> Threads.atomic_cas!(x, 3, 2);
julia> x
Base.Threads.Atomic{Int64}(2)
Threads.Atomic{Int64}(2)
```
"""
function atomic_cas! end
Expand All @@ -128,7 +128,7 @@ For further details, see LLVM's `atomicrmw xchg` instruction.
# Examples
```jldoctest
julia> x = Threads.Atomic{Int}(3)
Base.Threads.Atomic{Int64}(3)
Threads.Atomic{Int64}(3)
julia> Threads.atomic_xchg!(x, 2)
3
Expand All @@ -152,7 +152,7 @@ For further details, see LLVM's `atomicrmw add` instruction.
# Examples
```jldoctest
julia> x = Threads.Atomic{Int}(3)
Base.Threads.Atomic{Int64}(3)
Threads.Atomic{Int64}(3)
julia> Threads.atomic_add!(x, 2)
3
Expand All @@ -176,7 +176,7 @@ For further details, see LLVM's `atomicrmw sub` instruction.
# Examples
```jldoctest
julia> x = Threads.Atomic{Int}(3)
Base.Threads.Atomic{Int64}(3)
Threads.Atomic{Int64}(3)
julia> Threads.atomic_sub!(x, 2)
3
Expand All @@ -199,7 +199,7 @@ For further details, see LLVM's `atomicrmw and` instruction.
# Examples
```jldoctest
julia> x = Threads.Atomic{Int}(3)
Base.Threads.Atomic{Int64}(3)
Threads.Atomic{Int64}(3)
julia> Threads.atomic_and!(x, 2)
3
Expand All @@ -222,7 +222,7 @@ For further details, see LLVM's `atomicrmw nand` instruction.
# Examples
```jldoctest
julia> x = Threads.Atomic{Int}(3)
Base.Threads.Atomic{Int64}(3)
Threads.Atomic{Int64}(3)
julia> Threads.atomic_nand!(x, 2)
3
Expand All @@ -245,7 +245,7 @@ For further details, see LLVM's `atomicrmw or` instruction.
# Examples
```jldoctest
julia> x = Threads.Atomic{Int}(5)
Base.Threads.Atomic{Int64}(5)
Threads.Atomic{Int64}(5)
julia> Threads.atomic_or!(x, 7)
5
Expand All @@ -268,7 +268,7 @@ For further details, see LLVM's `atomicrmw xor` instruction.
# Examples
```jldoctest
julia> x = Threads.Atomic{Int}(5)
Base.Threads.Atomic{Int64}(5)
Threads.Atomic{Int64}(5)
julia> Threads.atomic_xor!(x, 7)
5
Expand All @@ -291,7 +291,7 @@ For further details, see LLVM's `atomicrmw max` instruction.
# Examples
```jldoctest
julia> x = Threads.Atomic{Int}(5)
Base.Threads.Atomic{Int64}(5)
Threads.Atomic{Int64}(5)
julia> Threads.atomic_max!(x, 7)
5
Expand All @@ -314,7 +314,7 @@ For further details, see LLVM's `atomicrmw min` instruction.
# Examples
```jldoctest
julia> x = Threads.Atomic{Int}(7)
Base.Threads.Atomic{Int64}(7)
Threads.Atomic{Int64}(7)
julia> Threads.atomic_min!(x, 5)
7
Expand Down
6 changes: 3 additions & 3 deletions base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ Uses [`BroadcastStyle`](@ref) to get the style for each argument, and uses
```jldoctest
julia> Broadcast.combine_styles([1], [1 2; 3 4])
Base.Broadcast.DefaultArrayStyle{2}()
Broadcast.DefaultArrayStyle{2}()
```
"""
function combine_styles end
Expand All @@ -431,10 +431,10 @@ determine a common `BroadcastStyle`.
```jldoctest
julia> Broadcast.result_style(Broadcast.DefaultArrayStyle{0}(), Broadcast.DefaultArrayStyle{3}())
Base.Broadcast.DefaultArrayStyle{3}()
Broadcast.DefaultArrayStyle{3}()
julia> Broadcast.result_style(Broadcast.Unknown(), Broadcast.DefaultArrayStyle{1}())
Base.Broadcast.DefaultArrayStyle{1}()
Broadcast.DefaultArrayStyle{1}()
```
"""
function result_style end
Expand Down
2 changes: 1 addition & 1 deletion base/iterators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ See [`Base.filter`](@ref) for an eager implementation of filtering for arrays.
# Examples
```jldoctest
julia> f = Iterators.filter(isodd, [1, 2, 3, 4, 5])
Base.Iterators.Filter{typeof(isodd), Vector{Int64}}(isodd, [1, 2, 3, 4, 5])
Iterators.Filter{typeof(isodd), Vector{Int64}}(isodd, [1, 2, 3, 4, 5])
julia> foreach(println, f)
1
Expand Down
2 changes: 1 addition & 1 deletion base/meta.jl
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ julia> Meta.parse("x = ")
:($(Expr(:incomplete, "incomplete: premature end of input")))
julia> Meta.parse("1.0.2")
ERROR: Base.Meta.ParseError("invalid numeric constant \\\"1.0.\\\"")
ERROR: Meta.ParseError("invalid numeric constant \\\"1.0.\\\"")
Stacktrace:
[...]
Expand Down
101 changes: 46 additions & 55 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 @@ -868,11 +840,43 @@ end
function isvisible(sym::Symbol, parent::Module, from::Module)
owner = ccall(:jl_binding_owner, Any, (Any, Any), parent, sym)
from_owner = ccall(:jl_binding_owner, Any, (Any, Any), from, sym)
return owner !== nothing && from_owner === owner &&
!isdeprecated(parent, sym) &&
return ((owner !== nothing && from_owner === owner && !isdeprecated(parent, sym)) ||
# name of a module is visible within itself
(from_owner isa Module && nameof(from_owner) === sym)) &&
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
show_sym(io, name)
quo && print(io, ')')
else
print(io, name)
end
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 +899,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,10 +1012,12 @@ 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

print(io::IO, m::Module) = join(io, fullname(m), ".")

function sourceinfo_slotnames(src::CodeInfo)
slotnames = src.slotnames
names = Dict{String,Int}()
Expand Down Expand Up @@ -1531,7 +1522,7 @@ show_unquoted(io::IO, ex::GotoNode, ::Int, ::Int) = print(io, "goto %", ex
show_unquoted(io::IO, ex::GlobalRef, ::Int, ::Int) = show_globalref(io, ex)

function show_globalref(io::IO, ex::GlobalRef; allow_macroname=false)
print(io, ex.mod)
show(io, ex.mod)
print(io, '.')
quoted = !isidentifier(ex.name) && !startswith(string(ex.name), "@")
parens = quoted && (!isoperator(ex.name) || (ex.name in quoted_syms))
Expand Down Expand Up @@ -2216,7 +2207,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(io, 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
2 changes: 1 addition & 1 deletion stdlib/Distributed/src/pmap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ and `tail`: an iterator over the remaining elements.
```jldoctest
julia> b, c = Distributed.head_and_tail(1:10, 3)
([1, 2, 3], Base.Iterators.Rest{UnitRange{Int64}, Int64}(1:10, 3))
([1, 2, 3], Iterators.Rest{UnitRange{Int64}, Int64}(1:10, 3))
julia> collect(c)
7-element Vector{Int64}:
Expand Down
22 changes: 17 additions & 5 deletions test/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ function f13127()
show(buf, f)
String(take!(buf))
end
@test startswith(f13127(), "$(@__MODULE__).var\"#f")
@test startswith(f13127(), "$(repr(@__MODULE__)).var\"#f")

@test startswith(sprint(show, typeof(x->x), context = :module=>@__MODULE__), "var\"")

Expand Down Expand Up @@ -1557,7 +1557,7 @@ end
@test repr(typeof(UnexportedOperators.:+)) == "typeof($(curmod_prefix)UnexportedOperators.:+)"
@test repr(typeof(UnexportedOperators.:(==))) == "typeof($(curmod_prefix)UnexportedOperators.:(==))"
anonfn = x->2x
modname = string(@__MODULE__)
modname = repr(@__MODULE__)
anonfn_type_repr = "$modname.var\"$(typeof(anonfn).name.name)\""
@test repr(typeof(anonfn)) == anonfn_type_repr
@test repr(anonfn) == anonfn_type_repr * "()"
Expand Down Expand Up @@ -1624,19 +1624,19 @@ module TestShowType
end

@testset "module prefix when printing type" begin
@test sprint(show, TestShowType.TypeA) == "$(@__MODULE__).TestShowType.TypeA"
@test sprint(show, TestShowType.TypeA) == "$(repr(@__MODULE__)).TestShowType.TypeA"

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)
@test String(take!(b)) == "TypeA"

using .TestShowType

@test sprint(show, TypeA) == "$(@__MODULE__).TestShowType.TypeA"
@test sprint(show, TypeA) == "$(repr(@__MODULE__)).TestShowType.TypeA"

b = IOBuffer()
show(IOContext(b, :module => @__MODULE__), 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.:+"
Loading

0 comments on commit 9d3850d

Please sign in to comment.