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

JuliaLang / julia / #37632

26 Sep 2023 06:44AM UTC coverage: 86.999% (-0.9%) from 87.914%
#37632

push

local

web-flow
inference: make `throw` block deoptimization concrete-eval friendly (#49235)

The deoptimization can sometimes destroy the effects analysis and
disable [semi-]concrete evaluation that is otherwise possible. This is
because the deoptimization was designed with the type domain
profitability in mind (#35982), and hasn't been adequately considering
the effects domain.

This commit makes the deoptimization aware of the effects domain more
and enables the `throw` block deoptimization only when the effects
already known to be ineligible for concrete-evaluation.

In our current effect system, `ALWAYS_FALSE`/`false` means that the
effect can not be refined to `ALWAYS_TRUE`/`true` anymore (unless given
user annotation later). Therefore we can enable the `throw` block
deoptimization without hindering the chance of concrete-evaluation when
any of the following conditions are met:
- `effects.consistent === ALWAYS_FALSE`
- `effects.effect_free === ALWAYS_FALSE`
- `effects.terminates === false`
- `effects.nonoverlayed === false`

Here are some numbers:

| Metric | master | this commit | #35982 reverted (set
`unoptimize_throw_blocks=false`) |

|-------------------------|-----------|-------------|--------------------------------------------|
| Base (seconds) | 15.579300 | 15.206645 | 15.296319 |
| Stdlibs (seconds) | 17.919013 | 17.667094 | 17.738128 |
| Total (seconds) | 33.499279 | 32.874737 | 33.035448 |
| Precompilation (seconds) | 49.967516 | 49.421121 | 49.999998 |
| First time `plot(rand(10,3))` [^1] | `2.476678 seconds (11.74 M
allocations)` | `2.430355 seconds (11.77 M allocations)` | `2.514874
seconds (11.64 M allocations)` |
| First time `solve(prob, QNDF())(5.0)` [^2] | `4.469492 seconds (15.32
M allocations)` | `4.499217 seconds (15.41 M allocations)` | `4.470772
seconds (15.38 M allocations)` |

[^1]: With disabling precompilation of Plots.jl.
[^2]: With disabling precompilation of OrdinaryDiffEq.

These numbers ma... (continued)

7 of 7 new or added lines in 1 file covered. (100.0%)

73407 of 84377 relevant lines covered (87.0%)

11275130.05 hits per line

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

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

3
"""
4
    Docs
5

6
The `Docs` module provides the `@doc` macro which can be used to set and retrieve
7
documentation metadata for Julia objects.
8

9
Please see the manual section on [documentation](@ref man-documentation) for more
10
information.
11
"""
12
module Docs
13

14
@nospecialize # don't specialize on any arguments of the methods declared herein
15

16
"""
17
# Documentation
18

19
Functions, methods and types can be documented by placing a string before the definition:
20

21
    \"\"\"
22
    # The Foo Function
23
    `foo(x)`: Foo the living hell out of `x`.
24
    \"\"\"
25
    foo(x) = ...
26

27
The `@doc` macro can be used directly to both set and retrieve documentation / metadata.
28
The macro has special parsing so that the documented object may occur on the next line:
29

30
    @doc "blah"
31
    function foo() ...
32

33
By default, documentation is written as Markdown, but any object can be used as
34
the first argument.
35

36
## Documenting objects separately from their definitions
37
You can document an object before or after its definition with
38

39
    @doc "foo" function_to_doc
40
    @doc "bar" TypeToDoc
41

42
For macros, the syntax is `@doc "macro doc" :(Module.@macro)` or `@doc "macro doc"
43
:(string_macro"")` for string macros. Without the quote `:()` the expansion of the macro
44
will be documented.
45

46
## Retrieving Documentation
47
You can retrieve docs for functions, macros and other objects as follows:
48

49
    @doc foo
50
    @doc @time
51
    @doc md""
52

53
## Functions & Methods
54
Placing documentation before a method definition (e.g. `function foo() ...` or `foo() = ...`)
55
will cause that specific method to be documented, as opposed to the whole function. Method
56
docs are concatenated together in the order they were defined to provide docs for the
57
function.
58
"""
59
:(Core.@doc)
60

61
include("bindings.jl")
62

63
import .Base.Meta: quot, isexpr
64
import .Base: Callable, with_output_color
65
using .Base: RefValue, mapany
66
import ..CoreDocs: lazy_iterpolate
67

68
export doc
69

70
# Basic API / Storage
71

72
const modules = Module[]
73
const META    = gensym(:meta)
74
const METAType = IdDict{Any,Any}
75

76
function meta(m::Module; autoinit::Bool=true)
28,368✔
77
    if !isdefined(m, META) || getfield(m, META) === nothing
27,932✔
78
        autoinit ? initmeta(m) : return nothing
×
79
    end
80
    return getfield(m, META)::METAType
13,966✔
81
end
82

83
function initmeta(m::Module)
241✔
84
    if !isdefined(m, META) || getfield(m, META) === nothing
448✔
85
        Core.eval(m, :($META = $(METAType())))
34✔
86
        push!(modules, m)
34✔
87
    end
88
    nothing
241✔
89
end
90

91
function signature!(tv::Vector{Any}, expr::Expr)
343✔
92
    is_macrocall = isexpr(expr, :macrocall)
343✔
93
    if is_macrocall || isexpr(expr, :call)
682✔
94
        sig = :(Union{Tuple{}})
171✔
95
        first_arg = is_macrocall ? 3 : 2 # skip function arguments
171✔
96
        for arg in expr.args[first_arg:end]
189✔
97
            isexpr(arg, :parameters) && continue
395✔
98
            if isexpr(arg, :kw) # optional arg
352✔
99
                push!(sig.args, :(Tuple{$((sig.args[end]::Expr).args[2:end]...)}))
18✔
100
            end
101
            push!((sig.args[end]::Expr).args, argtype(arg))
262✔
102
        end
458✔
103
        if isexpr(expr.args[1], :curly) && isempty(tv)
340✔
104
            append!(tv, mapany(tvar, (expr.args[1]::Expr).args[2:end]))
×
105
        end
106
        for i = length(tv):-1:1
178✔
107
            push!(sig.args, :(Tuple{$((tv[i]::Expr).args[1])}))
7✔
108
        end
7✔
109
        for i = length(tv):-1:1
178✔
110
            sig = Expr(:where, sig, tv[i])
7✔
111
        end
7✔
112
        return sig
171✔
113
    elseif isexpr(expr, :where)
172✔
114
        append!(tv, mapany(tvar, expr.args[2:end]))
7✔
115
        return signature!(tv, expr.args[1])
7✔
116
    else
117
        return signature!(tv, expr.args[1])
165✔
118
    end
119
end
120
signature!(tv::Vector{Any}, @nospecialize(other)) = :(Union{})
×
121
signature(expr::Expr) = signature!([], expr)
171✔
122
signature(@nospecialize other) = signature!([], other)
×
123

124
function argtype(expr::Expr)
193✔
125
    isexpr(expr, :(::))  && return expr.args[end]
193✔
126
    isexpr(expr, :(...)) && return :(Vararg{$(argtype(expr.args[1]))})
60✔
127
    if isexpr(expr, :meta) && length(expr.args) == 2
34✔
128
        a1 = expr.args[1]
12✔
129
        if a1 === :nospecialize || a1 === :specialize
12✔
130
            return argtype(expr.args[2])
12✔
131
        end
132
    end
133
    return argtype(expr.args[1])
22✔
134
end
135
argtype(@nospecialize other) = :Any
×
136

137
tvar(x::Expr)   = x
×
138
tvar(s::Symbol) = :($s <: Any)
×
139

140
# Docsystem types.
141
# ================
142

143
"""
144
    Docs.DocStr
145

146
Stores the contents of a single docstring as well as related metadata.
147

148
Both the raw text, `.text`, and the parsed markdown, `.object`, are tracked by this type.
149
Parsing of the raw text is done lazily when a request is made to render the docstring,
150
which helps to reduce total precompiled image size.
151

152
The `.data` fields stores several values related to the docstring, such as: path,
153
linenumber, source code, and fielddocs.
154
"""
155
mutable struct DocStr
156
    text   :: Core.SimpleVector
241✔
157
    object :: Any
158
    data   :: Dict{Symbol, Any}
159
end
160

161
function docstr(binding::Binding, typesig = Union{})
×
162
    @nospecialize typesig
×
163
    for m in modules
×
164
        dict = meta(m; autoinit=false)
×
165
        isnothing(dict) && continue
×
166
        if haskey(dict, binding)
×
167
            docs = dict[binding].docs
×
168
            if haskey(docs, typesig)
×
169
                return docs[typesig]
×
170
            end
171
        end
172
    end
×
173
    error("could not find matching docstring for '$binding :: $typesig'.")
×
174
end
175
docstr(object, data = Dict{Symbol,Any}()) = _docstr(object, data)
241✔
176

177
_docstr(vec::Core.SimpleVector, data::Dict{Symbol,Any}) = DocStr(vec,            nothing, data)
239✔
178
_docstr(str::AbstractString,    data::Dict{Symbol,Any}) = DocStr(Core.svec(str), nothing, data)
×
179
_docstr(object,                 data::Dict{Symbol,Any}) = DocStr(Core.svec(),     object, data)
2✔
180

181
_docstr(doc::DocStr, data::Dict{Symbol,Any}) = (doc.data = merge(data, doc.data); doc)
×
182

183
macro ref(x)
184
    binding = bindingexpr(namify(x))
185
    typesig = signature(x)
186
    return esc(docexpr(__source__, __module__, binding, typesig))
187
end
188

189
docexpr(__source__, __module__, args...) = Expr(:call, docstr, args...)
241✔
190

191
"""
192
    MultiDoc
193

194
Stores a collection of docstrings for related objects, ie. a `Function`/`DataType` and
195
associated `Method` objects.
196

197
Each documented object in a `MultiDoc` is referred to by it's signature which is represented
198
by a `Union` of `Tuple` types. For example, the following `Method` definition
199

200
    f(x, y) = ...
201

202
is stored as `Tuple{Any, Any}` in the `MultiDoc` while
203

204
    f(x::T, y = ?) where {T} = ...
205

206
is stored as `Union{Tuple{T, Any}, Tuple{T}} where T`.
207

208
Note: The `Function`/`DataType` object's signature is always `Union{}`.
209
"""
210
mutable struct MultiDoc
211
    "Ordered (via definition order) vector of object signatures."
212
    order::Vector{Type}
213
    "Documentation for each object. Keys are signatures."
214
    docs::METAType
215

216
    MultiDoc() = new(Type[], METAType())
241✔
217
end
218

219
# Docstring registration.
220
# =======================
221

222
"""
223
    Docs.doc!(__module__, binding, str, sig)
224

225
Adds a new docstring `str` to the docsystem of `__module__` for `binding` and signature `sig`.
226
"""
227
function doc!(__module__::Module, b::Binding, str::DocStr, @nospecialize sig = Union{})
247✔
228
    # Module docstrings are in the module itself
229
    if defined(b)
247✔
230
        obj = resolve(b)
240✔
231
        if isa(obj, Module)
240✔
232
            __module__ = obj
7✔
233
        end
234
    end
235
    initmeta(__module__)
241✔
236
    m = get!(meta(__module__), b, MultiDoc())
262✔
237
    if haskey(m.docs, sig)
241✔
238
        # We allow for docstrings to be updated, but print a warning since it is possible
239
        # that over-writing a docstring *may* have been accidental.  The warning
240
        # is suppressed for symbols in Main (or current active module),
241
        # for interactive use (#23011).
242
        __module__ === Base.active_module() ||
×
243
            @warn "Replacing docs for `$b :: $sig` in module `$(__module__)`"
244
    else
245
        # The ordering of docstrings for each Binding is defined by the order in which they
246
        # are initially added. Replacing a specific docstring does not change it's ordering.
247
        push!(m.order, sig)
241✔
248
    end
249
    m.docs[sig] = str
241✔
250
    str.data[:binding] = b
241✔
251
    str.data[:typesig] = sig
241✔
252
    return b
241✔
253
end
254

255
# Docstring lookup.
256
# =================
257

258
"""
259
    getdoc(obj)
260
    getdoc(obj, sig)
261

262
Return a custom docstring object associated with the object `obj` and, optionally, the tuple
263
type signature `sig`. See `MultiDoc` docs for an explanation of the possible values of `sig`.
264

265
The returned object can either be a markdown object generated by `Markdown.parse` or some
266
other custom type used to display non-markdown formatted documentation.
267

268
A return value of `nothing` can be used to signify to the docsystem that no documentation
269
was found for `obj`, in which case the docsystem will fall back to searching for the
270
`Binding` associated with `obj` instead.
271
"""
272
function getdoc end
273

274
getdoc(@nospecialize(x), @nospecialize(sig)) = getdoc(x)
×
275
getdoc(@nospecialize(x)) = nothing
×
276

277
# Utilities.
278
# ==========
279

280
"""
281
`catdoc(xs...)`: Combine the documentation metadata `xs` into a single meta object.
282
"""
283
catdoc() = nothing
×
284
catdoc(xs...) = vcat(xs...)
×
285

286
const keywords = Dict{Symbol, DocStr}()
287

288
function unblock(@nospecialize ex)
496✔
289
    while isexpr(ex, :var"hygienic-scope")
643✔
290
        isexpr(ex.args[1], :escape) || break
×
291
        ex = ex.args[1].args[1]
×
292
    end
×
293
    isexpr(ex, :block) || return ex
992✔
294
    exs = filter(ex -> !(isa(ex, LineNumberNode) || isexpr(ex, :line)), ex.args)
×
295
    length(exs) == 1 || return ex
×
296
    return unblock(exs[1])
×
297
end
298

299
# peek through ex to figure out what kind of expression it may eventually act like
300
# but ignoring scopes and line numbers
301
function unescape(@nospecialize ex)
×
302
    ex = unblock(ex)
249✔
303
    while isexpr(ex, :escape) || isexpr(ex, :var"hygienic-scope")
641✔
304
       ex = unblock(ex.args[1])
×
305
    end
×
306
    return ex
249✔
307
end
308

309
uncurly(@nospecialize ex) = isexpr(ex, :curly) ? ex.args[1] : ex
×
310

311
namify(@nospecialize x) = astname(x, isexpr(x, :macro))::Union{Symbol,Expr,GlobalRef}
280✔
312

313
function astname(x::Expr, ismacro::Bool)
502✔
314
    head = x.head
502✔
315
    if head === :.
502✔
316
        ismacro ? macroname(x) : x
21✔
317
    elseif head === :call && isexpr(x.args[1], :(::))
647✔
318
        return astname((x.args[1]::Expr).args[end], ismacro)
×
319
    else
320
        n = isexpr(x, (:module, :struct)) ? 2 : 1
481✔
321
        astname(x.args[n], ismacro)
481✔
322
    end
323
end
324
astname(q::QuoteNode, ismacro::Bool) = astname(q.value, ismacro)
10✔
325
astname(s::Symbol, ismacro::Bool)    = ismacro ? macroname(s) : s
259✔
326
astname(@nospecialize(other), ismacro::Bool) = other
×
327

328
macroname(s::Symbol) = Symbol('@', s)
19✔
329
macroname(x::Expr)   = Expr(x.head, x.args[1], macroname(x.args[end].value))
×
330

331
isfield(@nospecialize x) = isexpr(x, :.) &&
44✔
332
    (isa(x.args[1], Symbol) || isfield(x.args[1])) &&
333
    (isa(x.args[2], QuoteNode) || isexpr(x.args[2], :quote))
334

335
# @doc expression builders.
336
# =========================
337

338
"""
339
    Docs.metadata(source, module, expr, ismodule)
340

341
Build a `Dict` expression containing metadata captured from the expression `expr`.
342

343
Fields that may be included in the returned `Dict`:
344

345
- `:path`:       Symbol representing the file where `expr` is defined.
346
- `:linenumber`: Linenumber where `expr` is defined.
347
- `:module`:     Module where the docstring is defined.
348
- `:fields`:     `Dict` of all field docs found in `expr`. Only for concrete types.
349
"""
350
function metadata(__source__, __module__, expr, ismodule)
241✔
351
    args = []
241✔
352
    # Filename and linenumber of the docstring.
353
    __file__ = isa(__source__.file, Symbol) ? String(__source__.file) : ""
241✔
354
    push!(args, Pair(:path, __file__))
241✔
355
    push!(args, Pair(:linenumber, __source__.line))
241✔
356
    # Module in which the docstring is defined.
357
    if ismodule # Module docs go inside the module with name `expr`
241✔
358
        push!(args, :($Pair(:module, $expr)))
6✔
359
    else
360
        push!(args, Pair(:module, __module__))
235✔
361
    end
362
    if isexpr(expr, :struct)
251✔
363
        # Field docs for concrete types.
364
        P = Pair{Symbol,Any}
39✔
365
        fields = P[]
39✔
366
        last_docstr = nothing
×
367
        for each in (expr.args[3]::Expr).args
39✔
368
            eachex = unescape(each)
392✔
369
            if isa(eachex, Symbol) || isexpr(eachex, :(::))
613✔
370
                # a field declaration
371
                if last_docstr !== nothing
110✔
372
                    push!(fields, P(namify(eachex::Union{Symbol,Expr}), last_docstr))
×
373
                    last_docstr = nothing
110✔
374
                end
375
            elseif isexpr(eachex, :function) || isexpr(eachex, :(=))
399✔
376
                break
9✔
377
            elseif isa(eachex, String) || isexpr(eachex, :string) || isexpr(eachex, :call) ||
130✔
378
                (isexpr(eachex, :macrocall) && eachex.args[1] === Symbol("@doc_str"))
379
                # forms that might be doc strings
380
                last_docstr = each
×
381
            end
382
        end
270✔
383
        dict = :($(Dict{Symbol,Any})($([(:($(P)($(quot(f)), $d)))::Expr for (f, d) in fields]...)))
39✔
384
        push!(args, :($(Pair)(:fields, $dict)))
39✔
385
    end
386
    return :($(Dict{Symbol,Any})($(args...)))
241✔
387
end
388

389
function keyworddoc(__source__, __module__, str, def::Base.BaseDocs.Keyword)
×
390
    @nospecialize str
×
391
    docstr = esc(docexpr(__source__, __module__, lazy_iterpolate(str), metadata(__source__, __module__, def, false)))
×
392
    return :($setindex!($(keywords), $docstr, $(esc(quot(def.name)))); nothing)
×
393
end
394

395
function objectdoc(__source__, __module__, str, def, expr, sig = :(Union{}))
303✔
396
    @nospecialize str def expr sig
×
397
    binding = esc(bindingexpr(namify(expr)))
537✔
398
    docstr  = esc(docexpr(__source__, __module__, lazy_iterpolate(str), metadata(__source__, __module__, expr, false)))
237✔
399
    # Note: we want to avoid introducing line number nodes here (issue #24468)
400
    return Expr(:block, esc(def), :($(doc!)($__module__, $binding, $docstr, $(esc(sig)))))
235✔
401
end
402

403
function calldoc(__source__, __module__, str, def::Expr)
2✔
404
    @nospecialize str
×
405
    args = callargs(def)
2✔
406
    if isempty(args) || all(validcall, args)
4✔
407
        objectdoc(__source__, __module__, str, nothing, def, signature(def))
2✔
408
    else
409
        docerror(def)
×
410
    end
411
end
412
callargs(ex::Expr) = isexpr(ex, :where) ? callargs(ex.args[1]) :
2✔
413
    isexpr(ex, :call) ? ex.args[2:end] : error("Invalid expression to callargs: $ex")
414
validcall(x) = isa(x, Symbol) || isexpr(x, (:(::), :..., :kw, :parameters))
1✔
415

416
function moduledoc(__source__, __module__, meta, def, def′::Expr)
6✔
417
    @nospecialize meta def
×
418
    name  = namify(def′)
6✔
419
    docex = Expr(:call, doc!, name, bindingexpr(name),
12✔
420
        docexpr(__source__, name, lazy_iterpolate(meta), metadata(__source__, __module__, name, true)))
421
    if def === nothing
6✔
422
        esc(:(Core.eval($name, $(quot(docex)))))
×
423
    else
424
        def = unblock(def)
6✔
425
        block = def.args[3].args
6✔
426
        if !def.args[1]
6✔
427
            pushfirst!(block, :(import Base: @doc))
×
428
        end
429
        push!(block, docex)
6✔
430
        esc(Expr(:toplevel, def))
6✔
431
    end
432
end
433

434
# Shares a single doc, `meta`, between several expressions from the tuple expression `ex`.
435
function multidoc(__source__, __module__, meta, ex::Expr, define::Bool)
×
436
    @nospecialize meta
×
437
    out = Expr(:block)
×
438
    str = docexpr(__source__, __module__, lazy_iterpolate(meta), metadata(__source__, __module__, ex, false))
×
439
    ref = RefValue{DocStr}()
×
440
    first = true
×
441
    for arg in ex.args
×
442
        # The first `arg` to be documented needs to also create the docstring for the group
443
        # (after doing the action defined by the argument).
444
        # Subsequent `arg`s just need `ref` to be able to find the docstring without having
445
        # to create an entirely new one each.
446
        if first
×
447
            first = false
×
448
            docstr = :($getindex($setindex!($(ref), $str)))
×
449
        else
450
            docstr = :($getindex($(ref)))
×
451
        end
452
        push!(out.args, docm(__source__, __module__, docstr, arg, define))
×
453
    end
×
454
    return out
×
455
end
456

457
"""
458
    @__doc__(ex)
459

460
Low-level macro used to mark expressions returned by a macro that should be documented. If
461
more than one expression is marked then the same docstring is applied to each expression.
462

463
    macro example(f)
464
        quote
465
            \$(f)() = 0
466
            @__doc__ \$(f)(x) = 1
467
            \$(f)(x, y) = 2
468
        end |> esc
469
    end
470

471
`@__doc__` has no effect when a macro that uses it is not documented.
472
"""
473
:(Core.@__doc__)
474

475
function __doc__!(source, mod, meta, def, define::Bool)
×
476
    @nospecialize source mod meta def
×
477
    # Two cases must be handled here to avoid redefining all definitions contained in `def`:
478
    if define
4✔
479
        # `def` has not been defined yet (this is the common case, i.e. when not generating
480
        # the Base image). We just need to convert each `@__doc__` marker to an `@doc`.
481
        finddoc(def) do each
8✔
482
            each.head = :macrocall
483
            each.args = Any[Symbol("@doc"), source, mod, nothing, meta, each.args[end], define]
484
        end
485
    else
486
        # `def` has already been defined during Base image gen so we just need to find and
487
        # document any subexpressions marked with `@__doc__`.
488
        docs  = []
×
489
        found = finddoc(def) do each
×
490
            push!(docs, :(@doc($source, $mod, $meta, $(each.args[end]), $define)))
491
        end
492
        # If any subexpressions have been documented then replace the entire expression with
493
        # just those documented subexpressions to avoid redefining any definitions.
494
        if found
×
495
            def.head = :toplevel
×
496
            def.args = docs
×
497
        end
498
        found
×
499
    end
500
end
501
# Walk expression tree `def` and call `λ` when any `@__doc__` markers are found. Returns
502
# `true` to signify that at least one `@__doc__` has been found, and `false` otherwise.
503
function finddoc(λ, def::Expr)
×
504
    if isexpr(def, :block, 2) && isexpr(def.args[1], :meta, 1) && (def.args[1]::Expr).args[1] === :doc
×
505
        # Found the macroexpansion of an `@__doc__` expression.
506
        λ(def)
×
507
        true
×
508
    else
509
        found = false
×
510
        for each in def.args
×
511
            found |= finddoc(λ, each)
×
512
        end
×
513
        found
×
514
    end
515
end
516
finddoc(λ, @nospecialize def) = false
×
517

518
# Predicates and helpers for `docm` expression selection:
519

520
const FUNC_HEADS    = [:function, :macro, :(=)]
521
const BINDING_HEADS = [:const, :global, :(=)]
522
# For the special `:@mac` / `:(Base.@mac)` syntax for documenting a macro after definition.
523
isquotedmacrocall(@nospecialize x) =
20✔
524
    isexpr(x, :copyast, 1) &&
525
    isa(x.args[1], QuoteNode) &&
526
    isexpr(x.args[1].value, :macrocall, 2)
527
# Simple expressions / atoms the may be documented.
528
isbasicdoc(@nospecialize x) = isexpr(x, :.) || isa(x, Union{QuoteNode, Symbol})
8✔
529
is_signature(@nospecialize x) = isexpr(x, :call) || (isexpr(x, :(::), 2) && isexpr(x.args[1], :call)) || isexpr(x, :where)
184✔
530

531
function docm(source::LineNumberNode, mod::Module, ex)
48✔
532
    @nospecialize ex
×
533
    if isexpr(ex, :->) && length(ex.args) > 1
72✔
534
        return docm(source, mod, ex.args...)
×
535
    elseif isassigned(Base.REPL_MODULE_REF)
48✔
536
        # TODO: this is a shim to continue to allow `@doc` for looking up docstrings
537
        REPL = Base.REPL_MODULE_REF[]
48✔
538
        return invokelatest(REPL.lookup_doc, ex)
48✔
539
    end
540
    return nothing
×
541
end
542
# Drop incorrect line numbers produced by nested macro calls.
543
docm(source::LineNumberNode, mod::Module, _, _, x...) = docm(source, mod, x...)
×
544

545
# iscallexpr checks if an expression is a :call expression. The call expression may be
546
# also part of a :where expression, so it unwraps the :where layers until it reaches the
547
# "actual" expression
548
iscallexpr(ex::Expr) = isexpr(ex, :where) ? iscallexpr(ex.args[1]) : isexpr(ex, :call)
70✔
549
iscallexpr(ex) = false
×
550

551
function docm(source::LineNumberNode, mod::Module, meta, ex, define::Bool = true)
482✔
552
    @nospecialize meta ex
×
553
    # Some documented expressions may be decorated with macro calls which obscure the actual
554
    # expression. Expand the macro calls and remove extra blocks.
555
    x = unblock(macroexpand(mod, ex))
482✔
556
    # Don't try to redefine expressions. This is only needed for `Base` img gen since
557
    # otherwise calling `loaddocs` would redefine all documented functions and types.
558
    def = define ? x : nothing
241✔
559
    if isa(x, GlobalRef) && (x::GlobalRef).mod == mod
241✔
560
        x = (x::GlobalRef).name
×
561
    end
562

563
    # Keywords using the `@kw_str` macro in `base/docs/basedocs.jl`.
564
    #
565
    #   "..."
566
    #   kw"if", kw"else"
567
    #
568
    doc =
482✔
569
    isa(x, Base.BaseDocs.Keyword) ? keyworddoc(source, mod, meta, x) :
570

571
    # Method / macro definitions and "call" syntax.
572
    #
573
    #   function f(...) ... end
574
    #   f(...) = ...
575
    #   macro m(...) end
576
    #   function f end
577
    #   f(...)
578
    #
579
    # Including if the "call" expression is wrapped in "where" expression(s) (#32960), i.e.
580
    #
581
    #   f(::T) where T
582
    #   f(::T, ::U) where T where U
583
    #
584
    isexpr(x, FUNC_HEADS) && is_signature((x::Expr).args[1]) ? objectdoc(source, mod, meta, def, x::Expr, signature(x::Expr)) :
585
    isexpr(x, [:function, :macro])  && !isexpr((x::Expr).args[1], :call) ? objectdoc(source, mod, meta, def, x::Expr) :
586
    iscallexpr(x) ? calldoc(source, mod, meta, x::Expr) :
587

588
    # Type definitions.
589
    #
590
    #   struct T ... end
591
    #   abstract type T end
592
    #   primitive type T N end
593
    #
594
    isexpr(x, [:struct, :abstract, :primitive]) ? objectdoc(source, mod, meta, def, x::Expr) :
595

596
    # "Bindings". Names that resolve to objects with different names, ie.
597
    #
598
    #   const T = S
599
    #   T = S
600
    #   global T = S
601
    #
602
    isexpr(x, BINDING_HEADS) && !isexpr((x::Expr).args[1], :call) ? objectdoc(source, mod, meta, def, x::Expr) :
603

604
    # Quoted macrocall syntax. `:@time` / `:(Base.@time)`.
605
    isquotedmacrocall(x) ? objectdoc(source, mod, meta, def, x) :
606
    # Modules and baremodules.
607
    isexpr(x, :module) ? moduledoc(source, mod, meta, def, x::Expr) :
608
    # Document several expressions with the same docstring. `a, b, c`.
609
    isexpr(x, :tuple) ? multidoc(source, mod, meta, x::Expr, define) :
610
    # Errors generated by calling `macroexpand` are passed back to the call site.
611
    isexpr(x, :error) ? esc(x) :
612
    # When documenting macro-generated code we look for embedded `@__doc__` calls.
613
    __doc__!(source, mod, meta, x, define) ? esc(x) :
614
    # Any "basic" expression such as a bare function or module name or numeric literal.
615
    isbasicdoc(x) ? objectdoc(source, mod, meta, nothing, x) :
616

617
    # All other expressions are undocumentable and should be handled on a case-by-case basis
618
    # with `@__doc__`. Unbound string literals are also undocumentable since they cannot be
619
    # retrieved from the module's metadata `IdDict` without a reference to the string.
620
    docerror(ex)
621

622
    return doc
241✔
623
end
624

625
function docerror(@nospecialize ex)
×
626
    txt = """
×
627
    cannot document the following expression:
628

629
    $(isa(ex, AbstractString) ? repr(ex) : ex)"""
630
    if isexpr(ex, :macrocall)
×
631
        txt *= "\n\n'$(ex.args[1])' not documentable. See 'Base.@__doc__' docs for details."
×
632
    end
633
    return :($(error)($txt, "\n"))
×
634
end
635

636
include("utils.jl")
637

638
# Swap out the bootstrap macro with the real one.
639
Core.atdoc!(docm)
640

641
function loaddocs(docs::Vector{Core.SimpleVector})
×
642
    for (mod, ex, str, file, line) in docs
×
643
        data = Dict{Symbol,Any}(:path => string(file), :linenumber => line)
×
644
        doc = docstr(str, data)
×
645
        lno = LineNumberNode(line, file)
×
646
        docstring = docm(lno, mod, doc, ex, false) # expand the real @doc macro now
×
647
        Core.eval(mod, Expr(:var"hygienic-scope", docstring, Docs, lno))
×
648
    end
×
649
    empty!(docs)
×
650
    nothing
×
651
end
652

653
function formatdoc end
654
function parsedoc end
655
function apropos end
656
function doc end
657

658
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

© 2025 Coveralls, Inc