From 9d3850de6e9ae1e035eb931d874e4efb45a6ebf4 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/atomics.jl | 26 ++++----- base/broadcast.jl | 6 +- base/iterators.jl | 2 +- base/meta.jl | 2 +- base/show.jl | 101 +++++++++++++++------------------ stdlib/Distributed/src/pmap.jl | 2 +- test/show.jl | 22 +++++-- test/testenv.jl | 6 +- 9 files changed, 87 insertions(+), 91 deletions(-) diff --git a/base/Enums.jl b/base/Enums.jl index 06860402fbcb1..ed00af5d08809 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) + Base.print_qualified_name(io, parentmodule(typeof(x)), Symbol(x)) end function Base.show(io::IO, ::MIME"text/plain", x::Enum) diff --git a/base/atomics.jl b/base/atomics.jl index 97405d88fd408..59b936224fe80 100644 --- a/base/atomics.jl +++ b/base/atomics.jl @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/base/broadcast.jl b/base/broadcast.jl index 0477adcce5b45..95a322e67caee 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -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 @@ -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 diff --git a/base/iterators.jl b/base/iterators.jl index af5e108845afb..ba9c1f5185de6 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -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 diff --git a/base/meta.jl b/base/meta.jl index 98bbcc8cdd358..5d51eaef05da0 100644 --- a/base/meta.jl +++ b/base/meta.jl @@ -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: [...] diff --git a/base/show.jl b/base/show.jl index b668296f875b4..32712a3d9b49d 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 @@ -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) @@ -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 @@ -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}() @@ -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)) @@ -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) diff --git a/stdlib/Distributed/src/pmap.jl b/stdlib/Distributed/src/pmap.jl index 603dfa7e031ce..35579ae2e0ab3 100644 --- a/stdlib/Distributed/src/pmap.jl +++ b/stdlib/Distributed/src/pmap.jl @@ -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}: diff --git a/test/show.jl b/test/show.jl index b9ddbc689efdb..488338861db5c 100644 --- a/test/show.jl +++ b/test/show.jl @@ -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\"") @@ -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 * "()" @@ -1624,11 +1624,11 @@ 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) @@ -1636,7 +1636,7 @@ end 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) @@ -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