Skip to content

Commit

Permalink
Fix documenting parametric functor calls
Browse files Browse the repository at this point in the history
Differentiate signature of constructors, functors and normal function
calls, thus improving upon PR JuliaLang#25626 and fixing JuliaLang#44889.
  • Loading branch information
mgkurtz committed May 19, 2022
1 parent fb672da commit 7ca3806
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 35 deletions.
65 changes: 35 additions & 30 deletions base/docs/Docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -88,38 +88,44 @@ function initmeta(m::Module)
nothing
end

function signature!(tv::Vector{Any}, expr::Expr)
is_macrocall = isexpr(expr, :macrocall)
if is_macrocall || isexpr(expr, :call)
sig = :(Union{Tuple{}})
first_arg = is_macrocall ? 3 : 2 # skip function arguments
for arg in expr.args[first_arg:end]
isexpr(arg, :parameters) && continue
if isexpr(arg, :kw) # optional arg
push!(sig.args, :(Tuple{$((sig.args[end]::Expr).args[2:end]...)}))
end
push!((sig.args[end]::Expr).args, argtype(arg))
end
if isexpr(expr.args[1], :curly) && isempty(tv)
append!(tv, mapany(tvar, (expr.args[1]::Expr).args[2:end]))
function callsignature(args::Vector{Any})
sig = :(Union{Tuple{}})
for arg in args
isexpr(arg, :parameters) && continue
if isexpr(arg, :kw) # optional arg
push!(sig.args, :(Tuple{$((sig.args[end]::Expr).args[2:end]...)}))
end
for i = length(tv):-1:1
push!(sig.args, :(Tuple{$((tv[i]::Expr).args[1])}))
end
for i = length(tv):-1:1
sig = Expr(:where, sig, tv[i])
push!((sig.args[end]::Expr).args, argtype(arg))
end
sig
end

function signature(expr::Expr)
typevars = Expr[]
while isexpr(expr, :where)
append!(typevars, tvar.(expr.args[2:end]))
expr = expr.args[1]
end
if isexpr(expr, :macrocall)
callsignature(expr.args[3:end])
elseif isexpr(expr, :call)
sig = callsignature(expr.args[2:end])
if expr.args[1] isa Expr && expr.args[1].head !== :.
sig = (expr.args[1]::Expr).head === :(::) ?
:(Base.Docs.Functor{<:$((expr.args[1]::Expr).args[end]), <:$sig}) :
:(Base.Docs.ParametricConstructor{$(expr.args[1]), <:$sig})
end
return sig
elseif isexpr(expr, :where)
append!(tv, mapany(tvar, expr.args[2:end]))
return signature!(tv, expr.args[1])
isempty(typevars) ? sig : Expr(:where, sig, typevars...)
elseif isexpr(expr, :quote) && isexpr(expr.args[1], :macrocall)
:(Union{})
else
return signature!(tv, expr.args[1])
signature(expr.args[1])
end
end
signature!(tv::Vector{Any}, @nospecialize(other)) = :(Union{})
signature(expr::Expr) = signature!([], expr)
signature(@nospecialize other) = signature!([], other)
signature(@nospecialize other) = :(Union{})

struct ParametricConstructor{S, T} end
struct Functor{S, T} end

function argtype(expr::Expr)
isexpr(expr, :(::)) && return expr.args[end]
Expand Down Expand Up @@ -297,9 +303,8 @@ function astname(x::Expr, ismacro::Bool)
head = x.head
if head === :.
ismacro ? macroname(x) : x
# Call overloading, e.g. `(a::A)(b) = b` or `function (a::A)(b) b end` should document `A(b)`
elseif (head === :function || head === :(=)) && isexpr(x.args[1], :call) && isexpr((x.args[1]::Expr).args[1], :(::))
return astname(((x.args[1]::Expr).args[1]::Expr).args[end], ismacro)
elseif head === :call && isexpr(x.args[1], :(::))
return astname((x.args[1]::Expr).args[end], ismacro)
else
n = isexpr(x, (:module, :struct)) ? 2 : 1
astname(x.args[n], ismacro)
Expand Down
47 changes: 42 additions & 5 deletions test/docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ t(::AbstractString)
"t-2"
t(::Int, ::Any)
"t-3"
t{S <: Integer}(::S)
t(::S) where {S <: Integer}

# Docstrings to parametric methods after definition using where syntax (#32960):
tw(x::T) where T = nothing
Expand Down Expand Up @@ -357,7 +357,7 @@ let d1 = @doc(DocsTest.t(::Int, ::Any)),
@test docstrings_equal(d1,d2)
end

let d1 = @doc(DocsTest.t{S <: Integer}(::S)),
let d1 = @doc(DocsTest.t(::S) where {S <: Integer}),
d2 = doc"t-3"
@test docstrings_equal(d1,d2)
end
Expand Down Expand Up @@ -655,7 +655,7 @@ end
@doc "This should document @m1... since its the result of expansion" @m2_11993
@test (@doc @m1_11993) !== nothing
let d = (@doc :@m2_11993),
macro_doc = Markdown.parse("`$(curmod_prefix)@m2_11993` is a macro.")
macro_doc = Markdown.parse("`$(curmod_prefix == "Main." ? "" : curmod_prefix)@m2_11993` is a macro.")
@test docstring_startswith(d, doc"""
No documentation found.
Expand Down Expand Up @@ -704,7 +704,7 @@ Base.collect(::Type{EmptyType{T}}) where {T} = "borked"
end

let fd = meta(I12515)[@var(Base.collect)]
@test fd.order[1] == (Union{Tuple{Type{I12515.EmptyType{T}}}, Tuple{T}} where T)
@test fd.order[1] == (Union{Tuple{Type{I12515.EmptyType{T}}}} where T)
end

# PR #12593
Expand Down Expand Up @@ -1457,7 +1457,7 @@ function (f::MyFunc)(x)
return f
end

@test docstrings_equal(@doc(MyFunc(2)),
@test docstrings_equal(@doc((::MyFunc)(2)),
doc"""
Docs for calling `f::MyFunc`.
""")
Expand Down Expand Up @@ -1513,3 +1513,40 @@ struct S41727
end
@test S41727(1) isa S41727
@test string(@repl S41727.x) == "x is 4\n"

module FunctorCalls

struct A{T, U}
x
end

"A()"
A() = nothing

"A{Int, Int}()"
A{Int, Int}() = nothing

"A{S, T}(::S, ::T) where {S, T}"
(A{S, T}(::S, ::T) where {S, T}) = nothing

"(::A)()"
(::A)() = nothing

"(::A{S, T})(::S, ::T) where {S, T}"
((a::A{S, T})(b::S, c::T) where {S, T}) = nothing

"(::A{T, T})() where T"
(::A{T, T})() where T = nothing

#"A(x)"
#eval(:($A() = nothing))

end

debug = true
@test docstrings_equal(@doc(FunctorCalls.A()), doc"A()"; debug)
@test docstrings_equal(@doc(FunctorCalls.A{Int, Int}()), doc"A{Int, Int}()"; debug)
@test docstrings_equal(@doc(FunctorCalls.A{S, T}(::S, ::T) where T where S), doc"A{S, T}(::S, ::T) where {S, T}"; debug)
@test docstrings_equal(@doc((::FunctorCalls.A)()), doc"(::A)()"; debug)
@test docstrings_equal(@doc((::FunctorCalls.A{S, T})(::S, ::T) where {S, T}), doc"(::A{S, T})(::S, ::T) where {S, T}"; debug)
@test docstrings_equal(@doc((a::(FunctorCalls.A::UnionAll){T::TypeVar, T::TypeVar})() where T), Docs.catdoc(doc"(::A)()", doc"(::A{T, T})() where T"); debug)

0 comments on commit 7ca3806

Please sign in to comment.