Skip to content

Commit

Permalink
Skip @Deprecated documentables with HIDDEN level (#2486)
Browse files Browse the repository at this point in the history
  • Loading branch information
IgnatBeresnev committed May 6, 2022
1 parent 88d3623 commit 12bf21b
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 184 deletions.
5 changes: 2 additions & 3 deletions plugins/base/api/base.api
Expand Up @@ -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 <init> (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 {
Expand Down
@@ -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<DModule>) = 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<DokkaConfiguration.PackageOptions>
) {

fun <T> T.isAllowedInPackage(): Boolean where T : WithExtraProperties<T>, 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<DPackage>): Pair<Boolean, List<DPackage>> {
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<DFunction>
) = functions.filter { it.isAllowedInPackage() }.let {
Pair(it.size != functions.size, it)
private fun WithExtraProperties<*>.annotations(): List<Annotations.Annotation> {
return this.extra.allOfType<Annotations>().flatMap { annotations ->
annotations.directAnnotations.values.singleOrNull() ?: emptyList()
}
}

private fun filterProperties(
properties: List<DProperty>
): Pair<Boolean, List<DProperty>> = properties.filter {
it.isAllowedInPackage()
}.let {
Pair(properties.size != it.size, it)
private fun filterDeprecatedAnnotations(annotations: List<Annotations.Annotation>): List<Annotations.Annotation> {
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<DEnumEntry>) =
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<DTypeAlias>) =
typeAliases.filter { it.isAllowedInPackage() }.let {
Pair(typeAliases.size != it.size, it)
}

private fun filterClasslikes(
classlikeList: List<DClasslike>
): Pair<Boolean, List<DClasslike>> {
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"
}
}
80 changes: 80 additions & 0 deletions plugins/base/src/test/kotlin/filter/DeprecationFilterTest.kt
Expand Up @@ -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 {
Expand Down Expand Up @@ -37,6 +78,7 @@ class DeprecationFilterTest : BaseAbstractTest() {
}
}
}

@Test
fun `deprecated function with false global skipDeprecated`() {
val configuration = dokkaConfiguration {
Expand Down Expand Up @@ -67,6 +109,7 @@ class DeprecationFilterTest : BaseAbstractTest() {
}
}
}

@Test
fun `deprecated function with true global skipDeprecated`() {
val configuration = dokkaConfiguration {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -137,6 +216,7 @@ class DeprecationFilterTest : BaseAbstractTest() {
}
}
}

@Test
fun `deprecated function with true global false package skipDeprecated`() {
val configuration = dokkaConfiguration {
Expand Down

0 comments on commit 12bf21b

Please sign in to comment.