diff options
5 files changed, 103 insertions, 38 deletions
diff --git a/plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt b/plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt index 94a6b932..c32a5cc2 100644 --- a/plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt +++ b/plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt @@ -15,8 +15,7 @@ import org.jetbrains.dokka.pages.ContentGroup import org.jsoup.Jsoup import org.jsoup.nodes.Element import signatures.renderedContent -import utils.TestOutputWriter -import utils.TestOutputWriterPlugin +import utils.* import java.net.URL import kotlin.test.Test import kotlin.test.assertEquals @@ -329,6 +328,47 @@ class KotlinEnumsTest : BaseAbstractTest() { } @Test + @OnlyDescriptors("K2 has `compareTo`, that should be suppressed, due to #3196") + fun `enum should have functions on page`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/TestEnum.kt + |package testpackage + | + | + |interface Sample { + | fun toBeImplemented(): String + |} + | + |enum class TestEnum: Sample { + | E1 { + | override fun toBeImplemented(): String = "e1" + | } + |} + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { root -> + root.contentPage<ClasslikePageNode>("E1") { + assertHasFunctions("toBeImplemented") + } + + root.contentPage<ClasslikePageNode>("TestEnum") { + assertHasFunctions("toBeImplemented", "valueOf", "values") + } + } + } + } + + @Test fun enumWithAnnotationsOnEntries() { val configuration = dokkaConfiguration { sourceSets { diff --git a/plugins/base/src/test/kotlin/transformers/InheritedEntriesDocumentableFilterTransfromerTest.kt b/plugins/base/src/test/kotlin/transformers/InheritedEntriesDocumentableFilterTransfromerTest.kt index 831d2680..c07dd5b8 100644 --- a/plugins/base/src/test/kotlin/transformers/InheritedEntriesDocumentableFilterTransfromerTest.kt +++ b/plugins/base/src/test/kotlin/transformers/InheritedEntriesDocumentableFilterTransfromerTest.kt @@ -9,7 +9,6 @@ import org.jetbrains.dokka.model.DEnum import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -import utils.OnlyDescriptors class InheritedEntriesDocumentableFilterTransformerTest : BaseAbstractTest() { val suppressingInheritedConfiguration = dokkaConfiguration { @@ -138,7 +137,6 @@ class InheritedEntriesDocumentableFilterTransformerTest : BaseAbstractTest() { } } - @OnlyDescriptors("Entry does not have `name` and `ordinal`") // TODO @Test fun `should work with enum entries when not suppressing`(){ testInline( @@ -146,7 +144,8 @@ class InheritedEntriesDocumentableFilterTransformerTest : BaseAbstractTest() { /src/suppressed/Suppressed.kt package suppressed enum class Suppressed { - ENTRY_SUPPRESSED + ENTRY_SUPPRESSED; + class A } """.trimIndent(), nonSuppressingInheritedConfiguration diff --git a/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt b/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt index f7118f33..18e42e47 100644 --- a/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt +++ b/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt @@ -13,10 +13,10 @@ import org.jetbrains.dokka.model.dfs import org.jetbrains.dokka.model.firstChildOfType import org.jetbrains.dokka.pages.* import utils.assertNotNull +import utils.findSectionWithName import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull -import utils.OnlyDescriptors class MergeImplicitExpectActualDeclarationsTest : BaseAbstractTest() { @@ -52,15 +52,6 @@ class MergeImplicitExpectActualDeclarationsTest : BaseAbstractTest() { ) } - private fun ClasslikePageNode.findSectionWithName(name: String) : ContentNode? { - var sectionHeader: ContentHeader? = null - return content.dfs { node -> - node.children.filterIsInstance<ContentHeader>().any { header -> - header.children.firstOrNull { it is ContentText && it.text == name }?.also { sectionHeader = header } != null - } - }?.children?.dropWhile { child -> child != sectionHeader }?.drop(1)?.firstOrNull() - } - private fun ContentNode.findTabWithType(type: TabbedContentType): ContentNode? = dfs { node -> node.children.filterIsInstance<ContentGroup>().any { gr -> gr.extra[TabbedContentTypeExtra]?.value == type @@ -274,7 +265,6 @@ class MergeImplicitExpectActualDeclarationsTest : BaseAbstractTest() { fun PageNode.childrenRec(): List<PageNode> = listOf(this) + children.flatMap { it.childrenRec() } - @OnlyDescriptors("Enum entry [SMTH] does not have functions") // TODO @Test fun `should merge enum entries`() { testInline( @@ -304,7 +294,8 @@ class MergeImplicitExpectActualDeclarationsTest : BaseAbstractTest() { assertNotNull(classPage, "Tested class not found!") val functions = classPage.findSectionWithName("Functions").assertNotNull("Functions") - val method1 = functions.children.singleOrNull().assertNotNull("method1") + val method1 = functions.children.single { it.sourceSets.size == 2 && it.dci.dri.singleOrNull()?.callable?.name == "method1" } + .assertNotNull("method1") assertEquals( 2, diff --git a/plugins/base/src/test/kotlin/utils/contentUtils.kt b/plugins/base/src/test/kotlin/utils/contentUtils.kt index b83d8697..3ca0bd2d 100644 --- a/plugins/base/src/test/kotlin/utils/contentUtils.kt +++ b/plugins/base/src/test/kotlin/utils/contentUtils.kt @@ -5,10 +5,9 @@ package utils import matchers.content.* -import org.jetbrains.dokka.pages.BasicTabbedContentType -import org.jetbrains.dokka.pages.ContentGroup -import org.jetbrains.dokka.pages.ContentPage -import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.model.dfs +import org.jetbrains.dokka.pages.* +import kotlin.test.assertEquals //TODO: Try to unify those functions after update to 1.4 fun ContentMatcherBuilder<*>.functionSignature( @@ -327,6 +326,24 @@ fun ContentMatcherBuilder<*>.unwrapAnnotation(elem: Map.Entry<String, Set<String } } } +inline fun<reified T> PageNode.contentPage(name: String, block: T.() -> Unit) { + (dfs { it.name == name } as? T).assertNotNull("The page `$name` is not found").block() +} + +fun ClasslikePageNode.assertHasFunctions(vararg expectedFunctionName: String) { + val functions = this.findSectionWithName("Functions").assertNotNull("Functions") + val functionsName = functions.children.map { (it.dfs { it is ContentText } as ContentText).text } + assertEquals(expectedFunctionName.toList(), functionsName) +} + +fun ClasslikePageNode.findSectionWithName(name: String) : ContentNode? { + var sectionHeader: ContentHeader? = null + return content.dfs { node -> + node.children.filterIsInstance<ContentHeader>().any { header -> + header.children.firstOrNull { it is ContentText && it.text == name }?.also { sectionHeader = header } != null + } + }?.children?.dropWhile { child -> child != sectionHeader }?.drop(1)?.firstOrNull() +} data class ParamAttributes( val annotations: Map<String, Set<String>>, diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt index 4e741d09..f217c88f 100644 --- a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt +++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt @@ -343,7 +343,30 @@ internal class DokkaSymbolVisitor( KtClassKind.ANONYMOUS_OBJECT -> throw NotImplementedError("ANONYMOUS_OBJECT does not support") KtClassKind.ENUM_CLASS -> { - val entries = namedClassOrObjectSymbol.getEnumEntries().map { visitEnumEntrySymbol(it) } + /** + * See https://github.com/Kotlin/dokka/issues/3129 + * + * e.g. the `A` enum entry in the `enum E` is + * ``` + * static val A: E = object : E() { + * val x: Int = 5 + * } + * ``` + * it needs to exclude all static members like `values` and `valueOf` from the enum class's scope + */ + val enumEntryScope = lazy { + getDokkaScopeFrom(namedClassOrObjectSymbol, dri, includeStaticScope = false).let { + it.copy( + functions = it.functions.map { it.withNewExtras( it.extra + InheritedMember(dri.copy(callable = null).toSourceSetDependent())) }, + properties = it.properties.map { it.withNewExtras( it.extra + InheritedMember(dri.copy(callable = null).toSourceSetDependent())) } + ) + } + } + + val entries = + namedClassOrObjectSymbol.getEnumEntries().map { + visitEnumEntrySymbol(it, enumEntryScope.value) + } DEnum( dri = dri, @@ -383,12 +406,17 @@ internal class DokkaSymbolVisitor( val properties: List<DProperty>, val classlikes: List<DClasslike> ) + + /** + * @param includeStaticScope flag to add static members, e.g. `valueOf`, `values` and `entries` members for Enum + */ private fun KtAnalysisSession.getDokkaScopeFrom( namedClassOrObjectSymbol: KtNamedClassOrObjectSymbol, - dri: DRI + dri: DRI, + includeStaticScope: Boolean = true ): DokkaScope { // e.g. getStaticMemberScope contains `valueOf`, `values` and `entries` members for Enum - val scope = listOf(namedClassOrObjectSymbol.getMemberScope(), namedClassOrObjectSymbol.getStaticMemberScope()).asCompositeScope() + val scope = if(includeStaticScope) listOf(namedClassOrObjectSymbol.getMemberScope(), namedClassOrObjectSymbol.getStaticMemberScope()).asCompositeScope() else namedClassOrObjectSymbol.getMemberScope() val constructors = scope.getConstructors().map { visitConstructorSymbol(it) }.toList() val callables = scope.getCallableSymbols().toList() @@ -453,28 +481,18 @@ internal class DokkaSymbolVisitor( } private fun KtAnalysisSession.visitEnumEntrySymbol( - enumEntrySymbol: KtEnumEntrySymbol + enumEntrySymbol: KtEnumEntrySymbol, scope: DokkaScope ): DEnumEntry = withExceptionCatcher(enumEntrySymbol) { val dri = getDRIFromEnumEntry(enumEntrySymbol) val isExpect = false - val scope = enumEntrySymbol.getMemberScope() - val callables = scope.getCallableSymbols().toList() - val classifiers = scope.getClassifierSymbols().toList() - - val functions = callables.filterIsInstance<KtFunctionSymbol>().map { visitFunctionSymbol(it, dri) } - val properties = callables.filterIsInstance<KtPropertySymbol>().map { visitPropertySymbol(it, dri) } - val classlikes = - classifiers.filterIsInstance<KtNamedClassOrObjectSymbol>() - .map { visitNamedClassOrObjectSymbol(it, dri) } - return DEnumEntry( dri = dri, name = enumEntrySymbol.name.asString(), documentation = getDocumentation(enumEntrySymbol)?.toSourceSetDependent() ?: emptyMap(), - functions = functions, - properties = properties, - classlikes = classlikes, + functions = scope.functions, + properties = scope.properties, + classlikes = emptyList(), // always empty, see https://github.com/Kotlin/dokka/issues/3129 sourceSets = setOf(sourceSet), expectPresentInSet = sourceSet.takeIf { isExpect }, extra = PropertyContainer.withAll( |