• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #38182

15 Aug 2025 03:55AM UTC coverage: 77.87% (-0.4%) from 78.28%
#38182

push

local

web-flow
🤖 [master] Bump the SparseArrays stdlib from 30201ab to bb5ecc0 (#59263)

Stdlib: SparseArrays
URL: https://github.com/JuliaSparse/SparseArrays.jl.git
Stdlib branch: main
Julia branch: master
Old commit: 30201ab
New commit: bb5ecc0
Julia version: 1.13.0-DEV
SparseArrays version: 1.13.0
Bump invoked by: @ViralBShah
Powered by:
[BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl)

Diff:
https://github.com/JuliaSparse/SparseArrays.jl/compare/30201abcb...bb5ecc091

```
$ git log --oneline 30201ab..bb5ecc0
bb5ecc0 fast quadratic form for dense matrix, sparse vectors (#640)
34ece87 Extend 3-arg `dot` to generic `HermOrSym` sparse matrices (#643)
095b685 Exclude unintended complex symmetric sparse matrices from 3-arg `dot` (#642)
8049287 Fix signature for 2-arg matrix-matrix `dot` (#641)
cff971d Make cond(::SparseMatrix, 1 / Inf) discoverable from 2-norm error (#629)
```

Co-authored-by: ViralBShah <744411+ViralBShah@users.noreply.github.com>

48274 of 61993 relevant lines covered (77.87%)

9571166.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.1
/base/show.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
using .Compiler: has_typevar
4

5
function show(io::IO, ::MIME"text/plain", u::UndefInitializer)
6
    show(io, u)
6✔
7
    get(io, :compact, false)::Bool && return
22✔
8
    print(io, ": array initializer with undefined values")
4✔
9
end
10

11
# first a few multiline show functions for types defined before the MIME type:
12

13
show(io::IO, ::MIME"text/plain", r::AbstractRange) = show(io, r) # always use the compact form for printing ranges
3✔
14

15
function show(io::IO, ::MIME"text/plain", r::UnitRange)
1✔
16
    show(io, r)
3✔
17
    if !(get(io, :compact, false)::Bool) && isempty(r)
8✔
18
        print(io, " (empty range)")
×
19
    end
20
end
21

22
function show(io::IO, ::MIME"text/plain", r::LinRange)
2✔
23
    isempty(r) && return show(io, r)
2✔
24
    # show for LinRange, e.g.
25
    # range(1, stop=3, length=7)
26
    # 7-element LinRange{Float64}:
27
    #   1.0,1.33333,1.66667,2.0,2.33333,2.66667,3.0
28
    summary(io, r)
2✔
29
    println(io, ":")
2✔
30
    print_range(io, r)
2✔
31
end
32

33
function show(io::IO, ::MIME"text/plain", r::LogRange)  # display LogRange like LinRange
2✔
34
    isempty(r) && return show(io, r)
2✔
35
    summary(io, r)
1✔
36
    println(io, ":")
1✔
37
    print_range(io, r, " ", ", ", "", " \u2026 ")
1✔
38
end
39

40
function _isself(ft::DataType)
4✔
41
    ftname = ft.name
264✔
42
    name = ftname.singletonname
264✔
43
    ftname.name === name && return false
264✔
44
    mod = parentmodule(ft)
263✔
45
    return isdefinedglobal(mod, name) && ft === typeof(getglobal(mod, name))
263✔
46
end
47

48
function show(io::IO, ::MIME"text/plain", f::Function)
24✔
49
    get(io, :compact, false)::Bool && return show(io, f)
40✔
50
    ft = typeof(f)
24✔
51
    name = ft.name.singletonname
24✔
52
    if isa(f, Core.IntrinsicFunction)
24✔
53
        print(io, f)
1✔
54
        id = Core.Intrinsics.bitcast(Int32, f)
1✔
55
        print(io, " (intrinsic function #$id)")
1✔
56
    elseif isa(f, Core.Builtin)
23✔
57
        print(io, name, " (built-in function)")
1✔
58
    else
59
        n = length(methods(f))
22✔
60
        m = n==1 ? "method" : "methods"
22✔
61
        sname = string(name)
22✔
62
        ns = (_isself(ft) || '#' in sname) ? sname : string("(::", ft, ")")
34✔
63
        what = startswith(ns, '@') ? "macro" : "generic function"
22✔
64
        print(io, ns, " (", what, " with $n $m)")
22✔
65
    end
66
end
67

68
show(io::IO, ::MIME"text/plain", c::ComposedFunction) = show(io, c)
1✔
69
show(io::IO, ::MIME"text/plain", c::Returns) = show(io, c)
×
70
show(io::IO, ::MIME"text/plain", s::Splat) = show(io, s)
×
71

72
const ansi_regex = r"(?s)(?:\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]))|."
73

74
# Pseudo-character representing an ANSI delimiter
75
struct ANSIDelimiter
76
    del::SubString{String}
×
77
end
78
ncodeunits(c::ANSIDelimiter) = ncodeunits(c.del)
×
79
textwidth(::ANSIDelimiter) = 0
×
80

81
# An iterator similar to `pairs(::String)` but whose values are Char or ANSIDelimiter
82
struct ANSIIterator{S}
83
    captures::RegexMatchIterator{S}
369,711✔
84
end
85
ANSIIterator(s::AbstractString) = ANSIIterator(eachmatch(ansi_regex, s))
369,711✔
86

87
IteratorSize(::Type{<:ANSIIterator}) = SizeUnknown()
×
88
eltype(::Type{<:ANSIIterator}) = Pair{Int, Union{Char,ANSIDelimiter}}
×
89
function iterate(I::ANSIIterator, (i, m_st)=(1, iterate(I.captures)))
369,904✔
90
    m_st === nothing && return nothing
369,946✔
91
    m, (j, new_m_st) = m_st
14✔
92
    c = lastindex(m.match) == 1 ? only(m.match) : ANSIDelimiter(m.match)
14✔
93
    return (i => c, (j, iterate(I.captures, (j, new_m_st))))
14✔
94
end
95
textwidth(I::ANSIIterator) = mapreduce(textwidth∘last, +, I; init=0)
369,697✔
96

97
function _truncate_at_width_or_chars(ignore_ANSI::Bool, str::AbstractString, width::Int, rpad::Bool=false, chars="\r\n", truncmark="…")
×
98
    truncwidth = textwidth(truncmark)
931✔
99
    (width <= 0 || width < truncwidth) && return ""
×
100
    wid = truncidx = lastidx = 0
×
101
    # if str needs to be truncated, truncidx is the index of truncation.
102
    stop = false # once set, only ANSI delimiters will be kept as new characters.
×
103
    needANSIend = false # set if the last ANSI delimiter before truncidx is not "\033[0m".
×
104
    I = ignore_ANSI ? ANSIIterator(str) : pairs(str)
×
105
    for (i, c) in I
×
106
        if c isa ANSIDelimiter
×
107
            truncidx == 0 && (needANSIend = c != "\033[0m")
×
108
            lastidx = i + ncodeunits(c) - 1
×
109
        else
110
            stop && break
×
111
            wid += textwidth(c)
×
112
            truncidx == 0 && wid > (width - truncwidth) && (truncidx = lastidx)
×
113
            lastidx = i
×
114
            c in chars && break
×
115
            stop = wid >= width
×
116
        end
117
    end
×
118
    lastidx == 0 && return rpad ? ' '^width : ""
×
119
    str[lastidx] in chars && (lastidx = prevind(str, lastidx))
×
120
    ANSIend = needANSIend ? "\033[0m" : ""
×
121
    pad = rpad ? repeat(' ', max(0, width-wid)) : ""
×
122
    truncidx == 0 && (truncidx = lastidx)
×
123
    if lastidx < lastindex(str)
×
124
        return string(SubString(str, 1, truncidx), ANSIend, truncmark, pad)
×
125
    else
126
        return string(str, ANSIend, pad)
×
127
    end
128
end
129

130
function show(io::IO, ::MIME"text/plain", iter::Union{KeySet,ValueIterator})
96✔
131
    isempty(iter) && get(io, :compact, false)::Bool && return show(io, iter)
96✔
132
    summary(io, iter)
96✔
133
    isempty(iter) && return
96✔
134
    print(io, ". ", isa(iter,KeySet) ? "Keys" : "Values", ":")
96✔
135
    limit = get(io, :limit, false)::Bool
195✔
136
    if limit
96✔
137
        sz = displaysize(io)
96✔
138
        rows, cols = sz[1] - 3, sz[2]
96✔
139
        rows < 2 && (print(io, " …"); return)
96✔
140
        cols < 4 && (cols = 4)
65✔
141
        cols -= 2 # For prefix "  "
65✔
142
        rows -= 1 # For summary
65✔
143
    else
144
        rows = cols = typemax(Int)
×
145
    end
146

147
    for (i, v) in enumerate(iter)
130✔
148
        print(io, "\n  ")
451✔
149
        i == rows < length(iter) && (print(io, "⋮"); break)
455✔
150

151
        if limit
419✔
152
            str = sprint(show, v, context=io, sizehint=0)
419✔
153
            str = _truncate_at_width_or_chars(get(io, :color, false)::Bool, str, cols)
1,266✔
154
            print(io, str)
419✔
155
        else
156
            show(io, v)
×
157
        end
158
    end
419✔
159
end
160

161
function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V}
73✔
162
    (isempty(t) || !haslength(t)) && return show(io, t)
75✔
163
    # show more descriptively, with one line per key/value pair
164
    recur_io = IOContext(io, :SHOWN_SET => t)
71✔
165
    limit = get(io, :limit, false)::Bool
137✔
166
    if !haskey(io, :compact)
241✔
167
        recur_io = IOContext(recur_io, :compact => true)
71✔
168
    end
169
    recur_io_k = IOContext(recur_io, :typeinfo=>keytype(t))
71✔
170
    recur_io_v = IOContext(recur_io, :typeinfo=>valtype(t))
71✔
171

172
    summary(io, t)
72✔
173
    isempty(t) && return
71✔
174
    print(io, ":")
71✔
175
    show_circular(io, t) && return
76✔
176
    if limit
71✔
177
        sz = displaysize(io)
69✔
178
        rows, cols = sz[1] - 3, sz[2]
69✔
179
        rows < 2   && (print(io, " …"); return)
69✔
180
        cols < 12  && (cols = 12) # Minimum widths of 2 for key, 4 for value
53✔
181
        cols -= 6 # Subtract the widths of prefix "  " separator " => "
53✔
182
        rows -= 1 # Subtract the summary
53✔
183

184
        # determine max key width to align the output, caching the strings
185
        hascolor = get(recur_io, :color, false)
264✔
186
        ks = Vector{String}(undef, min(rows, length(t)))
54✔
187
        vs = Vector{String}(undef, min(rows, length(t)))
54✔
188
        keywidth = 0
53✔
189
        valwidth = 0
53✔
190
        for (i, (k, v)) in enumerate(t)
100✔
191
            i > rows && break
280✔
192
            ks[i] = sprint(show, k, context=recur_io_k, sizehint=0)
263✔
193
            vs[i] = sprint(show, v, context=recur_io_v, sizehint=0)
263✔
194
            keywidth = clamp(hascolor ? textwidth(ANSIIterator(ks[i])) : textwidth(ks[i]), keywidth, cols)
440✔
195
            valwidth = clamp(hascolor ? textwidth(ANSIIterator(vs[i])) : textwidth(vs[i]), valwidth, cols)
427✔
196
        end
263✔
197
        if keywidth > max(div(cols, 2), cols - valwidth)
53✔
198
            keywidth = max(cld(cols, 3), cols - valwidth)
19✔
199
        end
200
    else
201
        rows = cols = typemax(Int)
2✔
202
    end
203

204
    for (i, (k, v)) in enumerate(t)
104✔
205
        print(io, "\n  ")
266✔
206
        if i == rows < length(t)
267✔
207
            print(io, rpad("⋮", keywidth), " => ⋮")
17✔
208
            break
17✔
209
        end
210

211
        if limit
249✔
212
            key = _truncate_at_width_or_chars(hascolor, ks[i], keywidth, true)
246✔
213
        else
214
            key = sprint(show, k, context=recur_io_k, sizehint=0)
3✔
215
        end
216
        print(recur_io, key)
249✔
217
        print(io, " => ")
249✔
218

219
        if limit
249✔
220
            val = _truncate_at_width_or_chars(hascolor, vs[i], cols - keywidth)
246✔
221
            print(io, val)
246✔
222
        else
223
            show(recur_io_v, v)
3✔
224
        end
225
    end
249✔
226
end
227

228
function summary(io::IO, t::AbstractSet)
229
    n = length(t)
2✔
230
    showarg(io, t, true)
2✔
231
    print(io, " with ", n, (n==1 ? " element" : " elements"))
2✔
232
end
233

234
function show(io::IO, ::MIME"text/plain", t::AbstractSet{T}) where T
2✔
235
    isempty(t) && return show(io, t)
2✔
236
    # show more descriptively, with one line per value
237
    recur_io = IOContext(io, :SHOWN_SET => t)
2✔
238
    limit = get(io, :limit, false)::Bool
4✔
239

240
    summary(io, t)
2✔
241
    isempty(t) && return
2✔
242
    print(io, ":")
2✔
243
    show_circular(io, t) && return
2✔
244
    if limit
2✔
245
        sz = displaysize(io)
2✔
246
        rows, cols = sz[1] - 3, sz[2]
2✔
247
        rows < 2   && (print(io, " …"); return)
2✔
248
        cols -= 2 # Subtract the width of prefix "  "
2✔
249
        cols < 4  && (cols = 4) # Minimum widths of 4 for value
2✔
250
        rows -= 1 # Subtract the summary
2✔
251
    else
252
        rows = cols = typemax(Int)
×
253
    end
254

255
    for (i, v) in enumerate(t)
4✔
256
        print(io, "\n  ")
21✔
257
        if i == rows < length(t)
21✔
258
            print(io, rpad("⋮", 2))
1✔
259
            break
1✔
260
        end
261

262
        if limit
20✔
263
            str = sprint(show, v, context=recur_io, sizehint=0)
20✔
264
            print(io, _truncate_at_width_or_chars(get(io, :color, false)::Bool, str, cols))
60✔
265
        else
266
            show(recur_io, v)
×
267
        end
268
    end
20✔
269
end
270

271
function show(io::IO, ::MIME"text/plain", opt::JLOptions)
×
272
    println(io, "JLOptions(")
×
273
    fields = fieldnames(JLOptions)
×
274
    nfields = length(fields)
×
275
    for (i, f) in enumerate(fields)
×
276
        v = getfield(opt, i)
×
277
        if isa(v, Ptr{UInt8})
×
278
            v = (v != C_NULL) ? unsafe_string(v) : ""
×
279
        elseif isa(v, Ptr{Ptr{UInt8}})
×
280
            v = unsafe_load_commands(v)
×
281
        end
282
        println(io, "  ", f, " = ", repr(v), i < nfields ? "," : "")
×
283
    end
×
284
    print(io, ")")
×
285
end
286

287
function show(io::IO, ::MIME"text/plain", t::Task)
×
288
    show(io, t)
×
289
    if istaskfailed(t)
×
290
        println(io)
×
291
        show_task_exception(io, t, indent = false)
×
292
    end
293
end
294

295

296
print(io::IO, s::Symbol) = (write(io,s); nothing)
59,258✔
297

298
"""
299
    IOContext
300

301
`IOContext` provides a mechanism for passing output configuration settings among [`show`](@ref) methods.
302

303
In short, it is an immutable dictionary that is a subclass of `IO`. It supports standard
304
dictionary operations such as [`getindex`](@ref), and can also be used as an I/O stream.
305
"""
306
struct IOContext{IO_t <: IO} <: AbstractPipe
307
    io::IO_t
308
    dict::ImmutableDict{Symbol, Any}
309

310
    function IOContext{IO_t}(io::IO_t, dict::ImmutableDict{Symbol, Any}) where IO_t<:IO
51✔
311
        io isa IOContext && (io = io.io) # implicitly unwrap, since the io.dict field is not useful anymore, and could confuse pipe_reader consumers
302,400✔
312
        return new(io, dict)
2,606,672✔
313
    end
314
end
315

316
# (Note that TTY and TTYTerminal io types have an implied :color property.)
317
ioproperties(io::IO) = get(io, :color, false) ? ImmutableDict{Symbol,Any}(:color, true) : ImmutableDict{Symbol,Any}()
13,510✔
318
ioproperties(io::IOContext) = io.dict
2,471,669✔
319
# these can probably be deprecated, but there is a use in the ecosystem for them
320
unwrapcontext(io::IO) = (io,)
×
321
unwrapcontext(io::IOContext) = (io.io,)
×
322

323
function IOContext(io::IO, dict::ImmutableDict{Symbol, Any})
51✔
324
    return IOContext{typeof(io)}(io, dict)
2,449,708✔
325
end
326

327
function IOContext(io::IOContext, dict::ImmutableDict{Symbol, Any})
×
328
    return typeof(io)(io.io, dict)
1,471,844✔
329
end
330

331

332
convert(::Type{IOContext}, io::IOContext) = io
43✔
333
convert(::Type{IOContext}, io::IO) = IOContext(io, ioproperties(io))::IOContext
42✔
334
convert(::Type{IOContext{IO_t}}, io::IOContext{IO_t}) where {IO_t} = io
×
335
convert(::Type{IOContext{IO_t}}, io::IO) where {IO_t} = IOContext{IO_t}(io, ioproperties(io))::IOContext{IO_t}
114✔
336

337
IOContext(io::IO) = convert(IOContext, io)
62✔
338
IOContext{IO_t}(io::IO) where {IO_t} = convert(IOContext{IO_t}, io)
116✔
339

340
function IOContext(io::IO, KV::Pair)
476✔
341
    d = ioproperties(io)
1,473,392✔
342
    return IOContext(io, ImmutableDict{Symbol,Any}(d, KV[1], KV[2]))
1,651,440✔
343
end
344

345
"""
346
    IOContext(io::IO, context::IOContext)
347

348
Create an `IOContext` that wraps an alternate `IO` but inherits the properties of `context`.
349

350
!!! note
351
    Unless explicitly set in the wrapped `io` the `displaysize` of `io` will not be inherited.
352
    This is because by default `displaysize` is not a property of IO objects themselves, but lazily inferred,
353
    as the size of the terminal window can change during the lifetime of the IO object.
354
"""
355
IOContext(io::IO, context::IO) = IOContext(io, ioproperties(context))
2,335,204✔
356

357
"""
358
    IOContext(io::IO, KV::Pair...)
359

360
Create an `IOContext` that wraps a given stream, adding the specified `key=>value` pairs to
361
the properties of that stream (note that `io` can itself be an `IOContext`).
362

363
 - use `(key => value) in io` to see if this particular combination is in the properties set
364
 - use `get(io, key, default)` to retrieve the most recent value for a particular key
365

366
The following properties are in common use:
367

368
 - `:compact`: Boolean specifying that values should be printed more compactly, e.g.
369
   that numbers should be printed with fewer digits. This is set when printing array
370
   elements. `:compact` output should not contain line breaks.
371
 - `:limit`: Boolean specifying that containers should be truncated, e.g. showing `…` in
372
   place of most elements.
373
 - `:displaysize`: A `Tuple{Int,Int}` giving the size in rows and columns to use for text
374
   output. This can be used to override the display size for called functions, but to
375
   get the size of the screen use the `displaysize` function.
376
 - `:typeinfo`: a `Type` characterizing the information already printed
377
   concerning the type of the object about to be displayed. This is mainly useful when
378
   displaying a collection of objects of the same type, so that redundant type information
379
   can be avoided (e.g. `[Float16(0)]` can be shown as "Float16[0.0]" instead
380
   of "Float16[Float16(0.0)]" : while displaying the elements of the array, the `:typeinfo`
381
   property will be set to `Float16`).
382
 - `:color`: Boolean specifying whether ANSI color/escape codes are supported/expected.
383
   By default, this is determined by whether `io` is a compatible terminal and by any
384
   `--color` command-line flag when `julia` was launched.
385

386
# Examples
387

388
```jldoctest
389
julia> io = IOBuffer();
390

391
julia> printstyled(IOContext(io, :color => true), "string", color=:red)
392

393
julia> takestring!(io)
394
"\\e[31mstring\\e[39m"
395

396
julia> printstyled(io, "string", color=:red)
397

398
julia> takestring!(io)
399
"string"
400
```
401

402
```jldoctest
403
julia> print(IOContext(stdout, :compact => false), 1.12341234)
404
1.12341234
405
julia> print(IOContext(stdout, :compact => true), 1.12341234)
406
1.12341
407
```
408

409
```jldoctest
410
julia> function f(io::IO)
411
           if get(io, :short, false)
412
               print(io, "short")
413
           else
414
               print(io, "loooooong")
415
           end
416
       end
417
f (generic function with 1 method)
418

419
julia> f(stdout)
420
loooooong
421
julia> f(IOContext(stdout, :short => true))
422
short
423
```
424
"""
425
IOContext(io::IO, KV::Pair, KVs::Pair...) = IOContext(IOContext(io, KV), KVs...)
30,545✔
426

427
show(io::IO, ctx::IOContext) = (print(io, "IOContext("); show(io, ctx.io); print(io, ")"))
3✔
428

429
pipe_reader(io::IOContext) = io.io
1✔
430
pipe_writer(io::IOContext) = io.io
7,981,724✔
431
lock(io::IOContext) = lock(io.io)
157,301✔
432
unlock(io::IOContext) = unlock(io.io)
157,075✔
433

434
in(key_value::Pair, io::IOContext) = in(key_value, io.dict, ===)
2✔
435
in(key_value::Pair, io::IO) = false
×
436
haskey(io::IOContext, key) = haskey(io.dict, key)
67,920✔
437
haskey(io::IO, key) = false
×
438
getindex(io::IOContext, key) = getindex(io.dict, key)
305✔
439
getindex(io::IO, key) = throw(KeyError(key))
×
440
get(io::IOContext, key, default) = get(io.dict, key, default)
5,381,153✔
441
get(io::IO, key, default) = default
661,457✔
442
keys(io::IOContext) = keys(io.dict)
1✔
443
keys(io::IO) = keys(ImmutableDict{Symbol,Any}())
2✔
444

445
displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize]::Tuple{Int,Int} : displaysize(io.io)::Tuple{Int,Int}
30,167✔
446

447
show_circular(io::IO, @nospecialize(x)) = false
7,944✔
448
function show_circular(io::IOContext, @nospecialize(x))
×
449
    d = 1
834✔
450
    for (k, v) in io.dict
2,034✔
451
        if k === :SHOWN_SET
3,155✔
452
            if v === x
741✔
453
                printstyled(io, "#= circular reference @-$d =#"; color = :yellow)
66✔
454
                return true
66✔
455
            end
456
            d += 1
675✔
457
        end
458
    end
5,236✔
459
    return false
960✔
460
end
461

462
"""
463
    show([io::IO = stdout], x)
464

465
Write a text representation of a value `x` to the output stream `io`. New types `T`
466
should overload `show(io::IO, x::T)`. The representation used by `show` generally
467
includes Julia-specific formatting and type information, and should be parseable
468
Julia code when possible.
469

470
[`repr`](@ref) returns the output of `show` as a string.
471

472
For a more verbose human-readable text output for objects of type `T`, define
473
`show(io::IO, ::MIME"text/plain", ::T)` in addition. Checking the `:compact`
474
[`IOContext`](@ref) key (often checked as `get(io, :compact, false)::Bool`)
475
of `io` in such methods is recommended,
476
since some containers show their elements by calling this method with
477
`:compact => true`.
478

479
See also [`print`](@ref), which writes un-decorated representations.
480

481
# Examples
482
```jldoctest
483
julia> show("Hello World!")
484
"Hello World!"
485
julia> print("Hello World!")
486
Hello World!
487
```
488
"""
489
show(io::IO, @nospecialize(x)) = show_default(io, x)
1,186✔
490

491
show(x) = show(stdout, x)
3,450✔
492

493
# avoid inferring show_default on the type of `x`
494
show_default(io::IO, @nospecialize(x)) = _show_default(io, inferencebarrier(x))
1,227✔
495

496
function _show_default(io::IO, @nospecialize(x))
37✔
497
    t = typeof(x)
37✔
498
    show(io, inferencebarrier(t)::DataType)
37✔
499
    print(io, '(')
37✔
500
    nf = nfields(x)
37✔
501
    nb = sizeof(x)::Int
37✔
502
    if nf != 0 || nb == 0
47✔
503
        if !show_circular(io, x)
36✔
504
            recur_io = IOContext(io, Pair{Symbol,Any}(:SHOWN_SET, x),
36✔
505
                                 Pair{Symbol,Any}(:typeinfo, Any))
506
            for i in 1:nf
62✔
507
                f = fieldname(t, i)
85✔
508
                if !isdefined(x, f)
84✔
509
                    print(io, undef_ref_str)
×
510
                else
511
                    show(recur_io, getfield(x, i))
84✔
512
                end
513
                if i < nf
83✔
514
                    print(io, ", ")
57✔
515
                end
516
            end
140✔
517
        end
518
    else
519
        print(io, "0x")
1✔
520
        r = Ref{Any}(x)
1✔
521
        GC.@preserve r begin
1✔
522
            p = unsafe_convert(Ptr{Cvoid}, r)
1✔
523
            for i in (nb - 1):-1:0
2✔
524
                print(io, string(unsafe_load(convert(Ptr{UInt8}, p + i)), base = 16, pad = 2))
8✔
525
            end
8✔
526
        end
527
    end
528
    print(io,')')
36✔
529
end
530

531
function active_module()
532
    if ccall(:jl_is_in_pure_context, Bool, ())
176✔
533
        error("active_module() should not be called from a pure context")
×
534
    end
535
    if !@isdefined(active_repl) || active_repl === nothing
178✔
536
        return Main
174✔
537
    end
538
    return invokelatest(active_module, active_repl)::Module
2✔
539
end
540

541
module UsesBaseOnly
542
end
543

544
function show_function(io::IO, f::Function, compact::Bool, fallback::Function)
4✔
545
    fname = typeof(f).name
43✔
546
    if fname.name === fname.singletonname
43✔
547
        fallback(io, f)
39✔
548
    elseif compact
4✔
549
        print(io, fname.singletonname)
1✔
550
    elseif isdefined(fname, :module) && isdefinedglobal(fname.module, fname.singletonname) && isconst(fname.module, fname.singletonname) &&
3✔
551
            getglobal(fname.module, fname.singletonname) === f
552
        # this used to call the removed internal function `is_exported_from_stdlib`, which effectively
553
        # just checked for exports from Base.
554
        mod = get(io, :module, UsesBaseOnly)
3✔
555
        if !(isvisible(fname.singletonname, fname.module, mod) || fname.module === mod)
2✔
556
            print(io, fname.module, ".")
1✔
557
        end
558
        show_sym(io, fname.singletonname)
1✔
559
    else
560
        fallback(io, f)
2✔
561
    end
562
end
563

564
show(io::IO, f::Function) = show_function(io, f, get(io, :compact, false)::Bool, show_default)
539✔
565
print(io::IO, f::Function) = show_function(io, f, true, show)
8,855✔
566

567
function show(io::IO, f::Core.IntrinsicFunction)
11✔
568
    if !(get(io, :compact, false)::Bool)
35✔
569
        print(io, "Core.Intrinsics.")
11✔
570
    end
571
    print(io, nameof(f))
11✔
572
end
573

574
print(io::IO, f::Core.IntrinsicFunction) = print(io, nameof(f))
1✔
575

576
show(io::IO, ::Core.TypeofBottom) = print(io, "Union{}")
239✔
577
show(io::IO, ::MIME"text/plain", ::Core.TypeofBottom) = print(io, "Union{}")
×
578

579
function print_without_params(@nospecialize(x))
236✔
580
    b = unwrap_unionall(x)
64,501✔
581
    return isa(b, DataType) && b.name.wrapper === x
66,368✔
582
end
583

584
function io_has_tvar_name(io::IOContext, name::Symbol, @nospecialize(x))
×
585
    for (key, val) in io.dict
×
586
        if key === :unionall_env && val isa TypeVar && val.name === name && has_typevar(x, val)
×
587
            return true
×
588
        end
589
    end
×
590
    return false
×
591
end
592
io_has_tvar_name(io::IO, name::Symbol, @nospecialize(x)) = false
×
593

594
modulesof!(s::Set{Module}, x::TypeVar) = modulesof!(s, x.ub)
×
595
function modulesof!(s::Set{Module}, x::Type)
457✔
596
    x = unwrap_unionall(x)
457✔
597
    if x isa DataType
457✔
598
        push!(s, parentmodule(x))
451✔
599
    elseif x isa Union
6✔
600
        modulesof!(s, x.a)
12✔
601
        modulesof!(s, x.b)
6✔
602
    end
603
    s
457✔
604
end
605

606
# given an IO context for printing a type, reconstruct the proper type that
607
# we're attempting to represent.
608
# Union{T} where T is a degenerate case and is equal to T.ub, but we don't want
609
# to print them that way, so filter those out from our aliases completely.
610
function makeproper(io::IO, @nospecialize(x::Type))
1✔
611
    if io isa IOContext
446✔
612
        for (key, val) in io.dict
765✔
613
            if key === :unionall_env && val isa TypeVar
650✔
614
                x = UnionAll(val, x)
×
615
            end
616
        end
979✔
617
    end
618
    has_free_typevars(x) && return Any
448✔
619
    return x
448✔
620
end
621

622
function make_typealias(@nospecialize(x::Type))
461✔
623
    Any === x && return nothing
461✔
624
    x <: Tuple && return nothing
461✔
625
    mods = modulesof!(Set{Module}(), x)
442✔
626
    replace!(mods, Core=>Base)
442✔
627
    aliases = Tuple{GlobalRef,SimpleVector}[]
442✔
628
    xenv = UnionAll[]
442✔
629
    for p in uniontypes(unwrap_unionall(x))
442✔
630
        p isa UnionAll && push!(xenv, p)
445✔
631
    end
445✔
632
    x isa UnionAll && push!(xenv, x)
442✔
633
    for mod in mods
883✔
634
        for name in unsorted_names(mod)
442✔
635
            if isdefinedglobal(mod, name) && !isdeprecated(mod, name) && isconst(mod, name)
320,550✔
636
                alias = getglobal(mod, name)
319,412✔
637
                if alias isa Type && !has_free_typevars(alias) && !print_without_params(alias) && x <: alias
321,260✔
638
                    if alias isa UnionAll
312✔
639
                        (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), x, alias)::SimpleVector
624✔
640
                        # ti === Union{} && continue # impossible, since we already checked that x <: alias
641
                        env = env::SimpleVector
312✔
642
                        # TODO: In some cases (such as the following), the `env` is over-approximated.
643
                        #       We'd like to disable `fix_inferred_var_bound` since we'll already do that fix-up here.
644
                        #       (or detect and reverse the computation of it here).
645
                        #   T = Array{Array{T,1}, 1} where T
646
                        #   (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), T, Vector)
647
                        #   env[1].ub.var == T.var
648
                        applied = try
312✔
649
                                # this can fail if `x` contains a covariant
650
                                # union, and the non-matching branch of the
651
                                # union has additional restrictions on the
652
                                # bounds of the environment that are not met by
653
                                # the instantiation found above
654
                                alias{env...}
312✔
655
                            catch ex
656
                                ex isa TypeError || rethrow()
×
657
                                continue
×
658
                            end
659
                        for p in xenv
312✔
660
                            applied = rewrap_unionall(applied, p)
×
661
                        end
×
662
                        has_free_typevars(applied) && continue
312✔
663
                        applied === x || continue # it couldn't figure out the parameter matching
587✔
664
                    elseif alias === x
×
665
                        env = Core.svec()
×
666
                    else
667
                        continue # not a complete match
×
668
                    end
669
                    push!(aliases, (GlobalRef(mod, name), env))
37✔
670
                end
671
            end
672
        end
320,550✔
673
    end
883✔
674
    if length(aliases) == 1 # TODO: select the type with the "best" (shortest?) environment
442✔
675
        return aliases[1]
37✔
676
    end
677
end
678

679
isgensym(s::Symbol) = '#' in string(s)
36✔
680

681
function show_can_elide(p::TypeVar, wheres::Vector, elide::Int, env::SimpleVector, skip::Int)
×
682
    elide == 0 && return false
×
683
    wheres[elide] === p || return false
×
684
    for i = (elide + 1):length(wheres)
×
685
        v = wheres[i]::TypeVar
×
686
        has_typevar(v.lb, p) && return false
×
687
        has_typevar(v.ub, p) && return false
×
688
    end
×
689
    for i = eachindex(env)
×
690
        i == skip && continue
×
691
        has_typevar(env[i], p) && return false
×
692
    end
×
693
    return true
×
694
end
695

696
function show_typeparams(io::IO, env::SimpleVector, orig::SimpleVector, wheres::Vector)
417✔
697
    n = length(env)
417✔
698
    elide = length(wheres)
417✔
699
    function egal_var(p::TypeVar, @nospecialize o)
417✔
700
        return o isa TypeVar &&
4✔
701
            ccall(:jl_types_egal, Cint, (Any, Any), p.ub, o.ub) != 0 &&
702
            ccall(:jl_types_egal, Cint, (Any, Any), p.lb, o.lb) != 0
703
    end
704
    for i = n:-1:1
830✔
705
        p = env[i]
677✔
706
        if p isa TypeVar
677✔
707
            if i == n && egal_var(p, orig[i]) && show_can_elide(p, wheres, elide, env, i)
4✔
708
                n -= 1
4✔
709
                elide -= 1
4✔
710
            elseif p.lb === Union{} && isgensym(p.name) && show_can_elide(p, wheres, elide, env, i)
×
711
                elide -= 1
×
712
            elseif p.ub === Any && isgensym(p.name) && show_can_elide(p, wheres, elide, env, i)
×
713
                elide -= 1
×
714
            end
715
        end
716
    end
936✔
717
    if n > 0
417✔
718
        print(io, "{")
415✔
719
        for i = 1:n
826✔
720
            p = env[i]
673✔
721
            if p isa TypeVar
673✔
722
                if p.lb === Union{} && something(findfirst(@nospecialize(w) -> w === p, wheres), 0) > elide
×
723
                    print(io, "<:")
×
724
                    show(io, p.ub)
×
725
                elseif p.ub === Any && something(findfirst(@nospecialize(w) -> w === p, wheres), 0) > elide
×
726
                    print(io, ">:")
×
727
                    show(io, p.lb)
×
728
                else
729
                    show(io, p)
×
730
                end
731
            else
732
                show(io, p)
673✔
733
            end
734
            i < n && print(io, ", ")
673✔
735
        end
930✔
736
        print(io, "}")
415✔
737
    end
738
    resize!(wheres, elide)
417✔
739
    nothing
417✔
740
end
741

742
function show_typealias(io::IO, name::GlobalRef, x::Type, env::SimpleVector, wheres::Vector)
41✔
743
    if !(get(io, :compact, false)::Bool)
168✔
744
        # Print module prefix unless alias is visible from module passed to
745
        # IOContext. If :module is not set, default to Main.
746
        # nothing can be used to force printing prefix.
747
        from = get(io, :module, Main)
157✔
748
        if (from === nothing || !isvisible(name.name, name.mod, from))
82✔
749
            show(io, name.mod)
1✔
750
            print(io, ".")
1✔
751
        end
752
    end
753
    print(io, name.name)
41✔
754
    isempty(env) && return
41✔
755
    io = IOContext(io)
41✔
756
    for p in wheres
41✔
757
        io = IOContext(io, :unionall_env => p)
4✔
758
    end
4✔
759
    orig = getfield(name.mod, name.name)
41✔
760
    vars = TypeVar[]
41✔
761
    while orig isa UnionAll
84✔
762
        push!(vars, orig.var)
43✔
763
        orig = orig.body
43✔
764
    end
43✔
765
    show_typeparams(io, env, Core.svec(vars...), wheres)
41✔
766
    nothing
41✔
767
end
768

769
function make_wheres(io::IO, env::SimpleVector, @nospecialize(x::Type))
41✔
770
    seen = IdSet()
41✔
771
    wheres = TypeVar[]
41✔
772
    # record things printed by the context
773
    if io isa IOContext
41✔
774
        for (key, val) in io.dict
71✔
775
            if key === :unionall_env && val isa TypeVar && has_typevar(x, val)
127✔
776
                push!(seen, val)
×
777
            end
778
        end
224✔
779
    end
780
    # record things in x to print outermost
781
    while x isa UnionAll
45✔
782
        if !(x.var in seen)
4✔
783
            push!(seen, x.var)
4✔
784
            push!(wheres, x.var)
4✔
785
        end
786
        x = x.body
4✔
787
    end
4✔
788
    # record remaining things in env to print innermost
789
    for i = length(env):-1:1
80✔
790
        p = env[i]
43✔
791
        if p isa TypeVar && !(p in seen)
43✔
792
            push!(seen, p)
×
793
            pushfirst!(wheres, p)
×
794
        end
795
    end
45✔
796
    return wheres
41✔
797
end
798

799
function show_wheres(io::IO, wheres::Vector{TypeVar})
41✔
800
    isempty(wheres) && return
41✔
801
    io = IOContext(io)
×
802
    n = length(wheres)
×
803
    for i = 1:n
×
804
        p = wheres[i]
×
805
        print(io, n == 1 ? " where " : i == 1 ? " where {" : ", ")
×
806
        show(io, p)
×
807
        io = IOContext(io, :unionall_env => p)
×
808
    end
×
809
    n > 1 && print(io, "}")
×
810
    nothing
×
811
end
812

813
function show_typealias(io::IO, @nospecialize(x::Type))
443✔
814
    properx = makeproper(io, x)
885✔
815
    alias = make_typealias(properx)
443✔
816
    alias === nothing && return false
443✔
817
    wheres = make_wheres(io, alias[2], x)
41✔
818
    show_typealias(io, alias[1], x, alias[2], wheres)
41✔
819
    show_wheres(io, wheres)
41✔
820
    return true
41✔
821
end
822

823
function make_typealiases(@nospecialize(x::Type))
3✔
824
    aliases = SimpleVector[]
3✔
825
    Any === x && return aliases, Union{}
3✔
826
    x <: Tuple && return aliases, Union{}
3✔
827
    mods = modulesof!(Set{Module}(), x)
3✔
828
    replace!(mods, Core=>Base)
3✔
829
    vars = Dict{Symbol,TypeVar}()
3✔
830
    xenv = UnionAll[]
3✔
831
    each = Any[]
3✔
832
    for p in uniontypes(unwrap_unionall(x))
3✔
833
        p isa UnionAll && push!(xenv, p)
6✔
834
        push!(each, rewrap_unionall(p, x))
12✔
835
    end
6✔
836
    x isa UnionAll && push!(xenv, x)
3✔
837
    for mod in mods
6✔
838
        for name in unsorted_names(mod)
3✔
839
            if isdefinedglobal(mod, name) && !isdeprecated(mod, name) && isconst(mod, name)
3,609✔
840
                alias = getglobal(mod, name)
3,597✔
841
                if alias isa Type && !has_free_typevars(alias) && !print_without_params(alias) && !(alias <: Tuple)
3,618✔
842
                    (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), x, alias)::SimpleVector
168✔
843
                    ti === Union{} && continue
84✔
844
                    # make sure this alias wasn't from an unrelated part of the Union
845
                    mod2 = modulesof!(Set{Module}(), alias)
×
846
                    mod in mod2 || (mod === Base && Core in mod2) || continue
×
847
                    env = env::SimpleVector
×
848
                    applied = alias
×
849
                    if !isempty(env)
×
850
                        applied = try
×
851
                                # this can fail if `x` contains a covariant
852
                                # union, and the non-matching branch of the
853
                                # union has additional restrictions on the
854
                                # bounds of the environment that are not met by
855
                                # the instantiation found above
856
                                alias{env...}
×
857
                            catch ex
858
                                ex isa TypeError || rethrow()
×
859
                                continue
×
860
                            end
861
                    end
862
                    ul = unionlen(applied)
×
863
                    for p in xenv
×
864
                        applied = rewrap_unionall(applied, p)
×
865
                    end
×
866
                    has_free_typevars(applied) && continue
×
867
                    applied <: x || continue # parameter matching didn't make a subtype
×
868
                    print_without_params(x) && (env = Core.svec())
×
869
                    for typ in each # check that the alias also fully subsumes at least component of the input
×
870
                        if typ <: applied
×
871
                            push!(aliases, Core.svec(GlobalRef(mod, name), env, applied, (ul, -length(env))))
×
872
                            break
×
873
                        end
874
                    end
×
875
                end
876
            end
877
        end
3,609✔
878
    end
6✔
879
    if isempty(aliases)
3✔
880
        return aliases, Union{}
3✔
881
    end
882
    sort!(aliases, by = x -> x[4]::Tuple{Int,Int}, rev = true) # heuristic sort by "best" environment
×
883
    let applied = Union{}
×
884
        applied1 = Union{}
×
885
        keep = SimpleVector[]
×
886
        prev = (0, 0)
×
887
        for alias in aliases
×
888
            alias4 = alias[4]::Tuple{Int,Int}
×
889
            if alias4[1] < 2
×
890
                if !(alias[3] <: applied)
×
891
                    applied1 = Union{applied1, alias[3]}
×
892
                    push!(keep, alias)
×
893
                end
894
            elseif alias4 == prev || !(alias[3] <: applied)
×
895
                applied = applied1 = Union{applied1, alias[3]}
×
896
                push!(keep, alias)
×
897
                prev = alias4
×
898
            end
899
        end
×
900
        return keep, applied1
×
901
    end
902
end
903

904
function show_unionaliases(io::IO, x::Union)
3✔
905
    properx = makeproper(io, x)
6✔
906
    aliases, applied = make_typealiases(properx)
3✔
907
    isempty(aliases) && return false
3✔
908
    first = true
×
909
    tvar = false
×
910
    for typ in uniontypes(x)
×
911
        if isa(typ, TypeVar)
×
912
            tvar = true # sort bare TypeVars to the end
×
913
            continue
×
914
        elseif rewrap_unionall(typ, properx) <: applied
×
915
            continue
×
916
        end
917
        print(io, first ? "Union{" : ", ")
×
918
        first = false
×
919
        show(io, typ)
×
920
    end
×
921
    if first && !tvar && length(aliases) == 1
×
922
        alias = aliases[1]
×
923
        env = alias[2]::SimpleVector
×
924
        wheres = make_wheres(io, env, x)
×
925
        show_typealias(io, alias[1], x, env, wheres)
×
926
        show_wheres(io, wheres)
×
927
    else
928
        for alias in aliases
×
929
            print(io, first ? "Union{" : ", ")
×
930
            first = false
×
931
            env = alias[2]::SimpleVector
×
932
            wheres = make_wheres(io, env, x)
×
933
            show_typealias(io, alias[1], x, env, wheres)
×
934
            show_wheres(io, wheres)
×
935
        end
×
936
        if tvar
×
937
            for typ in uniontypes(x)
×
938
                if isa(typ, TypeVar)
×
939
                    print(io, ", ")
×
940
                    show(io, typ)
×
941
                end
942
            end
×
943
        end
944
        print(io, "}")
×
945
    end
946
    return true
×
947
end
948

949
function show(io::IO, ::MIME"text/plain", @nospecialize(x::Type))
9✔
950
    if !print_without_params(x)
10✔
951
        properx = makeproper(io, x)
4✔
952
        if make_typealias(properx) !== nothing || (unwrap_unionall(x) isa Union && x <: make_typealiases(properx)[2])
4✔
953
            show(IOContext(io, :compact => true), x)
×
954
            if !(get(io, :compact, false)::Bool)
×
955
                printstyled(io, " (alias for "; color = :light_black)
×
956
                printstyled(IOContext(io, :compact => false), x, color = :light_black)
×
957
                printstyled(io, ")"; color = :light_black)
×
958
            end
959
            return
×
960
        end
961
    end
962
    show(io, x)
16✔
963
    # give a helpful hint for function types
964
    if x isa DataType && x !== UnionAll && !(get(io, :compact, false)::Bool)
15✔
965
        tn = x.name::Core.TypeName
5✔
966
        globname = tn.singletonname
5✔
967
        if is_global_function(tn, globname)
5✔
968
            print(io, " (singleton type of function ")
1✔
969
            show_sym(io, globname)
1✔
970
            print(io, ", subtype of Function)")
1✔
971
        end
972
    end
973
end
974

975
show(io::IO, @nospecialize(x::Type)) = _show_type(io, inferencebarrier(x))
9,503✔
976
function _show_type(io::IO, @nospecialize(x::Type))
1,907✔
977
    if print_without_params(x)
1,910✔
978
        show_type_name(io, (unwrap_unionall(x)::DataType).name)
1,464✔
979
        return
1,464✔
980
    elseif get(io, :compact, true)::Bool && show_typealias(io, x)
1,089✔
981
        return
41✔
982
    elseif x isa DataType
402✔
983
        show_datatype(io, x)
399✔
984
        return
399✔
985
    elseif x isa Union
3✔
986
        if get(io, :compact, true)::Bool && show_unionaliases(io, x)
6✔
987
            return
×
988
        end
989
        print(io, "Union")
3✔
990
        show_delim_array(io, uniontypes(x), '{', ',', '}', false)
3✔
991
        return
3✔
992
    end
993

994
    x = x::UnionAll
×
995
    wheres = TypeVar[]
×
996
    let io = IOContext(io)
×
997
        while x isa UnionAll
×
998
            var = x.var
×
999
            if var.name === :_ || io_has_tvar_name(io, var.name, x)
×
1000
                counter = 1
×
1001
                while true
×
1002
                    newname = Symbol(var.name, counter)
×
1003
                    if !io_has_tvar_name(io, newname, x)
×
1004
                        var = TypeVar(newname, var.lb, var.ub)
×
1005
                        x = x{var}
×
1006
                        break
×
1007
                    end
1008
                    counter += 1
×
1009
                end
×
1010
            else
1011
                x = x.body
×
1012
            end
1013
            push!(wheres, var)
×
1014
            io = IOContext(io, :unionall_env => var)
×
1015
        end
×
1016
        if x isa DataType
×
1017
            show_datatype(io, x, wheres)
×
1018
        else
1019
            show(io, x)
×
1020
        end
1021
    end
1022
    show_wheres(io, wheres)
×
1023
end
1024

1025
# Check whether 'sym' (defined in module 'parent') is visible from module 'from'
1026
# If an object with this name exists in 'from', we need to check that it's the same binding
1027
# and that it's not deprecated.
1028
function isvisible(sym::Symbol, parent::Module, from::Module)
1,335✔
1029
    isdeprecated(parent, sym) && return false
1,335✔
1030
    isdefinedglobal(from, sym) || return false
1,729✔
1031
    isdefinedglobal(parent, sym) || return false
941✔
1032
    parent_binding = convert(Core.Binding, GlobalRef(parent, sym))
941✔
1033
    from_binding = convert(Core.Binding, GlobalRef(from, sym))
941✔
1034
    while true
941✔
1035
        from_binding === parent_binding && return true
941✔
1036
        partition = lookup_binding_partition(tls_world_age(), from_binding)
868✔
1037
        is_some_explicit_imported(binding_kind(partition)) || break
1,736✔
1038
        from_binding = partition_restriction(partition)::Core.Binding
×
1039
    end
×
1040
    parent_partition = lookup_binding_partition(tls_world_age(), parent_binding)
868✔
1041
    from_partition = lookup_binding_partition(tls_world_age(), from_binding)
868✔
1042
    if is_defined_const_binding(binding_kind(parent_partition)) && is_defined_const_binding(binding_kind(from_partition))
1,736✔
1043
        return parent_partition.restriction === from_partition.restriction
868✔
1044
    end
1045
    return false
×
1046
end
1047

1048
function is_global_function(tn::Core.TypeName, globname::Union{Symbol,Nothing})
1,884✔
1049
    if globname !== nothing && isconcretetype(tn.wrapper) && tn !== DataType.name # ignore that typeof(DataType)===DataType, since it is valid but not useful
1,884✔
1050
        globname_str = string(globname::Symbol)
1,365✔
1051
        if '#' ∉ globname_str && '@' ∉ globname_str && isdefined(tn, :module) &&
1,365✔
1052
                isdefinedglobal(tn.module, globname) && isconst(tn.module, globname) &&
1053
                isa(getglobal(tn.module, globname), tn.wrapper)
1054
            return true
6✔
1055
        end
1056
    end
1057
    return false
1,878✔
1058
end
1059

1060
function check_world_bounded(tn::Core.TypeName)
1,885✔
1061
    bnd = ccall(:jl_get_module_binding, Ref{Core.Binding}, (Any, Any, Cint), tn.module, tn.name, true)
1,885✔
1062
    isdefined(bnd, :partitions) || return nothing
1,885✔
1063
    partition = @atomic bnd.partitions
1,885✔
1064
    while true
1,885✔
1065
        if is_defined_const_binding(binding_kind(partition))
1,885✔
1066
            cval = partition_restriction(partition)
1,885✔
1067
            if isa(cval, Type) && cval <: tn.wrapper
1,885✔
1068
                max_world = @atomic partition.max_world
1,885✔
1069
                max_world == typemax(UInt) && return nothing
1,885✔
1070
                return Int(partition.min_world):Int(max_world)
×
1071
            end
1072
        end
1073
        isdefined(partition, :next) || return nothing
×
1074
        partition = @atomic partition.next
×
1075
    end
×
1076
end
1077

1078
function show_type_name(io::IO, tn::Core.TypeName)
1,852✔
1079
    if tn === UnionAll.name
1,852✔
1080
        # by coincidence, `typeof(Type)` is a valid representation of the UnionAll type.
1081
        # intercept this case and print `UnionAll` instead.
1082
        return print(io, "UnionAll")
×
1083
    end
1084
    globname = tn.singletonname
1,852✔
1085
    globfunc = is_global_function(tn, globname)
1,852✔
1086
    sym = (globfunc ? globname : tn.name)::Symbol
3,698✔
1087
    globfunc && print(io, "typeof(")
1,852✔
1088
    quo = false
1,852✔
1089
    world = check_world_bounded(tn)
1,852✔
1090
    world !== nothing && print(io, "@world(")
1,852✔
1091
    if !(get(io, :compact, false)::Bool)
5,542✔
1092
        # Print module prefix unless type is visible from module passed to
1093
        # IOContext If :module is not set, default to Main.
1094
        # nothing can be used to force printing prefix
1095
        from = get(io, :module, Main)
4,928✔
1096
        if isdefined(tn, :module) && (from === nothing || !isvisible(sym, tn.module, from::Module))
3,078✔
1097
            show(io, tn.module)
982✔
1098
            print(io, ".")
982✔
1099
            if globfunc && !is_id_start_char(first(string(sym)))
982✔
1100
                print(io, ':')
×
1101
                if sym in quoted_syms
×
1102
                    print(io, '(')
×
1103
                    quo = true
×
1104
                end
1105
            end
1106
        end
1107
    end
1108
    show_sym(io, sym)
1,852✔
1109
    world !== nothing && print(io, ", ", world, ")")
1,852✔
1110
    quo      && print(io, ")")
1,852✔
1111
    globfunc && print(io, ")")
1,852✔
1112
    nothing
1,852✔
1113
end
1114

1115
function maybe_kws_nt(x::DataType)
420✔
1116
    x.name === typename(Pairs) || return nothing
842✔
1117
    length(x.parameters) == 4 || return nothing
×
1118
    x.parameters[1] === Symbol || return nothing
×
1119
    p4 = x.parameters[4]
×
1120
    if (isa(p4, DataType) && p4.name === typename(NamedTuple) && length(p4.parameters) == 2)
×
1121
        syms, types = p4.parameters
×
1122
        types isa DataType || return nothing
×
1123
        x.parameters[2] === eltype(p4) || return nothing
×
1124
        isa(syms, Tuple) || return nothing
×
1125
        x.parameters[3] === typeof(syms) || return nothing
×
1126
        return p4
×
1127
    end
1128
    return nothing
×
1129
end
1130

1131
function show_datatype(io::IO, x::DataType, wheres::Vector{TypeVar}=TypeVar[])
400✔
1132
    parameters = x.parameters::SimpleVector
802✔
1133
    istuple = x.name === Tuple.name
401✔
1134
    isnamedtuple = x.name === typename(NamedTuple)
401✔
1135
    kwsnt = maybe_kws_nt(x)
401✔
1136
    n = length(parameters)
401✔
1137

1138
    # Print tuple types with homogeneous tails longer than max_n compactly using `NTuple` or `Vararg`
1139
    if istuple
401✔
1140
        if n == 0
19✔
1141
            print(io, "Tuple{}")
×
1142
            return
×
1143
        end
1144

1145
        # find the length of the homogeneous tail
1146
        max_n = 3
19✔
1147
        taillen = 1
19✔
1148
        pn = parameters[n]
19✔
1149
        fulln = n
19✔
1150
        vakind = :none
19✔
1151
        vaN = 0
19✔
1152
        if pn isa Core.TypeofVararg
19✔
1153
            if isdefined(pn, :N)
×
1154
                vaN = pn.N
×
1155
                if vaN isa Int
×
1156
                    taillen = vaN
×
1157
                    fulln += taillen - 1
×
1158
                    vakind = :fixed
×
1159
                else
1160
                    vakind = :bound
×
1161
                end
1162
            else
1163
                vakind = :unbound
×
1164
            end
1165
            pn = unwrapva(pn)
×
1166
        end
1167
        if !(pn isa TypeVar || pn isa Type)
38✔
1168
            # prefer Tuple over NTuple if it contains something other than types
1169
            # (e.g. if the user has switched the N and T accidentally)
1170
            taillen = 0
×
1171
        elseif vakind === :none || vakind === :fixed
19✔
1172
            for i in (n-1):-1:1
22✔
1173
                if parameters[i] === pn
3✔
1174
                    taillen += 1
1✔
1175
                else
1176
                    break
2✔
1177
                end
1178
            end
1✔
1179
        end
1180

1181
        # prefer NTuple over Tuple if it is a Vararg without a fixed length
1182
        # and prefer Tuple for short lists of elements
1183
        if (vakind == :bound && n == 1 == taillen) || (vakind === :fixed && taillen == fulln > max_n) ||
38✔
1184
           (vakind === :none && taillen == fulln > max_n)
1185
            print(io, "NTuple{")
×
1186
            vakind === :bound ? show(io, vaN) : print(io, fulln)
×
1187
            print(io, ", ")
×
1188
            show(io, pn)
×
1189
            print(io, "}")
×
1190
        else
1191
            print(io, "Tuple{")
19✔
1192
            headlen = (taillen > max_n ? fulln - taillen : fulln)
19✔
1193
            for i = 1:headlen
38✔
1194
                i > 1 && print(io, ", ")
22✔
1195
                show(io, vakind === :fixed && i >= n ? pn : parameters[i])
44✔
1196
            end
25✔
1197
            if headlen < fulln
19✔
1198
                headlen > 0 && print(io, ", ")
×
1199
                print(io, "Vararg{")
×
1200
                show(io, pn)
×
1201
                print(io, ", ", fulln - headlen, "}")
×
1202
            end
1203
            print(io, "}")
19✔
1204
        end
1205
        return
19✔
1206
    elseif isnamedtuple
382✔
1207
        syms, types = parameters
8✔
1208
        if syms isa Tuple && types isa DataType
4✔
1209
            print(io, "@NamedTuple{")
4✔
1210
            show_at_namedtuple(io, syms, types)
4✔
1211
            print(io, "}")
4✔
1212
            return
4✔
1213
        end
1214
    elseif get(io, :backtrace, false)::Bool && kwsnt !== nothing
554✔
1215
        # simplify the type representation of keyword arguments
1216
        # when printing signature of keyword method in the stack trace
1217
        print(io, "@Kwargs{")
×
1218
        show_at_namedtuple(io, kwsnt.parameters[1]::Tuple, kwsnt.parameters[2]::DataType)
×
1219
        print(io, "}")
×
1220
        return
×
1221
    end
1222

1223
    show_type_name(io, x.name)
378✔
1224
    show_typeparams(io, parameters, (unwrap_unionall(x.name.wrapper)::DataType).parameters, wheres)
378✔
1225
end
1226

1227
function show_at_namedtuple(io::IO, syms::Tuple, types::DataType)
556✔
1228
    first = true
556✔
1229
    for i in eachindex(syms)
556✔
1230
        if !first
426✔
1231
            print(io, ", ")
81✔
1232
        end
1233
        show_sym(io, syms[i])
426✔
1234
        typ = types.parameters[i]
426✔
1235
        if typ !== Any
426✔
1236
            print(io, "::")
395✔
1237
            show(io, typ)
395✔
1238
        end
1239
        first = false
426✔
1240
    end
426✔
1241
end
1242

1243
function show_supertypes(io::IO, typ::DataType)
14✔
1244
    print(io, typ)
14✔
1245
    while typ != Any
58✔
1246
        typ = supertype(typ)
44✔
1247
        print(io, " <: ", typ)
44✔
1248
    end
44✔
1249
end
1250

1251
show_supertypes(typ::DataType) = show_supertypes(stdout, typ)
×
1252

1253
"""
1254
    @show exs...
1255

1256
Prints one or more expressions, and their results, to `stdout`, and returns the last result.
1257

1258
See also: [`show`](@ref), [`@info`](@ref man-logging), [`println`](@ref).
1259

1260
# Examples
1261
```jldoctest
1262
julia> x = @show 1+2
1263
1 + 2 = 3
1264
3
1265

1266
julia> @show x^2 x/2;
1267
x ^ 2 = 9
1268
x / 2 = 1.5
1269
```
1270
"""
1271
macro show(exs...)
30✔
1272
    blk = Expr(:block)
30✔
1273
    for ex in exs
30✔
1274
        push!(blk.args, :(println($(sprint(show_unquoted,ex)*" = "),
30✔
1275
                                  repr(begin local value = $(esc(ex)) end))))
60,005✔
1276
    end
30✔
1277
    isempty(exs) || push!(blk.args, :value)
60✔
1278
    return blk
30✔
1279
end
1280

1281
function show(io::IO, tn::Core.TypeName)
2✔
1282
    print(io, "typename(")
3✔
1283
    show_type_name(io, tn)
3✔
1284
    print(io, ")")
3✔
1285
end
1286

1287
nonnothing_nonmissing_typeinfo(io::IO) = nonmissingtype(nonnothingtype(get(io, :typeinfo, Any)))
322,726✔
1288
show(io::IO, b::Bool) = print(io, nonnothing_nonmissing_typeinfo(io) === Bool ? (b ? "1" : "0") : (b ? "true" : "false"))
831✔
1289
show(io::IO, ::Nothing) = print(io, "nothing")
425✔
1290
show(io::IO, n::Signed) = (write(io, string(n)); nothing)
439,091✔
1291
show(io::IO, n::Unsigned) = print(io, "0x", string(n, pad = sizeof(n)<<1, base = 16))
6,720✔
1292
print(io::IO, n::Unsigned) = print(io, string(n))
5,046✔
1293

1294
has_tight_type(p::Pair) =
301✔
1295
    typeof(p.first)  == typeof(p).parameters[1] &&
1296
    typeof(p.second) == typeof(p).parameters[2]
1297

1298
isdelimited(io::IO, x) = true
×
1299
isdelimited(io::IO, x::Function) = !isoperator(Symbol(x))
2✔
1300

1301
# !isdelimited means that the Pair is printed with "=>" (like in "1 => 2"),
1302
# without its explicit type (like in "Pair{Integer,Integer}(1, 2)")
1303
isdelimited(io::IO, p::Pair) = !(has_tight_type(p) || get(io, :typeinfo, Any) == typeof(p))
301✔
1304

1305
function gettypeinfos(io::IO, p::Pair)
233✔
1306
    typeinfo = get(io, :typeinfo, Any)
345✔
1307
    p isa typeinfo <: Pair ?
241✔
1308
        fieldtype(typeinfo, 1) => fieldtype(typeinfo, 2) :
1309
        Any => Any
1310
end
1311

1312
function show(io::IO, p::Pair)
223✔
1313
    isdelimited(io, p) && return show_pairtyped(io, p)
225✔
1314
    typeinfos = gettypeinfos(io, p)
213✔
1315
    for i = (1, 2)
213✔
1316
        io_i = IOContext(io, :typeinfo => typeinfos[i])
426✔
1317
        isdelimited(io_i, p[i]) || print(io, "(")
437✔
1318
        show(io_i, p[i])
482✔
1319
        isdelimited(io_i, p[i]) || print(io, ")")
437✔
1320
        i == 1 && print(io, get(io, :compact, false)::Bool ? "=>" : " => ")
1,153✔
1321
    end
428✔
1322
end
1323

1324
function show_pairtyped(io::IO, p::Pair{K,V}) where {K,V}
12✔
1325
    show(io, typeof(p))
12✔
1326
    show(io, (p.first, p.second))
12✔
1327
end
1328

1329
function show(io::IO, m::Module)
21,297✔
1330
    if is_root_module(m)
22,176✔
1331
        print(io, nameof(m))
21,215✔
1332
    else
1333
        print_fullname(io, m)
82✔
1334
    end
1335
end
1336
# The call to print_fullname above was originally `print(io, join(fullname(m),"."))`,
1337
# which allocates. The method below provides the same behavior without allocating.
1338
# See https://github.com/JuliaLang/julia/pull/42773 for perf information.
1339
function print_fullname(io::IO, m::Module)
165✔
1340
    mp = parentmodule(m)
165✔
1341
    if m === Main || m === Base || m === Core || mp === m
300✔
1342
        show_sym(io, nameof(m))
82✔
1343
    else
1344
        print_fullname(io, mp)
83✔
1345
        print(io, '.')
83✔
1346
        show_sym(io, nameof(m))
83✔
1347
    end
1348
end
1349

1350
sourceinfo_slotnames(src::CodeInfo) = sourceinfo_slotnames(src.slotnames)
47✔
1351
function sourceinfo_slotnames(slotnames::Vector{Symbol})
×
1352
    names = Dict{String,Int}()
×
1353
    printnames = Vector{String}(undef, length(slotnames))
×
1354
    for i in eachindex(slotnames)
×
1355
        if slotnames[i] === :var"#unused#"
×
1356
            printnames[i] = "_"
×
1357
            continue
×
1358
        end
1359
        name = string(slotnames[i])
×
1360
        idx = get!(names, name, i)
×
1361
        if idx != i || isempty(name)
×
1362
            printname = "$name@_$i"
×
1363
            idx > 0 && (printnames[idx] = "$name@_$idx")
×
1364
            names[name] = 0
×
1365
        else
1366
            printname = name
×
1367
        end
1368
        printnames[i] = printname
×
1369
    end
×
1370
    return printnames
×
1371
end
1372

1373
show(io::IO, mi::Core.MethodInstance) = show_mi(io, mi)
12✔
1374
function show(io::IO, codeinst::Core.CodeInstance)
3✔
1375
    print(io, "CodeInstance for ")
8✔
1376
    def = codeinst.def
8✔
1377
    if isa(def, Core.ABIOverride)
8✔
1378
        show_mi(io, def.def)
×
1379
        print(io, " (ABI Overridden)")
×
1380
    else
1381
        show_mi(io, def::MethodInstance)
8✔
1382
    end
1383
end
1384

1385
function show_mi(io::IO, mi::Core.MethodInstance, from_stackframe::Bool=false)
17✔
1386
    def = mi.def
37✔
1387
    if isa(def, Method)
17✔
1388
        if isdefined(def, :generator) && mi === def.generator
17✔
1389
            print(io, "MethodInstance generator for ")
×
1390
            show(io, def)
×
1391
        else
1392
            print(io, "MethodInstance for ")
17✔
1393
            show_tuple_as_call(io, def.name, mi.specTypes; qualified=true)
17✔
1394
        end
1395
    else
1396
        print(io, "Toplevel MethodInstance thunk")
×
1397
        # `thunk` is not very much information to go on. If this
1398
        # MethodInstance is part of a stacktrace, it gets location info
1399
        # added by other means.  But if it isn't, then we should try
1400
        # to print a little more identifying information.
1401
        if !from_stackframe && isdefined(mi, :cache)
×
1402
            ci = mi.cache
×
1403
            if ci.owner === :uninferred
×
1404
                di = ci.inferred.debuginfo
×
1405
                file, line = IRShow.debuginfo_firstline(di)
×
1406
                file = string(file)
×
1407
                line = isempty(file) || line < 0 ? "<unknown>" : "$file:$line"
×
1408
                print(io, " from ", def, " starting at ", line)
×
1409
            end
1410
        end
1411
    end
1412
end
1413

1414
function show_delim_array(io::IO, itr::Union{AbstractArray,SimpleVector}, op, delim, cl,
979✔
1415
                          delim_one, i1=first(LinearIndices(itr)), l=last(LinearIndices(itr)))
1416
    print(io, op)
1,991✔
1417
    if !show_circular(io, itr)
1,382✔
1418
        recur_io = IOContext(io, :SHOWN_SET => itr)
933✔
1419
        first = true
933✔
1420
        i = i1
933✔
1421
        if l >= i1
933✔
1422
            while true
7,030✔
1423
                if !isassigned(itr, i)
7,030✔
1424
                    print(io, undef_ref_str)
11✔
1425
                else
1426
                    x = itr[i]
7,019✔
1427
                    show(recur_io, x)
7,019✔
1428
                end
1429
                if i == l
7,030✔
1430
                    delim_one && first && print(io, delim)
922✔
1431
                    break
922✔
1432
                end
1433
                i += 1
6,108✔
1434
                first = false
6,108✔
1435
                print(io, delim)
6,108✔
1436
                print(io, ' ')
6,108✔
1437
            end
6,108✔
1438
        end
1439
    end
1440
    print(io, cl)
976✔
1441
end
1442

1443
function show_delim_array(io::IO, itr, op, delim, cl, delim_one, i1=1, n=typemax(Int))
864✔
1444
    print(io, op)
1,784✔
1445
    if !show_circular(io, itr)
1,067✔
1446
        recur_io = IOContext(io, :SHOWN_SET => itr)
892✔
1447
        y = iterate(itr)
925✔
1448
        first = true
892✔
1449
        i0 = i1-1
892✔
1450
        while i1 > 1 && y !== nothing
907✔
1451
            y = iterate(itr, y[2])
30✔
1452
            i1 -= 1
15✔
1453
        end
15✔
1454
        if y !== nothing
892✔
1455
            typeinfo = get(io, :typeinfo, Any)
1,069✔
1456
            while true
1,990✔
1457
                x = y[1]
1,990✔
1458
                y = iterate(itr, y[2])
2,859✔
1459
                show(IOContext(recur_io, :typeinfo => itr isa typeinfo <: Tuple ?
2,666✔
1460
                                             fieldtype(typeinfo, i1+i0) :
1461
                                             typeinfo),
1462
                     x)
1463
                i1 += 1
1,990✔
1464
                if y === nothing || i1 > n
3,153✔
1465
                    delim_one && first && print(io, delim)
832✔
1466
                    break
832✔
1467
                end
1468
                first = false
1,158✔
1469
                print(io, delim)
1,158✔
1470
                print(io, ' ')
1,158✔
1471
            end
1,158✔
1472
        end
1473
    end
1474
    print(io, cl)
894✔
1475
end
1476

1477
show(io::IO, t::Tuple) = show_delim_array(io, t, '(', ',', ')', true)
859✔
1478
show(io::IO, v::SimpleVector) = show_delim_array(io, v, "svec(", ',', ')', false)
6✔
1479

1480
show(io::IO, s::Symbol) = show_unquoted_quote_expr(io, s, 0, 0, 0)
3,201✔
1481

1482
## Abstract Syntax Tree (AST) printing ##
1483

1484
# Summary:
1485
#   print(io, ex) defers to show_unquoted(io, ex)
1486
#   show(io, ex) defers to show_unquoted(io, QuoteNode(ex))
1487
#   show_unquoted(io, ex) does the heavy lifting
1488
#
1489
# AST printing should follow two rules:
1490
#   1. Meta.parse(string(ex)) == ex
1491
#   2. eval(Meta.parse(repr(ex))) == ex
1492
#
1493
# Rule 1 means that printing an expression should generate Julia code which
1494
# could be reparsed to obtain the original expression. This code should be
1495
# unambiguous and as readable as possible.
1496
#
1497
# Rule 2 means that showing an expression should generate a quoted version of
1498
# print’s output. Parsing and then evaling this output should return the
1499
# original expression.
1500
#
1501
# This is consistent with many other show methods, i.e.:
1502
#   show(Set([1,2,3]))                     # ==> "Set{Int64}([2,3,1])"
1503
#   eval(Meta.parse("Set{Int64}([2,3,1])")) # ==> An actual set
1504
# While this isn’t true of ALL show methods, it is of all ASTs.
1505

1506
const ExprNode = Union{Expr, QuoteNode, SlotNumber, LineNumberNode, SSAValue,
1507
                       GotoNode, GotoIfNot, GlobalRef, PhiNode, PhiCNode, UpsilonNode,
1508
                       ReturnNode}
1509
# Operators have precedence levels from 1-N, and show_unquoted defaults to a
1510
# precedence level of 0 (the fourth argument). The top-level print and show
1511
# methods use a precedence of -1 to specially allow space-separated macro syntax.
1512
# IOContext(io, :unquote_fallback => false) tells show_unquoted to treat any
1513
# Expr whose head is :$ as if it is inside a quote, preventing fallback to the
1514
# "unhandled" case: this is used by print/string to be lawful to Rule 1 above.
1515
# On the contrary, show/repr have to follow Rule 2, requiring any Expr whose
1516
# head is :$ and which is not inside a quote to fallback to the "unhandled" case:
1517
# this is behavior is triggered by IOContext(io, :unquote_fallback => true)
1518
print(        io::IO, ex::ExprNode)    = (show_unquoted(IOContext(io, :unquote_fallback => false), ex, 0, -1); nothing)
172,742✔
1519
show(         io::IO, ex::ExprNode)    = show_unquoted_quote_expr(IOContext(io, :unquote_fallback => true), ex, 0, -1, 0)
3,062✔
1520
show_unquoted(io::IO, ex)              = show_unquoted(io, ex, 0, 0)
74✔
1521
show_unquoted(io::IO, ex, indent::Int) = show_unquoted(io, ex, indent, 0)
3,107✔
1522
show_unquoted(io::IO, ex, ::Int,::Int) = show(io, ex)
17,009✔
1523
show_unquoted(io::IO, ex, indent::Int, prec::Int, ::Int) = show_unquoted(io, ex, indent, prec)
45,667✔
1524

1525
## AST printing constants ##
1526

1527
const indent_width = 4
1528
const quoted_syms = Set{Symbol}([:(:),:(::),:(:=),:(=),:(==),:(===),:(=>)])
1529
const uni_syms = Set{Symbol}([:(::), :(<:), :(>:)])
1530
const uni_ops = Set{Symbol}([:(+), :(-), :(!), :(¬), :(~), :(<:), :(>:), :(√), :(∛), :(∜), :(∓), :(±)])
1531
const expr_infix_wide = Set{Symbol}([
1532
    :(=), :(+=), :(-=), :(*=), :(/=), :(\=), :(^=), :(&=), :(|=), :(÷=), :(%=), :(>>>=), :(>>=), :(<<=),
1533
    :(.=), :(.+=), :(.-=), :(.*=), :(./=), :(.\=), :(.^=), :(.&=), :(.|=), :(.÷=), :(.%=), :(.>>>=), :(.>>=), :(.<<=),
1534
    :(&&), :(||), :(<:), :($=), :(⊻=), :(>:), :(-->),
1535
    :(:=), :(≔), :(⩴), :(≕)])
1536
const expr_infix = Set{Symbol}([:(:), :(->), :(::)])
1537
const expr_infix_any = union(expr_infix, expr_infix_wide)
1538
const expr_calls  = Dict(:call => ('(',')'), :calldecl => ('(',')'),
1539
                         :ref => ('[',']'), :curly => ('{','}'), :(.) => ('(',')'))
1540
const expr_parens = Dict(:tuple=>('(',')'), :vcat=>('[',']'),
1541
                         :hcat =>('[',']'), :row =>('[',']'), :vect=>('[',']'),
1542
                         :ncat =>('[',']'), :nrow =>('[',']'),
1543
                         :braces=>('{','}'), :bracescat=>('{','}'))
1544

1545
## AST decoding helpers ##
1546

1547
is_id_start_char(c::AbstractChar) = ccall(:jl_id_start_char, Cint, (UInt32,), c) != 0
12,152✔
1548
is_id_char(c::AbstractChar) = ccall(:jl_id_char, Cint, (UInt32,), c) != 0
56,784✔
1549

1550
"""
1551
     isidentifier(s) -> Bool
1552

1553
Return whether the symbol or string `s` contains characters that are parsed as
1554
a valid ordinary identifier (not a binary/unary operator) in Julia code;
1555
see also [`Base.isoperator`](@ref).
1556

1557
Internally Julia allows any sequence of characters in a `Symbol` (except `\\0`s),
1558
and macros automatically use variable names containing `#` in order to avoid
1559
naming collision with the surrounding code. In order for the parser to
1560
recognize a variable, it uses a limited set of characters (greatly extended by
1561
Unicode). `isidentifier()` makes it possible to query the parser directly
1562
whether a symbol contains valid characters.
1563

1564
# Examples
1565
```jldoctest
1566
julia> Meta.isidentifier(:x), Meta.isidentifier("1x")
1567
(true, false)
1568
```
1569
"""
1570
function isidentifier(s::AbstractString)
12,151✔
1571
    x = Iterators.peel(s)
24,293✔
1572
    isnothing(x) && return false
24,297✔
1573
    (s == "true" || s == "false") && return false
12,146✔
1574
    c, rest = x
12,145✔
1575
    is_id_start_char(c) || return false
12,561✔
1576
    return all(is_id_char, rest)
11,729✔
1577
end
1578
isidentifier(s::Symbol) = isidentifier(string(s))
12,308✔
1579

1580
is_op_suffix_char(c::AbstractChar) = ccall(:jl_op_suffix_char, Cint, (UInt32,), c) != 0
×
1581

1582
_isoperator(s) = ccall(:jl_is_operator, Cint, (Cstring,), s) != 0
3,901✔
1583

1584
"""
1585
    isoperator(s::Symbol)
1586

1587
Return `true` if the symbol can be used as an operator, `false` otherwise.
1588

1589
# Examples
1590
```jldoctest
1591
julia> Meta.isoperator(:+), Meta.isoperator(:f)
1592
(true, false)
1593
```
1594
"""
1595
isoperator(s::Union{Symbol,AbstractString}) = _isoperator(s) || ispostfixoperator(s)
7,080✔
1596

1597
"""
1598
    isunaryoperator(s::Symbol)
1599

1600
Return `true` if the symbol can be used as a unary (prefix) operator, `false` otherwise.
1601

1602
# Examples
1603
```jldoctest
1604
julia> Meta.isunaryoperator(:-), Meta.isunaryoperator(:√), Meta.isunaryoperator(:f)
1605
(true, true, false)
1606
```
1607
"""
1608
isunaryoperator(s::Symbol) = ccall(:jl_is_unary_operator, Cint, (Cstring,), s) != 0
×
1609
is_unary_and_binary_operator(s::Symbol) = ccall(:jl_is_unary_and_binary_operator, Cint, (Cstring,), s) != 0
×
1610
is_syntactic_operator(s::Symbol) = ccall(:jl_is_syntactic_operator, Cint, (Cstring,), s) != 0
102✔
1611

1612
"""
1613
    isbinaryoperator(s::Symbol)
1614

1615
Return `true` if the symbol can be used as a binary (infix) operator, `false` otherwise.
1616

1617
# Examples
1618
```jldoctest
1619
julia> Meta.isbinaryoperator(:-), Meta.isbinaryoperator(:√), Meta.isbinaryoperator(:f)
1620
(true, false, false)
1621
```
1622
"""
1623
function isbinaryoperator(s::Symbol)
×
1624
    return _isoperator(s) && (!isunaryoperator(s) || is_unary_and_binary_operator(s)) &&
×
1625
        s !== Symbol("'")
1626
end
1627

1628
"""
1629
    ispostfixoperator(s::Union{Symbol,AbstractString})
1630

1631
Return `true` if the symbol can be used as a postfix operator, `false` otherwise.
1632

1633
# Examples
1634
```jldoctest
1635
julia> Meta.ispostfixoperator(Symbol("'")), Meta.ispostfixoperator(Symbol("'ᵀ")), Meta.ispostfixoperator(:-)
1636
(true, true, false)
1637
```
1638
"""
1639
function ispostfixoperator(s::Union{Symbol,AbstractString})
3,690✔
1640
    s = String(s)::String
3,690✔
1641
    return startswith(s, '\'') && all(is_op_suffix_char, SubString(s, 2))
3,690✔
1642
end
1643

1644
"""
1645
    operator_precedence(s::Symbol)
1646

1647
Return an integer representing the precedence of operator `s`, relative to
1648
other operators. Higher-numbered operators take precedence over lower-numbered
1649
operators. Return `0` if `s` is not a valid operator.
1650

1651
# Examples
1652
```jldoctest
1653
julia> Base.operator_precedence(:+), Base.operator_precedence(:*), Base.operator_precedence(:.)
1654
(11, 12, 17)
1655

1656
julia> Base.operator_precedence(:sin), Base.operator_precedence(:+=), Base.operator_precedence(:(=))  # (Note the necessary parens on `:(=)`)
1657
(0, 1, 1)
1658
```
1659
"""
1660
operator_precedence(s::Symbol) = Int(ccall(:jl_operator_precedence, Cint, (Cstring,), s))
33,405✔
1661
operator_precedence(x::Any) = 0 # fallback for generic expression nodes
×
1662
const prec_assignment = operator_precedence(:(=))
1663
const prec_pair = operator_precedence(:(=>))
1664
const prec_control_flow = operator_precedence(:(&&))
1665
const prec_arrow = operator_precedence(:(-->))
1666
const prec_comparison = operator_precedence(:(>))
1667
const prec_power = operator_precedence(:(^))
1668
const prec_decl = operator_precedence(:(::))
1669

1670
"""
1671
    operator_associativity(s::Symbol)
1672

1673
Return a symbol representing the associativity of operator `s`. Left- and right-associative
1674
operators return `:left` and `:right`, respectively. Return `:none` if `s` is non-associative
1675
or an invalid operator.
1676

1677
# Examples
1678
```jldoctest
1679
julia> Base.operator_associativity(:-), Base.operator_associativity(:+), Base.operator_associativity(:^)
1680
(:left, :none, :right)
1681

1682
julia> Base.operator_associativity(:⊗), Base.operator_associativity(:sin), Base.operator_associativity(:→)
1683
(:left, :none, :right)
1684
```
1685
"""
1686
function operator_associativity(s::Symbol)
×
1687
    if operator_precedence(s) in (prec_arrow, prec_assignment, prec_control_flow, prec_pair, prec_power) ||
×
1688
        (isunaryoperator(s) && !is_unary_and_binary_operator(s)) ||
1689
        (s === :<| || s === :|| || s == :?)
1690
        return :right
×
1691
    elseif operator_precedence(s) in (0, prec_comparison) || s in (:+, :++, :*)
×
1692
        return :none
×
1693
    end
1694
    return :left
×
1695
end
1696

1697
is_quoted(ex)            = false
×
1698
is_quoted(ex::QuoteNode) = true
×
1699
is_quoted(ex::Expr)      = is_expr(ex, :quote, 1) || is_expr(ex, :inert, 1)
20,147✔
1700

1701
unquoted(ex::QuoteNode)  = ex.value
1,321✔
1702
unquoted(ex::Expr)       = ex.args[1]
×
1703

1704
## AST printing helpers ##
1705

1706
function printstyled end
1707
function with_output_color end
1708

1709
emphasize(io, str::AbstractString, col = Base.error_color()) = get(io, :color, false)::Bool ?
7✔
1710
    printstyled(io, str; color=col, bold=true) :
1711
    print(io, uppercase(str))
1712

1713
show_linenumber(io::IO, line)       = printstyled(io, "#= line ", line, " =#", color=:light_black)
5✔
1714
show_linenumber(io::IO, line, file) = printstyled(io, "#= ", file, ":", line, " =#", color=:light_black)
10,744✔
1715
show_linenumber(io::IO, line, file::Nothing) = show_linenumber(io, line)
3✔
1716

1717
# show a block, e g if/for/etc
1718
function show_block(io::IO, head, args::Vector, body, indent::Int, quote_level::Int)
540✔
1719
    print(io, head)
540✔
1720
    if !isempty(args)
540✔
1721
        print(io, ' ')
261✔
1722
        if head === :elseif
261✔
1723
            show_list(io, args, " ", indent, 0, quote_level)
×
1724
        else
1725
            show_list(io, args, ", ", indent, 0, quote_level)
261✔
1726
        end
1727
    end
1728

1729
    ind = head === :module || head === :baremodule ? indent : indent + indent_width
784✔
1730
    exs = (is_expr(body, :block) || is_expr(body, :quote)) ? body.args : Any[body]
544✔
1731
    for ex in exs
540✔
1732
        print(io, '\n', " "^ind)
761✔
1733
        show_unquoted(io, ex, ind, -1, quote_level)
1,384✔
1734
    end
761✔
1735
    print(io, '\n', " "^indent)
540✔
1736
end
1737
show_block(io::IO,head,    block,i::Int, quote_level::Int) = show_block(io,head, [], block,i, quote_level)
278✔
1738
function show_block(io::IO, head, arg, block, i::Int, quote_level::Int)
1,020✔
1739
    if is_expr(arg, :block) || is_expr(arg, :quote)
1,770✔
1740
        show_block(io, head, arg.args, block, i, quote_level)
113✔
1741
    else
1742
        show_block(io, head, Any[arg], block, i, quote_level)
907✔
1743
    end
1744
end
1745

1746
# show an indented list
1747
function show_list(io::IO, items, sep, indent::Int, prec::Int=0, quote_level::Int=0, enclose_operators::Bool=false,
18,134✔
1748
                   kw::Bool=false)
1749
    n = length(items)
18,384✔
1750
    n == 0 && return
13,469✔
1751
    indent += indent_width
12,719✔
1752
    first = true
12,719✔
1753
    for item in items
12,719✔
1754
        !first && print(io, sep)
21,247✔
1755
        parens = !is_quoted(item) &&
21,247✔
1756
            (first && prec >= prec_power &&
1757
             ((item isa Expr && item.head === :call && (callee = item.args[1]; isa(callee, Symbol) && callee in uni_ops)) ||
21,269✔
1758
              (item isa Real && item < 0))) ||
1759
            (enclose_operators && item isa Symbol && isoperator(item) && is_valid_identifier(item))
1760
        parens && print(io, '(')
21,247✔
1761
        if kw && is_expr(item, :kw, 2)
21,247✔
1762
            item = item::Expr
565✔
1763
            show_unquoted(io, Expr(:(=), item.args[1], item.args[2]), indent, parens ? 0 : prec, quote_level)
565✔
1764
        elseif kw && is_expr(item, :(=), 2)
20,682✔
1765
            item = item::Expr
×
1766
            show_unquoted_expr_fallback(io, item, indent, quote_level)
×
1767
        else
1768
            show_unquoted(io, item, indent, parens ? 0 : prec, quote_level)
20,682✔
1769
        end
1770
        parens && print(io, ')')
21,247✔
1771
        first = false
21,247✔
1772
    end
21,247✔
1773
end
1774
# show an indented list inside the parens (op, cl)
1775
function show_enclosed_list(io::IO, op, items, sep, cl, indent, prec=0, quote_level=0, encl_ops=false, kw::Bool=false)
7,761✔
1776
    print(io, op)
14,303✔
1777
    show_list(io, items, sep, indent, prec, quote_level, encl_ops, kw)
14,099✔
1778
    print(io, cl)
14,099✔
1779
end
1780

1781
const keyword_syms = Set([
1782
    :baremodule, :begin, :break, :catch, :const, :continue, :do, :else, :elseif,
1783
    :end, :export, :var"false", :finally, :for, :function, :global, :if, :import,
1784
    :let, :local, :macro, :module, :public, :quote, :return, :struct, :var"true",
1785
    :try, :using, :while ])
1786

1787
function is_valid_identifier(sym)
8,569✔
1788
    return (isidentifier(sym) && !(sym in keyword_syms)) ||
8,569✔
1789
        (_isoperator(sym) &&
1790
        !(sym in (Symbol("'"), :(::), :?)) &&
1791
        !is_syntactic_operator(sym)
1792
    )
1793
end
1794

1795
# show a normal (non-operator) function call, e.g. f(x, y) or A[z]
1796
# kw: `=` expressions are parsed with head `kw` in this context
1797
function show_call(io::IO, head, func, func_args, indent, quote_level, kw::Bool)
13,977✔
1798
    op, cl = expr_calls[head]
13,977✔
1799
    if (isa(func, Symbol) && func !== :(:) && !(head === :. && isoperator(func))) ||
14,641✔
1800
            (isa(func, Symbol) && !is_valid_identifier(func)) ||
1801
            (isa(func, Expr) && (func.head === :. || func.head === :curly || func.head === :macroname)) ||
1802
            isa(func, GlobalRef)
1803
        show_unquoted(io, func, indent, 0, quote_level)
13,511✔
1804
    else
1805
        print(io, '(')
466✔
1806
        show_unquoted(io, func, indent, 0, quote_level)
658✔
1807
        print(io, ')')
466✔
1808
    end
1809
    if head === :(.)
13,977✔
1810
        print(io, '.')
118✔
1811
    end
1812
    if !isempty(func_args) && isa(func_args[1], Expr) && (func_args[1]::Expr).head === :parameters
13,977✔
1813
        print(io, op)
82✔
1814
        show_list(io, func_args[2:end], ", ", indent, 0, quote_level, false, kw)
157✔
1815
        print(io, "; ")
82✔
1816
        show_list(io, (func_args[1]::Expr).args, ", ", indent, 0, quote_level, false, kw)
82✔
1817
        print(io, cl)
82✔
1818
    else
1819
        show_enclosed_list(io, op, func_args, ", ", cl, indent, 0, quote_level, false, kw)
13,895✔
1820
    end
1821
end
1822

1823
# Print `sym` as it would appear as an identifier name in code
1824
# * Print valid identifiers & operators literally; also macros names if allow_macroname=true
1825
# * Escape invalid identifiers with var"" syntax
1826
function show_sym(io::IO, sym::Symbol; allow_macroname=false)
28,276✔
1827
    if is_valid_identifier(sym)
8,575✔
1828
        print(io, sym)
8,334✔
1829
    elseif allow_macroname && (sym_str = string(sym); startswith(sym_str, '@'))
354✔
1830
        print(io, '@')
113✔
1831
        show_sym(io, Symbol(sym_str[2:end]))
113✔
1832
    else
1833
        print(io, "var\"", escape_raw_string(string(sym)), '"')
128✔
1834
    end
1835
end
1836

1837
## AST printing ##
1838

1839
function show_unquoted(io::IO, val::SSAValue, ::Int, ::Int)
58✔
1840
    if get(io, :maxssaid, typemax(Int))::Int < val.id
10,859✔
1841
        # invalid SSAValue, print this in red for better recognition
1842
        printstyled(io, "%", val.id; color=:red)
×
1843
    else
1844
        print(io, "%", val.id)
10,471✔
1845
    end
1846
end
1847
show_unquoted(io::IO, sym::Symbol, ::Int, ::Int)        = show_sym(io, sym, allow_macroname=false)
10,743✔
1848
show_unquoted(io::IO, ex::LineNumberNode, ::Int, ::Int) = show_linenumber(io, ex.line, ex.file)
17,399✔
1849
show_unquoted(io::IO, ex::GotoNode, ::Int, ::Int)       = print(io, "goto %", ex.label)
16✔
1850
show_unquoted(io::IO, ex::GlobalRef, ::Int, ::Int)      = show_globalref(io, ex)
5,755✔
1851

1852
function show_globalref(io::IO, ex::GlobalRef; allow_macroname=false)
5,759✔
1853
    print(io, ex.mod)
4✔
1854
    print(io, '.')
4✔
1855
    quoted = !isidentifier(ex.name) && !startswith(string(ex.name), "@")
4✔
1856
    parens = quoted && (!isoperator(ex.name) || (ex.name in quoted_syms))
4✔
1857
    quoted && print(io, ':')
4✔
1858
    parens && print(io, '(')
4✔
1859
    show_sym(io, ex.name, allow_macroname=allow_macroname)
4✔
1860
    parens && print(io, ')')
4✔
1861
    nothing
4✔
1862
end
1863

1864
function show_unquoted(io::IO, ex::SlotNumber, ::Int, ::Int)
120✔
1865
    slotid = ex.id
835✔
1866
    slotnames = get(io, :SOURCE_SLOTNAMES, false)
1,857✔
1867
    if isa(slotnames, Vector{String}) && slotid ≤ length(slotnames)
835✔
1868
        print(io, slotnames[slotid])
263✔
1869
    else
1870
        print(io, "_", slotid)
572✔
1871
    end
1872
end
1873

1874
function show_unquoted(io::IO, ex::QuoteNode, indent::Int, prec::Int)
171✔
1875
    if isa(ex.value, Symbol)
171✔
1876
        show_unquoted_quote_expr(io, ex.value, indent, prec, 0)
171✔
1877
    else
1878
        print(io, "\$(QuoteNode(")
×
1879
        # QuoteNode does not allows for interpolation, so if ex.value is an
1880
        # Expr it should be shown with quote_level equal to zero.
1881
        # Calling show(io, ex.value) like this implicitly enforce that.
1882
        show(io, ex.value)
×
1883
        print(io, "))")
×
1884
    end
1885
end
1886

1887
function show_unquoted_quote_expr(io::IO, @nospecialize(value), indent::Int, prec::Int, quote_level::Int)
2,717✔
1888
    if isa(value, Symbol)
2,778✔
1889
        sym = value::Symbol
2,678✔
1890
        if value in quoted_syms
5,356✔
1891
            print(io, ":(", sym, ")")
5✔
1892
        else
1893
            if isidentifier(sym) || (_isoperator(sym) && sym !== Symbol("'"))
2,679✔
1894
                print(io, ":", sym)
2,673✔
1895
            else
1896
                print(io, "Symbol(", repr(String(sym)), ")")
×
1897
            end
1898
        end
1899
    else
1900
        if isa(value,Expr) && value.head === :block
100✔
1901
            value = value::Expr
×
1902
            show_block(IOContext(io, beginsym=>false), "quote", value, indent, quote_level)
×
1903
            print(io, "end")
×
1904
        else
1905
            print(io, ":(")
116✔
1906
            show_unquoted(io, value, indent+2, -1, quote_level)  # +2 for `:(`
117✔
1907
            print(io, ")")
116✔
1908
        end
1909
    end
1910
end
1911

1912
function show_generator(io, ex::Expr, indent, quote_level)
11✔
1913
    if ex.head === :flatten
11✔
1914
        fg::Expr = ex
×
1915
        ranges = Any[]
×
1916
        while isa(fg, Expr) && fg.head === :flatten
×
1917
            push!(ranges, (fg.args[1]::Expr).args[2:end])
×
1918
            fg = (fg.args[1]::Expr).args[1]::Expr
×
1919
        end
×
1920
        push!(ranges, fg.args[2:end])
×
1921
        show_unquoted(io, fg.args[1], indent, 0, quote_level)
×
1922
        for r in ranges
×
1923
            print(io, " for ")
×
1924
            show_list(io, r, ", ", indent, 0, quote_level)
×
1925
        end
×
1926
    else
1927
        show_unquoted(io, ex.args[1], indent, 0, quote_level)
16✔
1928
        print(io, " for ")
11✔
1929
        show_list(io, ex.args[2:end], ", ", indent, 0, quote_level)
11✔
1930
    end
1931
end
1932

1933
function valid_import_path(@nospecialize(ex), allow_as = true)
1934
    if allow_as && is_expr(ex, :as) && length((ex::Expr).args) == 2
38✔
1935
        ex = (ex::Expr).args[1]
×
1936
    end
1937
    return is_expr(ex, :(.)) && length((ex::Expr).args) > 0 && all(a->isa(a,Symbol), (ex::Expr).args)
38✔
1938
end
1939

1940
function show_import_path(io::IO, ex, quote_level)
499✔
1941
    if !isa(ex, Expr)
499✔
1942
        show_unquoted(io, ex)
×
1943
    elseif ex.head === :(:)
499✔
1944
        show_import_path(io, ex.args[1], quote_level)
80✔
1945
        print(io, ": ")
80✔
1946
        for i = 2:length(ex.args)
84✔
1947
            if i > 2
178✔
1948
                print(io, ", ")
98✔
1949
            end
1950
            show_import_path(io, ex.args[i], quote_level)
178✔
1951
        end
178✔
1952
    elseif ex.head === :(.)
419✔
1953
        for i = 1:length(ex.args)
392✔
1954
            sym = ex.args[i]::Symbol
589✔
1955
            if sym === :(.)
589✔
1956
                print(io, '.')
46✔
1957
            else
1958
                if sym === :(..)
543✔
1959
                    # special case for https://github.com/JuliaLang/julia/issues/49168
1960
                    print(io, "(..)")
4✔
1961
                else
1962
                    show_sym(io, sym, allow_macroname=(i==length(ex.args)))
539✔
1963
                end
1964
                i < length(ex.args) && print(io, '.')
543✔
1965
            end
1966
        end
589✔
1967
    else
1968
        show_unquoted(io, ex, 0, 0, quote_level)
42✔
1969
    end
1970
end
1971

1972
# Wrap symbols for macro names to allow them to be printed literally
1973
function allow_macroname(ex)
6,701✔
1974
    if (ex isa Symbol && first(string(ex)) == '@') ||
13,437✔
1975
       ex isa GlobalRef ||
1976
       (is_expr(ex, :(.)) && length(ex.args) == 2 &&
1977
        (is_expr(ex.args[2], :quote) || ex.args[2] isa QuoteNode))
1978
       return Expr(:macroname, ex)
6,657✔
1979
    else
1980
        ex
44✔
1981
    end
1982
end
1983

1984
is_core_macro(arg::GlobalRef, macro_name::AbstractString) = is_core_macro(arg, Symbol(macro_name))
×
1985
is_core_macro(arg::GlobalRef, macro_name::Symbol) = arg == GlobalRef(Core, macro_name)
8✔
1986
is_core_macro(@nospecialize(arg), macro_name::AbstractString) = false
×
1987
is_core_macro(@nospecialize(arg), macro_name::Symbol) = false
468✔
1988

1989
# symbol for IOContext flag signaling whether "begin" is treated
1990
# as an ordinary symbol, which is true in indexing expressions.
1991
const beginsym = gensym(:beginsym)
1992

1993
function show_unquoted_expr_fallback(io::IO, ex::Expr, indent::Int, quote_level::Int)
1,211✔
1994
    print(io, "\$(Expr(")
1,211✔
1995
    show(io, ex.head)
1,211✔
1996
    for arg in ex.args
1,211✔
1997
        print(io, ", ")
1,211✔
1998
        show(io, arg)
1,211✔
1999
    end
1,211✔
2000
    print(io, "))")
1,211✔
2001
end
2002

2003
# TODO: implement interpolated strings
2004
function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::Int = 0)
14,813✔
2005
    head, args, nargs = ex.head, ex.args, length(ex.args)
174,240✔
2006
    unhandled = false
14,791✔
2007
    # dot (i.e. "x.y"), but not compact broadcast exps
2008
    if head === :(.) && (nargs != 2 || !is_expr(args[2], :tuple))
16,228✔
2009
        # standalone .op
2010
        if nargs == 1 && args[1] isa Symbol && isoperator(args[1]::Symbol)
1,322✔
2011
            print(io, "(.", args[1], ")")
1✔
2012
        elseif nargs == 2 && is_quoted(args[2])
1,321✔
2013
            item = args[1]
1,321✔
2014
            # field
2015
            field = unquoted(args[2])
1,321✔
2016
            parens = !is_quoted(item) && !(item isa Symbol && isidentifier(item)) && !is_expr(item, :(.))
1,583✔
2017
            parens && print(io, '(')
1,321✔
2018
            show_unquoted(io, item, indent, 0, quote_level)
2,380✔
2019
            parens && print(io, ')')
1,321✔
2020
            # .
2021
            print(io, '.')
1,321✔
2022
            # item
2023
            if isa(field, Symbol)
1,321✔
2024
                parens = field in quoted_syms
2,642✔
2025
                quoted = parens || isoperator(field)
2,636✔
2026
            else
2027
                parens = quoted = true
×
2028
            end
2029
            quoted && print(io, ':')
1,321✔
2030
            parens && print(io, '(')
1,321✔
2031
            show_unquoted(io, field, indent, 0, quote_level)
2,642✔
2032
            parens && print(io, ')')
1,321✔
2033
        else
2034
            unhandled = true
×
2035
        end
2036

2037
    # infix (i.e. "x <: y" or "x = y")
2038
    elseif (head in expr_infix_any && nargs==2)
26,938✔
2039
        func_prec = operator_precedence(head)
834✔
2040
        head_ = head in expr_infix_wide ? " $head " : head
1,668✔
2041
        if func_prec <= prec
834✔
2042
            show_enclosed_list(io, '(', args, head_, ')', indent, func_prec, quote_level, true)
62✔
2043
        else
2044
            show_list(io, args, head_, indent, func_prec, quote_level, true)
772✔
2045
        end
2046

2047
    elseif head === :tuple
12,635✔
2048
        print(io, "(")
351✔
2049
        if nargs > 0 && is_expr(args[1], :parameters)
351✔
2050
            arg1 = args[1]::Expr
14✔
2051
            show_list(io, args[2:end], ", ", indent, 0, quote_level)
14✔
2052
            nargs == 2 && print(io, ',')
14✔
2053
            print(io, ";")
14✔
2054
            if !isempty(arg1.args)
14✔
2055
                print(io, " ")
14✔
2056
            end
2057
            show_list(io, arg1.args, ", ", indent, 0, quote_level, false, true)
14✔
2058
        else
2059
            show_list(io, args, ", ", indent, 0, quote_level)
337✔
2060
            nargs == 1 && print(io, ',')
337✔
2061
        end
2062
        print(io, ")")
351✔
2063

2064
    # list-like forms, e.g. "[1, 2, 3]"
2065
    elseif haskey(expr_parens, head) ||                          # :vcat etc.
36,406✔
2066
        head === :typed_vcat || head === :typed_hcat || head === :typed_ncat
2067
        # print the type and defer to the untyped case
2068
        if head === :typed_vcat || head === :typed_hcat || head === :typed_ncat
903✔
2069
            show_unquoted(io, args[1], indent, prec, quote_level)
12✔
2070
            if head === :typed_vcat
7✔
2071
                head = :vcat
3✔
2072
            elseif head === :typed_hcat
4✔
2073
                head = :hcat
4✔
2074
            else
2075
                head = :ncat
×
2076
            end
2077
            args = args[2:end]
14✔
2078
            nargs = nargs - 1
7✔
2079
        end
2080
        op, cl = expr_parens[head]
453✔
2081
        if head === :vcat || head === :bracescat
858✔
2082
            sep = "; "
48✔
2083
        elseif head === :hcat || head === :row
782✔
2084
            sep = " "
102✔
2085
        elseif head === :ncat || head === :nrow
606✔
2086
            sep = ";"^args[1]::Int * " "
×
2087
            args = args[2:end]
×
2088
            nargs = nargs - 1
×
2089
        else
2090
            sep = ", "
303✔
2091
        end
2092
        head !== :row && head !== :nrow && print(io, op)
453✔
2093
        show_list(io, args, sep, indent, 0, quote_level)
453✔
2094
        if nargs <= 1 && (head === :vcat || head === :ncat)
572✔
2095
            print(io, sep[1:end-1])
10✔
2096
        end
2097
        head !== :row && head !== :nrow && print(io, cl)
453✔
2098

2099
    # transpose
2100
    elseif (head === Symbol("'") && nargs == 1) || (
23,649✔
2101
        # ' with unicode suffix is a call expression
2102
        head === :call && nargs == 2 && args[1] isa Symbol &&
2103
        ispostfixoperator(args[1]::Symbol) && args[1]::Symbol !== Symbol("'")
2104
    )
2105
        op, arg1 = head === Symbol("'") ? (head, args[1]) : (args[1], args[2])
13✔
2106
        if isa(arg1, Expr) || (isa(arg1, Symbol) && isoperator(arg1::Symbol))
17✔
2107
            show_enclosed_list(io, '(', [arg1::Union{Expr, Symbol}], ", ", ')', indent, 0)
11✔
2108
        else
2109
            show_unquoted(io, arg1, indent, 0, quote_level)
2✔
2110
        end
2111
        print(io, op)
13✔
2112

2113
    # function call
2114
    elseif head === :call && nargs >= 1
11,818✔
2115
        func = args[1]
8,214✔
2116
        fname = isa(func, GlobalRef) ? func.name : func
8,214✔
2117
        func_prec = operator_precedence(fname)
8,637✔
2118
        if func_prec > 0 || (isa(fname, Symbol) && fname in uni_ops)
13,461✔
2119
            func = fname
3,072✔
2120
        end
2121
        func_args = args[2:end]
16,164✔
2122

2123
        # :kw exprs are only parsed inside parenthesized calls
2124
        if any(a->is_expr(a, :kw), func_args) || (!isempty(func_args) && is_expr(func_args[1], :parameters))
28,942✔
2125
            show_call(io, head, func, func_args, indent, quote_level, true)
353✔
2126

2127
        # scalar multiplication (i.e. "100x")
2128
        elseif (func === :* &&
7,917✔
2129
            length(func_args) == 2 && isa(func_args[1], Union{Int, Int64, Float32, Float64}) &&
2130
            isa(func_args[2], Symbol) &&
2131
            !in(string(func_args[2]::Symbol)[1], ('e', 'E', 'f', (func_args[1] == 0 && func_args[1] isa Integer ?
2132
                                                                  # don't juxtapose 0 with b, o, x
2133
                                                                  ('b', 'o', 'x') : ())...)))
2134
            if func_prec <= prec
57✔
2135
                show_enclosed_list(io, '(', func_args, "", ')', indent, func_prec, quote_level)
6✔
2136
            else
2137
                show_list(io, func_args, "", indent, func_prec, quote_level)
51✔
2138
            end
2139

2140
        # unary operator (i.e. "!z")
2141
        elseif isa(func,Symbol) && length(func_args) == 1 && func in uni_ops
11,208✔
2142
            show_unquoted(io, func, indent, 0, quote_level)
129✔
2143
            arg1 = func_args[1]
129✔
2144
            if isa(arg1, Expr) || (isa(arg1, Symbol) && isoperator(arg1) && is_valid_identifier(arg1))
195✔
2145
                show_enclosed_list(io, '(', func_args, ", ", ')', indent, func_prec)
96✔
2146
            else
2147
                show_unquoted(io, arg1, indent, func_prec, quote_level)
33✔
2148
            end
2149

2150
        # binary operator (i.e. "x + y")
2151
        elseif func_prec > 0 # is a binary operator
7,675✔
2152
            func = func::Symbol    # operator_precedence returns func_prec == 0 for non-Symbol
2,880✔
2153
            na = length(func_args)
2,880✔
2154
            if (na == 2 || (na > 2 && func in (:+, :++, :*)) || (na == 3 && func === :(:))) &&
2,880✔
2155
                    all(a -> !isa(a, Expr) || a.head !== :..., func_args)
9,321✔
2156
                sep = func === :(:) ? "$func" : " $func "
5,508✔
2157

2158
                if func_prec <= prec
2,870✔
2159
                    show_enclosed_list(io, '(', func_args, sep, ')', indent, func_prec, quote_level, true)
20✔
2160
                else
2161
                    show_list(io, func_args, sep, indent, func_prec, quote_level, true)
2,850✔
2162
                end
2163
            elseif na == 1
10✔
2164
                # 1-argument call to normally-binary operator
2165
                op, cl = expr_calls[head]
9✔
2166
                print(io, "(")
9✔
2167
                show_unquoted(io, func, indent, 0, quote_level)
9✔
2168
                print(io, ")")
9✔
2169
                show_enclosed_list(io, op, func_args, ", ", cl, indent, 0, quote_level)
9✔
2170
            else
2171
                show_call(io, head, func, func_args, indent, quote_level, true)
1✔
2172
            end
2173

2174
        # normal function (i.e. "f(x,y)")
2175
        else
2176
            show_call(io, head, func, func_args, indent, quote_level, true)
4,795✔
2177
        end
2178

2179
    # new expr
2180
    elseif head === :new || head === :splatnew
7,208✔
2181
        show_enclosed_list(io, "%$head(", args, ", ", ")", indent, 0, quote_level)
×
2182

2183
    # other call-like expressions ("A[1,2]", "T{X,Y}", "f.(X,Y)")
2184
    elseif haskey(expr_calls, head) && nargs >= 1  # :ref/:curly/:calldecl/:(.)
7,208✔
2185
        funcargslike = head === :(.) ? (args[2]::Expr).args : args[2:end]
1,354✔
2186
        show_call(head === :ref ? IOContext(io, beginsym=>true) : io, head, args[1], funcargslike, indent, quote_level, head !== :curly)
1,354✔
2187

2188
    # comprehensions
2189
    elseif head === :typed_comprehension && nargs == 2
2,250✔
2190
        show_unquoted(io, args[1], indent, 0, quote_level)
2✔
2191
        print(io, '[')
1✔
2192
        show_generator(io, args[2], indent, quote_level)
1✔
2193
        print(io, ']')
1✔
2194

2195
    elseif head === :comprehension && nargs == 1
2,249✔
2196
        print(io, '[')
3✔
2197
        show_generator(io, args[1], indent, quote_level)
3✔
2198
        print(io, ']')
3✔
2199

2200
    elseif (head === :generator && nargs >= 2) || (head === :flatten && nargs == 1)
4,485✔
2201
        print(io, '(')
7✔
2202
        show_generator(io, ex, indent, quote_level)
7✔
2203
        print(io, ')')
7✔
2204

2205
    elseif head === :filter && nargs == 2
2,239✔
2206
        show_unquoted(io, args[2], indent, 0, quote_level)
2✔
2207
        print(io, " if ")
2✔
2208
        show_unquoted(io, args[1], indent, 0, quote_level)
2✔
2209

2210
    # comparison (i.e. "x < y < z")
2211
    elseif head === :comparison && nargs >= 3 && (nargs&1==1)
2,237✔
2212
        comp_prec = minimum(operator_precedence, args[2:2:end]; init=typemax(Int))
148✔
2213
        if comp_prec <= prec
74✔
2214
            show_enclosed_list(io, '(', args, " ", ')', indent, comp_prec, quote_level)
×
2215
        else
2216
            show_list(io, args, " ", indent, comp_prec, quote_level)
74✔
2217
        end
2218

2219
    # function calls need to transform the function from :call to :calldecl
2220
    # so that operators are printed correctly
2221
    elseif head === :function && nargs==2 && is_expr(args[1], :call)
2,163✔
2222
        show_block(IOContext(io, beginsym=>false), head, Expr(:calldecl, (args[1]::Expr).args...), args[2], indent, quote_level)
3✔
2223
        print(io, "end")
3✔
2224

2225
    elseif (head === :function || head === :macro) && nargs == 1
4,319✔
2226
        print(io, head, ' ')
1✔
2227
        show_unquoted(IOContext(io, beginsym=>false), args[1])
1✔
2228
        print(io, " end")
1✔
2229

2230
    elseif head === :do && nargs == 2
2,159✔
2231
        iob = IOContext(io, beginsym=>false)
10✔
2232
        show_unquoted(iob, args[1], indent, -1, quote_level)
10✔
2233
        print(io, " do")
10✔
2234
        do_args = (((args[2]::Expr).args[1])::Expr).args
10✔
2235
        if !isempty(do_args)
10✔
2236
            print(io, ' ')
9✔
2237
            show_list(iob, do_args, ", ", 0, 0, quote_level)
9✔
2238
        end
2239
        for stmt in (((args[2]::Expr).args[2])::Expr).args
10✔
2240
            print(io, '\n', " "^(indent + indent_width))
24✔
2241
            show_unquoted(iob, stmt, indent + indent_width, -1, quote_level)
36✔
2242
        end
24✔
2243
        print(io, '\n', " "^indent)
10✔
2244
        print(io, "end")
10✔
2245

2246
    # block with argument
2247
    elseif head in (:for,:while,:function,:macro,:if,:elseif,:let) && nargs==2
2,149✔
2248
        if head === :function && is_expr(args[1], :...)
13✔
2249
            # fix printing of "function (x...) x end"
2250
            block_args = Expr(:tuple, args[1])
×
2251
        else
2252
            block_args = args[1]
13✔
2253
        end
2254
        if is_expr(args[2], :block)
13✔
2255
            show_block(IOContext(io, beginsym=>false), head, block_args, args[2], indent, quote_level)
13✔
2256
        else
2257
            show_block(IOContext(io, beginsym=>false), head, block_args, Expr(:block, args[2]), indent, quote_level)
×
2258
        end
2259
        print(io, "end")
13✔
2260

2261
    elseif (head === :if || head === :elseif) && nargs == 3
4,268✔
2262
        iob = IOContext(io, beginsym=>false)
4✔
2263
        show_block(iob, head, args[1], args[2], indent, quote_level)
4✔
2264
        arg3 = args[3]
4✔
2265
        if isa(arg3, Expr) && arg3.head === :elseif
4✔
2266
            show_unquoted(iob, arg3::Expr, indent, prec, quote_level)
×
2267
        else
2268
            show_block(iob, "else", arg3, indent, quote_level)
4✔
2269
            print(io, "end")
4✔
2270
        end
2271

2272
    elseif head === :module && nargs==3 && isa(args[1],Bool)
2,132✔
2273
        show_block(IOContext(io, beginsym=>false), args[1] ? :module : :baremodule, args[2], args[3], indent, quote_level)
6✔
2274
        print(io, "end")
6✔
2275

2276
    # type declaration
2277
    elseif head === :struct && nargs==3
2,126✔
2278
        show_block(IOContext(io, beginsym=>false), args[1] ? Symbol("mutable struct") : Symbol("struct"), args[2], args[3], indent, quote_level)
1✔
2279
        print(io, "end")
1✔
2280

2281
    elseif head === :primitive && nargs == 2
2,125✔
2282
        print(io, "primitive type ")
×
2283
        show_list(io, args, ' ', indent, 0, quote_level)
×
2284
        print(io, " end")
×
2285

2286
    elseif head === :abstract && nargs == 1
2,125✔
2287
        print(io, "abstract type ")
×
2288
        show_list(IOContext(io, beginsym=>false), args, ' ', indent, 0, quote_level)
×
2289
        print(io, " end")
×
2290

2291
    # empty return (i.e. "function f() return end")
2292
    elseif head === :return && nargs == 1 && args[1] === nothing
2,125✔
2293
        print(io, head)
×
2294

2295
    # type annotation (i.e. "::Int")
2296
    elseif head in uni_syms && nargs == 1
4,250✔
2297
        print(io, head)
13✔
2298
        show_unquoted(io, args[1], indent, 0, quote_level)
13✔
2299

2300
    # var-arg declaration or expansion
2301
    # (i.e. "function f(L...) end" or "f(B...)")
2302
    elseif head === :(...) && nargs == 1
2,112✔
2303
        dotsprec = operator_precedence(:(:)) - 1
25✔
2304
        parens = dotsprec <= prec
25✔
2305
        parens && print(io, "(")
25✔
2306
        show_unquoted(io, args[1], indent, dotsprec, quote_level)
38✔
2307
        print(io, "...")
25✔
2308
        parens && print(io, ")")
25✔
2309

2310
    elseif (nargs == 0 && head in (:break, :continue))
2,087✔
2311
        print(io, head)
×
2312

2313
    elseif (nargs == 1 && head in (:return, :const)) ||
4,171✔
2314
                          head in (:local,  :global)
2315
        print(io, head, ' ')
9✔
2316
        show_list(io, args, ", ", indent, 0, quote_level)
9✔
2317

2318
    elseif head in (:export, :public)
2,078✔
2319
        print(io, head, ' ')
×
2320
        show_list(io, mapany(allow_macroname, args), ", ", indent)
×
2321

2322
    elseif head === :macrocall && nargs >= 2
2,078✔
2323
        # handle some special syntaxes
2324
        # `a b c`
2325
        if is_core_macro(args[1], :var"@cmd")
639✔
2326
            print(io, "`", args[3], "`")
6✔
2327
        # 11111111111111111111, 0xfffffffffffffffff, 1111...many digits...
2328
        elseif is_core_macro(args[1], :var"@int128_str") ||
949✔
2329
               is_core_macro(args[1], :var"@uint128_str") ||
2330
               is_core_macro(args[1], :var"@big_str")
2331
            print(io, args[3])
1✔
2332
        # x"y" and x"y"z
2333
        elseif isa(args[1], Symbol) && nargs >= 3 && isa(args[3], String) &&
316✔
2334
               startswith(string(args[1]::Symbol), "@") &&
2335
               endswith(string(args[1]::Symbol), "_str")
2336
            s = string(args[1]::Symbol)
79✔
2337
            print(io, s[2:prevind(s,end,4)], "\"")
158✔
2338
            escape_raw_string(io, args[3])
79✔
2339
            print(io, "\"")
79✔
2340
            if nargs == 4
79✔
2341
                print(io, args[4])
×
2342
            end
2343
        # general case
2344
        else
2345
            # first show the line number argument as a comment
2346
            if isa(args[2], LineNumberNode) || is_expr(args[2], :line)
237✔
2347
                print(io, args[2], ' ')
237✔
2348
            end
2349
            # Use the functional syntax unless specifically designated with
2350
            # prec=-1 and hide the line number argument from the argument list
2351
            mname = allow_macroname(args[1])
237✔
2352
            if prec >= 0
237✔
2353
                show_call(io, :call, mname, args[3:end], indent, quote_level, false)
154✔
2354
            else
2355
                show_args = Vector{Any}(undef, nargs - 1)
83✔
2356
                show_args[1] = mname
83✔
2357
                show_args[2:end] = args[3:end]
165✔
2358
                show_list(io, show_args, ' ', indent, 0, quote_level)
83✔
2359
            end
2360
        end
2361

2362
    elseif head === :macroname && nargs == 1
1,755✔
2363
        arg1 = args[1]
279✔
2364
        if arg1 isa Symbol
279✔
2365
            show_sym(io, arg1, allow_macroname=true)
276✔
2366
        elseif arg1 isa GlobalRef
3✔
2367
            show_globalref(io, arg1, allow_macroname=true)
×
2368
        elseif is_expr(arg1, :(.)) && length((arg1::Expr).args) == 2
3✔
2369
            arg1 = arg1::Expr
3✔
2370
            m = arg1.args[1]
3✔
2371
            if m isa Symbol || m isa GlobalRef || is_expr(m, :(.), 2)
3✔
2372
                show_unquoted(io, m)
6✔
2373
            else
2374
                print(io, "(")
×
2375
                show_unquoted(io, m)
×
2376
                print(io, ")")
×
2377
            end
2378
            print(io, '.')
3✔
2379
            if is_expr(arg1.args[2], :quote)
3✔
2380
                mname = (arg1.args[2]::Expr).args[1]
×
2381
            else
2382
                mname = (arg1.args[2]::QuoteNode).value
3✔
2383
            end
2384
            if mname isa Symbol
3✔
2385
                show_sym(io, mname, allow_macroname=true)
3✔
2386
            else
2387
                show_unquoted(io, mname)
×
2388
            end
2389
        else
2390
            show_unquoted(io, arg1)
×
2391
        end
2392

2393
    elseif head === :line && 1 <= nargs <= 2
1,476✔
2394
        show_linenumber(io, args...)
×
2395

2396
    elseif head === :try && 3 <= nargs <= 5
1,476✔
2397
        iob = IOContext(io, beginsym=>false)
5✔
2398
        show_block(iob, "try", args[1], indent, quote_level)
5✔
2399
        if is_expr(args[3], :block)
5✔
2400
            show_block(iob, "catch", args[2] === false ? Any[] : args[2], args[3]::Expr, indent, quote_level)
5✔
2401
        end
2402
        if nargs >= 5 && is_expr(args[5], :block)
5✔
2403
            show_block(iob, "else", Any[], args[5]::Expr, indent, quote_level)
×
2404
        end
2405
        if nargs >= 4 && is_expr(args[4], :block)
5✔
2406
            show_block(iob, "finally", Any[], args[4]::Expr, indent, quote_level)
×
2407
        end
2408
        print(io, "end")
5✔
2409

2410
    elseif head === :block
1,471✔
2411
        # print as (...; ...; ...;) inside indexing expression
2412
        if get(io, beginsym, false)
107✔
2413
            print(io, '(')
×
2414
            ind = indent + indent_width
×
2415
            for i = eachindex(ex.args)
×
2416
                if i > 1
×
2417
                    # if there was only a comment before the first semicolon, the expression would get parsed as a NamedTuple
2418
                    if !(i == 2 && ex.args[1] isa LineNumberNode)
×
2419
                        print(io, ';')
×
2420
                    end
2421
                    print(io, "\n", ' '^ind)
×
2422
                end
2423
                show_unquoted(io, ex.args[i], ind, -1, quote_level)
×
2424
            end
×
2425
            if length(ex.args) < 2
×
2426
                print(io, isempty(ex.args) ? ";;)" : ";)")
×
2427
            else
2428
                print(io, ')')
×
2429
            end
2430
        else
2431
            show_block(io, "begin", ex, indent, quote_level)
57✔
2432
            print(io, "end")
57✔
2433
        end
2434

2435
    elseif head === :quote && nargs == 1 && isa(args[1], Symbol)
1,414✔
2436
        show_unquoted_quote_expr(IOContext(io, beginsym=>false), args[1]::Symbol, indent, 0, quote_level+1)
×
2437
    elseif head === :quote && !(get(io, :unquote_fallback, true)::Bool)
1,419✔
2438
        if nargs == 1 && is_expr(args[1], :block)
83✔
2439
            show_block(IOContext(io, beginsym=>false), "quote", Expr(:quote, (args[1]::Expr).args...), indent,
2✔
2440
                       quote_level+1)
2441
            print(io, "end")
2✔
2442
        elseif nargs == 1
81✔
2443
            print(io, ":(")
81✔
2444
            show_unquoted(IOContext(io, beginsym=>false), args[1], indent+2, 0, quote_level+1)
83✔
2445
            print(io, ")")
81✔
2446
        else
2447
            show_block(IOContext(io, beginsym=>false), "quote", ex, indent, quote_level+1)
×
2448
            print(io, "end")
×
2449
        end
2450

2451
    elseif head === :gotoifnot && nargs == 2 && isa(args[2], Int)
1,331✔
2452
        print(io, "unless ")
×
2453
        show_unquoted(io, args[1], indent, 0, quote_level)
×
2454
        print(io, " goto %")
×
2455
        print(io, args[2]::Int)
×
2456

2457
    elseif head === :string && nargs == 1 && isa(args[1], AbstractString)
1,331✔
2458
        show(io, args[1])
×
2459

2460
    elseif head === :null
1,331✔
2461
        print(io, "nothing")
×
2462

2463
    elseif head === :string
1,331✔
2464
        print(io, '"')
34✔
2465
        for x in args
34✔
2466
            if !isa(x,AbstractString)
105✔
2467
                print(io, "\$(")
46✔
2468
                if isa(x,Symbol) && !(x in quoted_syms)
65✔
2469
                    show_sym(io, x)
19✔
2470
                else
2471
                    show_unquoted(io, x, 0, 0, quote_level)
27✔
2472
                end
2473
                print(io, ")")
46✔
2474
            else
2475
                escape_string(io, String(x)::String, "\"\$")
59✔
2476
            end
2477
        end
105✔
2478
        print(io, '"')
34✔
2479

2480
    elseif (head === :& || head === :$) && nargs == 1
2,594✔
2481
        if head === :$
36✔
2482
            quote_level -= 1
36✔
2483
        end
2484
        if head === :$ && get(io, :unquote_fallback, true)
68✔
2485
            unhandled = true
×
2486
        else
2487
            print(io, head)
36✔
2488
            a1 = args[1]
36✔
2489
            parens = (isa(a1,Expr) && !in(a1.head, (:tuple, :$, :vect, :braces))) ||
50✔
2490
                     (isa(a1,Symbol) && isoperator(a1))
2491
            parens && print(io, "(")
36✔
2492
            show_unquoted(io, a1, 0, 0, quote_level)
49✔
2493
            parens && print(io, ")")
36✔
2494
        end
2495

2496
    # `where` syntax
2497
    elseif head === :where && nargs > 1
1,261✔
2498
        parens = 1 <= prec
41✔
2499
        parens && print(io, "(")
41✔
2500
        show_unquoted(io, args[1], indent, operator_precedence(:(::)), quote_level)
41✔
2501
        print(io, " where ")
41✔
2502
        if nargs == 2
41✔
2503
            show_unquoted(io, args[2], indent, 1, quote_level)
64✔
2504
        else
2505
            print(io, "{")
4✔
2506
            show_list(io, args[2:end], ", ", indent, 0, quote_level)
8✔
2507
            print(io, "}")
4✔
2508
        end
2509
        parens && print(io, ")")
41✔
2510

2511
    elseif (head === :import || head === :using) &&
1,220✔
2512
           ((nargs == 1 && (valid_import_path(args[1]) ||
2513
                           (is_expr(args[1], :(:)) &&
2514
                            length((args[1]::Expr).args) > 1 &&
2515
                            all(valid_import_path, (args[1]::Expr).args)))) ||
2516
             all(valid_import_path, args))
2517
        print(io, head)
9✔
2518
        print(io, ' ')
9✔
2519
        first = true
9✔
2520
        for a in args
9✔
2521
            if !first
9✔
2522
                print(io, ", ")
×
2523
            end
2524
            first = false
9✔
2525
            show_import_path(io, a, quote_level)
9✔
2526
        end
9✔
2527
    elseif head === :as && nargs == 2 && valid_import_path(args[1], false)
1,211✔
2528
        show_import_path(io, args[1], quote_level)
×
2529
        print(io, " as ")
×
2530
        show_unquoted(io, args[2], indent, 0, quote_level)
×
2531
    elseif head === :meta && nargs >= 2 && args[1] === :push_loc
1,211✔
2532
        print(io, "# meta: location ", join(args[2:end], " "))
×
2533
    elseif head === :meta && nargs == 1 && args[1] === :pop_loc
1,211✔
2534
        print(io, "# meta: pop location")
×
2535
    elseif head === :meta && nargs == 2 && args[1] === :pop_loc
1,211✔
2536
        print(io, "# meta: pop locations ($(args[2]::Int))")
×
2537
    # print anything else as "Expr(head, args...)"
2538
    elseif head === :toplevel
1,211✔
2539
        # Reset SOURCE_SLOTNAMES. Raw SlotNumbers are not valid in Expr(:toplevel), but
2540
        # we want to show bad ASTs reasonably to make errors understandable.
2541
        lambda_io = IOContext(io, :SOURCE_SLOTNAMES => false)
×
2542
        show_unquoted_expr_fallback(lambda_io, ex, indent, quote_level)
×
2543
    else
2544
        unhandled = true
1,211✔
2545
    end
2546
    if unhandled
14,791✔
2547
        show_unquoted_expr_fallback(io, ex, indent, quote_level)
1,211✔
2548
    end
2549
    nothing
14,791✔
2550
end
2551

2552
demangle_function_name(name::Symbol) = Symbol(demangle_function_name(string(name)))
191✔
2553
function demangle_function_name(name::AbstractString)
2554
    demangle = split(name, '#')
1,155✔
2555
    # kw sorters and impl methods use the name scheme `f#...`
2556
    if length(demangle) >= 2 && demangle[1] != ""
1,155✔
2557
        return demangle[1]
×
2558
    end
2559
    return name
1,155✔
2560
end
2561

2562
# show the called object in a signature, given its type `ft`
2563
# `io` should contain the UnionAll env of the signature
2564
function show_signature_function(io::IO, @nospecialize(ft), demangle=false, fargname="", html=false, qualified=false)
247✔
2565
    uw = unwrap_unionall(ft)
1,698✔
2566
    if ft <: Function && isa(uw, DataType) && isempty(uw.parameters) && _isself(uw)
455✔
2567
        uwmod = parentmodule(uw)
193✔
2568
        if qualified && !isexported(uwmod, uw.name.singletonname) && uwmod !== Main
193✔
2569
            print_within_stacktrace(io, uwmod, '.', bold=true)
4✔
2570
        end
2571
        s = sprint(show_sym, (demangle ? demangle_function_name : identity)(uw.name.singletonname), context=io)
195✔
2572
        print_within_stacktrace(io, s, bold=true)
240✔
2573
    elseif isType(ft) && (f = ft.parameters[1]; !isa(f, TypeVar))
54✔
2574
        uwf = unwrap_unionall(f)
1✔
2575
        parens = isa(f, UnionAll) && !(isa(uwf, DataType) && f === uwf.name.wrapper)
1✔
2576
        parens && print(io, "(")
1✔
2577
        print_within_stacktrace(io, f, bold=true)
1✔
2578
        parens && print(io, ")")
1✔
2579
    else
2580
        if html
53✔
2581
            print(io, "($fargname::<b>", ft, "</b>)")
×
2582
        else
2583
            print_within_stacktrace(io, "($fargname::", ft, ")", bold=true)
53✔
2584
        end
2585
    end
2586
    nothing
247✔
2587
end
2588

2589
function print_within_stacktrace(io, s...; color=:normal, bold=false)
7,301✔
2590
    if get(io, :backtrace, false)::Bool
5,608✔
2591
        printstyled(io, s...; color, bold)
3,635✔
2592
    else
2593
        print(io, s...)
1,021✔
2594
    end
2595
end
2596

2597
function show_tuple_as_call(out::IO, name::Symbol, sig::Type;
2,569✔
2598
                            demangle=false, kwargs=nothing, argnames=nothing,
2599
                            qualified=false, hasfirst=true)
2600
    # print a method signature tuple for a lambda definition
2601
    if sig === Tuple
1,243✔
2602
        print(out, demangle ? demangle_function_name(name) : name, "(...)")
×
2603
        return
×
2604
    end
2605
    tv = Any[]
1,243✔
2606
    buf = IOBuffer()
1,243✔
2607
    io = IOContext(buf, out)
1,243✔
2608
    env_io = io
1,243✔
2609
    while isa(sig, UnionAll)
1,243✔
2610
        push!(tv, sig.var)
×
2611
        env_io = IOContext(env_io, :unionall_env => sig.var)
×
2612
        sig = sig.body
×
2613
    end
×
2614
    n = 1
1,243✔
2615
    sig = (sig::DataType).parameters
1,243✔
2616
    if hasfirst
1,243✔
2617
        show_signature_function(env_io, sig[1], demangle, "", false, qualified)
1,243✔
2618
        n += 1
1,243✔
2619
    end
2620
    first = true
1,243✔
2621
    print_within_stacktrace(io, "(", bold=true)
1,357✔
2622
    show_argnames = argnames !== nothing && length(argnames) == length(sig)
1,243✔
2623
    for i = n:length(sig)  # fixme (iter): `eachindex` with offset?
1,640✔
2624
        first || print(io, ", ")
4,013✔
2625
        first = false
2,490✔
2626
        if show_argnames
2,490✔
2627
            print_within_stacktrace(io, argnames[i]; color=:light_black)
2,058✔
2628
        end
2629
        print(io, "::")
2,490✔
2630
        print_type_bicolor(env_io, sig[i]; use_color = get(io, :backtrace, false)::Bool)
4,980✔
2631
    end
4,013✔
2632
    if kwargs !== nothing
1,243✔
2633
        print(io, "; ")
154✔
2634
        first = true
154✔
2635
        for (k, t) in kwargs
308✔
2636
            first || print(io, ", ")
198✔
2637
            first = false
176✔
2638
            print_within_stacktrace(io, k; color=:light_black)
176✔
2639
            if t == pairs(NamedTuple)
176✔
2640
                # omit type annotation for splat keyword argument
2641
                print(io, "...")
7✔
2642
            else
2643
                print(io, "::")
169✔
2644
                print_type_bicolor(io, t; use_color = get(io, :backtrace, false)::Bool)
169✔
2645
            end
2646
        end
198✔
2647
    end
2648
    print_within_stacktrace(io, ")", bold=true)
1,357✔
2649
    show_method_params(io, tv)
1,243✔
2650
    str = takestring!(buf)
1,243✔
2651
    str = type_limited_string_from_context(out, str)
1,243✔
2652
    print(out, str)
1,243✔
2653
    nothing
1,243✔
2654
end
2655

2656
function type_limited_string_from_context(out::IO, str::String)
281✔
2657
    typelimitflag = get(out, :stacktrace_types_limited, nothing)
1,074✔
2658
    if typelimitflag isa RefValue{Bool}
278✔
2659
        sz = get(out, :displaysize, Base.displaysize_(out))::Tuple{Int, Int}
34✔
2660
        str_lim = type_depth_limit(str, max(sz[2], 120))
34✔
2661
        if sizeof(str_lim) < sizeof(str)
34✔
2662
            typelimitflag[] = true
8✔
2663
        end
2664
        str = str_lim
34✔
2665
    end
2666
    return str
335✔
2667
end
2668

2669
# limit nesting depth of `{ }` until string textwidth is less than `n`
2670
function type_depth_limit(str::String, n::Int; maxdepth = nothing)
48✔
2671
    depth = 0
7✔
2672
    width_at = Int[]                       # total textwidth at each nesting depth
7✔
2673
    depths = zeros(Int16, lastindex(str))  # depth at each character index
397✔
2674
    levelcount = Int[]                     # number of nodes at each level
7✔
2675
    strwid = 0
7✔
2676
    st_0, st_backslash, st_squote, st_dquote = 0,1,2,4
7✔
2677
    state::Int = st_0
7✔
2678
    stateis(s) = (state & s) != 0
504✔
2679
    quoted() = stateis(st_squote) || stateis(st_dquote)
107✔
2680
    enter(s) = (state |= s)
7✔
2681
    leave(s) = (state &= ~s)
7✔
2682
    for (i, c) in ANSIIterator(str)
7✔
2683
        if c isa ANSIDelimiter
397✔
2684
            depths[i] = depth
×
2685
            continue
×
2686
        end
2687

2688
        if c == '\\' && quoted()
397✔
2689
            enter(st_backslash)
×
2690
        elseif c == '\''
397✔
2691
            if stateis(st_backslash) || stateis(st_dquote)
×
2692
            elseif stateis(st_squote)
×
2693
                leave(st_squote)
×
2694
            else
2695
                enter(st_squote)
×
2696
            end
2697
        elseif c == '"'
397✔
2698
            if stateis(st_backslash) || stateis(st_squote)
×
2699
            elseif stateis(st_dquote)
×
2700
                leave(st_dquote)
×
2701
            else
2702
                enter(st_dquote)
×
2703
            end
2704
        end
2705
        if c == '}' && !quoted()
422✔
2706
            depth -= 1
25✔
2707
        end
2708

2709
        wid = textwidth(c)
397✔
2710
        strwid += wid
397✔
2711
        if depth > 0
397✔
2712
            width_at[depth] += wid
306✔
2713
        end
2714
        depths[i] = depth
397✔
2715

2716
        if c == '{' && !quoted()
422✔
2717
            depth += 1
25✔
2718
            if depth > length(width_at)
25✔
2719
                push!(width_at, 0)
18✔
2720
                push!(levelcount, 0)
18✔
2721
            end
2722
            levelcount[depth] += 1
25✔
2723
        end
2724
        if c != '\\' && stateis(st_backslash)
397✔
2725
            leave(st_backslash)
×
2726
        end
2727
    end
397✔
2728
    if maxdepth === nothing
7✔
2729
        limit_at = length(width_at) + 1
×
2730
        while strwid > n
×
2731
            limit_at -= 1
×
2732
            limit_at <= 1 && break
×
2733
            # add levelcount[] to include space taken by `…`
2734
            strwid = strwid - width_at[limit_at] + levelcount[limit_at]
×
2735
            if limit_at < length(width_at)
×
2736
                # take away the `…` from the previous considered level
2737
                strwid -= levelcount[limit_at+1]
×
2738
            end
2739
        end
×
2740
    else
2741
        limit_at = maxdepth
7✔
2742
    end
2743
    output = IOBuffer()
7✔
2744
    prev = 0
7✔
2745
    for (i, c) in ANSIIterator(str)
7✔
2746
        di = depths[i]
397✔
2747
        if di < limit_at
397✔
2748
            if c isa ANSIDelimiter
286✔
2749
                write(output, c.del)
×
2750
            else
2751
                write(output, c)
286✔
2752
            end
2753
        end
2754
        if di > prev && di == limit_at
404✔
2755
            write(output, "…")
7✔
2756
        end
2757
        prev = di
397✔
2758
    end
397✔
2759
    return unsafe_takestring!(output)
7✔
2760
end
2761

2762
function print_type_bicolor(io, type; kwargs...)
7,619✔
2763
    str = sprint(show, type, context=io)
7,388✔
2764
    print_type_bicolor(io, str; kwargs...)
7,388✔
2765
end
2766

2767
function print_type_bicolor(io, str::String; color=:normal, inner_color=:light_black, use_color::Bool=true)
7,934✔
2768
    i = findfirst('{', str)
480✔
2769
    if !use_color # fix #41928
480✔
2770
        print(io, str)
171✔
2771
    elseif i === nothing
309✔
2772
        printstyled(io, str; color=color)
280✔
2773
    else
2774
        printstyled(io, str[1:prevind(str,i)]; color=color)
58✔
2775
        if endswith(str, "...")
29✔
2776
            printstyled(io, str[i:prevind(str,end,3)]; color=inner_color)
×
2777
            printstyled(io, "..."; color=color)
×
2778
        else
2779
            printstyled(io, str[i:end]; color=inner_color)
29✔
2780
        end
2781
    end
2782
end
2783

2784
resolvebinding(@nospecialize(ex)) = ex
×
2785
resolvebinding(ex::QuoteNode) = ex.value
×
2786
resolvebinding(ex::Symbol) = resolvebinding(GlobalRef(Main, ex))
×
2787
function resolvebinding(ex::Expr)
×
2788
    if ex.head === :. && isa(ex.args[2], Symbol)
×
2789
        parent = resolvebinding(ex.args[1])
×
2790
        if isa(parent, Module)
×
2791
            return resolvebinding(GlobalRef(parent, ex.args[2]))
×
2792
        end
2793
    end
2794
    return nothing
×
2795
end
2796
function resolvebinding(ex::GlobalRef)
×
2797
    isdefined(ex.mod, ex.name) || return nothing
×
2798
    isconst(ex.mod, ex.name) || return nothing
×
2799
    m = getfield(ex.mod, ex.name)
×
2800
    isa(m, Module) || return nothing
×
2801
    return m
×
2802
end
2803

2804
function ismodulecall(ex::Expr)
×
2805
    return ex.head === :call && (ex.args[1] === GlobalRef(Base,:getfield) ||
×
2806
                                ex.args[1] === GlobalRef(Core,:getfield)) &&
2807
           isa(resolvebinding(ex.args[2]), Module)
2808
end
2809

2810
function show(io::IO, tv::TypeVar)
×
2811
    # If we are in the `unionall_env`, the type-variable is bound
2812
    # and the type constraints are already printed.
2813
    # We don't need to print it again.
2814
    # Otherwise, the lower bound should be printed if it is not `Bottom`
2815
    # and the upper bound should be printed if it is not `Any`.
2816
    in_env = (:unionall_env => tv) in io
×
2817
    function show_bound(io::IO, @nospecialize(b))
×
2818
        parens = isa(b,UnionAll) && !print_without_params(b)
×
2819
        parens && print(io, "(")
×
2820
        show(io, b)
×
2821
        parens && print(io, ")")
×
2822
    end
2823
    lb, ub = tv.lb, tv.ub
×
2824
    if !in_env && lb !== Bottom
×
2825
        if ub === Any
×
2826
            show_unquoted(io, tv.name)
×
2827
            print(io, ">:")
×
2828
            show_bound(io, lb)
×
2829
        else
2830
            show_bound(io, lb)
×
2831
            print(io, "<:")
×
2832
            show_unquoted(io, tv.name)
×
2833
        end
2834
    else
2835
        show_unquoted(io, tv.name)
×
2836
    end
2837
    if !in_env && ub !== Any
×
2838
        print(io, "<:")
×
2839
        show_bound(io, ub)
×
2840
    end
2841
    nothing
×
2842
end
2843

2844
function show(io::IO, vm::Core.TypeofVararg)
364✔
2845
    print(io, "Vararg")
364✔
2846
    if isdefined(vm, :T)
364✔
2847
        print(io, "{")
362✔
2848
        show(io, vm.T)
362✔
2849
        if isdefined(vm, :N)
362✔
2850
            print(io, ", ")
10✔
2851
            show(io, vm.N)
10✔
2852
        end
2853
        print(io, "}")
362✔
2854
    end
2855
end
2856

2857
Compiler.load_irshow!()
2858

2859
function show(io::IO, src::CodeInfo; debuginfo::Symbol=:source)
36✔
2860
    # Fix slot names and types in function body
2861
    print(io, "CodeInfo(")
18✔
2862
    lambda_io::IOContext = io
18✔
2863
    if src.slotnames !== nothing
18✔
2864
        lambda_io = IOContext(lambda_io, :SOURCE_SLOTNAMES => sourceinfo_slotnames(src))
18✔
2865
    end
2866
    println(io)
18✔
2867
    # TODO: static parameter values?
2868
    # only accepts :source or :none, we can't have a fallback for default since
2869
    # that would break code_typed(, debuginfo=:source) iff IRShow.default_debuginfo[] = :none
2870
    IRShow.show_ir(lambda_io, src, IRShow.IRShowConfig(IRShow.__debuginfo[debuginfo](src)))
18✔
2871
    print(io, ")")
18✔
2872
end
2873

2874
show_unquoted(io::IO, val::Argument, indent::Int, prec::Int) = show_unquoted(io, Core.SlotNumber(val.n), indent, prec)
497✔
2875

2876
show_unquoted(io::IO, stmt::PhiNode, indent::Int, ::Int) = show_unquoted_phinode(io, stmt, indent, "%")
6✔
2877
function show_unquoted_phinode(io::IO, stmt::PhiNode, indent::Int, prefix::String)
362✔
2878
    args = String[let
362✔
2879
        e = stmt.edges[i]
2,290✔
2880
        v = !isassigned(stmt.values, i) ? "#undef" :
4,580✔
2881
            sprint(; context=io) do io′
2882
                show_unquoted(io′, stmt.values[i], indent)
2883
            end
2884
        "$prefix$e => $v"
4,218✔
2885
        end for i in 1:length(stmt.edges)
2886
    ]
2887
    print(io, "φ ", '(')
362✔
2888
    join(io, args, ", ")
362✔
2889
    print(io, ')')
362✔
2890
end
2891

2892
function show_unquoted(io::IO, stmt::PhiCNode, indent::Int, ::Int)
1✔
2893
    print(io, "φᶜ (")
1✔
2894
    first = true
1✔
2895
    for v in stmt.values
1✔
2896
        first ? (first = false) : print(io, ", ")
3✔
2897
        show_unquoted(io, v, indent)
2✔
2898
    end
2✔
2899
    print(io, ")")
1✔
2900
end
2901

2902
function show_unquoted(io::IO, stmt::PiNode, indent::Int, ::Int)
1✔
2903
    print(io, "π (")
1✔
2904
    show_unquoted(io, stmt.val, indent)
1✔
2905
    print(io, ", ")
1✔
2906
    printstyled(io, stmt.typ, color=:cyan)
1✔
2907
    print(io, ")")
1✔
2908
end
2909

2910
function show_unquoted(io::IO, stmt::UpsilonNode, indent::Int, ::Int)
1✔
2911
    print(io, "ϒ (")
1✔
2912
    isdefined(stmt, :val) ?
1✔
2913
        show_unquoted(io, stmt.val, indent) :
2914
        print(io, "#undef")
2915
    print(io, ")")
1✔
2916
end
2917

2918
function show_unquoted(io::IO, stmt::ReturnNode, indent::Int, ::Int)
299✔
2919
    if !isdefined(stmt, :val)
299✔
2920
        print(io, "unreachable")
31✔
2921
    else
2922
        print(io, "return ")
268✔
2923
        show_unquoted(io, stmt.val, indent)
268✔
2924
    end
2925
end
2926

2927
show_unquoted(io::IO, stmt::GotoIfNot, indent::Int, ::Int) = show_unquoted_gotoifnot(io, stmt, indent, "%")
10✔
2928
function show_unquoted_gotoifnot(io::IO, stmt::GotoIfNot, indent::Int, prefix::String)
465✔
2929
    print(io, "goto ", prefix, stmt.dest, " if not ")
465✔
2930
    show_unquoted(io, stmt.cond, indent)
465✔
2931
end
2932

2933
function dump(io::IOContext, x::SimpleVector, n::Int, indent)
1✔
2934
    if isempty(x)
1✔
2935
        print(io, "empty SimpleVector")
1✔
2936
        return
1✔
2937
    end
2938
    print(io, "SimpleVector")
×
2939
    if n > 0
×
2940
        for i in eachindex(x)
×
2941
            println(io)
×
2942
            print(io, indent, "  ", i, ": ")
×
2943
            if isassigned(x,i)
×
2944
                dump(io, x[i], n - 1, string(indent, "  "))
×
2945
            else
2946
                print(io, undef_ref_str)
×
2947
            end
2948
        end
×
2949
    end
2950
    nothing
×
2951
end
2952

2953
function dump(io::IOContext, @nospecialize(x), n::Int, indent)
19✔
2954
    if x === Union{}
20✔
2955
        show(io, x)
3✔
2956
        return
3✔
2957
    end
2958
    T = typeof(x)
17✔
2959
    if isa(x, Function)
17✔
2960
        print(io, x, " (function of type ", T, ")")
1✔
2961
    else
2962
        print(io, T)
16✔
2963
    end
2964
    nf = nfields(x)
17✔
2965
    if nf > 0
17✔
2966
        if n > 0 && !show_circular(io, x)
28✔
2967
            recur_io = IOContext(io, Pair{Symbol,Any}(:SHOWN_SET, x))
12✔
2968
            for field in 1:nf
12✔
2969
                println(io)
31✔
2970
                fname = string(fieldname(T, field))
62✔
2971
                print(io, indent, "  ", fname, ": ")
31✔
2972
                if isdefined(x,field)
31✔
2973
                    dump(recur_io, getfield(x, field), n - 1, string(indent, "  "))
25✔
2974
                else
2975
                    print(io, undef_ref_str)
6✔
2976
                end
2977
            end
50✔
2978
        end
2979
    elseif !isa(x, Function)
3✔
2980
        print(io, " ")
2✔
2981
        show(io, x)
2✔
2982
    end
2983
    nothing
17✔
2984
end
2985

2986
dump(io::IOContext, x::Module, n::Int, indent) = print(io, "Module ", x)
1✔
2987
dump(io::IOContext, x::String, n::Int, indent) = (print(io, "String "); show(io, x))
2✔
2988
dump(io::IOContext, x::Symbol, n::Int, indent) = print(io, typeof(x), " ", x)
9✔
2989
dump(io::IOContext, x::Union,  n::Int, indent) = print(io, x)
1✔
2990
dump(io::IOContext, x::Ptr,    n::Int, indent) = print(io, x)
1✔
2991

2992
function dump_elts(io::IOContext, x::Array, n::Int, indent, i0, i1)
10✔
2993
    for i in i0:i1
10✔
2994
        print(io, indent, "  ", i, ": ")
24✔
2995
        if !isassigned(x,i)
24✔
2996
            print(io, undef_ref_str)
8✔
2997
        else
2998
            dump(io, x[i], n - 1, string(indent, "  "))
16✔
2999
        end
3000
        i < i1 && println(io)
24✔
3001
    end
24✔
3002
end
3003

3004
function dump(io::IOContext, x::Array, n::Int, indent)
14✔
3005
    print(io, "Array{", eltype(x), "}(", size(x), ")")
14✔
3006
    if eltype(x) <: Number
14✔
3007
        print(io, " ")
×
3008
        show(io, x)
×
3009
    else
3010
        if n > 0 && !isempty(x) && !show_circular(io, x)
29✔
3011
            println(io)
9✔
3012
            recur_io = IOContext(io, :SHOWN_SET => x)
9✔
3013
            lx = length(x)
9✔
3014
            if get(io, :limit, false)::Bool
19✔
3015
                dump_elts(recur_io, x, n, indent, 1, (lx <= 10 ? lx : 5))
2✔
3016
                if lx > 10
2✔
3017
                    println(io)
1✔
3018
                    println(io, indent, "  ...")
1✔
3019
                    dump_elts(recur_io, x, n, indent, lx - 4, lx)
1✔
3020
                end
3021
            else
3022
                dump_elts(recur_io, x, n, indent, 1, lx)
7✔
3023
            end
3024
        end
3025
    end
3026
    nothing
14✔
3027
end
3028

3029
# Types
3030
function dump(io::IOContext, x::DataType, n::Int, indent)
11✔
3031
    # For some reason, tuples are structs
3032
    is_struct = isstructtype(x) && !(x <: Tuple)
13✔
3033
    is_mut = is_struct && ismutabletype(x)
11✔
3034
    is_mut && print(io, "mutable ")
11✔
3035
    is_struct && print(io, "struct ")
11✔
3036
    isprimitivetype(x) && print(io, "primitive type ")
11✔
3037
    isabstracttype(x) && print(io, "abstract type ")
11✔
3038
    print(io, x)
11✔
3039
    if x !== Any
11✔
3040
        print(io, " <: ", supertype(x))
9✔
3041
    end
3042
    if n > 0 && !(x <: Tuple) && !isabstracttype(x)
11✔
3043
        tvar_io::IOContext = io
5✔
3044
        for tparam in x.parameters
8✔
3045
            # approximately recapture the list of tvar parameterization
3046
            # that may be used by the internal fields
3047
            if isa(tparam, TypeVar)
6✔
3048
                tvar_io = IOContext(tvar_io, :unionall_env => tparam)
3✔
3049
            end
3050
        end
9✔
3051
        if x.name === _NAMEDTUPLE_NAME && !(x.parameters[1] isa Tuple)
5✔
3052
            # named tuple type with unknown field names
3053
            return
1✔
3054
        end
3055
        fields = fieldnames(x)
4✔
3056
        fieldtypes = datatype_fieldtypes(x)
4✔
3057
        for idx in eachindex(fields)
6✔
3058
            println(io)
4✔
3059
            print(io, indent, "  ")
4✔
3060
            is_mut && isconst(x, idx) && print(io, "const ")
4✔
3061
            print(io, fields[idx])
8✔
3062
            if isassigned(fieldtypes, idx)
6✔
3063
                print(io, "::")
2✔
3064
                print(tvar_io, fieldtypes[idx])
2✔
3065
            end
3066
        end
6✔
3067
    end
3068
    nothing
10✔
3069
end
3070

3071
const DUMP_DEFAULT_MAXDEPTH = 8
3072

3073
function dump(io::IO, @nospecialize(x); maxdepth=DUMP_DEFAULT_MAXDEPTH)
24✔
3074
    dump(IOContext(io), x, maxdepth, "")
22✔
3075
    println(io)
19✔
3076
end
3077

3078
"""
3079
    dump(x; maxdepth=$DUMP_DEFAULT_MAXDEPTH)
3080

3081
Show every part of the representation of a value.
3082
The depth of the output is truncated at `maxdepth`.
3083

3084
# Examples
3085
```jldoctest
3086
julia> struct MyStruct
3087
           x
3088
           y
3089
       end
3090

3091
julia> x = MyStruct(1, (2,3));
3092

3093
julia> dump(x)
3094
MyStruct
3095
  x: Int64 1
3096
  y: Tuple{Int64, Int64}
3097
    1: Int64 2
3098
    2: Int64 3
3099

3100
julia> dump(x; maxdepth = 1)
3101
MyStruct
3102
  x: Int64 1
3103
  y: Tuple{Int64, Int64}
3104
```
3105
"""
3106
function dump(arg; maxdepth=DUMP_DEFAULT_MAXDEPTH)
4✔
3107
    # this is typically used interactively, so default to being in Main (or current active module)
3108
    mod = get(stdout, :module, active_module())
3✔
3109
    dump(IOContext(stdout::IO, :limit => true, :module => mod), arg; maxdepth=maxdepth)
2✔
3110
end
3111

3112
nocolor(io::IO) = IOContext(io, :color => false)
1,378,992✔
3113
alignment_from_show(io::IO, x::Any) =
71,833✔
3114
    textwidth(sprint(show, x, context=nocolor(io), sizehint=0))
3115

3116
"""
3117
`alignment(io, X)` returns a tuple (left,right) showing how many characters are
3118
needed on either side of an alignment feature such as a decimal point.
3119

3120
# Examples
3121
```jldoctest
3122
julia> Base.alignment(stdout, 42)
3123
(2, 0)
3124

3125
julia> Base.alignment(stdout, 4.23)
3126
(1, 3)
3127

3128
julia> Base.alignment(stdout, 1 + 10im)
3129
(3, 5)
3130
```
3131
"""
3132
alignment(io::IO, x::Any) = (0, alignment_from_show(io, x))
117✔
3133
alignment(io::IO, x::Number) = (alignment_from_show(io, x), 0)
400✔
3134
alignment(io::IO, x::Integer) = (alignment_from_show(io, x), 0)
71,256✔
3135
function alignment(io::IO, x::Real)
393,425✔
3136
    s = sprint(show, x, context=nocolor(io), sizehint=0)
393,425✔
3137
    m = match(r"^(.*?)((?:[\.eEfF].*)?)$", s)
393,425✔
3138
    m === nothing ? (textwidth(s), 0) :
393,425✔
3139
                    (textwidth(m.captures[1]), textwidth(m.captures[2]))
3140
end
3141
function alignment(io::IO, x::Complex)
644,930✔
3142
    s = sprint(show, x, context=nocolor(io), sizehint=0)
644,930✔
3143
    m = match(r"^(.*[^ef][\+\-])(.*)$", s)
644,930✔
3144
    m === nothing ? (textwidth(s), 0) :
644,930✔
3145
                    (textwidth(m.captures[1]), textwidth(m.captures[2]))
3146
end
3147
function alignment(io::IO, x::Rational)
268,804✔
3148
    s = sprint(show, x, context=nocolor(io), sizehint=0)
268,804✔
3149
    m = match(r"^(.*?/)(/.*)$", s)
268,804✔
3150
    m === nothing ? (textwidth(s), 0) :
268,804✔
3151
                    (textwidth(m.captures[1]), textwidth(m.captures[2]))
3152
end
3153

3154
function alignment(io::IO, x::Pair)
32✔
3155
    fullwidth = alignment_from_show(io, x)
32✔
3156
    if !isdelimited(io, x) # i.e. use "=>" for display
32✔
3157
        ctx = IOContext(io, :typeinfo => gettypeinfos(io, x)[1])
28✔
3158
        left = alignment_from_show(ctx, x.first)
28✔
3159
        left += 2 * !isdelimited(ctx, x.first) # for parens around p.first
28✔
3160
        left += !(get(io, :compact, false)::Bool) # spaces are added around "=>"
132✔
3161
        (left+1, fullwidth-left-1) # +1 for the "=" part of "=>"
28✔
3162
    else
3163
        (0, fullwidth) # as for x::Any
4✔
3164
    end
3165
end
3166

3167
const undef_ref_str = "#undef"
3168

3169
show(io::IO, ::UndefInitializer) = print(io, "UndefInitializer()")
27✔
3170

3171
"""
3172
    summary(io::IO, x)
3173
    str = summary(x)
3174

3175
Print to a stream `io`, or return a string `str`, giving a brief description of
3176
a value. By default returns `string(typeof(x))`, e.g. [`Int64`](@ref).
3177

3178
For arrays, returns a string of size and type info,
3179
e.g. `10-element Vector{Int64}` or `9×4×5 Array{Float64, 3}`.
3180

3181
# Examples
3182
```jldoctest
3183
julia> summary(1)
3184
"Int64"
3185

3186
julia> summary(zeros(2))
3187
"2-element Vector{Float64}"
3188
```
3189
"""
3190
summary(io::IO, x) = print(io, typeof(x))
6,893✔
3191
function summary(x)
3,376✔
3192
    io = IOBuffer()
3,638✔
3193
    summary(io, x)
3,638✔
3194
    takestring!(io)
3,638✔
3195
end
3196

3197
## `summary` for AbstractArrays
3198
# sizes such as 0-dimensional, 4-dimensional, 2x3
3199
dims2string(d) = isempty(d) ? "0-dimensional" :
10,499✔
3200
                 length(d) == 1 ? "$(d[1])-element" :
3201
                 join(map(string,d), '×')
3202

3203
inds2string(inds) = join(map(_indsstring,inds), '×')
9✔
3204
_indsstring(i) = string(i)
16✔
3205
_indsstring(i::Union{IdentityUnitRange, Slice}) = string(i.indices)
1✔
3206

3207
# anything array-like gets summarized e.g. 10-element Array{Int64,1}
3208
summary(io::IO, a::AbstractArray) = array_summary(io, a, axes(a))
10,247✔
3209
function array_summary(io::IO, a, inds::Tuple{Vararg{OneTo}})
11✔
3210
    print(io, dims2string(length.(inds)), " ")
10,213✔
3211
    showarg(io, a, true)
10,213✔
3212
end
3213
function array_summary(io::IO, a, inds)
7✔
3214
    print(io, dims2string(length.(inds)), " ")
8✔
3215
    showarg(io, a, true)
8✔
3216
    print(io, " with indices ", inds2string(inds))
8✔
3217
end
3218

3219
## `summary` for Function
3220
summary(io::IO, f::Function) = show(io, MIME"text/plain"(), f)
9✔
3221

3222
"""
3223
    showarg(io::IO, x, toplevel)
3224

3225
Show `x` as if it were an argument to a function. This function is
3226
used by [`summary`](@ref) to display type information in terms of sequences of
3227
function calls on objects. `toplevel` is `true` if this is
3228
the direct call from `summary` and `false` for nested (recursive) calls.
3229

3230
The fallback definition is to print `x` as "::\\\$(typeof(x))",
3231
representing argument `x` in terms of its type. (The double-colon is
3232
omitted if `toplevel=true`.) However, you can
3233
specialize this function for specific types to customize printing.
3234

3235
# Examples
3236

3237
A SubArray created as `view(a, :, 3, 2:5)`, where `a` is a
3238
3-dimensional Float64 array, has type
3239

3240
    SubArray{Float64, 2, Array{Float64, 3}, Tuple{Colon, Int64, UnitRange{Int64}}, false}
3241

3242
The default `show` printing would display this full type.
3243
However, the summary for SubArrays actually prints as
3244

3245
    2×4 view(::Array{Float64, 3}, :, 3, 2:5) with eltype Float64
3246

3247
because of a definition similar to
3248

3249
    function Base.showarg(io::IO, v::SubArray, toplevel)
3250
        print(io, "view(")
3251
        showarg(io, parent(v), false)
3252
        print(io, ", ", join(v.indices, ", "))
3253
        print(io, ')')
3254
        toplevel && print(io, " with eltype ", eltype(v))
3255
    end
3256

3257
Note that we're calling `showarg` recursively for the parent array
3258
type, indicating that any recursed calls are not at the top level.
3259
Printing the parent as `::Array{Float64,3}` is the fallback (non-toplevel)
3260
behavior, because no specialized method for `Array` has been defined.
3261
"""
3262
function showarg(io::IO, T::Type, toplevel)
×
3263
    toplevel || print(io, "::")
×
3264
    print(io, "Type{", T, "}")
×
3265
end
3266
function showarg(io::IO, @nospecialize(x), toplevel)
8✔
3267
    toplevel || print(io, "::")
10,422✔
3268
    print(io, typeof(x))
10,414✔
3269
end
3270
# This method resolves an ambiguity for packages that specialize on eltype
3271
function showarg(io::IO, a::Array{Union{}}, toplevel)
3272
    toplevel || print(io, "::")
×
3273
    print(io, typeof(a))
×
3274
end
3275

3276
# Container specializations
3277
function showarg(io::IO, v::SubArray, toplevel)
16✔
3278
    print(io, "view(")
16✔
3279
    showarg(io, parent(v), false)
16✔
3280
    showindices(io, v.indices...)
16✔
3281
    print(io, ')')
16✔
3282
    toplevel && print(io, " with eltype ", eltype(v))
16✔
3283
    return nothing
16✔
3284
end
3285
showindices(io, ::Slice, inds...) =
8✔
3286
    (print(io, ", :"); showindices(io, inds...))
8✔
3287
showindices(io, ind1, inds...) =
39✔
3288
    (print(io, ", ", ind1); showindices(io, inds...))
39✔
3289
showindices(io) = nothing
24✔
3290

3291
function showarg(io::IO, r::ReshapedArray, toplevel)
7✔
3292
    print(io, "reshape(")
7✔
3293
    showarg(io, parent(r), false)
7✔
3294
    if !isempty(r.dims)
7✔
3295
        print(io, ", ", join(r.dims, ", "))
5✔
3296
    end
3297
    print(io, ')')
7✔
3298
    toplevel && print(io, " with eltype ", eltype(r))
7✔
3299
    return nothing
7✔
3300
end
3301

3302
function showarg(io::IO, r::NonReshapedReinterpretArray{T}, toplevel) where {T}
×
3303
    print(io, "reinterpret(", T, ", ")
×
3304
    showarg(io, parent(r), false)
×
3305
    print(io, ')')
×
3306
end
3307

3308
function showarg(io::IO, r::ReshapedReinterpretArray{T}, toplevel) where {T}
2✔
3309
    print(io, "reinterpret(reshape, ", T, ", ")
2✔
3310
    showarg(io, parent(r), false)
2✔
3311
    print(io, ')')
2✔
3312
    toplevel && print(io, " with eltype ", eltype(r))
2✔
3313
    return nothing
2✔
3314
end
3315

3316
# printing iterators from Base.Iterators
3317

3318
function show(io::IO, e::Iterators.Enumerate)
1✔
3319
    print(io, "enumerate(")
4✔
3320
    show(io, e.itr)
4✔
3321
    print(io, ')')
4✔
3322
end
3323
show(io::IO, z::Iterators.Zip) = show_delim_array(io, z.is, "zip(", ',', ')', false)
5✔
3324

3325
# pretty printing for Iterators.Pairs
3326
function Base.showarg(io::IO, r::Iterators.Pairs{<:Integer, <:Any, <:Any, T}, toplevel) where T<:AbstractArray
1✔
3327
    print(io, "pairs(IndexLinear(), ::", T, ")")
1✔
3328
end
3329

3330
function Base.showarg(io::IO, r::Iterators.Pairs{Symbol, <:Any, <:Any, T}, toplevel) where {T <: NamedTuple}
1✔
3331
    print(io, "pairs(::NamedTuple)")
1✔
3332
end
3333

3334
function Base.showarg(io::IO, r::Iterators.Pairs{<:Any, <:Any, I, D}, toplevel) where {D, I}
×
3335
    print(io, "Iterators.Pairs(::", D, ", ::", I, ")")
×
3336
end
3337

3338
# printing BitArrays
3339

3340
# (following functions not exported - mainly intended for debug)
3341

3342
function print_bit_chunk(io::IO, c::UInt64, l::Integer = 64)
×
3343
    for s = 0:l-1
×
3344
        d = (c >>> s) & 1
×
3345
        print(io, "01"[d + 1])
×
3346
        if (s + 1) & 7 == 0
×
3347
            print(io, " ")
×
3348
        end
3349
    end
×
3350
end
3351

3352
print_bit_chunk(c::UInt64, l::Integer) = print_bit_chunk(stdout, c, l)
×
3353
print_bit_chunk(c::UInt64) = print_bit_chunk(stdout, c)
×
3354

3355
function bitshow(io::IO, B::BitArray)
×
3356
    isempty(B) && return
×
3357
    Bc = B.chunks
×
3358
    for i = 1:length(Bc)-1
×
3359
        print_bit_chunk(io, Bc[i])
×
3360
        print(io, ": ")
×
3361
    end
×
3362
    l = _mod64(length(B)-1) + 1
×
3363
    print_bit_chunk(io, Bc[end], l)
×
3364
end
3365
bitshow(B::BitArray) = bitshow(stdout, B)
×
3366

3367
bitstring(B::BitArray) = sprint(bitshow, B)
×
3368

3369
# printing OpaqueClosure
3370
function show(io::IO, oc::Core.OpaqueClosure)
1✔
3371
    A, R = typeof(oc).parameters
1✔
3372
    show_tuple_as_call(io, Symbol(""), A; hasfirst=false)
1✔
3373
    print(io, "->◌")
1✔
3374
    print(io, "::", R)
1✔
3375
end
3376

3377
function show(io::IO, ::MIME"text/plain", oc::Core.OpaqueClosure{A, R}) where {A, R}
×
3378
    show(io, oc)
×
3379
end
3380

3381
# printing bindings and partitions
3382
function print_partition(io::IO, partition::Core.BindingPartition)
×
3383
    print(io, partition.min_world)
×
3384
    print(io, ":")
×
3385
    max_world = @atomic partition.max_world
×
3386
    if max_world == typemax(UInt)
×
3387
        print(io, '∞')
×
3388
    else
3389
        print(io, max_world)
×
3390
    end
3391
    if (partition.kind & PARTITION_MASK_FLAG) != 0
×
3392
        first = false
×
3393
        print(io, " [")
×
3394
        if (partition.kind & PARTITION_FLAG_EXPORTED) != 0
×
3395
            print(io, "exported")
×
3396
        end
3397
        if (partition.kind & PARTITION_FLAG_DEPRECATED) != 0
×
3398
            first ? (first = false) : print(io, ",")
×
3399
            print(io, "deprecated")
×
3400
        end
3401
        if (partition.kind & PARTITION_FLAG_DEPWARN) != 0
×
3402
            first ? (first = false) : print(io, ",")
×
3403
            print(io, "depwarn")
×
3404
        end
3405
        print(io, "]")
×
3406
    end
3407
    print(io, " - ")
×
3408
    kind = binding_kind(partition)
×
3409
    if kind == PARTITION_KIND_BACKDATED_CONST
×
3410
        print(io, "backdated constant binding to ")
×
3411
        print(io, partition_restriction(partition))
×
3412
    elseif kind == PARTITION_KIND_CONST
×
3413
        print(io, "constant binding to ")
×
3414
        print(io, partition_restriction(partition))
×
3415
    elseif kind == PARTITION_KIND_CONST_IMPORT
×
3416
        print(io, "constant binding (declared with `import`) to ")
×
3417
        print(io, partition_restriction(partition))
×
3418
    elseif kind == PARTITION_KIND_UNDEF_CONST
×
3419
        print(io, "undefined const binding")
×
3420
    elseif kind == PARTITION_KIND_GUARD
×
3421
        print(io, "undefined binding - guard entry")
×
3422
    elseif kind == PARTITION_KIND_FAILED
×
3423
        print(io, "ambiguous binding - guard entry")
×
3424
    elseif kind == PARTITION_KIND_DECLARED
×
3425
        print(io, "weak global binding declared using `global` (implicit type Any)")
×
3426
    elseif kind == PARTITION_KIND_IMPLICIT_GLOBAL
×
3427
        print(io, "implicit `using` resolved to global ")
×
3428
        print(io, partition_restriction(partition).globalref)
×
3429
    elseif kind == PARTITION_KIND_IMPLICIT_CONST
×
3430
        print(io, "implicit `using` resolved to constant ")
×
3431
        print(io, partition_restriction(partition))
×
3432
    elseif kind == PARTITION_KIND_EXPLICIT
×
3433
        print(io, "explicit `using` from ")
×
3434
        print(io, partition_restriction(partition).globalref)
×
3435
    elseif kind == PARTITION_KIND_IMPORTED
×
3436
        print(io, "explicit `import` from ")
×
3437
        print(io, partition_restriction(partition).globalref)
×
3438
    else
3439
        @assert kind == PARTITION_KIND_GLOBAL
×
3440
        print(io, "global variable with type ")
×
3441
        print(io, partition_restriction(partition))
×
3442
    end
3443
end
3444

3445
function show(io::IO, ::MIME"text/plain", partition::Core.BindingPartition)
×
3446
    print(io, "BindingPartition ")
×
3447
    print_partition(io, partition)
×
3448
end
3449

3450
function show(io::IO, ::MIME"text/plain", bnd::Core.Binding)
×
3451
    print(io, "Binding ")
×
3452
    print(io, bnd.globalref)
×
3453
    if !isdefined(bnd, :partitions)
×
3454
        print(io, " - No partitions")
×
3455
    else
3456
        partition = @atomic bnd.partitions
×
3457
        while true
×
3458
            println(io)
×
3459
            print(io, "   ")
×
3460
            print_partition(io, partition)
×
3461
            isdefined(partition, :next) || break
×
3462
            partition = @atomic partition.next
×
3463
        end
×
3464
    end
3465
end
3466

3467
# Special pretty printing for EvalInto/IncludeInto
3468
function show(io::IO, ii::IncludeInto)
×
3469
    if getglobal(ii.m, :include) === ii
×
3470
        print(io, ii.m)
×
3471
        print(io, ".include")
×
3472
    else
3473
        show_default(io, ii)
×
3474
    end
3475
end
3476

3477
function show(io::IO, ei::Core.EvalInto)
×
3478
    if getglobal(ei.m, :eval) === ei
×
3479
        print(io, ei.m)
×
3480
        print(io, ".eval")
×
3481
    else
3482
        show_default(io, ei)
×
3483
    end
3484
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc