Skip to content

Commit

Permalink
Documenting macro-generated code
Browse files Browse the repository at this point in the history
This adds a `@__doc__` macro, not exported currently, for use by macro authors
that want their macros to hook into the docsystem properly.

Usage:

    macro example(f)
        quote
            $(f)() = 0
            @__doc__ $(f)(x) = 1
            $(f)(x, y) = 2
        end |> esc
    end

    "Docs for `g(x)` only."
    @example g

will attach the docstring to the 1-arg method only.

Also improve invalid doc expression error message by displaying the
unexpanded form of the expression instead of its expansion.

Enable docstrings for `at-enum` using the newly added macro.

Fixes #12705.
  • Loading branch information
MichaelHatherly committed Sep 15, 2015
1 parent b8d71b7 commit 0b9ef7d
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 2 deletions.
2 changes: 1 addition & 1 deletion base/Enums.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
43 changes: 42 additions & 1 deletion base/docs/Docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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) &&
Expand All @@ -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

Expand Down
2 changes: 2 additions & 0 deletions base/docs/bootstrap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
48 changes: 48 additions & 0 deletions test/docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down

0 comments on commit 0b9ef7d

Please sign in to comment.