Skip to content

Commit

Permalink
Merge pull request #119 from Kotlin/0.9.10/GH-115
Browse files Browse the repository at this point in the history
Fix for #115  `@param` and `@return` tags is missing in javadoc output
  • Loading branch information
semoro authored Nov 29, 2016
2 parents 4b2c12c + f3dd7eb commit edda34b
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 25 deletions.
4 changes: 3 additions & 1 deletion core/src/main/kotlin/Model/Content.kt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ class ContentSection(val tag: String, val subjectName: String?) : ContentBlock()
object ContentTags {
val Description = "Description"
val SeeAlso = "See Also"
val Return = "Return"
val Exceptions = "Exceptions"
}

fun content(body: ContentBlock.() -> Unit): ContentBlock {
Expand Down Expand Up @@ -236,6 +238,6 @@ open class MutableContent() : Content() {
fun javadocSectionDisplayName(sectionName: String?): String? =
when(sectionName) {
"param" -> "Parameters"
"throws", "exception" -> "Exceptions"
"throws", "exception" -> ContentTags.Exceptions
else -> sectionName?.capitalize()
}
50 changes: 34 additions & 16 deletions core/src/main/kotlin/javadoc/docbase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,10 @@ fun classOf(fqName: String, kind: NodeKind = NodeKind.Class) = DocumentationNode
node
}

private fun DocumentationNode.hasNonEmptyContent() =
this.content.summary !is ContentEmpty || this.content.description !is ContentEmpty || this.content.sections.isNotEmpty()


open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), ProgramElementDoc by ProgramElementAdapter(module, node), ExecutableMemberDoc {

override fun isSynthetic(): Boolean = false
Expand All @@ -305,20 +309,16 @@ open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: Documentatio
override fun thrownExceptions(): Array<out ClassDoc> = emptyArray() // TODO
override fun throwsTags(): Array<out ThrowsTag> =
node.content.sections
.filter { it.tag == "Exceptions" }
.map { it.subjectName }
.filterNotNull()
.map { ThrowsTagAdapter(this, ClassDocumentationNodeAdapter(module, classOf(it, NodeKind.Exception))) }
.filter { it.tag == ContentTags.Exceptions && it.subjectName != null }
.map { ThrowsTagAdapter(this, ClassDocumentationNodeAdapter(module, classOf(it.subjectName!!, NodeKind.Exception)), it.children) }
.toTypedArray()

override fun isVarArgs(): Boolean = node.details(NodeKind.Parameter).any { false } // TODO

override fun isSynchronized(): Boolean = node.annotations.any { it.name == "synchronized" }

override fun paramTags(): Array<out ParamTag> = node.details(NodeKind.Parameter)
.filter { it.content.summary !is ContentEmpty || it.content.description !is ContentEmpty || it.content.sections.isNotEmpty() }
.map { ParamTagAdapter(module, this, it.name, false, it.content.children) }
.toTypedArray()
override fun paramTags(): Array<out ParamTag> =
collectParamTags(NodeKind.Parameter, sectionFilter = { it.subjectName in parameters().map { it.name() } })

override fun thrownExceptionTypes(): Array<out Type> = emptyArray()
override fun receiverType(): Type? = receiverNode()?.let { receiver -> TypeAdapter(module, receiver) }
Expand All @@ -332,9 +332,8 @@ open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: Documentatio

override fun typeParameters(): Array<out TypeVariable> = node.details(NodeKind.TypeParameter).map { TypeVariableAdapter(module, it) }.toTypedArray()

override fun typeParamTags(): Array<out ParamTag> = node.details(NodeKind.TypeParameter).filter { it.content.summary !is ContentEmpty || it.content.description !is ContentEmpty || it.content.sections.isNotEmpty() }.map {
ParamTagAdapter(module, this, it.name, true, it.content.children)
}.toTypedArray()
override fun typeParamTags(): Array<out ParamTag> =
collectParamTags(NodeKind.TypeParameter, sectionFilter = { it.subjectName in typeParameters().map { it.simpleTypeName() } })

private fun receiverNode() = node.details(NodeKind.Receiver).let { receivers ->
when {
Expand Down Expand Up @@ -365,6 +364,17 @@ class MethodAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : Docume
override fun isDefault(): Boolean = false

override fun returnType(): Type = TypeAdapter(module, node.detail(NodeKind.Type))

override fun tags(tagname: String?) = super.tags(tagname)

override fun tags(): Array<out Tag> {
val tags = super.tags().toMutableList()
node.content.findSectionByTag(ContentTags.Return)?.let {
tags += ReturnTagAdapter(module, this, it.children)
}

return tags.toTypedArray()
}
}

class FieldAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), ProgramElementDoc by ProgramElementAdapter(module, node), FieldDoc {
Expand Down Expand Up @@ -416,11 +426,8 @@ open class ClassDocumentationNodeAdapter(module: ModuleNodeAdapter, val classNod
.map { ClassDocumentationNodeAdapter(module, it) }
.toTypedArray()

override fun typeParamTags(): Array<out ParamTag> = (classNode.details(NodeKind.TypeParameter).filter { it.content.summary !is ContentEmpty || it.content.description !is ContentEmpty || it.content.sections.isNotEmpty() }.map {
ParamTagAdapter(module, this, it.name, true, it.content.children)
} + classNode.content.sections.filter { it.subjectName in typeParameters().map { it.simpleTypeName() } }.map {
ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children)
}).toTypedArray()
override fun typeParamTags(): Array<out ParamTag> =
collectParamTags(NodeKind.TypeParameter, sectionFilter = { it.subjectName in typeParameters().map { it.simpleTypeName() } })

override fun fields(): Array<out FieldDoc> = fields(true)
override fun fields(filter: Boolean): Array<out FieldDoc> = classNode.members(NodeKind.Field).map { FieldAdapter(module, it) }.toTypedArray()
Expand Down Expand Up @@ -503,3 +510,14 @@ class ModuleNodeAdapter(val module: DocumentationModule, val reporter: DocErrorR

override fun specifiedClasses(): Array<out ClassDoc> = classes()
}

private fun DocumentationNodeAdapter.collectParamTags(kind: NodeKind, sectionFilter: (ContentSection) -> Boolean) =
(node.details(kind)
.filter(DocumentationNode::hasNonEmptyContent)
.map { ParamTagAdapter(module, this, it.name, true, it.content.children) }

+ node.content.sections
.filter(sectionFilter)
.map { ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children) })

.toTypedArray()
29 changes: 21 additions & 8 deletions core/src/main/kotlin/javadoc/tags.kt
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ class ParamTagAdapter(val module: ModuleNodeAdapter,
override fun holder(): Doc = holder
override fun position(): SourcePosition? = holder.position()

override fun text(): String = "@param $parameterName ..."
override fun inlineTags(): Array<out Tag> = content.flatMap { buildInlineTags(module, holder, it) }.toTypedArray()
override fun text(): String = "@param $parameterName ${parameterComment()}" // Seems has no effect, so used for debug
override fun inlineTags(): Array<out Tag> = buildInlineTags(module, holder, content).toTypedArray()
override fun firstSentenceTags(): Array<out Tag> = arrayOf(TextTag(holder, ContentText(text())))

override fun isTypeParameter(): Boolean = typeParameter
Expand All @@ -114,23 +114,36 @@ class ParamTagAdapter(val module: ModuleNodeAdapter,
}


class ThrowsTagAdapter(val holder: Doc, val type: ClassDocumentationNodeAdapter) : ThrowsTag {
class ThrowsTagAdapter(val holder: Doc, val type: ClassDocumentationNodeAdapter, val content: List<ContentNode>) : ThrowsTag {
override fun name(): String = "@throws"
override fun kind(): String = name()
override fun holder(): Doc = holder
override fun position(): SourcePosition? = holder.position()

override fun text(): String = "@throws ${type.qualifiedTypeName()}"
override fun inlineTags(): Array<out Tag> = emptyArray()
override fun text(): String = "${name()} ${exceptionName()} ${exceptionComment()}"
override fun inlineTags(): Array<out Tag> = buildInlineTags(type.module, holder, content).toTypedArray()
override fun firstSentenceTags(): Array<out Tag> = emptyArray()

override fun exceptionComment(): String = ""
override fun exceptionComment(): String = content.toString()
override fun exceptionType(): Type = type
override fun exception(): ClassDoc = type
override fun exceptionName(): String = type.qualifiedName()
override fun exceptionName(): String = type.qualifiedTypeName()
}

fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, root: ContentNode): List<Tag> = ArrayList<Tag>().let { buildInlineTags(module, holder, root, it); it }
class ReturnTagAdapter(val module: ModuleNodeAdapter, val holder: Doc, val content: List<ContentNode>) : Tag {
override fun name(): String = "@return"
override fun kind() = name()
override fun holder() = holder
override fun position(): SourcePosition? = holder.position()

override fun text(): String = "@return $content" // Seems has no effect, so used for debug
override fun inlineTags(): Array<Tag> = buildInlineTags(module, holder, content).toTypedArray()
override fun firstSentenceTags(): Array<Tag> = inlineTags()
}

fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, tags: List<ContentNode>): List<Tag> = ArrayList<Tag>().apply { tags.forEach { buildInlineTags(module, holder, it, this) } }

fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, root: ContentNode): List<Tag> = ArrayList<Tag>().apply { buildInlineTags(module, holder, root, this) }

private fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, nodes: List<ContentNode>, result: MutableList<Tag>) {
nodes.forEach {
Expand Down
8 changes: 8 additions & 0 deletions core/src/test/kotlin/javadoc/JavadocTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ class JavadocTest {
}
}

@Test fun testKDocKeywordsOnMethod() {
verifyJavadoc("testdata/javadoc/kdocKeywordsOnMethod.kt", withKotlinRuntime = true) { doc ->
val method = doc.classNamed("KdocKeywordsOnMethodKt")!!.methods()[0]
assertEquals("@return [ContentText(text=value of a)]", method.tags("return").first().text())
assertEquals("@param a [ContentText(text=Some string)]", method.paramTags().first().text())
assertEquals("@throws FireException [ContentText(text=in case of fire)]", method.throwsTags().first().text())
}
}

private fun verifyJavadoc(name: String,
withJdk: Boolean = false,
Expand Down
12 changes: 12 additions & 0 deletions core/testdata/javadoc/kdocKeywordsOnMethod.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class FireException : Exception


/**
* COMM
* @param a Some string
* @return value of a
* @throws FireException in case of fire
*/
@Throws(FireException::class)
fun my(a: String): String = a

0 comments on commit edda34b

Please sign in to comment.