render annotations with @; render only @MustBeDocumented annotations; render all modifiers
diff --git a/.idea/runConfigurations/All_tests.xml b/.idea/runConfigurations/All_tests.xml
index b0528c8..57e4134 100644
--- a/.idea/runConfigurations/All_tests.xml
+++ b/.idea/runConfigurations/All_tests.xml
@@ -8,7 +8,7 @@
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
- <option name="VM_PARAMETERS" value="" />
+ <option name="VM_PARAMETERS" value="-Xmx256m" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="ENV_VARIABLES" />
diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt
index bf33693..a02717f 100644
--- a/src/Kotlin/DocumentationBuilder.kt
+++ b/src/Kotlin/DocumentationBuilder.kt
@@ -15,12 +15,15 @@
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import org.jetbrains.kotlin.lexer.JetSingleValueToken
+import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.JetModifierListOwner
import org.jetbrains.kotlin.psi.JetParameter
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.constants.TypedCompileTimeConstant
+import org.jetbrains.kotlin.resolve.descriptorUtil.isDocumentedAnnotation
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
import org.jetbrains.kotlin.resolve.source.getPsi
@@ -50,6 +53,10 @@
val boringBuiltinClasses = setOf(
"kotlin.Unit", "kotlin.Byte", "kotlin.Short", "kotlin.Int", "kotlin.Long", "kotlin.Char", "kotlin.Boolean",
"kotlin.Float", "kotlin.Double", "kotlin.String", "kotlin.Array", "kotlin.Any")
+ val knownModifiers = setOf(
+ JetTokens.PUBLIC_KEYWORD, JetTokens.PROTECTED_KEYWORD, JetTokens.INTERNAL_KEYWORD, JetTokens.PRIVATE_KEYWORD,
+ JetTokens.OPEN_KEYWORD, JetTokens.FINAL_KEYWORD,
+ JetTokens.OVERRIDE_KEYWORD)
fun parseDocumentation(descriptor: DeclarationDescriptor): Content {
val kdoc = KDocFinder.findKDoc(descriptor) ?: findStdlibKDoc(descriptor)
@@ -328,7 +335,7 @@
DescriptorUtils.getFqName(this).asString() in boringBuiltinClasses
fun DocumentationNode.appendAnnotations(annotated: Annotated) {
- annotated.getAnnotations().forEach {
+ annotated.annotations.filter { it.source.getPsi() != null && it.mustBeDocumented() }.forEach {
val annotationNode = it.build()
if (annotationNode != null) {
append(annotationNode,
@@ -337,6 +344,20 @@
}
}
+ fun AnnotationDescriptor.mustBeDocumented(): Boolean {
+ val annotationClass = type.constructor.declarationDescriptor as? Annotated ?: return false
+ return annotationClass.isDocumentedAnnotation()
+ }
+
+ fun DocumentationNode.appendModifiers(descriptor: DeclarationDescriptor) {
+ val psi = (descriptor as DeclarationDescriptorWithSource).source.getPsi() as? JetModifierListOwner ?: return
+ JetTokens.MODIFIER_KEYWORDS_ARRAY.filter { it !in knownModifiers }.forEach {
+ if (psi.hasModifier(it)) {
+ appendTextNode(it.value, Kind.Modifier)
+ }
+ }
+ }
+
fun DocumentationNode.isDeprecation() = name == "Deprecated" || name == "deprecated"
fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) {
@@ -442,9 +463,6 @@
else -> Kind.Class
}
val node = DocumentationNode(this, kind)
- if (isInner()) {
- node.appendTextNode("inner", Kind.Modifier)
- }
node.appendSupertypes(this)
if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) {
node.appendInPageChildren(getTypeConstructor().getParameters(), DocumentationReference.Kind.Detail)
@@ -462,6 +480,7 @@
DocumentationReference.Kind.Member)
}
node.appendAnnotations(this)
+ node.appendModifiers(this)
node.appendSourceLink(getSource())
register(this, node)
return node
@@ -504,6 +523,7 @@
node.appendInPageChildren(getValueParameters(), DocumentationReference.Kind.Detail)
node.appendType(getReturnType())
node.appendAnnotations(this)
+ node.appendModifiers(this)
node.appendSourceLink(getSource())
node.appendOperatorOverloadNote(this)
@@ -584,6 +604,7 @@
getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
node.appendType(getReturnType())
node.appendAnnotations(this)
+ node.appendModifiers(this)
node.appendSourceLink(getSource())
if (isVar()) {
node.appendTextNode("var", DocumentationNode.Kind.Modifier)
@@ -623,13 +644,7 @@
fun ValueParameterDescriptor.build(): DocumentationNode {
val node = DocumentationNode(this, Kind.Parameter)
- val varargType = getVarargElementType()
- if (varargType != null) {
- node.appendTextNode("vararg", Kind.Annotation, DocumentationReference.Kind.Annotation)
- node.appendType(varargType)
- } else {
- node.appendType(getType())
- }
+ node.appendType(varargElementType ?: type)
if (declaresDefaultValue()) {
val psi = getSource().getPsi() as? JetParameter
if (psi != null) {
@@ -640,6 +655,7 @@
}
}
node.appendAnnotations(this)
+ node.appendModifiers(this)
register(this, node)
return node
}
diff --git a/src/Kotlin/KotlinLanguageService.kt b/src/Kotlin/KotlinLanguageService.kt
index a22ba8a..d61db54 100644
--- a/src/Kotlin/KotlinLanguageService.kt
+++ b/src/Kotlin/KotlinLanguageService.kt
@@ -154,8 +154,9 @@
}
}
- private fun ContentBlock.renderParameter(node: DocumentationNode) {
+ private fun ContentBlock.renderParameter(node: DocumentationNode, renderMode: RenderMode) {
renderAnnotationsForNode(node)
+ renderModifiersForNode(node, renderMode)
identifier(node.name, IdentifierKind.ParameterName)
symbol(":")
nbsp()
@@ -212,7 +213,7 @@
}
private fun ContentBlock.renderAnnotation(node: DocumentationNode) {
- identifier(node.name, IdentifierKind.AnnotationName)
+ identifier("@" + node.name, IdentifierKind.AnnotationName)
val parameters = node.details(DocumentationNode.Kind.Parameter)
if (!parameters.isEmpty()) {
symbol("(")
@@ -229,9 +230,9 @@
renderAnnotationsForNode(node)
when (node.kind) {
DocumentationNode.Kind.Class,
- DocumentationNode.Kind.AnnotationClass -> keyword("class ")
+ DocumentationNode.Kind.AnnotationClass,
+ DocumentationNode.Kind.Enum -> keyword("class ")
DocumentationNode.Kind.Interface -> keyword("interface ")
- DocumentationNode.Kind.Enum -> keyword("enum class ")
DocumentationNode.Kind.EnumItem -> keyword("enum val ")
DocumentationNode.Kind.Object -> keyword("object ")
else -> throw IllegalArgumentException("Node $node is not a class-like object")
@@ -266,7 +267,7 @@
symbol("(")
renderList(node.details(DocumentationNode.Kind.Parameter)) {
- renderParameter(it)
+ renderParameter(it, renderMode)
}
symbol(")")
if (needReturnType(node)) {
diff --git a/test/data/classes/annotatedClass.kt b/test/data/classes/annotatedClass.kt
index 62c6f0e..1b58f56 100644
--- a/test/data/classes/annotatedClass.kt
+++ b/test/data/classes/annotatedClass.kt
@@ -1 +1 @@
-data class Foo() {}
+@Strictfp class Foo() {}
diff --git a/test/data/classes/dataClass.kt b/test/data/classes/dataClass.kt
new file mode 100644
index 0000000..62c6f0e
--- /dev/null
+++ b/test/data/classes/dataClass.kt
@@ -0,0 +1 @@
+data class Foo() {}
diff --git a/test/data/format/annotationParams.kt b/test/data/format/annotationParams.kt
index ee5b524..f259a74 100644
--- a/test/data/format/annotationParams.kt
+++ b/test/data/format/annotationParams.kt
@@ -1 +1 @@
-inlineOptions(InlineOption.LOCAL_CONTINUE_AND_BREAK) fun f() {}
+@JvmName("FFF") fun f() {}
diff --git a/test/data/format/annotationParams.md b/test/data/format/annotationParams.md
index 80fe52c..8cdd6e9 100644
--- a/test/data/format/annotationParams.md
+++ b/test/data/format/annotationParams.md
@@ -3,6 +3,6 @@
# f
-`inlineOptions([InlineOption.LOCAL_CONTINUE_AND_BREAK]) fun f(): Unit`
+`@JvmName("FFF") fun f(): Unit`
diff --git a/test/data/functions/annotatedFunction.kt b/test/data/functions/annotatedFunction.kt
index 11c1967..f7abbf6 100644
--- a/test/data/functions/annotatedFunction.kt
+++ b/test/data/functions/annotatedFunction.kt
@@ -1,2 +1,2 @@
-inline fun f() {
+@Strictfp fun f() {
}
diff --git a/test/data/functions/functionWithAnnotatedParam.kt b/test/data/functions/functionWithAnnotatedParam.kt
index 640bec8..f858e67 100644
--- a/test/data/functions/functionWithAnnotatedParam.kt
+++ b/test/data/functions/functionWithAnnotatedParam.kt
@@ -1,2 +1,7 @@
-fun function(noinline notInlined: () -> Unit) {
+@Target(AnnotationTarget.VALUE_PARAMETER)
+@Retention(AnnotationRetention.SOURCE)
+@MustBeDocumented
+public annotation class Fancy
+
+fun function(@Fancy notInlined: () -> Unit) {
}
diff --git a/test/data/functions/functionWithNoinlineParam.kt b/test/data/functions/functionWithNoinlineParam.kt
new file mode 100644
index 0000000..640bec8
--- /dev/null
+++ b/test/data/functions/functionWithNoinlineParam.kt
@@ -0,0 +1,2 @@
+fun function(noinline notInlined: () -> Unit) {
+}
diff --git a/test/data/functions/functionWithNotDocumentedAnnotation.kt b/test/data/functions/functionWithNotDocumentedAnnotation.kt
new file mode 100644
index 0000000..3c7e2ff
--- /dev/null
+++ b/test/data/functions/functionWithNotDocumentedAnnotation.kt
@@ -0,0 +1,2 @@
+@Suppress("FOO") fun f() {
+}
diff --git a/test/data/functions/inlineFunction.kt b/test/data/functions/inlineFunction.kt
new file mode 100644
index 0000000..11c1967
--- /dev/null
+++ b/test/data/functions/inlineFunction.kt
@@ -0,0 +1,2 @@
+inline fun f() {
+}
diff --git a/test/data/properties/annotatedProperty.kt b/test/data/properties/annotatedProperty.kt
index f70c28b..8990af2 100644
--- a/test/data/properties/annotatedProperty.kt
+++ b/test/data/properties/annotatedProperty.kt
@@ -1 +1 @@
-inline val property = "test"
\ No newline at end of file
+@Volatile var property = "test"
\ No newline at end of file
diff --git a/test/src/TestAPI.kt b/test/src/TestAPI.kt
index 165278f..88d1903 100644
--- a/test/src/TestAPI.kt
+++ b/test/src/TestAPI.kt
@@ -9,7 +9,6 @@
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
import org.jetbrains.kotlin.config.ContentRoot
import org.jetbrains.kotlin.config.KotlinSourceRoot
-import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import org.junit.Assert
import java.io.File
import kotlin.test.fail
@@ -33,8 +32,10 @@
}
val environment = AnalysisEnvironment(messageCollector) {
- val stringRoot = PathManager.getResourceRoot(javaClass<String>(), "/java/lang/String.class")
+ val stringRoot = PathManager.getResourceRoot(String::class.java, "/java/lang/String.class")
addClasspath(File(stringRoot))
+ val kotlinPairRoot = PathManager.getResourceRoot(Pair::class.java, "/kotlin/Pair.class")
+ addClasspath(File(kotlinPairRoot))
addRoots(roots.toList())
}
val options = DocumentationOptions(includeNonPublic = true, skipEmptyPackages = false, sourceLinks = listOf<SourceLinkDefinition>())
diff --git a/test/src/format/MarkdownFormatTest.kt b/test/src/format/MarkdownFormatTest.kt
index 059b491..8800305 100644
--- a/test/src/format/MarkdownFormatTest.kt
+++ b/test/src/format/MarkdownFormatTest.kt
@@ -1,7 +1,8 @@
package org.jetbrains.dokka.tests
+import org.jetbrains.dokka.KotlinLanguageService
+import org.jetbrains.dokka.MarkdownFormatService
import org.junit.Test
-import org.jetbrains.dokka.*
public class MarkdownFormatTest {
private val markdownService = MarkdownFormatService(InMemoryLocationService, KotlinLanguageService())
@@ -30,7 +31,7 @@
}
}
- Test fun annotationParams() {
+ @Test fun annotationParams() {
verifyOutput("test/data/format/annotationParams.kt", ".md") { model, output ->
markdownService.appendNodes(tempLocation, output, model.members.single().members)
}
diff --git a/test/src/model/ClassTest.kt b/test/src/model/ClassTest.kt
index 05fc2fc..bcedf49 100644
--- a/test/src/model/ClassTest.kt
+++ b/test/src/model/ClassTest.kt
@@ -1,8 +1,11 @@
package org.jetbrains.dokka.tests
+import org.jetbrains.dokka.Content
+import org.jetbrains.dokka.DocumentationNode
+import org.jetbrains.dokka.DocumentationReference
import org.junit.Test
-import kotlin.test.*
-import org.jetbrains.dokka.*
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
public class ClassTest {
Test fun emptyClass() {
@@ -148,19 +151,24 @@
}
}
- Test fun annotatedClass() {
- verifyModel("test/data/classes/annotatedClass.kt") { model ->
- with(model.members.single().members.single()) {
- assertEquals(1, annotations.count())
- with(annotations[0]) {
- assertEquals("data", name)
- assertEquals(Content.Empty, content)
- assertEquals(DocumentationNode.Kind.Annotation, kind)
- }
+ @Test fun annotatedClass() {
+ verifyPackageMember("test/data/classes/annotatedClass.kt") { cls ->
+ assertEquals(1, cls.annotations.count())
+ with(cls.annotations[0]) {
+ assertEquals("Strictfp", name)
+ assertEquals(Content.Empty, content)
+ assertEquals(DocumentationNode.Kind.Annotation, kind)
}
}
}
+ @Test fun dataClass() {
+ verifyPackageMember("test/data/classes/dataClass.kt") { cls ->
+ val modifiers = cls.details(DocumentationNode.Kind.Modifier).map { it.name }
+ assertTrue("data" in modifiers)
+ }
+ }
+
Test fun annotatedClassWithAnnotationParameters() {
verifyModel("test/data/classes/annotatedClassWithAnnotationParameters.kt") { model ->
with(model.members.single().members.single()) {
diff --git a/test/src/model/FunctionTest.kt b/test/src/model/FunctionTest.kt
index 80ae223..734675d 100644
--- a/test/src/model/FunctionTest.kt
+++ b/test/src/model/FunctionTest.kt
@@ -7,7 +7,7 @@
import kotlin.test.assertTrue
public class FunctionTest {
- Test fun function() {
+ @Test fun function() {
verifyModel("test/data/functions/function.kt") { model ->
with(model.members.single().members.single()) {
assertEquals("fn", name)
@@ -20,7 +20,7 @@
}
}
- Test fun functionWithReceiver() {
+ @Test fun functionWithReceiver() {
verifyModel("test/data/functions/functionWithReceiver.kt") { model ->
with(model.members.single().members.single()) {
assertEquals("String", name)
@@ -52,7 +52,7 @@
}
}
- Test fun genericFunction() {
+ @Test fun genericFunction() {
verifyModel("test/data/functions/genericFunction.kt") { model ->
with(model.members.single().members.single()) {
assertEquals("generic", name)
@@ -76,7 +76,7 @@
}
}
}
- Test fun genericFunctionWithConstraints() {
+ @Test fun genericFunctionWithConstraints() {
verifyModel("test/data/functions/genericFunctionWithConstraints.kt") { model ->
with(model.members.single().members.single()) {
assertEquals("generic", name)
@@ -115,7 +115,7 @@
}
}
- Test fun functionWithParams() {
+ @Test fun functionWithParams() {
verifyModel("test/data/functions/functionWithParams.kt") { model ->
with(model.members.single().members.single()) {
assertEquals("function", name)
@@ -141,30 +141,50 @@
}
}
- Test fun annotatedFunction() {
- verifyModel("test/data/functions/annotatedFunction.kt") { model ->
- with(model.members.single().members.single()) {
- assertEquals(1, annotations.count())
- with(annotations[0]) {
- assertEquals("inline", name)
- assertEquals(Content.Empty, content)
- assertEquals(DocumentationNode.Kind.Annotation, kind)
+ @Test fun annotatedFunction() {
+ verifyPackageMember("test/data/functions/annotatedFunction.kt") { func ->
+ assertEquals(1, func.annotations.count())
+ with(func.annotations[0]) {
+ assertEquals("Strictfp", name)
+ assertEquals(Content.Empty, content)
+ assertEquals(DocumentationNode.Kind.Annotation, kind)
+ }
+ }
+ }
+
+ @Test fun functionWithNotDocumentedAnnotation() {
+ verifyPackageMember("test/data/functions/functionWithNotDocumentedAnnotation.kt") { func ->
+ assertEquals(0, func.annotations.count())
+ }
+ }
+
+ @Test fun inlineFunction() {
+ verifyPackageMember("test/data/functions/inlineFunction.kt") { func ->
+ val modifiers = func.details(DocumentationNode.Kind.Modifier).map { it.name }
+ assertTrue("inline" in modifiers)
+ }
+ }
+
+ @Test fun functionWithAnnotatedParam() {
+ verifyModel("test/data/functions/functionWithAnnotatedParam.kt") { model ->
+ with(model.members.single().members.single { it.name == "function"} ) {
+ with(details.elementAt(2)) {
+ assertEquals(1, annotations.count())
+ with(annotations[0]) {
+ assertEquals("Fancy", name)
+ assertEquals(Content.Empty, content)
+ assertEquals(DocumentationNode.Kind.Annotation, kind)
+ }
}
}
}
}
- Test fun functionWithAnnotatedParam() {
- verifyModel("test/data/functions/functionWithAnnotatedParam.kt") { model ->
- with(model.members.single().members.single()) {
- with(details.elementAt(2)) {
- assertEquals(1, annotations.count())
- with(annotations[0]) {
- assertEquals("noinline", name)
- assertEquals(Content.Empty, content)
- assertEquals(DocumentationNode.Kind.Annotation, kind)
- }
- }
+ @Test fun functionWithNoinlineParam() {
+ verifyPackageMember("test/data/functions/functionWithNoinlineParam.kt") { func ->
+ with(func.details.elementAt(2)) {
+ val modifiers = details(DocumentationNode.Kind.Modifier).map { it.name }
+ assertTrue("noinline" in modifiers)
}
}
}
diff --git a/test/src/model/PropertyTest.kt b/test/src/model/PropertyTest.kt
index 405b260..0853cbc 100644
--- a/test/src/model/PropertyTest.kt
+++ b/test/src/model/PropertyTest.kt
@@ -69,7 +69,7 @@
with(model.members.single().members.single()) {
assertEquals(1, annotations.count())
with(annotations[0]) {
- assertEquals("inline", name)
+ assertEquals("Volatile", name)
assertEquals(Content.Empty, content)
assertEquals(DocumentationNode.Kind.Annotation, kind)
}