Skip to content

Commit

Permalink
Hardcode documentation for the synthetic Enum.entries property (#3071)
Browse files Browse the repository at this point in the history
  • Loading branch information
IgnatBeresnev committed Jul 17, 2023
1 parent 20e06e8 commit 72541d1
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Returns a representation of an immutable list of all enum entries, in the order they're declared.

This method may be used to iterate over the enum entries.
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,69 @@ val soapXml = node("soap-env:Envelope", soapAttrs,
}
}

@Test
fun `should have documentation for synthetic Enum entries property`() {
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 entriesProperty = kotlinEnum.properties.single { it.name == "entries" }
val expectedEntriesType = GenericTypeConstructor(
dri = DRI(
packageName = "kotlin.enums",
classNames = "EnumEntries"
),
projections = listOf(
Invariance(
GenericTypeConstructor(
dri = DRI(
packageName = "test",
classNames = "KotlinEnum"
),
projections = emptyList()
)
)
)
)
assertEquals(expectedEntriesType, entriesProperty.type)

val expectedDocumentation = DocumentationNode(listOf(
Description(
CustomDocTag(
children = listOf(
P(listOf(
Text(
"Returns a representation of an immutable list of all enum entries, " +
"in the order they're declared."
),
)),
P(listOf(
Text("This method may be used to iterate over the enum entries.")
))
),
name = "MARKDOWN_FILE"
)
)
))
assertEquals(expectedDocumentation, entriesProperty.documentation.values.single())
}
}
}

@Test
fun `should have documentation for synthetic Enum valueOf functions`() {
testInline(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,11 +319,13 @@ private class DokkaDescriptorVisitor(
val descriptorsWithKind = this.unsubstitutedMemberScope.getDescriptorsWithKind()
val staticScopeForKotlinEnum = (this.staticScope as? StaticScopeForKotlinEnum) ?: return descriptorsWithKind

// synthetic values() and valueOf() functions are not present among average class functions
val enumSyntheticFunctions = staticScopeForKotlinEnum.getContributedDescriptors { true }
.filterIsInstance<FunctionDescriptor>()

return descriptorsWithKind.copy(functions = descriptorsWithKind.functions + enumSyntheticFunctions)
// synthetic declarations, such as `entries`, `values()` and `valueOf()`,
// are not present among real in-code declarationg
val contributedDescriptors = staticScopeForKotlinEnum.getContributedDescriptors { true }
return descriptorsWithKind.copy(
properties = descriptorsWithKind.properties + contributedDescriptors.filterIsInstance<PropertyDescriptor>(),
functions = descriptorsWithKind.functions + contributedDescriptors.filterIsInstance<FunctionDescriptor>()
)
}

private suspend fun visitEnumEntryDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DEnumEntry {
Expand Down Expand Up @@ -507,7 +509,7 @@ private class DokkaDescriptorVisitor(
getter = getter,
setter = setter,
visibility = descriptor.getVisibility(implicitAccessors).toSourceSetDependent(),
documentation = descriptor.resolveDescriptorData(),
documentation = descriptor.getDocumentation(),
modifier = descriptor.modifier().toSourceSetDependent(),
type = descriptor.returnType!!.toBound(),
expectPresentInSet = sourceSet.takeIf { isExpect },
Expand Down Expand Up @@ -609,8 +611,8 @@ private class DokkaDescriptorVisitor(
}
}

private fun FunctionDescriptor.getDocumentation(): SourceSetDependent<DocumentationNode> {
val isSynthesized = this.kind == CallableMemberDescriptor.Kind.SYNTHESIZED
private fun DeclarationDescriptor.getDocumentation(): SourceSetDependent<DocumentationNode> {
val isSynthesized = this is CallableMemberDescriptor && this.kind == CallableMemberDescriptor.Kind.SYNTHESIZED
return if (isSynthesized) {
syntheticDocProvider.getDocumentation(this)?.toSourceSetDependent() ?: emptyMap()
} else {
Expand Down Expand Up @@ -914,7 +916,7 @@ private class DokkaDescriptorVisitor(
coroutineScope { parallelMap { visitEnumEntryDescriptor(it, parent) } }

private fun DeclarationDescriptor.resolveDescriptorData(): SourceSetDependent<DocumentationNode> =
getDocumentation()?.toSourceSetDependent() ?: emptyMap()
resolveDocumentation()?.toSourceSetDependent() ?: emptyMap()


private suspend fun toTypeConstructor(kt: KotlinType) =
Expand Down Expand Up @@ -1038,7 +1040,7 @@ private class DokkaDescriptorVisitor(
return effectiveReferencedDescriptors.firstOrNull()?.let { DescriptorToSourceUtils.getSourceFromDescriptor(it) }
}

private fun DeclarationDescriptor.getDocumentation(): DocumentationNode? {
private fun DeclarationDescriptor.resolveDocumentation(): DocumentationNode? {
val find = with(kDocFinder) {
find(::descriptorToAnyDeclaration)
}
Expand All @@ -1050,7 +1052,7 @@ private class DokkaDescriptorVisitor(
try {
val kdocLink = with(kDocFinder) {
resolveKDocLink(
fromDescriptor = this@getDocumentation,
fromDescriptor = this@resolveDocumentation,
qualifiedName = link,
sourceSet = sourceSet
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,57 @@ import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.fr
import org.jetbrains.dokka.analysis.markdown.jb.MarkdownParser
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.doc.DocumentationNode
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.DescriptorUtils

private const val ENUM_ENTRIES_TEMPLATE_PATH = "/dokka/docs/kdoc/EnumEntries.kt.template"
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 kDocFinder: KDocFinder,
private val sourceSet: DokkaConfiguration.DokkaSourceSet
) {
fun isDocumented(descriptor: DeclarationDescriptor): Boolean = descriptor is FunctionDescriptor
&& (DescriptorFactory.isEnumValuesMethod(descriptor) || DescriptorFactory.isEnumValueOfMethod(descriptor))
fun isDocumented(descriptor: DeclarationDescriptor): Boolean {
return when(descriptor) {
is PropertyDescriptor -> descriptor.isEnumEntries()
is FunctionDescriptor -> {
DescriptorFactory.isEnumValuesMethod(descriptor) || DescriptorFactory.isEnumValueOfMethod(descriptor)
}
else -> false
}
}

private fun PropertyDescriptor.isEnumEntries(): Boolean {
return this.name == StandardNames.ENUM_ENTRIES
&& this.kind == CallableMemberDescriptor.Kind.SYNTHESIZED
&& DescriptorUtils.isEnumClass(this.containingDeclaration)
}

fun getDocumentation(descriptor: DeclarationDescriptor): DocumentationNode? {
val function = descriptor as? FunctionDescriptor ?: return null
return when(descriptor) {
is PropertyDescriptor -> descriptor.getDocumentation()
is FunctionDescriptor -> descriptor.getDocumentation()
else -> null
}
}

private fun PropertyDescriptor.getDocumentation(): DocumentationNode? {
return when {
this.isEnumEntries() -> loadTemplate(this, ENUM_ENTRIES_TEMPLATE_PATH)
else -> null
}
}

private fun FunctionDescriptor.getDocumentation(): DocumentationNode? {
return when {
DescriptorFactory.isEnumValuesMethod(function) -> loadTemplate(descriptor, ENUM_VALUES_TEMPLATE_PATH)
DescriptorFactory.isEnumValueOfMethod(function) -> loadTemplate(descriptor, ENUM_VALUEOF_TEMPLATE_PATH)
DescriptorFactory.isEnumValuesMethod(this) -> loadTemplate(this, ENUM_VALUES_TEMPLATE_PATH)
DescriptorFactory.isEnumValueOfMethod(this) -> loadTemplate(this, ENUM_VALUEOF_TEMPLATE_PATH)
else -> null
}
}
Expand Down

0 comments on commit 72541d1

Please sign in to comment.