Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions base/docs/Docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,22 @@ function objectdoc(__source__, __module__, str, def, expr, sig = :(Union{}))
@nospecialize str def expr sig
binding = esc(bindingexpr(namify(expr)))
docstr = esc(docexpr(__source__, __module__, lazy_iterpolate(str), metadata(__source__, __module__, expr, false)))
# Note: we want to avoid introducing line number nodes here (issue #24468)
return Expr(:block, esc(def), :($(doc!)($__module__, $binding, $docstr, $(esc(sig)))))
# Store the result of the definition and return it after documenting
docex = :($(doc!)($__module__, $binding, $docstr, $(esc(sig))))
if def === nothing
return Expr(:block, docex)
else
exdef = esc(def)
if isexpr(def, :global, 1) && def.args[1] isa Union{Symbol,GlobalRef}
# Special case: `global x` should return nothing to avoid syntax errors with assigning to a value
val = nothing
else
val = :val
exdef = Expr(:(=), val, exdef)
end
# Note: we want to avoid introducing line number nodes here (issue #24468) for def
return Expr(:block, exdef, docex, val)
end
end

function calldoc(__source__, __module__, str, def::Expr)
Expand Down Expand Up @@ -431,7 +445,9 @@ function moduledoc(__source__, __module__, meta, def, def′::Expr)
end
end

# Shares a single doc, `meta`, between several expressions from the tuple expression `ex`.
# Shares a single doc, `meta`, between several expressions from the tuple expression `ex`
# (but don't actually create the tuple for the result and just return the final one,
# as if this was a C++ comma operator or a block separated by `;` instead of `,`).
function multidoc(__source__, __module__, meta, ex::Expr, define::Bool)
@nospecialize meta
out = Expr(:block)
Expand Down Expand Up @@ -657,7 +673,7 @@ docm(source::LineNumberNode, mod::Module, _, _, x...) = docm(source, mod, x...)
# also part of a :where expression, so it unwraps the :where layers until it reaches the
# "actual" expression
iscallexpr(ex::Expr) = isexpr(ex, :where) ? iscallexpr(ex.args[1]) : isexpr(ex, :call)
iscallexpr(ex) = false
iscallexpr(@nospecialize ex) = false

function docm(source::LineNumberNode, mod::Module, meta, ex, define::Bool = true)
@nospecialize meta ex
Expand Down Expand Up @@ -722,7 +738,7 @@ function _docm(source::LineNumberNode, mod::Module, meta, x, define::Bool = true
# f(::T, ::U) where T where U
#
isexpr(x, FUNC_HEADS) && is_signature((x::Expr).args[1]) ? objectdoc(source, mod, meta, def, x::Expr, signature(x::Expr)) :
isexpr(x, [:function, :macro]) && !isexpr((x::Expr).args[1], :call) ? objectdoc(source, mod, meta, def, x::Expr) :
(isexpr(x, :function) || isexpr(x, :macro)) && !isexpr((x::Expr).args[1], :call) ? objectdoc(source, mod, meta, def, x::Expr) :
iscallexpr(x) ? calldoc(source, mod, meta, x::Expr) :

# Type definitions.
Expand All @@ -742,7 +758,7 @@ function _docm(source::LineNumberNode, mod::Module, meta, x, define::Bool = true
isexpr(x, BINDING_HEADS) && !isexpr((x::Expr).args[1], :call) ? objectdoc(source, mod, meta, def, x::Expr) :

# Quoted macrocall syntax. `:@time` / `:(Base.@time)`.
isquotedmacrocall(x) ? objectdoc(source, mod, meta, def, x) :
isquotedmacrocall(x) ? objectdoc(source, mod, meta, nothing, x) :
# Modules and baremodules.
isexpr(x, :module) ? moduledoc(source, mod, meta, def, x::Expr) :
# Document several expressions with the same docstring. `a, b, c`.
Expand Down
74 changes: 74 additions & 0 deletions test/docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1585,3 +1585,77 @@ This docmacroception has a docstring
@docmacroception()

@test Docs.hasdoc(@__MODULE__, :var"@docmacrofoo")

# Test that @doc returns the value of the documented expression
module DocReturnValue
using Test
# Test function definition returns the function
result = begin
"docstring for f"
function f end
end
@test result === f
# Test with regular function syntax
result2 = begin
"docstring for g"
g(x) = x + 1
end
@test result2 === g
# Test with struct definition
result3 = begin
"docstring for S"
struct S; x; end
end
@test result3 === nothing
# Test with const binding
result4 = begin
"docstring for K"
const K = 42
end
@test result4 === 42
# Test that documenting a global declaration returns nothing to avoid syntax errors
result5 = begin
"docstring for global x"
global x
end
@test result5 === nothing
@test Base.binding_module(DocReturnValue, :x) === DocReturnValue
# Test that assignment returns the RHS
result6 = begin
"docstring for global y"
global y = 4
end
@test result6 === 4
@test y === 4
# Test that assignment returns the RHS
result7 = begin
"docstring for const z"
const z = 5
end
@test result7 === z === 5
# Test module returns module
result8 = begin
"docstring for module A"
module A end
end
@test result8 === A
# Tests without definition effect
function t end
result9 = begin
"docstring for existing value t"
:t
end
@test result9 isa Base.Docs.Binding
macro s end
result10 = begin
"docstring for existing macro s"
:@s
end
@test result10 isa Base.Docs.Binding
function h end
result11 = begin
"docstring for existing function"
h()
end
@test result11 isa Base.Docs.Binding
end