From a6796b9dd9781d155f74b9ea984cac6e09ea2ea2 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Mon, 27 May 2024 04:27:00 -0400 Subject: [PATCH] make `@doc x` work without REPL loaded (#54499) fix #52141, fix #52986 (cherry picked from commit 6f569c7ba0397825acec58d29f3e5e47774d1bae) --- base/docs/Docs.jl | 45 +++++++++++++++++++++++++++++++++++++++++++++ test/docs.jl | 3 +++ 2 files changed, 48 insertions(+) diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 637a8e1a50bd9..6be96cd8b1799 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -505,6 +505,49 @@ isquotedmacrocall(@nospecialize x) = isbasicdoc(@nospecialize x) = isexpr(x, :.) || isa(x, Union{QuoteNode, Symbol}) is_signature(@nospecialize x) = isexpr(x, :call) || (isexpr(x, :(::), 2) && isexpr(x.args[1], :call)) || isexpr(x, :where) +function _doc(binding::Binding, sig::Type = Union{}) + if defined(binding) + result = getdoc(resolve(binding), sig) + result === nothing || return result + end + # Lookup first match for `binding` and `sig` in all modules of the docsystem. + for mod in modules + dict = meta(mod; autoinit=false) + isnothing(dict) && continue + if haskey(dict, binding) + multidoc = dict[binding] + for msig in multidoc.order + sig <: msig && return multidoc.docs[msig] + end + end + end + return nothing +end + +# Some additional convenience `doc` methods that take objects rather than `Binding`s. +_doc(obj::UnionAll) = _doc(Base.unwrap_unionall(obj)) +_doc(object, sig::Type = Union{}) = _doc(aliasof(object, typeof(object)), sig) +_doc(object, sig...) = _doc(object, Tuple{sig...}) + +function simple_lookup_doc(ex) + if isa(ex, Expr) && ex.head !== :(.) && Base.isoperator(ex.head) + # handle syntactic operators, e.g. +=, ::, .= + ex = ex.head + end + if haskey(keywords, ex) + return keywords[ex] + elseif !isa(ex, Expr) && !isa(ex, Symbol) + return :($(_doc)($(typeof)($(esc(ex))))) + end + binding = esc(bindingexpr(namify(ex))) + if isexpr(ex, :call) || isexpr(ex, :macrocall) || isexpr(ex, :where) + sig = esc(signature(ex)) + :($(_doc)($binding, $sig)) + else + :($(_doc)($binding)) + end +end + function docm(source::LineNumberNode, mod::Module, ex) @nospecialize ex if isexpr(ex, :->) && length(ex.args) > 1 @@ -513,6 +556,8 @@ function docm(source::LineNumberNode, mod::Module, ex) # TODO: this is a shim to continue to allow `@doc` for looking up docstrings REPL = Base.REPL_MODULE_REF[] return invokelatest(REPL.lookup_doc, ex) + else + return simple_lookup_doc(ex) end return nothing end diff --git a/test/docs.jl b/test/docs.jl index 36893b8614170..8e6023b6b385d 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -2,6 +2,9 @@ import Base.Docs: meta, @var, DocStr, parsedoc +# check that @doc can work before REPL is loaded +@test !startswith(read(`$(Base.julia_cmd()) -E '@doc sin'`, String), "nothing") + using Markdown using REPL