Skip to content

Commit

Permalink
Support synthetic KDoc for enum members
Browse files Browse the repository at this point in the history
  • Loading branch information
vmishenev committed Aug 14, 2023
1 parent b47a0c0 commit 9b79b24
Show file tree
Hide file tree
Showing 13 changed files with 96 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import org.jetbrains.dokka.model.dfs
import org.jetbrains.dokka.pages.*
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import utils.OnlyDescriptors
import kotlin.test.assertNotNull
import kotlin.test.assertNull

Expand Down Expand Up @@ -60,7 +59,6 @@ class ContentForBriefTest : BaseAbstractTest() {
""".trimIndent()


@OnlyDescriptors // TODO
@Test
fun `primary constructor should not inherit docs from its parameter`() {
testInline(codeWithSecondaryAndPrimaryConstructorsDocumented, testConfiguration) {
Expand Down Expand Up @@ -117,7 +115,6 @@ class ContentForBriefTest : BaseAbstractTest() {
.dfs { it is ContentText && it.dci.kind == ContentKind.Comment } as ContentText
}

@OnlyDescriptors // TODO
@Test
fun `primary constructor should not inherit docs from its parameter when no specific docs are provided`() {
testInline(codeWithDocumentedParameter, testConfiguration) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,6 @@ class ConstructorsSignaturesTest : BaseAbstractTest() {
}
}

@OnlyDescriptors // TODO
@Test
fun `class with explicitly documented constructor`() {
testInline(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ class KotlinArrayDocumentableReplacerTest : BaseAbstractTest() {
}
}

@OnlyDescriptors // TODO fix module.contentScope [getKtModuleForKtElement]
@OnlyDescriptors("Fix module.contentScope in new Standalone API") // TODO fix module.contentScope [getKtModuleForKtElement]
@Test
fun `no jvm source set`() {
val configurationWithNoJVM = dokkaConfiguration {
Expand Down
9 changes: 4 additions & 5 deletions plugins/base/src/test/kotlin/markdown/ParserTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import org.jetbrains.dokka.analysis.markdown.jb.MarkdownParser
import org.jetbrains.dokka.model.doc.*
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import utils.OnlyDescriptors
import kotlin.test.assertEquals


Expand Down Expand Up @@ -1475,7 +1474,6 @@ class ParserTest : KDocTest() {
}


@OnlyDescriptors // TODO
@Test
fun `exception thrown by empty header should point to location of a file`() {
val kdoc = """
Expand All @@ -1484,9 +1482,10 @@ class ParserTest : KDocTest() {
val expectedDocumentationNode = DocumentationNode(emptyList())
val exception = runCatching { executeTest(kdoc, expectedDocumentationNode) }.exceptionOrNull()

assertEquals(
"Wrong AST Tree. Header does not contain expected content in Test.kt/example.Test, element starts from offset 0 and ends 3: ###",
exception?.message
val expectedMessage = "Wrong AST Tree. Header does not contain expected content in Test.kt/example.Test, element starts from offset 0 and ends 3: ###"
assert(
exception?.message == expectedMessage
|| /* for K2 */ exception?.cause?.cause?.message == expectedMessage
)
}

Expand Down
2 changes: 1 addition & 1 deletion plugins/base/src/test/kotlin/model/FunctionsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ class FunctionTest : AbstractModelTest("/src/main/kotlin/function/Test.kt", "fun
}
}

@OnlyDescriptors("DRI entry")
@OnlyDescriptors("Bug in descriptors, DRI of entry should have [EnumEntryDRIExtra]")
@Test
fun annotatedFunctionWithAnnotationParameters() {
inlineModelTest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ class InheritedEntriesDocumentableFilterTransformerTest : BaseAbstractTest() {
}
}

@OnlyDescriptors // TODO
@OnlyDescriptors("Entry does not have `name` and `ordinal`") // TODO
@Test
fun `should work with enum entries when not suppressing`(){
testInline(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ class MergeImplicitExpectActualDeclarationsTest : BaseAbstractTest() {

fun PageNode.childrenRec(): List<PageNode> = listOf(this) + children.flatMap { it.childrenRec() }

@OnlyDescriptors // TODO
@OnlyDescriptors("Enum entry [SMTH] does not have functions") // TODO
@Test
fun `should merge enum entries`() {
testInline(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -856,7 +856,6 @@ val soapXml = node("soap-env:Envelope", soapAttrs,
}
}

@OnlyDescriptors // TODO
@Test
fun `should have documentation for synthetic Enum values functions`() {
testInline(
Expand Down Expand Up @@ -919,7 +918,6 @@ val soapXml = node("soap-env:Envelope", soapAttrs,
}
}

@OnlyDescriptors // TODO
@Test
fun `should have documentation for synthetic Enum entries property`() {
testInline(
Expand Down Expand Up @@ -983,7 +981,7 @@ val soapXml = node("soap-env:Envelope", soapAttrs,
}
}

@OnlyDescriptors // TODO
@OnlyDescriptors("Fix kdoc link") // TODO
@Test
fun `should have documentation for synthetic Enum valueOf functions`() {
testInline(
Expand Down
2 changes: 1 addition & 1 deletion plugins/base/src/test/kotlin/utils/TagsAnnotations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ annotation class OnlyDescriptorsMPP(val reason: String = "")
annotation class JavaCode

/**
* For test containing .java code
* For test using JDK
*/
@Target(
AnnotationTarget.CLASS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.jetbrains.dokka.analysis.kotlin.symbols.translators.getDRIFromSymbol
import org.jetbrains.dokka.model.doc.DocumentationNode
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolOrigin
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtNamedSymbol
Expand Down Expand Up @@ -45,7 +46,13 @@ internal fun KtAnalysisSession.getKDocDocumentationFrom(symbol: KtSymbol) = find

val ktElement = symbol.psi
val kdocLocation = ktElement?.containingFile?.name?.let {
val name = (symbol as? KtNamedSymbol)?.name?.asString()
val name = when(symbol) {
is KtCallableSymbol -> symbol.callableIdIfNonLocal?.toString()
is KtClassOrObjectSymbol -> symbol.classIdIfNonLocal?.toString()
is KtNamedSymbol -> symbol.name.asString()
else -> null
}?.replace('/', '.')

if (name != null) "$it/$name"
else it
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.jetbrains.dokka.analysis.kotlin.symbols.kdoc

import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.SymbolsAnalysisPlugin
import org.jetbrains.dokka.analysis.kotlin.symbols.translators.getDRIFromSymbol
import org.jetbrains.dokka.analysis.markdown.jb.MarkdownParser
import org.jetbrains.dokka.model.doc.DocumentationNode
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.*
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtPossibleMemberSymbol
import org.jetbrains.kotlin.builtins.StandardNames

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 fun KtAnalysisSession.hasGeneratedKDocDocumentation(symbol: KtSymbol): Boolean =
getDocumentationTemplatePath(symbol) != null

private fun KtAnalysisSession.getDocumentationTemplatePath(symbol: KtSymbol): String? =
when (symbol) {
is KtPropertySymbol -> if (isEnumEntriesProperty(symbol)) ENUM_ENTRIES_TEMPLATE_PATH else null
is KtFunctionSymbol -> {
when {
isEnumValuesMethod(symbol) -> ENUM_VALUES_TEMPLATE_PATH
isEnumValueOfMethod(symbol) -> ENUM_VALUEOF_TEMPLATE_PATH
else -> null
}
}
else -> null
}

private fun KtAnalysisSession.isEnumSpecialMember(symbol: KtPossibleMemberSymbol): Boolean =
symbol.origin == KtSymbolOrigin.SOURCE_MEMBER_GENERATED
&& (symbol.getContainingSymbol() as? KtClassOrObjectSymbol)?.classKind == KtClassKind.ENUM_CLASS

private fun KtAnalysisSession.isEnumEntriesProperty(symbol: KtPropertySymbol): Boolean =
symbol.name == StandardNames.ENUM_ENTRIES && isEnumSpecialMember(symbol)

private fun KtAnalysisSession.isEnumValuesMethod(symbol: KtFunctionSymbol): Boolean =
symbol.name == StandardNames.ENUM_VALUES && isEnumSpecialMember(symbol)

private fun KtAnalysisSession.isEnumValueOfMethod(symbol: KtFunctionSymbol): Boolean =
symbol.name == StandardNames.ENUM_VALUE_OF && isEnumSpecialMember(symbol)

internal fun KtAnalysisSession.getGeneratedKDocDocumentationFrom(symbol: KtSymbol): DocumentationNode? {
val templatePath = getDocumentationTemplatePath(symbol) ?: return null
return loadTemplate(symbol, templatePath)
}

private fun KtAnalysisSession.loadTemplate(symbol: KtSymbol, filePath: String): DocumentationNode? {
val kdoc = loadContent(filePath) ?: return null
val externalDriProvider = { link: String ->
try {
val linkedSymbol = resolveKDocLink(link, symbol)
if (linkedSymbol == null) null
else getDRIFromSymbol(linkedSymbol)
} catch (e1: IllegalArgumentException) {
/// logger.warn("Couldn't resolve link for $link")
null
}
}

val parser = MarkdownParser(externalDriProvider, filePath)
return parser.parse(kdoc)
}

private fun loadContent(filePath: String): String? =
SymbolsAnalysisPlugin::class.java.getResource(filePath)?.readText()
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ internal class SymbolExternalDocumentablesProvider(val context: DokkaContext) :
override fun findClasslike(dri: DRI, sourceSet: DokkaSourceSet): DClasslike? {
val classId = getClassIdFromDRI(dri)

// TODO research another ways to get AnalysisSession
val analysisContext = kotlinAnalysis[sourceSet]
return analyze(analysisContext.mainModule) {
val symbol = getClassOrObjectSymbolByClassId(classId) as? KtNamedClassOrObjectSymbol?: return@analyze null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import com.intellij.psi.util.PsiLiteralUtil
import org.jetbrains.dokka.DokkaConfiguration
import org.jetbrains.dokka.analysis.java.JavaAnalysisPlugin
import org.jetbrains.dokka.analysis.java.parsers.JavadocParser
import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.getGeneratedKDocDocumentationFrom
import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.AnalysisContext
import org.jetbrains.dokka.analysis.kotlin.symbols.services.KtPsiDocumentableSource
import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.getJavaDocDocumentationFrom
import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.getKDocDocumentationFrom
import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.hasGeneratedKDocDocumentation
import org.jetbrains.dokka.analysis.kotlin.symbols.translators.AnnotationTranslator.Companion.getPresentableName
import org.jetbrains.dokka.analysis.kotlin.symbols.utils.typeConstructorsBeingExceptions
import org.jetbrains.dokka.links.*
Expand Down Expand Up @@ -381,7 +383,8 @@ internal class DokkaSymbolVisitor(
namedClassOrObjectSymbol: KtNamedClassOrObjectSymbol,
dri: DRI
): DokkaScope {
val scope = namedClassOrObjectSymbol.getMemberScope()
// e.g. getStaticMemberScope contains `valueOf`, `values` and `entries` members for Enum
val scope = listOf(namedClassOrObjectSymbol.getMemberScope(), namedClassOrObjectSymbol.getStaticMemberScope()).asCompositeScope()
val constructors = scope.getConstructors().map { visitConstructorSymbol(it) }.toList()

val callables = scope.getCallableSymbols().toList()
Expand Down Expand Up @@ -849,10 +852,13 @@ internal class DokkaSymbolVisitor(
}

private fun KtAnalysisSession.getDocumentation(symbol: KtSymbol) =
getKDocDocumentationFrom(symbol) ?: javadocParser?.let { getJavaDocDocumentationFrom(symbol, it) }
if (symbol.origin == KtSymbolOrigin.SOURCE_MEMBER_GENERATED)
getGeneratedKDocDocumentationFrom(symbol)
else
getKDocDocumentationFrom(symbol) ?: javadocParser?.let { getJavaDocDocumentationFrom(symbol, it) }

private fun isObvious(functionSymbol: KtFunctionSymbol): Boolean {
return functionSymbol.origin == KtSymbolOrigin.SOURCE_MEMBER_GENERATED ||
private fun KtAnalysisSession.isObvious(functionSymbol: KtFunctionSymbol): Boolean {
return functionSymbol.origin == KtSymbolOrigin.SOURCE_MEMBER_GENERATED && !hasGeneratedKDocDocumentation(functionSymbol) ||
!functionSymbol.isOverride && functionSymbol.callableIdIfNonLocal?.classId?.isObvious() == true
}

Expand Down

0 comments on commit 9b79b24

Please sign in to comment.