diff --git a/base/Enums.jl b/base/Enums.jl index af241db044192..07ea8a5b610fe 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -74,7 +74,7 @@ macro enum(T,syms...) end blk = quote # enum definition - bitstype 32 $(esc(T)) <: Enum + Base.@__doc__(bitstype 32 $(esc(T)) <: Enum) function Base.convert(::Type{$(esc(typename))}, x::Integer) $(membershiptest(:x, values)) || enum_argument_error($(Expr(:quote, typename)), x) Intrinsics.box($(esc(typename)), convert(Int32, x)) diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index ce82dc069bc92..b488244c0d852 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -496,9 +496,49 @@ end multidoc(meta, objs) = quote $([:(@doc $(esc(meta)) $(esc(obj))) for obj in objs]...) end +doc""" + @__doc__(ex) + +Low-level macro used to mark expressions returned by a macro that should be documented. If +more than one expression is marked then the same docstring is applied to each expression. + + macro example(f) + quote + $(f)() = 0 + @__doc__ $(f)(x) = 1 + $(f)(x, y) = 2 + end |> esc + end + +`@__doc__` has no effect when a macro that uses it is not documented. +""" +:(Base.@__doc__) + +function __doc__!(meta, def::Expr) + if isexpr(def, :block) && length(def.args) == 2 && def.args[1] == symbol("#doc#") + # Convert `Expr(:block, :#doc#, ...)` created by `@__doc__` to an `@doc`. + def.head = :macrocall + def.args = [symbol("@doc"), meta, def.args[end]] + true + else + found = false + for each in def.args + found |= __doc__!(meta, each) + end + found + end +end +__doc__!(meta, def) = false + fexpr(ex) = isexpr(ex, :function, :stagedfunction, :(=)) && isexpr(ex.args[1], :call) function docm(meta, def, define = true) + + err = ( + "invalid doc expression:", def, isexpr(def, :macrocall) ? + "'$(def.args[1])' is not documentable. See 'help?> Base.@__doc__' for details." : "" + ) + def′ = unblock(def) isexpr(def′, :quote) && isexpr(def′.args[1], :macrocall) && @@ -522,7 +562,8 @@ function docm(meta, def, define = true) :global) ? vardoc(meta, def, namify(def′)) : isvar(def′) ? objdoc(meta, def′) : isexpr(def′, :tuple) ? multidoc(meta, def′.args) : - isa(def′, Expr) ? error("invalid doc expression $def′") : + __doc__!(meta, def′) ? esc(def′) : + isa(def′, Expr) ? error(strip(join(err, "\n\n"))) : objdoc(meta, def′) end diff --git a/base/docs/bootstrap.jl b/base/docs/bootstrap.jl index 7084219bc9596..a55d33028d340 100644 --- a/base/docs/bootstrap.jl +++ b/base/docs/bootstrap.jl @@ -4,6 +4,8 @@ macro doc(args...) DocBootstrap._expand_(args...) end +macro __doc__(ex) esc(Expr(:block, symbol("#doc#"), ex)) end + module DocBootstrap type List diff --git a/test/docs.jl b/test/docs.jl index 3566edcb0ae9a..52b0b84ced72a 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -270,6 +270,54 @@ let d1 = @doc(DocsTest.val) @test d1 !== nothing end +# Document specific expressions generated by macro calls. +module MacroGenerated + +import Base.@__doc__ + +macro example_1(f) + quote + $(f)() = 0 + @__doc__ $(f)(x) = x + $(f)(x, y) = x + y + end |> esc +end + +"f" +@example_1 f + +@example_1 _f + +macro example_2(f) + quote + $(f)() = 0 + @__doc__ $(f)(x) = x + @__doc__ $(f)(x, y) = x + y + end |> esc +end + +"g" +@example_2 g + +@example_2 _g + +end + +let funcdoc = meta(MacroGenerated)[MacroGenerated.f] + @test funcdoc.order == [Tuple{Any}] + @test funcdoc.meta[Tuple{Any}] == doc"f" +end + +@test isdefined(MacroGenerated, :_f) + +let funcdoc = meta(MacroGenerated)[MacroGenerated.g] + @test funcdoc.order == [Tuple{Any}, Tuple{Any, Any}] + @test funcdoc.meta[Tuple{Any}] == doc"g" + @test funcdoc.meta[Tuple{Any, Any}] == doc"g" +end + +@test isdefined(MacroGenerated, :_g) + # Issue #12700. @test @doc(DocsTest.@m) == doc"Inner.@m"