From edcd1fb24d01e11b5a8185328255f2005aadf037 Mon Sep 17 00:00:00 2001 From: Ignat Beresnev Date: Fri, 27 Oct 2023 13:11:41 +0200 Subject: Implement analysis test API (#3184) --- core/api/core.api | 26 ++ .../documentation/DefaultDocumentableMerger.kt | 307 +++++++++++++++++++++ .../main/kotlin/testApi/testRunner/TestRunner.kt | 2 +- plugins/base/api/base.api | 18 -- plugins/base/src/main/kotlin/DokkaBase.kt | 5 +- .../documentables/ClashingDriIdentifier.kt | 12 + .../documentables/DefaultDocumentableMerger.kt | 287 ------------------- .../documentables/DefaultPageCreator.kt | 2 +- subprojects/analysis-kotlin-api/build.gradle.kts | 20 ++ .../test/jvm/java/SampleJavaAnalysisTest.kt | 49 ++++ .../test/jvm/kotlin/SampleKotlinJvmAnalysisTest.kt | 43 +++ .../test/jvm/mixed/SampleMixedJvmAnalysisTest.kt | 81 ++++++ .../moduledocs/PackageDocumentationAnalysisTest.kt | 66 +++++ .../analysis/test/sample/SampleAnalysisTest.kt | 55 ++++ .../jetbrains/dokka/analysis/test/api/TestData.kt | 21 ++ .../dokka/analysis/test/api/TestDataFile.kt | 37 +++ .../dokka/analysis/test/api/TestProject.kt | 97 +++++++ .../dokka/analysis/test/api/TestProjectFactory.kt | 67 +++++ .../test/api/analysis/TestAnalysisContext.kt | 36 +++ .../test/api/analysis/TestAnalysisServices.kt | 20 ++ .../test/api/analysis/TestProjectAnalyzer.kt | 223 +++++++++++++++ .../api/configuration/TestDokkaConfiguration.kt | 171 ++++++++++++ .../configuration/TestDokkaConfigurationBuilder.kt | 145 ++++++++++ .../configuration/TestDokkaConfigurationMapper.kt | 177 ++++++++++++ .../test/api/jvm/java/JavaConfigurationBuilder.kt | 61 ++++ .../analysis/test/api/jvm/java/JavaFileCreator.kt | 25 ++ .../analysis/test/api/jvm/java/JavaTestData.kt | 54 ++++ .../analysis/test/api/jvm/java/JavaTestDataFile.kt | 27 ++ .../analysis/test/api/jvm/java/JavaTestProject.kt | 73 +++++ .../jvm/kotlin/KotlinJvmConfigurationBuilder.kt | 56 ++++ .../api/jvm/kotlin/KotlinJvmDependencyUtils.kt | 22 ++ .../test/api/jvm/kotlin/KotlinJvmTestProject.kt | 93 +++++++ .../api/jvm/mixed/MixedJvmConfigurationBuilder.kt | 69 +++++ .../test/api/jvm/mixed/MixedJvmTestData.kt | 47 ++++ .../test/api/jvm/mixed/MixedJvmTestProject.kt | 80 ++++++ .../analysis/test/api/kotlin/KotlinTestData.kt | 48 ++++ .../analysis/test/api/kotlin/KotlinTestDataFile.kt | 27 ++ .../analysis/test/api/kotlin/KtFileCreator.kt | 32 +++ .../api/kotlin/sample/KotlinSampleFileCreator.kt | 32 +++ .../test/api/kotlin/sample/KotlinSampleTestData.kt | 44 +++ .../api/kotlin/sample/KotlinSampleTestDataFile.kt | 27 ++ .../analysis/test/api/markdown/MarkdownTestData.kt | 40 +++ .../test/api/markdown/MarkdownTestDataFile.kt | 26 ++ .../analysis/test/api/markdown/MdFileCreator.kt | 28 ++ .../analysis/test/api/util/CollectionUtils.kt | 18 ++ .../dokka/analysis/test/api/util/DslApiUtils.kt | 9 + .../dokka/analysis/test/api/util/FileUtils.kt | 41 +++ 47 files changed, 2635 insertions(+), 311 deletions(-) create mode 100644 core/src/main/kotlin/transformers/documentation/DefaultDocumentableMerger.kt create mode 100644 plugins/base/src/main/kotlin/transformers/documentables/ClashingDriIdentifier.kt delete mode 100644 plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt create mode 100644 subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/java/SampleJavaAnalysisTest.kt create mode 100644 subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/kotlin/SampleKotlinJvmAnalysisTest.kt create mode 100644 subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/mixed/SampleMixedJvmAnalysisTest.kt create mode 100644 subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/moduledocs/PackageDocumentationAnalysisTest.kt create mode 100644 subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/sample/SampleAnalysisTest.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestData.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestDataFile.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestProject.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestProjectFactory.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestAnalysisContext.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestAnalysisServices.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestProjectAnalyzer.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/configuration/TestDokkaConfiguration.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/configuration/TestDokkaConfigurationBuilder.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/configuration/TestDokkaConfigurationMapper.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaConfigurationBuilder.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaFileCreator.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaTestData.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaTestDataFile.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaTestProject.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmConfigurationBuilder.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmDependencyUtils.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmTestProject.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/mixed/MixedJvmConfigurationBuilder.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/mixed/MixedJvmTestData.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/mixed/MixedJvmTestProject.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/kotlin/KotlinTestData.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/kotlin/KotlinTestDataFile.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/kotlin/KtFileCreator.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/kotlin/sample/KotlinSampleFileCreator.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/kotlin/sample/KotlinSampleTestData.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/kotlin/sample/KotlinSampleTestDataFile.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/markdown/MarkdownTestData.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/markdown/MarkdownTestDataFile.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/markdown/MdFileCreator.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/util/CollectionUtils.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/util/DslApiUtils.kt create mode 100644 subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/util/FileUtils.kt diff --git a/core/api/core.api b/core/api/core.api index 2399880b..fffcb877 100644 --- a/core/api/core.api +++ b/core/api/core.api @@ -4513,6 +4513,32 @@ public abstract interface class org/jetbrains/dokka/renderers/Renderer { public abstract fun render (Lorg/jetbrains/dokka/pages/RootPageNode;)V } +public final class org/jetbrains/dokka/transformers/documentation/ClashingDriIdentifier : org/jetbrains/dokka/model/properties/ExtraProperty { + public static final field Companion Lorg/jetbrains/dokka/transformers/documentation/ClashingDriIdentifier$Companion; + public fun (Ljava/util/Set;)V + public final fun component1 ()Ljava/util/Set; + public final fun copy (Ljava/util/Set;)Lorg/jetbrains/dokka/transformers/documentation/ClashingDriIdentifier; + public static synthetic fun copy$default (Lorg/jetbrains/dokka/transformers/documentation/ClashingDriIdentifier;Ljava/util/Set;ILjava/lang/Object;)Lorg/jetbrains/dokka/transformers/documentation/ClashingDriIdentifier; + public fun equals (Ljava/lang/Object;)Z + public fun getKey ()Lorg/jetbrains/dokka/model/properties/ExtraProperty$Key; + public final fun getValue ()Ljava/util/Set; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/jetbrains/dokka/transformers/documentation/ClashingDriIdentifier$Companion : org/jetbrains/dokka/model/properties/ExtraProperty$Key { + public synthetic fun mergeStrategyFor (Ljava/lang/Object;Ljava/lang/Object;)Lorg/jetbrains/dokka/model/properties/MergeStrategy; + public fun mergeStrategyFor (Lorg/jetbrains/dokka/transformers/documentation/ClashingDriIdentifier;Lorg/jetbrains/dokka/transformers/documentation/ClashingDriIdentifier;)Lorg/jetbrains/dokka/model/properties/MergeStrategy; +} + +public final class org/jetbrains/dokka/transformers/documentation/DefaultDocumentableMerger : org/jetbrains/dokka/transformers/documentation/DocumentableMerger { + public fun (Lorg/jetbrains/dokka/plugability/DokkaContext;)V + public fun invoke (Ljava/util/Collection;)Lorg/jetbrains/dokka/model/DModule; + public final fun mergeWith (Lorg/jetbrains/dokka/model/DFunction;Lorg/jetbrains/dokka/model/DFunction;)Lorg/jetbrains/dokka/model/DFunction; + public final fun mergeWith (Lorg/jetbrains/dokka/model/DPackage;Lorg/jetbrains/dokka/model/DPackage;)Lorg/jetbrains/dokka/model/DPackage; + public final fun mergeWith (Lorg/jetbrains/dokka/model/DProperty;Lorg/jetbrains/dokka/model/DProperty;)Lorg/jetbrains/dokka/model/DProperty; +} + public abstract interface class org/jetbrains/dokka/transformers/documentation/DocumentableMerger { public abstract fun invoke (Ljava/util/Collection;)Lorg/jetbrains/dokka/model/DModule; } diff --git a/core/src/main/kotlin/transformers/documentation/DefaultDocumentableMerger.kt b/core/src/main/kotlin/transformers/documentation/DefaultDocumentableMerger.kt new file mode 100644 index 00000000..fe1e5d64 --- /dev/null +++ b/core/src/main/kotlin/transformers/documentation/DefaultDocumentableMerger.kt @@ -0,0 +1,307 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.transformers.documentation + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.ExtraProperty +import org.jetbrains.dokka.model.properties.MergeStrategy +import org.jetbrains.dokka.model.properties.mergeExtras +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.CoreExtensions + +/** + * Should NOT be used outside of Dokka itself, there are no guarantees + * this class will continue to exist in future releases. + * + * This class resides in core because it is a non-trivial implementation + * for a core extension [CoreExtensions.documentableMerger], which is needed + * in modules that only have access to `dokka-core`. + */ +@InternalDokkaApi +public class DefaultDocumentableMerger(context: DokkaContext) : DocumentableMerger { + private val dependencyInfo = context.getDependencyInfo() + + override fun invoke(modules: Collection): DModule? = + modules.reduceOrNull { left, right -> + val list = listOf(left, right) + DModule( + name = modules.map { it.name }.distinct().joinToString("|"), + packages = merge( + list.flatMap { it.packages } + ) { pck1, pck2 -> pck1.mergeWith(pck2) }, + documentation = list.map { it.documentation }.flatMap { it.entries }.associate { (k, v) -> k to v }, + expectPresentInSet = list.firstNotNullOfOrNull { it.expectPresentInSet }, + sourceSets = list.flatMap { it.sourceSets }.toSet() + ).mergeExtras(left, right) + } + + private fun DokkaContext.getDependencyInfo() + : Map> { + + fun getDependencies(sourceSet: DokkaConfiguration.DokkaSourceSet): List = + listOf(sourceSet) + configuration.sourceSets.filter { + it.sourceSetID in sourceSet.dependentSourceSets + }.flatMap { getDependencies(it) } + + return configuration.sourceSets.associateWith { getDependencies(it) } + } + + private fun merge(elements: List, reducer: (T, T) -> T): List = + elements.groupingBy { it.dri } + .reduce { _, left, right -> reducer(left, right) } + .values.toList() + + private fun mergeExpectActual( + elements: List, + reducer: (T, T) -> T + ): List where T : Documentable, T : WithSources { + + fun mergeClashingElements(elements: List>>): List = + elements.groupBy { it.first.name }.values.flatMap { listOfDocumentableToSSIds -> + val merged = listOfDocumentableToSSIds.map { (documentable, sourceSets) -> + when (documentable) { + is DClass -> documentable.copy( + extra = documentable.extra + ClashingDriIdentifier( + sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet()) + ) + ) + is DObject -> documentable.copy( + extra = documentable.extra + ClashingDriIdentifier( + sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet()) + ) + ) + is DAnnotation -> documentable.copy( + extra = documentable.extra + ClashingDriIdentifier( + sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet()) + ) + ) + is DInterface -> documentable.copy( + extra = documentable.extra + ClashingDriIdentifier( + sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet()) + ) + ) + is DEnum -> documentable.copy( + extra = documentable.extra + ClashingDriIdentifier( + sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet()) + ) + ) + is DFunction -> documentable.copy( + extra = documentable.extra + ClashingDriIdentifier( + sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet()) + ) + ) + is DProperty -> documentable.copy( + extra = documentable.extra + ClashingDriIdentifier( + sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet()) + ) + ) + else -> documentable + } + } + @Suppress("UNCHECKED_CAST") + merged as List + } + + + fun analyzeExpectActual(sameDriElements: List): List { + val (expects, actuals) = sameDriElements.partition { it.expectPresentInSet != null } + val groupedByOwnExpectWithActualSourceSetIds = expects.map { expect -> + val actualsForGivenExpect = actuals.filter { actual -> + dependencyInfo[actual.sourceSets.single()] + ?.contains(expect.expectPresentInSet!!) + ?: throw IllegalStateException("Cannot resolve expect/actual relation for ${actual.name}") + } + (listOf(expect) + actualsForGivenExpect) to actualsForGivenExpect.flatMap { it.sourceSets }.toSet() + } + val reducedToOneDocumentableWithActualSourceSetIds = + groupedByOwnExpectWithActualSourceSetIds.map { it.first.reduce(reducer) to it.second } + return reducedToOneDocumentableWithActualSourceSetIds.let(::mergeClashingElements) + } + + + return elements.partition { + (it as? WithIsExpectActual)?.isExpectActual ?: false + }.let { (expectActuals, notExpectActuals) -> + notExpectActuals.map { it to it.sourceSets } + .groupBy { it.first.dri }.values.flatMap(::mergeClashingElements) + + expectActuals.groupBy { it.dri }.values.flatMap(::analyzeExpectActual) + } + } + + public fun DPackage.mergeWith(other: DPackage): DPackage = copy( + functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, + properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, + classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + typealiases = merge(typealiases + other.typealiases) { ta1, ta2 -> ta1.mergeWith(ta2) }, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + public fun DFunction.mergeWith(other: DFunction): DFunction = copy( + parameters = merge(this.parameters + other.parameters) { p1, p2 -> p1.mergeWith(p2) }, + receiver = receiver?.let { r -> other.receiver?.let { r.mergeWith(it) } ?: r } ?: other.receiver, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources + other.sources, + visibility = visibility + other.visibility, + modifier = modifier + other.modifier, + sourceSets = sourceSets + other.sourceSets, + generics = merge(generics + other.generics) { tp1, tp2 -> tp1.mergeWith(tp2) }, + ).mergeExtras(this, other) + + public fun DProperty.mergeWith(other: DProperty): DProperty = copy( + receiver = receiver?.let { r -> other.receiver?.let { r.mergeWith(it) } ?: r } ?: other.receiver, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources + other.sources, + visibility = visibility + other.visibility, + modifier = modifier + other.modifier, + sourceSets = sourceSets + other.sourceSets, + getter = getter?.let { g -> other.getter?.let { g.mergeWith(it) } ?: g } ?: other.getter, + setter = setter?.let { s -> other.setter?.let { s.mergeWith(it) } ?: s } ?: other.setter, + generics = merge(generics + other.generics) { tp1, tp2 -> tp1.mergeWith(tp2) }, + ).mergeExtras(this, other) + + private fun DClasslike.mergeWith(other: DClasslike): DClasslike = when { + this is DClass && other is DClass -> mergeWith(other) + this is DEnum && other is DEnum -> mergeWith(other) + this is DInterface && other is DInterface -> mergeWith(other) + this is DObject && other is DObject -> mergeWith(other) + this is DAnnotation && other is DAnnotation -> mergeWith(other) + else -> throw IllegalStateException("${this::class.qualifiedName} ${this.name} cannot be merged with ${other::class.qualifiedName} ${other.name}") + } + + private fun DClass.mergeWith(other: DClass): DClass = copy( + constructors = mergeExpectActual( + constructors + other.constructors + ) { f1, f2 -> f1.mergeWith(f2) }, + functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, + properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, + classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, + companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, + generics = merge(generics + other.generics) { tp1, tp2 -> tp1.mergeWith(tp2) }, + modifier = modifier + other.modifier, + supertypes = supertypes + other.supertypes, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources + other.sources, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + private fun DEnum.mergeWith(other: DEnum): DEnum = copy( + entries = merge(entries + other.entries) { ee1, ee2 -> ee1.mergeWith(ee2) }, + constructors = mergeExpectActual( + constructors + other.constructors + ) { f1, f2 -> f1.mergeWith(f2) }, + functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, + properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, + classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, + companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, + supertypes = supertypes + other.supertypes, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources + other.sources, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + private fun DEnumEntry.mergeWith(other: DEnumEntry): DEnumEntry = copy( + functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, + properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, + classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + private fun DObject.mergeWith(other: DObject): DObject = copy( + functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, + properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, + classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, + supertypes = supertypes + other.supertypes, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources + other.sources, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + private fun DInterface.mergeWith(other: DInterface): DInterface = copy( + functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, + properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, + classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, + companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, + generics = merge(generics + other.generics) { tp1, tp2 -> tp1.mergeWith(tp2) }, + supertypes = supertypes + other.supertypes, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources + other.sources, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + private fun DAnnotation.mergeWith(other: DAnnotation): DAnnotation = copy( + constructors = mergeExpectActual( + constructors + other.constructors + ) { f1, f2 -> f1.mergeWith(f2) }, + functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, + properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, + classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, + companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources + other.sources, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets, + generics = merge(generics + other.generics) { tp1, tp2 -> tp1.mergeWith(tp2) } + ).mergeExtras(this, other) + + private fun DParameter.mergeWith(other: DParameter): DParameter = copy( + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + private fun DTypeParameter.mergeWith(other: DTypeParameter): DTypeParameter = copy( + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + private fun DTypeAlias.mergeWith(other: DTypeAlias): DTypeAlias = copy( + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + underlyingType = underlyingType + other.underlyingType, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) +} + +public data class ClashingDriIdentifier(val value: Set) : ExtraProperty { + public companion object : ExtraProperty.Key { + override fun mergeStrategyFor( + left: ClashingDriIdentifier, + right: ClashingDriIdentifier + ): MergeStrategy = + MergeStrategy.Replace(ClashingDriIdentifier(left.value + right.value)) + } + + override val key: ExtraProperty.Key = ClashingDriIdentifier +} + +// TODO [beresnev] remove this copy-paste and use the same method from stdlib instead after updating to 1.5 +private inline fun Iterable.firstNotNullOfOrNull(transform: (T) -> R?): R? { + for (element in this) { + val result = transform(element) + if (result != null) { + return result + } + } + return null +} diff --git a/core/test-api/src/main/kotlin/testApi/testRunner/TestRunner.kt b/core/test-api/src/main/kotlin/testApi/testRunner/TestRunner.kt index 23300190..0958ea14 100644 --- a/core/test-api/src/main/kotlin/testApi/testRunner/TestRunner.kt +++ b/core/test-api/src/main/kotlin/testApi/testRunner/TestRunner.kt @@ -113,7 +113,7 @@ public abstract class AbstractTest, D : Dokk block(tempDir) } finally { if (cleanUpAfterUse) { - tempDir.delete() + tempDir.deleteRecursively() } } } diff --git a/plugins/base/api/base.api b/plugins/base/api/base.api index 788444b9..ae872558 100644 --- a/plugins/base/api/base.api +++ b/plugins/base/api/base.api @@ -1102,24 +1102,6 @@ public final class org/jetbrains/dokka/base/transformers/documentables/CallableE public fun mergeStrategyFor (Lorg/jetbrains/dokka/base/transformers/documentables/CallableExtensions;Lorg/jetbrains/dokka/base/transformers/documentables/CallableExtensions;)Lorg/jetbrains/dokka/model/properties/MergeStrategy; } -public final class org/jetbrains/dokka/base/transformers/documentables/ClashingDriIdentifier : org/jetbrains/dokka/model/properties/ExtraProperty { - public static final field Companion Lorg/jetbrains/dokka/base/transformers/documentables/ClashingDriIdentifier$Companion; - public fun (Ljava/util/Set;)V - public final fun component1 ()Ljava/util/Set; - public final fun copy (Ljava/util/Set;)Lorg/jetbrains/dokka/base/transformers/documentables/ClashingDriIdentifier; - public static synthetic fun copy$default (Lorg/jetbrains/dokka/base/transformers/documentables/ClashingDriIdentifier;Ljava/util/Set;ILjava/lang/Object;)Lorg/jetbrains/dokka/base/transformers/documentables/ClashingDriIdentifier; - public fun equals (Ljava/lang/Object;)Z - public fun getKey ()Lorg/jetbrains/dokka/model/properties/ExtraProperty$Key; - public final fun getValue ()Ljava/util/Set; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class org/jetbrains/dokka/base/transformers/documentables/ClashingDriIdentifier$Companion : org/jetbrains/dokka/model/properties/ExtraProperty$Key { - public synthetic fun mergeStrategyFor (Ljava/lang/Object;Ljava/lang/Object;)Lorg/jetbrains/dokka/model/properties/MergeStrategy; - 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/base/transformers/documentables/SuppressedByConditionDocumentableFilterTransformer { public fun (Lorg/jetbrains/dokka/plugability/DokkaContext;)V public fun shouldBeSuppressed (Lorg/jetbrains/dokka/model/Documentable;)Z diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index dfec2c15..ca86d4d5 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -33,10 +33,7 @@ import org.jetbrains.dokka.base.translators.documentables.DefaultDocumentableToP import org.jetbrains.dokka.generation.Generation import org.jetbrains.dokka.plugability.* import org.jetbrains.dokka.renderers.Renderer -import org.jetbrains.dokka.transformers.documentation.DocumentableMerger -import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator -import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer -import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer +import org.jetbrains.dokka.transformers.documentation.* import org.jetbrains.dokka.transformers.pages.PageTransformer @Suppress("unused") diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ClashingDriIdentifier.kt b/plugins/base/src/main/kotlin/transformers/documentables/ClashingDriIdentifier.kt new file mode 100644 index 00000000..e9c7342e --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/ClashingDriIdentifier.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.base.transformers.documentables + +@Deprecated( + message = "Declaration was moved to dokka-core", + replaceWith = ReplaceWith("org.jetbrains.dokka.transformers.documentation.ClashingDriIdentifier"), + level = DeprecationLevel.WARNING // TODO change to error after Kotlin 1.9.20 +) +public typealias ClashingDriIdentifier = org.jetbrains.dokka.transformers.documentation.ClashingDriIdentifier diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt b/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt deleted file mode 100644 index ec53df78..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.base.utils.firstNotNullOfOrNull -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.properties.ExtraProperty -import org.jetbrains.dokka.model.properties.MergeStrategy -import org.jetbrains.dokka.model.properties.mergeExtras -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.DocumentableMerger - -internal class DefaultDocumentableMerger(val context: DokkaContext) : DocumentableMerger { - private val dependencyInfo = context.getDependencyInfo() - - override fun invoke(modules: Collection): DModule? = - modules.reduceOrNull { left, right -> - val list = listOf(left, right) - DModule( - name = modules.map { it.name }.distinct().joinToString("|"), - packages = merge( - list.flatMap { it.packages } - ) { pck1, pck2 -> pck1.mergeWith(pck2) }, - documentation = list.map { it.documentation }.flatMap { it.entries }.associate { (k, v) -> k to v }, - expectPresentInSet = list.firstNotNullOfOrNull { it.expectPresentInSet }, - sourceSets = list.flatMap { it.sourceSets }.toSet() - ).mergeExtras(left, right) - } - - private fun DokkaContext.getDependencyInfo() - : Map> { - - fun getDependencies(sourceSet: DokkaConfiguration.DokkaSourceSet): List = - listOf(sourceSet) + configuration.sourceSets.filter { - it.sourceSetID in sourceSet.dependentSourceSets - }.flatMap { getDependencies(it) } - - return configuration.sourceSets.associateWith { getDependencies(it) } - } - - private fun merge(elements: List, reducer: (T, T) -> T): List = - elements.groupingBy { it.dri } - .reduce { _, left, right -> reducer(left, right) } - .values.toList() - - private fun mergeExpectActual( - elements: List, - reducer: (T, T) -> T - ): List where T : Documentable, T : WithSources { - - fun mergeClashingElements(elements: List>>): List = - elements.groupBy { it.first.name }.values.flatMap { listOfDocumentableToSSIds -> - val merged = listOfDocumentableToSSIds.map { (documentable, sourceSets) -> - when (documentable) { - is DClass -> documentable.copy( - extra = documentable.extra + ClashingDriIdentifier( - sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet()) - ) - ) - is DObject -> documentable.copy( - extra = documentable.extra + ClashingDriIdentifier( - sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet()) - ) - ) - is DAnnotation -> documentable.copy( - extra = documentable.extra + ClashingDriIdentifier( - sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet()) - ) - ) - is DInterface -> documentable.copy( - extra = documentable.extra + ClashingDriIdentifier( - sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet()) - ) - ) - is DEnum -> documentable.copy( - extra = documentable.extra + ClashingDriIdentifier( - sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet()) - ) - ) - is DFunction -> documentable.copy( - extra = documentable.extra + ClashingDriIdentifier( - sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet()) - ) - ) - is DProperty -> documentable.copy( - extra = documentable.extra + ClashingDriIdentifier( - sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet()) - ) - ) - else -> documentable - } - } - @Suppress("UNCHECKED_CAST") - merged as List - } - - - fun analyzeExpectActual(sameDriElements: List): List { - val (expects, actuals) = sameDriElements.partition { it.expectPresentInSet != null } - val groupedByOwnExpectWithActualSourceSetIds = expects.map { expect -> - val actualsForGivenExpect = actuals.filter { actual -> - dependencyInfo[actual.sourceSets.single()] - ?.contains(expect.expectPresentInSet!!) - ?: throw IllegalStateException("Cannot resolve expect/actual relation for ${actual.name}") - } - (listOf(expect) + actualsForGivenExpect) to actualsForGivenExpect.flatMap { it.sourceSets }.toSet() - } - val reducedToOneDocumentableWithActualSourceSetIds = - groupedByOwnExpectWithActualSourceSetIds.map { it.first.reduce(reducer) to it.second } - return reducedToOneDocumentableWithActualSourceSetIds.let(::mergeClashingElements) - } - - - return elements.partition { - (it as? WithIsExpectActual)?.isExpectActual ?: false - }.let { (expectActuals, notExpectActuals) -> - notExpectActuals.map { it to it.sourceSets } - .groupBy { it.first.dri }.values.flatMap(::mergeClashingElements) + - expectActuals.groupBy { it.dri }.values.flatMap(::analyzeExpectActual) - } - } - - fun DPackage.mergeWith(other: DPackage): DPackage = copy( - functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, - properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, - classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - typealiases = merge(typealiases + other.typealiases) { ta1, ta2 -> ta1.mergeWith(ta2) }, - sourceSets = sourceSets + other.sourceSets - ).mergeExtras(this, other) - - fun DFunction.mergeWith(other: DFunction): DFunction = copy( - parameters = merge(this.parameters + other.parameters) { p1, p2 -> p1.mergeWith(p2) }, - receiver = receiver?.let { r -> other.receiver?.let { r.mergeWith(it) } ?: r } ?: other.receiver, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sources = sources + other.sources, - visibility = visibility + other.visibility, - modifier = modifier + other.modifier, - sourceSets = sourceSets + other.sourceSets, - generics = merge(generics + other.generics) { tp1, tp2 -> tp1.mergeWith(tp2) }, - ).mergeExtras(this, other) - - fun DProperty.mergeWith(other: DProperty): DProperty = copy( - receiver = receiver?.let { r -> other.receiver?.let { r.mergeWith(it) } ?: r } ?: other.receiver, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sources = sources + other.sources, - visibility = visibility + other.visibility, - modifier = modifier + other.modifier, - sourceSets = sourceSets + other.sourceSets, - getter = getter?.let { g -> other.getter?.let { g.mergeWith(it) } ?: g } ?: other.getter, - setter = setter?.let { s -> other.setter?.let { s.mergeWith(it) } ?: s } ?: other.setter, - generics = merge(generics + other.generics) { tp1, tp2 -> tp1.mergeWith(tp2) }, - ).mergeExtras(this, other) - - fun DClasslike.mergeWith(other: DClasslike): DClasslike = when { - this is DClass && other is DClass -> mergeWith(other) - this is DEnum && other is DEnum -> mergeWith(other) - this is DInterface && other is DInterface -> mergeWith(other) - this is DObject && other is DObject -> mergeWith(other) - this is DAnnotation && other is DAnnotation -> mergeWith(other) - else -> throw IllegalStateException("${this::class.qualifiedName} ${this.name} cannot be merged with ${other::class.qualifiedName} ${other.name}") - } - - fun DClass.mergeWith(other: DClass): DClass = copy( - constructors = mergeExpectActual( - constructors + other.constructors - ) { f1, f2 -> f1.mergeWith(f2) }, - functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, - properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, - classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, - companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, - generics = merge(generics + other.generics) { tp1, tp2 -> tp1.mergeWith(tp2) }, - modifier = modifier + other.modifier, - supertypes = supertypes + other.supertypes, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sources = sources + other.sources, - visibility = visibility + other.visibility, - sourceSets = sourceSets + other.sourceSets - ).mergeExtras(this, other) - - fun DEnum.mergeWith(other: DEnum): DEnum = copy( - entries = merge(entries + other.entries) { ee1, ee2 -> ee1.mergeWith(ee2) }, - constructors = mergeExpectActual( - constructors + other.constructors - ) { f1, f2 -> f1.mergeWith(f2) }, - functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, - properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, - classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, - companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, - supertypes = supertypes + other.supertypes, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sources = sources + other.sources, - visibility = visibility + other.visibility, - sourceSets = sourceSets + other.sourceSets - ).mergeExtras(this, other) - - fun DEnumEntry.mergeWith(other: DEnumEntry): DEnumEntry = copy( - functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, - properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, - classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sourceSets = sourceSets + other.sourceSets - ).mergeExtras(this, other) - - fun DObject.mergeWith(other: DObject): DObject = copy( - functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, - properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, - classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, - supertypes = supertypes + other.supertypes, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sources = sources + other.sources, - visibility = visibility + other.visibility, - sourceSets = sourceSets + other.sourceSets - ).mergeExtras(this, other) - - fun DInterface.mergeWith(other: DInterface): DInterface = copy( - functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, - properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, - classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, - companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, - generics = merge(generics + other.generics) { tp1, tp2 -> tp1.mergeWith(tp2) }, - supertypes = supertypes + other.supertypes, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sources = sources + other.sources, - visibility = visibility + other.visibility, - sourceSets = sourceSets + other.sourceSets - ).mergeExtras(this, other) - - fun DAnnotation.mergeWith(other: DAnnotation): DAnnotation = copy( - constructors = mergeExpectActual( - constructors + other.constructors - ) { f1, f2 -> f1.mergeWith(f2) }, - functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, - properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, - classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, - companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sources = sources + other.sources, - visibility = visibility + other.visibility, - sourceSets = sourceSets + other.sourceSets, - generics = merge(generics + other.generics) { tp1, tp2 -> tp1.mergeWith(tp2) } - ).mergeExtras(this, other) - - fun DParameter.mergeWith(other: DParameter): DParameter = copy( - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sourceSets = sourceSets + other.sourceSets - ).mergeExtras(this, other) - - fun DTypeParameter.mergeWith(other: DTypeParameter): DTypeParameter = copy( - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sourceSets = sourceSets + other.sourceSets - ).mergeExtras(this, other) - - fun DTypeAlias.mergeWith(other: DTypeAlias): DTypeAlias = copy( - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - underlyingType = underlyingType + other.underlyingType, - visibility = visibility + other.visibility, - sourceSets = sourceSets + other.sourceSets - ).mergeExtras(this, other) -} - -public data class ClashingDriIdentifier(val value: Set) : ExtraProperty { - public companion object : ExtraProperty.Key { - override fun mergeStrategyFor( - left: ClashingDriIdentifier, - right: ClashingDriIdentifier - ): MergeStrategy = - MergeStrategy.Replace(ClashingDriIdentifier(left.value + right.value)) - } - - override val key: ExtraProperty.Key = ClashingDriIdentifier -} diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt index 58abee56..5c8ac512 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt @@ -9,7 +9,7 @@ import org.jetbrains.dokka.base.DokkaBaseConfiguration import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint import org.jetbrains.dokka.base.signatures.SignatureProvider import org.jetbrains.dokka.base.transformers.documentables.CallableExtensions -import org.jetbrains.dokka.base.transformers.documentables.ClashingDriIdentifier +import org.jetbrains.dokka.transformers.documentation.ClashingDriIdentifier import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter import org.jetbrains.dokka.base.transformers.pages.tags.CustomTagContentProvider import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder diff --git a/subprojects/analysis-kotlin-api/build.gradle.kts b/subprojects/analysis-kotlin-api/build.gradle.kts index 58247479..bf3b5b3c 100644 --- a/subprojects/analysis-kotlin-api/build.gradle.kts +++ b/subprojects/analysis-kotlin-api/build.gradle.kts @@ -7,10 +7,30 @@ import org.jetbrains.registerDokkaArtifactPublication plugins { id("org.jetbrains.conventions.kotlin-jvm") id("org.jetbrains.conventions.maven-publish") + `java-test-fixtures` } dependencies { compileOnly(projects.core) + + testFixturesApi(projects.core) + + testImplementation(kotlin("test")) + testImplementation(projects.subprojects.analysisKotlinDescriptors) +} + +disableTestFixturesPublishing() + +/** + * Test fixtures are automatically published by default, which at this moment in time is unwanted + * as the test api is unstable and is internal to the Dokka project, so it shouldn't be used outside of it. + * + * @see https://docs.gradle.org/current/userguide/java_testing.html#ex-disable-publishing-of-test-fixtures-variants + */ +fun disableTestFixturesPublishing() { + val javaComponent = components["java"] as AdhocComponentWithVariants + javaComponent.withVariantsFromConfiguration(configurations["testFixturesApiElements"]) { skip() } + javaComponent.withVariantsFromConfiguration(configurations["testFixturesRuntimeElements"]) { skip() } } registerDokkaArtifactPublication("analysisKotlinApi") { diff --git a/subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/java/SampleJavaAnalysisTest.kt b/subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/java/SampleJavaAnalysisTest.kt new file mode 100644 index 00000000..f6632f60 --- /dev/null +++ b/subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/java/SampleJavaAnalysisTest.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.jvm.java + +import org.jetbrains.dokka.analysis.test.api.javaTestProject +import org.jetbrains.dokka.analysis.test.api.parse +import kotlin.test.Test +import kotlin.test.assertEquals + +class SampleJavaAnalysisTest { + + /** + * Used as a sample for [javaTestProject] + */ + @Test + fun sample() { + val testProject = javaTestProject { + dokkaConfiguration { + moduleName = "java-module-name-for-unit-test" + + javaSourceSet { + // source-set specific configuration + } + } + javaFile(pathFromSrc = "org/jetbrains/dokka/test/java/Bar.java") { + +""" + public class Bar { + public static void bar() { + System.out.println("Bar"); + } + } + """ + } + } + + val module = testProject.parse() + assertEquals("java-module-name-for-unit-test", module.name) + assertEquals(1, module.packages.size) + + val pckg = module.packages[0] + assertEquals("org.jetbrains.dokka.test.java", pckg.name) + assertEquals(1, pckg.classlikes.size) + + val fooClass = pckg.classlikes[0] + assertEquals("Bar", fooClass.name) + } +} diff --git a/subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/kotlin/SampleKotlinJvmAnalysisTest.kt b/subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/kotlin/SampleKotlinJvmAnalysisTest.kt new file mode 100644 index 00000000..6c73af1f --- /dev/null +++ b/subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/kotlin/SampleKotlinJvmAnalysisTest.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.jvm.kotlin + +import org.jetbrains.dokka.analysis.test.api.kotlinJvmTestProject +import org.jetbrains.dokka.analysis.test.api.parse +import kotlin.test.Test +import kotlin.test.assertEquals + +class SampleKotlinJvmAnalysisTest { + + /** + * Used as a sample for [kotlinJvmTestProject] + */ + @Test + fun sample() { + val testProject = kotlinJvmTestProject { + dokkaConfiguration { + moduleName = "kotlin-jvm-module-name-for-unit-test" + + kotlinSourceSet { + // source-set specific configuration + } + } + ktFile(pathFromSrc = "org/jetbrains/dokka/test/kotlin/MyFile.kt") { + +"public class Foo {}" + } + } + + val module = testProject.parse() + assertEquals("kotlin-jvm-module-name-for-unit-test", module.name) + assertEquals(1, module.packages.size) + + val pckg = module.packages[0] + assertEquals("org.jetbrains.dokka.test.kotlin", pckg.name) + assertEquals(1, pckg.classlikes.size) + + val fooClass = pckg.classlikes[0] + assertEquals("Foo", fooClass.name) + } +} diff --git a/subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/mixed/SampleMixedJvmAnalysisTest.kt b/subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/mixed/SampleMixedJvmAnalysisTest.kt new file mode 100644 index 00000000..fec2ceb8 --- /dev/null +++ b/subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/mixed/SampleMixedJvmAnalysisTest.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.jvm.mixed + +import org.jetbrains.dokka.analysis.test.api.mixedJvmTestProject +import org.jetbrains.dokka.analysis.test.api.parse +import kotlin.test.Test +import kotlin.test.assertEquals + +class SampleMixedJvmAnalysisTest { + + /** + * Used as a sample for [mixedJvmTestProject] + */ + @Test + fun sample() { + val testProject = mixedJvmTestProject { + dokkaConfiguration { + moduleName = "mixed-project-module-name-for-unit-test" + + jvmSourceSet { + // source-set specific configuration + } + } + + kotlinSourceDirectory { + ktFile(pathFromSrc = "test/MyFile.kt") { + +"fun foo(): String = \"Foo\"" + } + javaFile(pathFromSrc = "test/MyJavaFileInKotlin.java") { + +""" + public class MyJavaFileInKotlin { + public static void bar() { + System.out.println("Bar"); + } + } + """ + } + } + + javaSourceDirectory { + ktFile(pathFromSrc = "test/MyFile.kt") { + +"fun bar(): String = \"Bar\"" + } + javaFile(pathFromSrc = "test/MyJavaFileInJava.java") { + +""" + public class MyJavaFileInJava { + public static void bar() { + System.out.println("Bar"); + } + } + """ + } + } + } + + val module = testProject.parse() + assertEquals("mixed-project-module-name-for-unit-test", module.name) + assertEquals(1, module.packages.size) + + val pckg = module.packages[0] + assertEquals("test", pckg.name) + + assertEquals(2, pckg.classlikes.size) + assertEquals(2, pckg.functions.size) + + val firstClasslike = pckg.classlikes[0] + assertEquals("MyJavaFileInKotlin", firstClasslike.name) + + val secondClasslike = pckg.classlikes[1] + assertEquals("MyJavaFileInJava", secondClasslike.name) + + val firstFunction = pckg.functions[0] + assertEquals("bar", firstFunction.name) + + val secondFunction = pckg.functions[1] + assertEquals("foo", secondFunction.name) + } +} diff --git a/subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/moduledocs/PackageDocumentationAnalysisTest.kt b/subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/moduledocs/PackageDocumentationAnalysisTest.kt new file mode 100644 index 00000000..55507023 --- /dev/null +++ b/subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/moduledocs/PackageDocumentationAnalysisTest.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.moduledocs + +import org.jetbrains.dokka.analysis.test.api.kotlinJvmTestProject +import org.jetbrains.dokka.analysis.test.api.useServices +import org.jetbrains.dokka.model.doc.CodeInline +import org.jetbrains.dokka.model.doc.Description +import org.jetbrains.dokka.model.doc.P +import org.jetbrains.dokka.model.doc.Text +import kotlin.test.Test +import kotlin.test.assertEquals + +class PackageDocumentationAnalysisTest { + + @Test + fun `should parse include description for a nested package in kotlin-jvm`() { + val testProject = kotlinJvmTestProject { + dokkaConfiguration { + kotlinSourceSet { + includes = setOf("/documentation/cool-package-description.md") + } + } + + ktFile(pathFromSrc = "org/jetbrains/dokka/pckg/docs/test/TestFile.kt") { + +"class TestFile {}" + } + + mdFile(pathFromProjectRoot = "/documentation/cool-package-description.md") { + +""" + # Package org.jetbrains.dokka.pckg.docs.test + + This is my test description for the package `org.jetbrains.dokka.pckg.docs.test`, + which contains only one file named TestFile.kt. It has one empty class. + """ + } + } + + testProject.useServices { context -> + val pckg = context.module.packages.single { it.name == "org.jetbrains.dokka.pckg.docs.test" } + + val allPackageDocs = moduleAndPackageDocumentationReader.read(pckg) + assertEquals(1, allPackageDocs.size) + + val sourceSetPackageDocs = allPackageDocs.entries.single().value + assertEquals(1, sourceSetPackageDocs.children.size) + + val descriptionTag = sourceSetPackageDocs.children.single() as Description + assertEquals(1, descriptionTag.children.size) + + val paragraphTag = descriptionTag.children.single() as P + assertEquals(3, paragraphTag.children.size) + + val expectedParagraphChildren = listOf( + Text("This is my test description for the package "), + CodeInline(children = listOf(Text( + "org.jetbrains.dokka.pckg.docs.test" + ))), + Text(", which contains only one file named TestFile.kt. It has one empty class.") + ) + assertEquals(expectedParagraphChildren, paragraphTag.children) + } + } +} diff --git a/subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/sample/SampleAnalysisTest.kt b/subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/sample/SampleAnalysisTest.kt new file mode 100644 index 00000000..618e28a8 --- /dev/null +++ b/subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/sample/SampleAnalysisTest.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.sample + +import org.jetbrains.dokka.analysis.test.api.kotlinJvmTestProject +import org.jetbrains.dokka.analysis.test.api.useServices +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class SampleAnalysisTest { + + @Test + fun `should return sources of a kotlin sample`() { + val testProject = kotlinJvmTestProject { + dokkaConfiguration { + kotlinSourceSet { + additionalSourceRoots = setOf("/samples") + } + } + sampleFile("/samples/stringListOf-sample.kt", fqPackageName = "org.jetbrains.dokka.sample.generator") { + +""" + import org.jetbrains.dokka.DokkaConfiguration + import org.jetbrains.dokka.DokkaGenerator + import org.jetbrains.dokka.utilities.DokkaLogger + + fun runGenerator(configuration: DokkaConfiguration, logger: DokkaLogger) { + DokkaGenerator(configuration, logger).generate() + } + """ + } + } + + testProject.use