From 16cbda4ccb79383fb918a760a3defda551e55ac4 Mon Sep 17 00:00:00 2001 From: Ignat Beresnev Date: Tue, 3 May 2022 22:17:45 +0300 Subject: [PATCH] Skip @Deprecated documentables with HIDDEN level --- plugins/base/api/base.api | 5 +- ...DeprecatedDocumentableFilterTransformer.kt | 225 ++++-------------- .../kotlin/filter/DeprecationFilterTest.kt | 80 +++++++ 3 files changed, 126 insertions(+), 184 deletions(-) diff --git a/plugins/base/api/base.api b/plugins/base/api/base.api index 101a00e9ce..1469cc785c 100644 --- a/plugins/base/api/base.api +++ b/plugins/base/api/base.api @@ -1124,10 +1124,9 @@ public final class org/jetbrains/dokka/base/transformers/documentables/ClashingD public fun mergeStrategyFor (Lorg/jetbrains/dokka/base/transformers/documentables/ClashingDriIdentifier;Lorg/jetbrains/dokka/base/transformers/documentables/ClashingDriIdentifier;)Lorg/jetbrains/dokka/model/properties/MergeStrategy; } -public final class org/jetbrains/dokka/base/transformers/documentables/DeprecatedDocumentableFilterTransformer : org/jetbrains/dokka/transformers/documentation/PreMergeDocumentableTransformer { +public final class org/jetbrains/dokka/base/transformers/documentables/DeprecatedDocumentableFilterTransformer : org/jetbrains/dokka/base/transformers/documentables/SuppressedByConditionDocumentableFilterTransformer { public fun (Lorg/jetbrains/dokka/plugability/DokkaContext;)V - public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; - public fun invoke (Ljava/util/List;)Ljava/util/List; + public fun shouldBeSuppressed (Lorg/jetbrains/dokka/model/Documentable;)Z } public abstract class org/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer : org/jetbrains/dokka/transformers/documentation/PreMergeDocumentableTransformer { diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt index 0f7795e66e..1112ac15a1 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt @@ -1,194 +1,57 @@ package org.jetbrains.dokka.base.transformers.documentables +import org.jetbrains.dokka.DokkaConfiguration.PackageOptions import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.Annotations +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.EnumValue import org.jetbrains.dokka.model.properties.WithExtraProperties import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer - -class DeprecatedDocumentableFilterTransformer(val context: DokkaContext) : PreMergeDocumentableTransformer { - override fun invoke(modules: List) = modules.map { original -> - val sourceSet = original.sourceSets.single() - val packageOptions = - sourceSet.perPackageOptions - original.let { - DeprecatedDocumentableFilter(sourceSet, packageOptions).processModule(it) - } +import org.jetbrains.dokka.transformers.documentation.perPackageOptions +import org.jetbrains.dokka.transformers.documentation.sourceSet + +/** + * If [PackageOptions.skipDeprecated] or [DokkaConfiguration.DokkaSourceSet.skipDeprecated] is set + * to `true`, suppresses documentables marked with [kotlin.Deprecated] or [java.lang.Deprecated]. + * Package options are given preference over global options. + * + * Documentables with [kotlin.Deprecated.level] set to [DeprecationLevel.HIDDEN] + * are suppressed regardless of global and package options. + */ +class DeprecatedDocumentableFilterTransformer(context: DokkaContext) : + SuppressedByConditionDocumentableFilterTransformer(context) { + + override fun shouldBeSuppressed(d: Documentable): Boolean { + val annotations = (d as? WithExtraProperties<*>)?.annotations() ?: return false + if (annotations.isEmpty()) + return false + + val deprecatedAnnotations = filterDeprecatedAnnotations(annotations) + if (deprecatedAnnotations.isEmpty()) + return false + + val kotlinDeprecated = deprecatedAnnotations.find { it.dri.packageName == "kotlin" } + if (kotlinDeprecated?.isHidden() == true) + return true + + return perPackageOptions(d)?.skipDeprecated ?: sourceSet(d).skipDeprecated } - private class DeprecatedDocumentableFilter( - val globalOptions: DokkaConfiguration.DokkaSourceSet, - val packageOptions: List - ) { - - fun T.isAllowedInPackage(): Boolean where T : WithExtraProperties, T : Documentable { - val packageName = this.dri.packageName - val condition = packageName != null && packageOptions.firstOrNull { - Regex(it.matchingRegex).matches(packageName) - }?.skipDeprecated - ?: globalOptions.skipDeprecated - - return !(condition && this.isDeprecated()) - } - - fun processModule(original: DModule) = - filterPackages(original.packages).let { (modified, packages) -> - if (!modified) original - else - original.copy( - packages = packages - ) - } - - - private fun filterPackages(packages: List): Pair> { - var packagesListChanged = false - val filteredPackages = packages.mapNotNull { pckg -> - var modified = false - val functions = filterFunctions(pckg.functions).let { (listModified, list) -> - modified = modified || listModified - list - } - val properties = filterProperties(pckg.properties).let { (listModified, list) -> - modified = modified || listModified - list - } - val classlikes = filterClasslikes(pckg.classlikes).let { (listModified, list) -> - modified = modified || listModified - list - } - val typeAliases = filterTypeAliases(pckg.typealiases).let { (listModified, list) -> - modified = modified || listModified - list - } - when { - !modified -> pckg - else -> { - packagesListChanged = true - pckg.copy( - functions = functions, - properties = properties, - classlikes = classlikes, - typealiases = typeAliases - ) - } - } - } - return Pair(packagesListChanged, filteredPackages) - } - - private fun filterFunctions( - functions: List - ) = functions.filter { it.isAllowedInPackage() }.let { - Pair(it.size != functions.size, it) + private fun WithExtraProperties<*>.annotations(): List { + return this.extra.allOfType().flatMap { annotations -> + annotations.directAnnotations.values.singleOrNull() ?: emptyList() } + } - private fun filterProperties( - properties: List - ): Pair> = properties.filter { - it.isAllowedInPackage() - }.let { - Pair(properties.size != it.size, it) + private fun filterDeprecatedAnnotations(annotations: List): List { + return annotations.filter { + (it.dri.packageName == "kotlin" && it.dri.classNames == "Deprecated") || + (it.dri.packageName == "java.lang" && it.dri.classNames == "Deprecated") } + } - private fun filterEnumEntries(entries: List) = - entries.filter { it.isAllowedInPackage() }.map { entry -> - entry.copy( - functions = filterFunctions(entry.functions).second, - properties = filterProperties(entry.properties).second, - classlikes = filterClasslikes(entry.classlikes).second, - ) - } - - private fun filterTypeAliases(typeAliases: List) = - typeAliases.filter { it.isAllowedInPackage() }.let { - Pair(typeAliases.size != it.size, it) - } - - private fun filterClasslikes( - classlikeList: List - ): Pair> { - var modified = false - return classlikeList.filter { classlike -> - when (classlike) { - is DClass -> classlike.isAllowedInPackage() - is DInterface -> classlike.isAllowedInPackage() - is DEnum -> classlike.isAllowedInPackage() - is DObject -> classlike.isAllowedInPackage() - is DAnnotation -> classlike.isAllowedInPackage() - } - }.map { classlike -> - fun helper(): DClasslike = when (classlike) { - is DClass -> classlike.copy( - constructors = filterFunctions(classlike.constructors).let { - modified = modified || it.first; it.second - }, - functions = filterFunctions(classlike.functions).let { - modified = modified || it.first; it.second - }, - properties = filterProperties(classlike.properties).let { - modified = modified || it.first; it.second - }, - classlikes = filterClasslikes(classlike.classlikes).let { - modified = modified || it.first; it.second - } - ) - is DAnnotation -> classlike.copy( - functions = filterFunctions(classlike.functions).let { - modified = modified || it.first; it.second - }, - properties = filterProperties(classlike.properties).let { - modified = modified || it.first; it.second - }, - classlikes = filterClasslikes(classlike.classlikes).let { - modified = modified || it.first; it.second - }, - constructors = filterFunctions(classlike.constructors).let { - modified = modified || it.first; it.second - } - ) - is DEnum -> classlike.copy( - entries = filterEnumEntries(classlike.entries), - functions = filterFunctions(classlike.functions).let { - modified = modified || it.first; it.second - }, - properties = filterProperties(classlike.properties).let { - modified = modified || it.first; it.second - }, - classlikes = filterClasslikes(classlike.classlikes).let { - modified = modified || it.first; it.second - }, - constructors = filterFunctions(classlike.constructors).let { - modified = modified || it.first; it.second - }, - ) - is DInterface -> classlike.copy( - functions = filterFunctions(classlike.functions).let { - modified = modified || it.first; it.second - }, - properties = filterProperties(classlike.properties).let { - modified = modified || it.first; it.second - }, - classlikes = filterClasslikes(classlike.classlikes).let { - modified = modified || it.first; it.second - } - ) - is DObject -> classlike.copy( - functions = filterFunctions(classlike.functions).let { - modified = modified || it.first; it.second - }, - properties = filterProperties(classlike.properties).let { - modified = modified || it.first; it.second - }, - classlikes = filterClasslikes(classlike.classlikes).let { - modified = modified || it.first; it.second - } - ) - } - helper() - }.let { - Pair(it.size != classlikeList.size || modified, it) - } - } + private fun Annotations.Annotation.isHidden(): Boolean { + val level = (this.params["level"] as? EnumValue) ?: return false + return level.enumName == "DeprecationLevel.HIDDEN" } } diff --git a/plugins/base/src/test/kotlin/filter/DeprecationFilterTest.kt b/plugins/base/src/test/kotlin/filter/DeprecationFilterTest.kt index 295a969bf6..44caecaaae 100644 --- a/plugins/base/src/test/kotlin/filter/DeprecationFilterTest.kt +++ b/plugins/base/src/test/kotlin/filter/DeprecationFilterTest.kt @@ -7,6 +7,47 @@ import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test class DeprecationFilterTest : BaseAbstractTest() { + + @Test + fun `should skip hidden deprecated level regardless of skipDeprecated`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + skipDeprecated = false + perPackageOptions = mutableListOf( + PackageOptionsImpl( + "example.*", + true, + false, + false, + false, + DokkaDefaults.documentedVisibilities + ) + ) + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |@Deprecated("dep", level = DeprecationLevel.HIDDEN) + |fun testFunction() { } + | + """.trimMargin(), + configuration + ) { + preMergeDocumentablesTransformationStage = { + Assertions.assertTrue( + it.first().packages.first().functions.isEmpty() + ) + } + } + } + @Test fun `function with false global skipDeprecated`() { val configuration = dokkaConfiguration { @@ -37,6 +78,7 @@ class DeprecationFilterTest : BaseAbstractTest() { } } } + @Test fun `deprecated function with false global skipDeprecated`() { val configuration = dokkaConfiguration { @@ -67,6 +109,7 @@ class DeprecationFilterTest : BaseAbstractTest() { } } } + @Test fun `deprecated function with true global skipDeprecated`() { val configuration = dokkaConfiguration { @@ -97,6 +140,42 @@ class DeprecationFilterTest : BaseAbstractTest() { } } } + + @Test + fun `should skip deprecated companion object`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + skipDeprecated = true + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |class Test { + | @Deprecated("dep") + | companion object { + | fun method() {} + | } + |} + | + | + """.trimMargin(), + configuration + ) { + preMergeDocumentablesTransformationStage = { + Assertions.assertTrue( + it.first().packages.first().classlikes.first().classlikes.isEmpty() + ) + } + } + } + @Test fun `deprecated function with false global true package skipDeprecated`() { val configuration = dokkaConfiguration { @@ -137,6 +216,7 @@ class DeprecationFilterTest : BaseAbstractTest() { } } } + @Test fun `deprecated function with true global false package skipDeprecated`() { val configuration = dokkaConfiguration {