aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgnat Beresnev <ignat.beresnev@jetbrains.com>2022-09-26 18:47:01 +0200
committerGitHub <noreply@github.com>2022-09-26 18:47:01 +0200
commit86f9559ebd40a07e996df49464fc9101dd21d3bc (patch)
tree88b91d93b49e3276b81143febb9cb381ab03b82d
parent9207f8f032fac8036c9aa5aa65633341a14efa62 (diff)
downloaddokka-86f9559ebd40a07e996df49464fc9101dd21d3bc.tar.gz
dokka-86f9559ebd40a07e996df49464fc9101dd21d3bc.tar.bz2
dokka-86f9559ebd40a07e996df49464fc9101dd21d3bc.zip
Add documentation for synthetic Enum `values()` and `valueOf()` functions (#2650)
-rw-r--r--integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/StdLibDocumentationIntegrationTest.kt38
-rw-r--r--integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/kotlin/StdlibGradleIntegrationTest.kt1
-rw-r--r--plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt15
-rw-r--r--plugins/base/src/main/kotlin/translators/descriptors/SyntheticDescriptorDocumentationProvider.kt47
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt15
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/SynheticElementDocumentationProvider.kt39
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt5
-rw-r--r--plugins/base/src/main/resources/dokka/docs/javadoc/EnumValueOf.java.template12
-rw-r--r--plugins/base/src/main/resources/dokka/docs/javadoc/EnumValues.java.template8
-rw-r--r--plugins/base/src/main/resources/dokka/docs/kdoc/EnumValueOf.kt.template4
-rw-r--r--plugins/base/src/main/resources/dokka/docs/kdoc/EnumValues.kt.template3
-rw-r--r--plugins/base/src/test/kotlin/enums/JavaEnumsTest.kt46
-rw-r--r--plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt134
-rw-r--r--plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt152
15 files changed, 461 insertions, 60 deletions
diff --git a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/StdLibDocumentationIntegrationTest.kt b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/StdLibDocumentationIntegrationTest.kt
new file mode 100644
index 00000000..ee6f4ea2
--- /dev/null
+++ b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/StdLibDocumentationIntegrationTest.kt
@@ -0,0 +1,38 @@
+package org.jetbrains.dokka.it
+
+import java.net.URL
+import kotlin.test.Test
+
+class StdLibDocumentationIntegrationTest {
+
+ /**
+ * Documentation for Enum's synthetic values() and valueOf() functions is only present in source code,
+ * but not present in the descriptors. However, Dokka needs to generate documentation for these functions,
+ * so it ships with hardcoded kdoc templates.
+ *
+ * This test exists to make sure documentation for these hardcoded synthetic functions does not change,
+ * and fails if it does, indicating that it needs to be updated.
+ */
+ @Test
+ fun shouldAssertEnumDocumentationHasNotChanged() {
+ val sourcesLink = "https://raw.githubusercontent.com/JetBrains/kotlin/master/core/builtins/native/kotlin/Enum.kt"
+ val sources = URL(sourcesLink).readText()
+
+ val expectedValuesDoc =
+ " /**\n" +
+ " * Returns an array containing the constants of this enum type, in the order they're declared.\n" +
+ " * This method may be used to iterate over the constants.\n" +
+ " * @values\n" +
+ " */"
+ check(sources.contains(expectedValuesDoc))
+
+ val expectedValueOfDoc =
+ " /**\n" +
+ " * Returns the enum constant of this type with the specified name. The string must match exactly " +
+ "an identifier used to declare an enum constant in this type. (Extraneous whitespace characters are not permitted.)\n" +
+ " * @throws IllegalArgumentException if this enum type has no constant with the specified name\n" +
+ " * @valueOf\n" +
+ " */"
+ check(sources.contains(expectedValueOfDoc))
+ }
+}
diff --git a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/kotlin/StdlibGradleIntegrationTest.kt b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/kotlin/StdlibGradleIntegrationTest.kt
index 84178fe8..22da0754 100644
--- a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/kotlin/StdlibGradleIntegrationTest.kt
+++ b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/kotlin/StdlibGradleIntegrationTest.kt
@@ -7,6 +7,7 @@ import org.jetbrains.dokka.it.gradle.AbstractGradleIntegrationTest
import org.jetbrains.dokka.it.gradle.BuildVersions
import org.junit.runners.Parameterized
import java.io.File
+import java.net.URL
import kotlin.test.*
class StdlibGradleIntegrationTest(override val versions: BuildVersions) : AbstractGradleIntegrationTest(),
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
index c52fbd37..e3e1a33e 100644
--- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
+++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
@@ -136,6 +136,7 @@ private class DokkaDescriptorVisitor(
private val logger: DokkaLogger
) {
private val javadocParser = JavadocParser(logger, resolutionFacade)
+ private val syntheticDocProvider = SyntheticDescriptorDocumentationProvider(resolutionFacade)
private fun Collection<DeclarationDescriptor>.filterDescriptorsInSourceSet() = filter {
it.toSourceElement.containingFile.toString().let { path ->
@@ -577,8 +578,7 @@ private class DokkaDescriptorVisitor(
sources = actual,
visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(),
generics = generics.await(),
- documentation = descriptor.takeIf { it.kind != CallableMemberDescriptor.Kind.SYNTHESIZED }
- ?.resolveDescriptorData() ?: emptyMap(),
+ documentation = descriptor.getDocumentation(),
modifier = descriptor.modifier().toSourceSetDependent(),
type = descriptor.returnType!!.toBound(),
sourceSets = setOf(sourceSet),
@@ -594,6 +594,15 @@ private class DokkaDescriptorVisitor(
}
}
+ private fun FunctionDescriptor.getDocumentation(): SourceSetDependent<DocumentationNode> {
+ val isSynthesized = this.kind == CallableMemberDescriptor.Kind.SYNTHESIZED
+ return if (isSynthesized) {
+ syntheticDocProvider.getDocumentation(this)?.toSourceSetDependent() ?: emptyMap()
+ } else {
+ this.resolveDescriptorData()
+ }
+ }
+
/**
* `createDRI` returns the DRI of the exact element and potential DRI of an element that is overriding it
* (It can be also FAKE_OVERRIDE which is in fact just inheritance of the symbol)
@@ -609,7 +618,7 @@ private class DokkaDescriptorVisitor(
private fun FunctionDescriptor.isObvious(): Boolean {
return kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE
- || kind == CallableMemberDescriptor.Kind.SYNTHESIZED
+ || (kind == CallableMemberDescriptor.Kind.SYNTHESIZED && !syntheticDocProvider.isDocumented(this))
|| containingDeclaration.fqNameOrNull()?.isObvious() == true
}
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/SyntheticDescriptorDocumentationProvider.kt b/plugins/base/src/main/kotlin/translators/descriptors/SyntheticDescriptorDocumentationProvider.kt
new file mode 100644
index 00000000..c96b888a
--- /dev/null
+++ b/plugins/base/src/main/kotlin/translators/descriptors/SyntheticDescriptorDocumentationProvider.kt
@@ -0,0 +1,47 @@
+package org.jetbrains.dokka.base.translators.descriptors
+
+import org.jetbrains.dokka.analysis.DokkaResolutionFacade
+import org.jetbrains.dokka.analysis.from
+import org.jetbrains.dokka.base.parsers.MarkdownParser
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.doc.DocumentationNode
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
+import org.jetbrains.kotlin.resolve.DescriptorFactory
+
+private const val ENUM_VALUEOF_TEMPLATE_PATH = "/dokka/docs/kdoc/EnumValueOf.kt.template"
+private const val ENUM_VALUES_TEMPLATE_PATH = "/dokka/docs/kdoc/EnumValues.kt.template"
+
+ internal class SyntheticDescriptorDocumentationProvider(
+ private val resolutionFacade: DokkaResolutionFacade
+) {
+ fun isDocumented(descriptor: DeclarationDescriptor): Boolean = descriptor is FunctionDescriptor
+ && (DescriptorFactory.isEnumValuesMethod(descriptor) || DescriptorFactory.isEnumValueOfMethod(descriptor))
+
+ fun getDocumentation(descriptor: DeclarationDescriptor): DocumentationNode? {
+ val function = descriptor as? FunctionDescriptor ?: return null
+ return when {
+ DescriptorFactory.isEnumValuesMethod(function) -> loadTemplate(descriptor, ENUM_VALUES_TEMPLATE_PATH)
+ DescriptorFactory.isEnumValueOfMethod(function) -> loadTemplate(descriptor, ENUM_VALUEOF_TEMPLATE_PATH)
+ else -> null
+ }
+ }
+
+ private fun loadTemplate(descriptor: DeclarationDescriptor, filePath: String): DocumentationNode? {
+ val kdoc = loadContent(filePath) ?: return null
+ val parser = MarkdownParser({ link -> resolveLink(descriptor, link)}, filePath)
+ return parser.parse(kdoc)
+ }
+
+ private fun loadContent(filePath: String): String? = javaClass.getResource(filePath)?.readText()
+
+ private fun resolveLink(descriptor: DeclarationDescriptor, link: String): DRI? =
+ resolveKDocLink(
+ resolutionFacade.resolveSession.bindingContext,
+ resolutionFacade,
+ descriptor,
+ null,
+ link.split('.')
+ ).firstOrNull()?.let { DRI.from(it) }
+}
diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
index 58524479..574fb2e8 100644
--- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
+++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
@@ -8,8 +8,6 @@ import com.intellij.lang.jvm.annotation.JvmAnnotationEnumFieldValue
import com.intellij.lang.jvm.types.JvmReferenceType
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.*
-import com.intellij.psi.impl.source.PsiClassReferenceType
-import com.intellij.psi.impl.source.PsiImmediateClassType
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
@@ -99,8 +97,8 @@ class DefaultPsiToDocumentableTranslator(
facade: DokkaResolutionFacade,
private val logger: DokkaLogger
) {
-
- private val javadocParser: JavaDocumentationParser = JavadocParser(logger, facade)
+ private val javadocParser = JavadocParser(logger, facade)
+ private val syntheticDocProvider = SyntheticElementDocumentationProvider(javadocParser, facade)
private val cachedBounds = hashMapOf<String, Bound>()
@@ -404,7 +402,7 @@ class DefaultPsiToDocumentableTranslator(
val dri = parentDRI?.let { dri ->
DRI.from(psi).copy(packageName = dri.packageName, classNames = dri.classNames)
} ?: DRI.from(psi)
- val docs = javadocParser.parseDocumentation(psi)
+ val docs = psi.getDocumentation()
return DFunction(
dri = dri,
name = psi.name,
@@ -452,8 +450,13 @@ class DefaultPsiToDocumentableTranslator(
)
}
+ private fun PsiMethod.getDocumentation(): DocumentationNode =
+ this.takeIf { it is SyntheticElement }?.let { syntheticDocProvider.getDocumentation(it) }
+ ?: javadocParser.parseDocumentation(this)
+
private fun PsiMethod.isObvious(inheritedFrom: DRI? = null): Boolean {
- return this is SyntheticElement || inheritedFrom?.isObvious() == true
+ return (this is SyntheticElement && !syntheticDocProvider.isDocumented(this))
+ || inheritedFrom?.isObvious() == true
}
private fun DRI.isObvious(): Boolean {
diff --git a/plugins/base/src/main/kotlin/translators/psi/SynheticElementDocumentationProvider.kt b/plugins/base/src/main/kotlin/translators/psi/SynheticElementDocumentationProvider.kt
new file mode 100644
index 00000000..376c0940
--- /dev/null
+++ b/plugins/base/src/main/kotlin/translators/psi/SynheticElementDocumentationProvider.kt
@@ -0,0 +1,39 @@
+package org.jetbrains.dokka.base.translators.psi
+
+import com.intellij.psi.*
+import com.intellij.psi.javadoc.PsiDocComment
+import org.jetbrains.dokka.analysis.DokkaResolutionFacade
+import org.jetbrains.dokka.base.translators.psi.parsers.JavadocParser
+import org.jetbrains.dokka.model.doc.DocumentationNode
+
+private const val ENUM_VALUEOF_TEMPLATE_PATH = "/dokka/docs/javadoc/EnumValueOf.java.template"
+private const val ENUM_VALUES_TEMPLATE_PATH = "/dokka/docs/javadoc/EnumValues.java.template"
+
+internal class SyntheticElementDocumentationProvider(
+ private val javadocParser: JavadocParser,
+ private val resolutionFacade: DokkaResolutionFacade
+) {
+ fun isDocumented(psiElement: PsiElement): Boolean = psiElement is PsiMethod
+ && (psiElement.isSyntheticEnumValuesMethod() || psiElement.isSyntheticEnumValueOfMethod())
+
+ fun getDocumentation(psiElement: PsiElement): DocumentationNode? {
+ val psiMethod = psiElement as? PsiMethod ?: return null
+ val templatePath = when {
+ psiMethod.isSyntheticEnumValuesMethod() -> ENUM_VALUES_TEMPLATE_PATH
+ psiMethod.isSyntheticEnumValueOfMethod() -> ENUM_VALUEOF_TEMPLATE_PATH
+ else -> return null
+ }
+ val docComment = loadSyntheticDoc(templatePath) ?: return null
+ return javadocParser.parseDocComment(docComment, psiElement)
+ }
+
+ private fun loadSyntheticDoc(path: String): PsiDocComment? {
+ val text = javaClass.getResource(path)?.readText() ?: return null
+ return JavaPsiFacade.getElementFactory(resolutionFacade.project).createDocCommentFromText(text)
+ }
+}
+
+private fun PsiMethod.isSyntheticEnumValuesMethod() = this.isSyntheticEnumFunction() && this.name == "values"
+private fun PsiMethod.isSyntheticEnumValueOfMethod() = this.isSyntheticEnumFunction() && this.name == "valueOf"
+private fun PsiMethod.isSyntheticEnumFunction() = this is SyntheticElement && this.containingClass?.isEnum == true
+
diff --git a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt
index 076d43a2..6a39652a 100644
--- a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt
+++ b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt
@@ -50,14 +50,13 @@ class JavadocParser(
override fun parseDocumentation(element: PsiNamedElement): DocumentationNode {
return when(val comment = findClosestDocComment(element, logger)){
- is JavaDocComment -> parseDocumentation(comment, element)
+ is JavaDocComment -> parseDocComment(comment.comment, element)
is KotlinDocComment -> parseDocumentation(comment)
else -> DocumentationNode(emptyList())
}
}
- private fun parseDocumentation(element: JavaDocComment, context: PsiNamedElement): DocumentationNode {
- val docComment = element.comment
+ internal fun parseDocComment(docComment: PsiDocComment, context: PsiNamedElement): DocumentationNode {
val nodes = listOfNotNull(docComment.getDescription()) + docComment.tags.mapNotNull { tag ->
parseDocTag(tag, docComment, context)
}
diff --git a/plugins/base/src/main/resources/dokka/docs/javadoc/EnumValueOf.java.template b/plugins/base/src/main/resources/dokka/docs/javadoc/EnumValueOf.java.template
new file mode 100644
index 00000000..233f8819
--- /dev/null
+++ b/plugins/base/src/main/resources/dokka/docs/javadoc/EnumValueOf.java.template
@@ -0,0 +1,12 @@
+/**
+ * Returns the enum constant of this type with the specified
+ * name.
+ * The string must match exactly an identifier used to declare
+ * an enum constant in this type. (Extraneous whitespace
+ * characters are not permitted.)
+ *
+ * @return the enum constant with the specified name
+ * @throws IllegalArgumentException if this enum type has no
+ * constant with the specified name
+ */
+ \ No newline at end of file
diff --git a/plugins/base/src/main/resources/dokka/docs/javadoc/EnumValues.java.template b/plugins/base/src/main/resources/dokka/docs/javadoc/EnumValues.java.template
new file mode 100644
index 00000000..4aed38a6
--- /dev/null
+++ b/plugins/base/src/main/resources/dokka/docs/javadoc/EnumValues.java.template
@@ -0,0 +1,8 @@
+/**
+ * Returns an array containing the constants of this enum
+ * type, in the order they're declared. This method may be
+ * used to iterate over the constants.
+ *
+ * @return an array containing the constants of this enum
+ * type, in the order they're declared
+ */
diff --git a/plugins/base/src/main/resources/dokka/docs/kdoc/EnumValueOf.kt.template b/plugins/base/src/main/resources/dokka/docs/kdoc/EnumValueOf.kt.template
new file mode 100644
index 00000000..fbf8fa8d
--- /dev/null
+++ b/plugins/base/src/main/resources/dokka/docs/kdoc/EnumValueOf.kt.template
@@ -0,0 +1,4 @@
+Returns the enum constant of this type with the specified name. The string must match exactly an identifier used
+to declare an enum constant in this type. (Extraneous whitespace characters are not permitted.)
+
+@throws kotlin.IllegalArgumentException if this enum type has no constant with the specified name
diff --git a/plugins/base/src/main/resources/dokka/docs/kdoc/EnumValues.kt.template b/plugins/base/src/main/resources/dokka/docs/kdoc/EnumValues.kt.template
new file mode 100644
index 00000000..c0e3559c
--- /dev/null
+++ b/plugins/base/src/main/resources/dokka/docs/kdoc/EnumValues.kt.template
@@ -0,0 +1,3 @@
+Returns an array containing the constants of this enum type, in the order they're declared.
+
+This method may be used to iterate over the constants.
diff --git a/plugins/base/src/test/kotlin/enums/JavaEnumsTest.kt b/plugins/base/src/test/kotlin/enums/JavaEnumsTest.kt
index 20805a7c..e8b9e92b 100644
--- a/plugins/base/src/test/kotlin/enums/JavaEnumsTest.kt
+++ b/plugins/base/src/test/kotlin/enums/JavaEnumsTest.kt
@@ -68,50 +68,4 @@ class JavaEnumsTest : BaseAbstractTest() {
}
}
}
-
- @Test
- fun `should mark synthetic functions generated for Kotlin as obvious`() {
- val writerPlugin = TestOutputWriterPlugin()
- testInline(
- """
- |/src/main/java/basic/JavaEnum.java
- |package testpackage
- |
- |public enum JavaEnum {
- | ONE, TWO
- |}
- """.trimMargin(),
- basicConfiguration,
- pluginOverrides = listOf(writerPlugin)
- ) {
- documentablesCreationStage = { modules ->
- val pckg = modules.flatMap { it.packages }.single { it.packageName == "testpackage" }
- val enum = pckg.children.single { it is DEnum } as DEnum
-
- // there's two with the same name, one inherited from
- // java.lang.Enum and one is synthetic for Kotlin interop
- enum.functions.filter { it.name == "valueOf" }.let { valueOfMethods ->
- assertEquals(2, valueOfMethods.size)
-
- val valueOfFromKotlin = valueOfMethods[0]
- assertEquals(
- "testpackage/JavaEnum/valueOf/#java.lang.String/PointingToDeclaration/",
- valueOfFromKotlin.dri.toString()
- )
- assertNotNull(valueOfFromKotlin.extra[ObviousMember])
-
- val valueOfFromJava = valueOfMethods[1]
- assertEquals(
- "java.lang/Enum/valueOf/#java.lang.Class<T>#java.lang.String/PointingToDeclaration/",
- valueOfFromJava.dri.toString()
- )
- assertNotNull(valueOfFromJava.extra[ObviousMember])
- }
-
- val valuesMethod = enum.functions.single { it.name == "values" }
- assertEquals("testpackage/JavaEnum/values/#/PointingToDeclaration/", valuesMethod.dri.toString())
- assertNotNull(valuesMethod.extra[ObviousMember])
- }
- }
- }
}
diff --git a/plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt b/plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt
index 1fd33f6f..655a8b82 100644
--- a/plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt
+++ b/plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt
@@ -116,7 +116,7 @@ class KotlinEnumsTest : BaseAbstractTest() {
val testEnumNode = packagePage.children[0]
assertEquals("TestEnum", testEnumNode.name)
- val enumEntries = testEnumNode.children
+ val enumEntries = testEnumNode.children.filterIsInstance<ClasslikePage>()
assertEquals(10, enumEntries.size)
assertEquals("ZERO", enumEntries[0].name)
diff --git a/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt b/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt
index fe1a42c8..c7e2bc21 100644
--- a/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt
+++ b/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt
@@ -3,6 +3,7 @@ package translators
import org.jetbrains.dokka.DokkaConfiguration
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.links.PointingToDeclaration
import org.jetbrains.dokka.model.*
import org.jetbrains.dokka.model.doc.*
import org.junit.jupiter.api.Assertions.assertEquals
@@ -19,6 +20,7 @@ class DefaultDescriptorToDocumentableTranslatorTest : BaseAbstractTest() {
sourceSets {
sourceSet {
sourceRoots = listOf("src/main/kotlin")
+ classpath = listOf(commonStdlibPath!!, jvmStdlibPath!!)
}
}
}
@@ -852,6 +854,138 @@ val soapXml = node("soap-env:Envelope", soapAttrs,
}
}
}
+
+ @Test
+ fun `should have documentation for synthetic Enum values functions`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/KotlinEnum.kt
+ |package test
+ |
+ |enum class KotlinEnum {
+ | FOO, BAR;
+ |}
+ """.trimIndent(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val kotlinEnum = module.packages.find { it.name == "test" }
+ ?.classlikes
+ ?.single { it.name == "KotlinEnum" }
+ checkNotNull(kotlinEnum)
+ val valuesFunction = kotlinEnum.functions.single { it.name == "values" }
+
+ val expectedValuesType = GenericTypeConstructor(
+ dri = DRI(
+ packageName = "kotlin",
+ classNames = "Array"
+ ),
+ projections = listOf(
+ Invariance(
+ GenericTypeConstructor(
+ dri = DRI(
+ packageName = "test",
+ classNames = "KotlinEnum"
+ ),
+ projections = emptyList()
+ )
+ )
+ )
+ )
+ assertEquals(expectedValuesType, valuesFunction.type)
+
+ val expectedDocumentation = DocumentationNode(listOf(
+ Description(
+ CustomDocTag(
+ children = listOf(
+ P(listOf(
+ Text(
+ "Returns an array containing the constants of this enum type, in the order " +
+ "they're declared."
+ ),
+ )),
+ P(listOf(
+ Text("This method may be used to iterate over the constants.")
+ ))
+ ),
+ name = "MARKDOWN_FILE"
+ )
+ )
+ ))
+ assertEquals(expectedDocumentation, valuesFunction.documentation.values.single())
+ }
+ }
+ }
+
+ @Test
+ fun `should have documentation for synthetic Enum valueOf functions`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/KotlinEnum.kt
+ |package test
+ |
+ |enum class KotlinEnum {
+ | FOO, BAR;
+ |}
+ """.trimIndent(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val kotlinEnum = module.packages.find { it.name == "test" }
+ ?.classlikes
+ ?.single { it.name == "KotlinEnum" }
+ checkNotNull(kotlinEnum)
+
+ val expectedValueOfType = GenericTypeConstructor(
+ dri = DRI(
+ packageName = "test",
+ classNames = "KotlinEnum"
+ ),
+ projections = emptyList()
+ )
+
+ val expectedDocumentation = DocumentationNode(listOf(
+ Description(
+ CustomDocTag(
+ children = listOf(
+ P(listOf(
+ Text(
+ "Returns the enum constant of this type with the specified name. " +
+ "The string must match exactly an identifier used to declare an enum " +
+ "constant in this type. (Extraneous whitespace characters are not permitted.)"
+ )
+ ))
+ ),
+ name = "MARKDOWN_FILE"
+ )
+ ),
+ Throws(
+ root = CustomDocTag(
+ children = listOf(
+ P(listOf(
+ Text("if this enum type has no constant with the specified name")
+ ))
+ ),
+ name = "MARKDOWN_FILE"
+ ),
+ name = "kotlin.IllegalArgumentException",
+ exceptionAddress = DRI(
+ packageName = "kotlin",
+ classNames = "IllegalArgumentException",
+ target = PointingToDeclaration
+ ),
+ )
+ ))
+
+ val valueOfFunction = kotlinEnum.functions.single { it.name == "valueOf" }
+ assertEquals(expectedDocumentation, valueOfFunction.documentation.values.single())
+ assertEquals(expectedValueOfType, valueOfFunction.type)
+
+ val valueOfParamDRI = (valueOfFunction.parameters.single().type as GenericTypeConstructor).dri
+ assertEquals(DRI(packageName = "kotlin", classNames = "String"), valueOfParamDRI)
+ }
+ }
+ }
}
private sealed class TestSuite {
diff --git a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt
index 5f42bd9a..711b9c02 100644
--- a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt
+++ b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt
@@ -6,7 +6,7 @@ import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.links.PointingToDeclaration
import org.jetbrains.dokka.model.*
-import org.jetbrains.dokka.model.doc.Text
+import org.jetbrains.dokka.model.doc.*
import org.jetbrains.dokka.plugability.DokkaPlugin
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Assertions.*
@@ -591,4 +591,154 @@ class DefaultPsiToDocumentableTranslatorTest : BaseAbstractTest() {
}
}
}
+
+ @Test
+ fun `should have documentation for synthetic Enum values functions`() {
+ testInline(
+ """
+ |/src/main/java/test/JavaEnum.java
+ |package test
+ |
+ |public enum JavaEnum {
+ | FOO, BAR;
+ |}
+ """.trimIndent(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val kotlinEnum = module.packages.find { it.name == "test" }
+ ?.classlikes
+ ?.single { it.name == "JavaEnum" }
+ checkNotNull(kotlinEnum)
+
+ val valuesFunction = kotlinEnum.functions.single { it.name == "values" }
+
+ val expectedDocumentation = DocumentationNode(listOf(
+ Description(
+ CustomDocTag(
+ children = listOf(
+ P(listOf(
+ Text(
+ "Returns an array containing the constants of this enum type, " +
+ "in the order they're declared. This method may be used to " +
+ "iterate over the constants."
+ ),
+ ))
+ ),
+ name = "MARKDOWN_FILE"
+ )
+ ),
+ Return(
+ CustomDocTag(
+ children = listOf(
+ P(listOf(
+ Text("an array containing the constants of this enum type, in the order they're declared")
+ ))
+ ),
+ name = "MARKDOWN_FILE"
+ )
+ )
+ ))
+ assertEquals(expectedDocumentation, valuesFunction.documentation.values.single())
+
+ val expectedValuesType = GenericTypeConstructor(
+ dri = DRI(
+ packageName = "kotlin",
+ classNames = "Array"
+ ),
+ projections = listOf(
+ GenericTypeConstructor(
+ dri = DRI(
+ packageName = "test",
+ classNames = "JavaEnum"
+ ),
+ projections = emptyList()
+ )
+ )
+ )
+ assertEquals(expectedValuesType, valuesFunction.type)
+ }
+ }
+ }
+
+ @Test
+ fun `should have documentation for synthetic Enum valueOf functions`() {
+ testInline(
+ """
+ |/src/main/java/test/JavaEnum.java
+ |package test
+ |
+ |public enum JavaEnum {
+ | FOO, BAR;
+ |}
+ """.trimIndent(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val kotlinEnum = module.packages.find { it.name == "test" }
+ ?.classlikes
+ ?.single { it.name == "JavaEnum" }
+ checkNotNull(kotlinEnum)
+
+ val valueOfFunction = kotlinEnum.functions.single { it.name == "valueOf" }
+
+ val expectedDocumentation = DocumentationNode(listOf(
+ Description(
+ CustomDocTag(
+ children = listOf(
+ P(listOf(
+ Text(
+ "Returns the enum constant of this type with the " +
+ "specified name. The string must match exactly an identifier used " +
+ "to declare an enum constant in this type. (Extraneous whitespace " +
+ "characters are not permitted.)"
+ )
+ ))
+ ),
+ name = "MARKDOWN_FILE"
+ )
+ ),
+ Return(
+ root = CustomDocTag(
+ children = listOf(
+ P(listOf(
+ Text("the enum constant with the specified name")
+ ))
+ ),
+ name = "MARKDOWN_FILE"
+ )
+ ),
+ Throws(
+ name = "java.lang.IllegalArgumentException",
+ exceptionAddress = DRI(
+ packageName = "java.lang",
+ classNames = "IllegalArgumentException",
+ target = PointingToDeclaration
+ ),
+ root = CustomDocTag(
+ children = listOf(
+ P(listOf(
+ Text("if this enum type has no constant with the specified name")
+ ))
+ ),
+ name = "MARKDOWN_FILE"
+ )
+ ),
+ ))
+ assertEquals(expectedDocumentation, valueOfFunction.documentation.values.single())
+
+ val expectedValueOfType = GenericTypeConstructor(
+ dri = DRI(
+ packageName = "test",
+ classNames = "JavaEnum"
+ ),
+ projections = emptyList()
+ )
+ assertEquals(expectedValueOfType, valueOfFunction.type)
+
+ val valueOfParamDRI = (valueOfFunction.parameters.single().type as GenericTypeConstructor).dri
+ assertEquals(DRI(packageName = "java.lang", classNames = "String"), valueOfParamDRI)
+ }
+ }
+ }
}