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 27, 2021
1 parent 76698ea commit ae005e1
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 86 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
99 changes: 47 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 @@ -866,13 +838,49 @@ end
# If an object with this name exists in 'from', we need to check that it's the same binding
# and that it's not deprecated.
function isvisible(sym::Symbol, parent::Module, from::Module)
if sym === nameof(from) && parent === parentmodule(from)
# the name of a module is visible within itself, even though it's
# a separate binding
return true
end
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) &&
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 +903,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 +1016,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) = print(io, nameof(m))

function sourceinfo_slotnames(src::CodeInfo)
slotnames = src.slotnames
names = Dict{String,Int}()
Expand Down Expand Up @@ -2216,7 +2211,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
4 changes: 2 additions & 2 deletions doc/src/devdocs/inference.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ map(f, t::Tuple{Any}) in Base at tuple.jl:179
1 │ %2 = Base.sitofp(Float64, %1)::Float64
2 │ %3 = Base.lt_float(%2, 0.0)::Bool
0 └── goto #3 if not %3
0 2 ─ invoke Base.Math.throw_complex_domainerror(:sqrt::Symbol, %2::Float64)::Union{}
0 2 ─ invoke Math.throw_complex_domainerror(:sqrt::Symbol, %2::Float64)::Union{}
0 └── unreachable
20 3 ─ %7 = Base.Math.sqrt_llvm(%2)::Float64
20 3 ─ %7 = Math.sqrt_llvm(%2)::Float64
0 └── goto #4
0 4 ─ goto #5
0 5 ─ %10 = Core.tuple(%7)::Tuple{Float64}
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
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 ae005e1

Please sign in to comment.