Skip to content

Commit d9b7850

Browse files
authored
Merge pull request #23411 from JuliaLang/jb/parseable
parseable printing of all type names and TypeVars
2 parents 03660a4 + 9a98dcb commit d9b7850

File tree

5 files changed

+121
-16
lines changed

5 files changed

+121
-16
lines changed

base/methodshow.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=tru
144144
n = length(ms)
145145
if header
146146
m = n==1 ? "method" : "methods"
147-
ns = isself ? string(name) : string("(::", name, ")")
147+
sname = string(name)
148+
ns = (isself || '#' in sname) ? sname : string("(::", name, ")")
148149
what = startswith(ns, '@') ? "macro" : "generic function"
149150
print(io, "# $n $m for ", what, " \"", ns, "\":")
150151
end

base/replutil.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ function show(io::IO, ::MIME"text/plain", f::Function)
109109
ft == typeof(getfield(ft.name.module, name))
110110
n = length(methods(f))
111111
m = n==1 ? "method" : "methods"
112-
ns = isself ? string(name) : string("(::", name, ")")
112+
sname = string(name)
113+
ns = (isself || '#' in sname) ? sname : string("(::", ft, ")")
113114
what = startswith(ns, '@') ? "macro" : "generic function"
114115
print(io, ns, " (", what, " with $n $m)")
115116
end

base/show.jl

Lines changed: 83 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,15 @@ end
172172
function show(io::IO, f::Function)
173173
ft = typeof(f)
174174
mt = ft.name.mt
175-
if !isdefined(mt, :module) || is_exported_from_stdlib(mt.name, mt.module) || mt.module === Main
176-
print(io, mt.name)
175+
if isdefined(mt, :module) && isdefined(mt.module, mt.name) &&
176+
getfield(mt.module, mt.name) === f
177+
if is_exported_from_stdlib(mt.name, mt.module) || mt.module === Main
178+
print(io, mt.name)
179+
else
180+
print(io, mt.module, ".", mt.name)
181+
end
177182
else
178-
print(io, mt.module, ".", mt.name)
183+
show_default(io, f)
179184
end
180185
end
181186

@@ -199,17 +204,89 @@ function print_without_params(@nospecialize(x))
199204
return false
200205
end
201206

207+
has_typevar(@nospecialize(t), v::TypeVar) = ccall(:jl_has_typevar, Cint, (Any, Any), t, v)!=0
208+
209+
function io_has_tvar_name(io::IOContext, name::Symbol, @nospecialize(x))
210+
for (key, val) in io.dict
211+
if key === :unionall_env && val isa TypeVar && val.name === name && has_typevar(x, val)
212+
return true
213+
end
214+
end
215+
return false
216+
end
217+
io_has_tvar_name(io::IO, name::Symbol, @nospecialize(x)) = false
218+
202219
function show(io::IO, x::UnionAll)
203220
if print_without_params(x)
204221
return show(io, unwrap_unionall(x).name)
205222
end
223+
224+
if x.var.name == :_ || io_has_tvar_name(io, x.var.name, x)
225+
counter = 1
226+
while true
227+
newname = Symbol(x.var.name, counter)
228+
if !io_has_tvar_name(io, newname, x)
229+
newtv = TypeVar(newname, x.var.lb, x.var.ub)
230+
x = UnionAll(newtv, x{newtv})
231+
break
232+
end
233+
counter += 1
234+
end
235+
end
236+
206237
show(IOContext(io, :unionall_env => x.var), x.body)
207238
print(io, " where ")
208239
show(io, x.var)
209240
end
210241

211242
show(io::IO, x::DataType) = show_datatype(io, x)
212243

244+
function show_type_name(io::IO, tn::TypeName)
245+
globname = isdefined(tn, :mt) ? tn.mt.name : nothing
246+
globfunc = false
247+
if globname !== nothing
248+
globname_str = string(globname)
249+
if ('#' globname_str && '@' globname_str && isdefined(tn, :module) &&
250+
isbindingresolved(tn.module, globname) && isdefined(tn.module, globname) &&
251+
isa(getfield(tn.module, globname), tn.wrapper) && isleaftype(tn.wrapper))
252+
globfunc = true
253+
end
254+
end
255+
sym = globfunc ? globname : tn.name
256+
sym_str = string(sym)
257+
hidden = !globfunc && '#' sym_str
258+
quo = false
259+
if hidden
260+
print(io, "getfield(")
261+
elseif globfunc
262+
print(io, "typeof(")
263+
end
264+
if isdefined(tn, :module) && !(is_exported_from_stdlib(sym, tn.module) || (tn.module === Main && !hidden))
265+
show(io, tn.module)
266+
if !hidden
267+
print(io, ".")
268+
if globfunc && !is_id_start_char(first(sym_str))
269+
print(io, ":")
270+
if sym == :(==)
271+
print(io, "(")
272+
quo = true
273+
end
274+
end
275+
end
276+
end
277+
if hidden
278+
print(io, ", Symbol(\"", sym_str, "\"))")
279+
else
280+
print(io, sym_str)
281+
if globfunc
282+
print(io, ")")
283+
if quo
284+
print(io, ")")
285+
end
286+
end
287+
end
288+
end
289+
213290
function show_datatype(io::IO, x::DataType)
214291
istuple = x.name === Tuple.name
215292
if (!isempty(x.parameters) || istuple) && x !== Tuple
@@ -219,7 +296,7 @@ function show_datatype(io::IO, x::DataType)
219296
if istuple && n > 3 && all(i -> (x.parameters[1] === i), x.parameters)
220297
print(io, "NTuple{", n, ',', x.parameters[1], "}")
221298
else
222-
show(io, x.name)
299+
show_type_name(io, x.name)
223300
# Do not print the type parameters for the primary type if we are
224301
# printing a method signature or type parameter.
225302
# Always print the type parameter if we are printing the type directly
@@ -232,7 +309,7 @@ function show_datatype(io::IO, x::DataType)
232309
print(io, '}')
233310
end
234311
else
235-
show(io, x.name)
312+
show_type_name(io, x.name)
236313
end
237314
end
238315

@@ -263,11 +340,7 @@ macro show(exs...)
263340
end
264341

265342
function show(io::IO, tn::TypeName)
266-
if is_exported_from_stdlib(tn.name, tn.module) || tn.module === Main
267-
print(io, tn.name)
268-
else
269-
print(io, tn.module, '.', tn.name)
270-
end
343+
show_type_name(io, tn)
271344
end
272345

273346
show(io::IO, ::Void) = print(io, "nothing")

test/replutil.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ let err_str,
347347
err_str = @except_str randn(1)() MethodError
348348
@test contains(err_str, "MethodError: objects of type Array{Float64,1} are not callable")
349349
end
350-
@test stringmime("text/plain", FunctionLike()) == "(::FunctionLike) (generic function with 0 methods)"
350+
@test stringmime("text/plain", FunctionLike()) == "(::$(curmod_prefix)FunctionLike) (generic function with 0 methods)"
351351
@test ismatch(r"^@doc \(macro with \d+ method[s]?\)$", stringmime("text/plain", getfield(Base, Symbol("@doc"))))
352352

353353
method_defs_lineno = @__LINE__() + 1
@@ -385,7 +385,7 @@ let err_str,
385385
"@doc(__source__::LineNumberNode, __module__::Module, x...) in Core at boot.jl:")
386386
@test startswith(sprint(show, which(FunctionLike(), Tuple{})),
387387
"(::$(curmod_prefix)FunctionLike)() in $curmod_str at $sp:$(method_defs_lineno + 7)")
388-
@test stringmime("text/plain", FunctionLike()) == "(::FunctionLike) (generic function with 1 method)"
388+
@test stringmime("text/plain", FunctionLike()) == "(::$(curmod_prefix)FunctionLike) (generic function with 1 method)"
389389
@test stringmime("text/plain", Core.arraysize) == "arraysize (built-in function)"
390390

391391
err_str = @except_stackframe Symbol() ErrorException

test/show.jl

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ function f13127()
508508
show(buf, f)
509509
String(take!(buf))
510510
end
511-
@test f13127() == "$(curmod_prefix)f"
511+
@test startswith(f13127(), "getfield($(@__MODULE__), Symbol(\"")
512512

513513
#test methodshow.jl functions
514514
@test Base.inbase(Base)
@@ -737,7 +737,7 @@ let sv = Core.svec(:a, :b, :c)
737737
@test repr == "SimpleVector\n 1: Symbol a\n 2: Symbol b\n 3: #undef\n"
738738
end
739739
let repr = sprint(dump, sin)
740-
@test repr == "sin (function of type Base.#sin)\n"
740+
@test repr == "sin (function of type typeof(sin))\n"
741741
end
742742
let repr = sprint(dump, Base.Test)
743743
@test repr == "Module Base.Test\n"
@@ -948,3 +948,33 @@ end
948948
" 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 … 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n",
949949
" ⋮ ⋮ ⋱ ⋮ ")
950950
end
951+
952+
module UnexportedOperators
953+
function + end
954+
function == end
955+
end
956+
957+
@testset "Parseable printing of types" begin
958+
@test repr(typeof(print)) == "typeof(print)"
959+
@test repr(typeof(Base.show_default)) == "typeof(Base.show_default)"
960+
@test repr(typeof(UnexportedOperators.:+)) == "typeof($(curmod_prefix)UnexportedOperators.:+)"
961+
@test repr(typeof(UnexportedOperators.:(==))) == "typeof($(curmod_prefix)UnexportedOperators.:(==))"
962+
anonfn = x->2x
963+
modname = string(@__MODULE__)
964+
anonfn_type_repr = "getfield($modname, Symbol(\"$(typeof(anonfn).name.name)\"))"
965+
@test repr(typeof(anonfn)) == anonfn_type_repr
966+
@test repr(anonfn) == anonfn_type_repr * "()"
967+
@test stringmime("text/plain", anonfn) == "$(typeof(anonfn).name.mt.name) (generic function with 1 method)"
968+
mkclosure = x->y->x+y
969+
clo = mkclosure(10)
970+
@test stringmime("text/plain", clo) == "$(typeof(clo).name.mt.name) (generic function with 1 method)"
971+
end
972+
973+
let x = TypeVar(:_), y = TypeVar(:_)
974+
@test repr(UnionAll(x, UnionAll(y, Pair{x,y}))) == "Pair{_1,_2} where _2 where _1"
975+
@test repr(UnionAll(x, UnionAll(y, Pair{UnionAll(x,Ref{x}),y}))) == "Pair{Ref{_1} where _1,_1} where _1"
976+
x = TypeVar(:a)
977+
y = TypeVar(:a)
978+
z = TypeVar(:a)
979+
@test repr(UnionAll(z, UnionAll(x, UnionAll(y, Tuple{x,y,z})))) == "Tuple{a1,a2,a} where a2 where a1 where a"
980+
end

0 commit comments

Comments
 (0)