diff options
20 files changed, 308 insertions, 60 deletions
diff --git a/examples/gradle/dokka-gradle-example/src/main/kotlin/demo/HelloWorld.kt b/examples/gradle/dokka-gradle-example/src/main/kotlin/demo/HelloWorld.kt index 172e18f7..e37fcfb7 100644 --- a/examples/gradle/dokka-gradle-example/src/main/kotlin/demo/HelloWorld.kt +++ b/examples/gradle/dokka-gradle-example/src/main/kotlin/demo/HelloWorld.kt @@ -17,4 +17,4 @@ class Greeter(val name: String) { fun main(args: Array<String>) { Greeter(args[0]).greet() -} +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/model/ClassesTest.kt b/plugins/base/src/test/kotlin/model/ClassesTest.kt index 763809e8..c18dfafb 100644 --- a/plugins/base/src/test/kotlin/model/ClassesTest.kt +++ b/plugins/base/src/test/kotlin/model/ClassesTest.kt @@ -126,6 +126,21 @@ class ClassesTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "class type.name equals "Unit" } } + + with((this.companion).cast<DObject>()) { + name equals "Companion" + children counts 5 + + with((this / "x").cast<DProperty>()) { + name equals "x" + } + + with((this / "foo").cast<DFunction>()) { + name equals "foo" + parameters counts 0 + type.name equals "Unit" + } + } } } } diff --git a/plugins/javadoc/build.gradle.kts b/plugins/javadoc/build.gradle.kts index 2a04366b..ea882bb4 100644 --- a/plugins/javadoc/build.gradle.kts +++ b/plugins/javadoc/build.gradle.kts @@ -7,6 +7,7 @@ import org.jetbrains.registerDokkaArtifactPublication plugins { id("org.jetbrains.conventions.kotlin-jvm") id("org.jetbrains.conventions.maven-publish") + id("org.jetbrains.conventions.base-unit-test") } dependencies { @@ -22,7 +23,11 @@ dependencies { implementation(libs.kotlinx.coroutines.core) testImplementation(kotlin("test")) - testImplementation(projects.plugins.base.baseTestUtils) + symbolsTestConfiguration(project(path = ":subprojects:analysis-kotlin-symbols", configuration = "shadow")) + descriptorsTestConfiguration(project(path = ":subprojects:analysis-kotlin-descriptors", configuration = "shadow")) + testImplementation(projects.plugins.base.baseTestUtils) { + exclude(module = "analysis-kotlin-descriptors") + } testImplementation(projects.core.testApi) testImplementation(libs.jsoup) } diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/AbstractJavadocTemplateMapTest.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/AbstractJavadocTemplateMapTest.kt index 070f1834..60265e33 100644 --- a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/AbstractJavadocTemplateMapTest.kt +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/AbstractJavadocTemplateMapTest.kt @@ -28,6 +28,7 @@ internal abstract class AbstractJavadocTemplateMapTest : BaseAbstractTest() { DokkaConfiguration.ExternalDocumentationLink.jdk(8), DokkaConfiguration.ExternalDocumentationLink.kotlinStdlib() ) + classpath = listOfNotNull(jvmStdlibPath) } } } diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocAccessorNamingTest.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocAccessorNamingTest.kt index 3f7e1610..95ce4d27 100644 --- a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocAccessorNamingTest.kt +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocAccessorNamingTest.kt @@ -8,6 +8,7 @@ import org.jsoup.Jsoup import utils.TestOutputWriterPlugin import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertTrue internal class JavadocAccessorNamingTest : AbstractJavadocTemplateMapTest() { @@ -16,6 +17,7 @@ internal class JavadocAccessorNamingTest : AbstractJavadocTemplateMapTest() { sourceSets { sourceSet { sourceRoots = listOf("src/main/kotlin") + classpath = listOfNotNull(jvmStdlibPath) } } } @@ -54,13 +56,20 @@ internal class JavadocAccessorNamingTest : AbstractJavadocTemplateMapTest() { .select("code") .map { it.text() } .toSet() - - assertEquals(setOf( - "getIssuesFetched()", - "setIssuesFetched(Integer issuesFetched)", - "isFoo()", - "setFoo(String isFoo)", - ), props) + // In K2 name of accessors parameter is `value` + assertTrue( + setOf( + "getIssuesFetched()", + "setIssuesFetched(Integer issuesFetched)", + "isFoo()", + "setFoo(String isFoo)", + ) == props || setOf( + "getIssuesFetched()", + "setIssuesFetched(Integer value)", + "isFoo()", + "setFoo(String value)", + ) == props + ) val descriptionLinks = html .select("div.description") diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocAllClassesTemplateMapTest.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocAllClassesTemplateMapTest.kt index a399f523..8d7ff1ea 100644 --- a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocAllClassesTemplateMapTest.kt +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocAllClassesTemplateMapTest.kt @@ -8,11 +8,13 @@ import org.jetbrains.dokka.javadoc.pages.AllClassesPage import org.jetbrains.dokka.javadoc.pages.LinkJavadocListEntry import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.pages.ContentKind +import org.junit.jupiter.api.Tag import kotlin.test.Test import kotlin.test.assertEquals internal class JavadocAllClassesTemplateMapTest : AbstractJavadocTemplateMapTest() { @Test + @Tag("onlyDescriptors") // https://github.com/Kotlin/dokka/issues/3250 fun `two classes from different packages`() { dualTestTemplateMapInline( """ diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocIndexTest.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocIndexTest.kt index ec36d0e3..ba8f73cf 100644 --- a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocIndexTest.kt +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocIndexTest.kt @@ -7,6 +7,7 @@ package org.jetbrains.dokka.javadoc import org.jetbrains.dokka.javadoc.pages.IndexPage import org.jetbrains.dokka.javadoc.renderer.TemplateMap import org.jetbrains.dokka.links.DRI +import org.junit.jupiter.api.Tag import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -76,7 +77,21 @@ internal class JavadocIndexTest : AbstractJavadocTemplateMapTest() { AnnotationTarget.ANNOTATION_CLASS::class.java.methods.any { it.name == "describeConstable" } testIndexPages(commonTestQuery) { indexPages -> - assertEquals(if (hasAdditionalFunction()) 33 else 32, indexPages.sumBy { it.elements.size }) + assertEquals("A-index: a, A\n" + + "B-index: b\n" + + "C-index: c, ClassA, ClassB, ClassC, ClassC.InnerClass, ClassCEnum, compareTo\n" + + "D-index: d, D\n" + + "E-index: e, E, equals, equals\n" + + "F-index: f\n" + + "G-index: g, getDeclaringClass, getEntries, getName, getOrdinal\n" + + "H-index: h, hashCode, hashCode\n" + + "J-index: j\n" + + "K-index: k\n" + + "P-index: package0, package1\n" + + "T-index: toString, toString\n" + + "V-index: valueOf, values", + indexPages.joinToString("\n") { it.title + ": " + it.elements.joinToString { it.getId() } }) + assertEquals(if (hasAdditionalFunction()) 34 else 33, indexPages.sumBy { it.elements.size }) } } diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationTest.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationTest.kt index 60de0e3d..bef81372 100644 --- a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationTest.kt +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationTest.kt @@ -247,6 +247,7 @@ class JavadocLocationTest : BaseAbstractTest() { DokkaConfiguration.ExternalDocumentationLink.kotlinStdlib() ) analysisPlatform = "jvm" + classpath = listOfNotNull(jvmStdlibPath) } } } diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/validity/MultiplatformConfiguredCheckerTest.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/validity/MultiplatformConfiguredCheckerTest.kt index 0bd6c4d5..cea32026 100644 --- a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/validity/MultiplatformConfiguredCheckerTest.kt +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/validity/MultiplatformConfiguredCheckerTest.kt @@ -18,7 +18,7 @@ class MultiplatformConfiguredCheckerTest : BaseAbstractTest() { format = "javadoc" sourceSets { sourceSet { - sourceRoots = listOf("src") + sourceRoots = listOf("src/jvm") analysisPlatform = "jvm" externalDocumentationLinks = listOf( ExternalDocumentationLink("https://docs.oracle.com/javase/8/docs/api/"), @@ -26,7 +26,7 @@ class MultiplatformConfiguredCheckerTest : BaseAbstractTest() { ) } sourceSet { - sourceRoots = listOf("src") + sourceRoots = listOf("src/js") analysisPlatform = "js" externalDocumentationLinks = listOf( ExternalDocumentationLink("https://docs.oracle.com/javase/8/docs/api/"), @@ -54,7 +54,9 @@ class MultiplatformConfiguredCheckerTest : BaseAbstractTest() { fun `mpp config should fail for javadoc`() { testInline( """ - |/src/main/kotlin/example/Test.kt + |/src/jvm/kotlin/example/Test.kt + |class Test + |/src/js/kotlin/example/Test.kt |class Test """.trimMargin(), mppConfig ) { diff --git a/plugins/kotlin-as-java/build.gradle.kts b/plugins/kotlin-as-java/build.gradle.kts index 19a7bf78..0b855895 100644 --- a/plugins/kotlin-as-java/build.gradle.kts +++ b/plugins/kotlin-as-java/build.gradle.kts @@ -7,6 +7,7 @@ import org.jetbrains.registerDokkaArtifactPublication plugins { id("org.jetbrains.conventions.kotlin-jvm") id("org.jetbrains.conventions.maven-publish") + id("org.jetbrains.conventions.base-unit-test") } dependencies { @@ -20,7 +21,11 @@ dependencies { testImplementation(kotlin("test")) testImplementation(libs.jsoup) testImplementation(projects.plugins.base) - testImplementation(projects.plugins.base.baseTestUtils) + symbolsTestConfiguration(project(path = ":subprojects:analysis-kotlin-symbols", configuration = "shadow")) + descriptorsTestConfiguration(project(path = ":subprojects:analysis-kotlin-descriptors", configuration = "shadow")) + testImplementation(projects.plugins.base.baseTestUtils) { + exclude(module = "analysis-kotlin-descriptors") + } testImplementation(projects.core.contentMatcherTestUtils) testImplementation(projects.core.testApi) } diff --git a/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt b/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt index f6124f88..93d5c1b5 100644 --- a/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt +++ b/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt @@ -377,6 +377,7 @@ class KotlinAsJavaPluginTest : BaseAbstractTest() { DokkaConfiguration.ExternalDocumentationLink.jdk(8), stdlibExternalDocumentationLink ) + classpath = listOfNotNull(jvmStdlibPath) } } } diff --git a/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaSignatureTest.kt b/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaSignatureTest.kt index c87fda5e..25312810 100644 --- a/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaSignatureTest.kt +++ b/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaSignatureTest.kt @@ -23,6 +23,7 @@ class KotlinAsJavaSignatureTest : BaseAbstractTest() { DokkaConfiguration.ExternalDocumentationLink.jdk(8), stdlibExternalDocumentationLink ) + classpath = listOfNotNull(jvmStdlibPath) } } } diff --git a/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/KotlinToJavaService.kt b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/KotlinToJavaService.kt index c8e99d10..1ce47031 100644 --- a/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/KotlinToJavaService.kt +++ b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/KotlinToJavaService.kt @@ -9,5 +9,21 @@ import org.jetbrains.dokka.links.DRI @InternalDokkaApi public interface KotlinToJavaService { + /** + * E.g. + * kotlin.Throwable -> java.lang.Throwable + * kotlin.Int -> java.lang.Integer + * kotlin.Int.Companion -> kotlin.jvm.internal.IntCompanionObject + * kotlin.Nothing -> java.lang.Void + * kotlin.IntArray -> null + * kotlin.Function3 -> kotlin.jvm.functions.Function3 + * kotlin.coroutines.SuspendFunction3 -> kotlin.jvm.functions.Function4 + * kotlin.Function42 -> kotlin.jvm.functions.FunctionN + * kotlin.coroutines.SuspendFunction42 -> kotlin.jvm.functions.FunctionN + * kotlin.reflect.KFunction3 -> kotlin.reflect.KFunction + * kotlin.reflect.KSuspendFunction3 -> kotlin.reflect.KFunction + * kotlin.reflect.KFunction42 -> kotlin.reflect.KFunction + * kotlin.reflect.KSuspendFunction42 -> kotlin.reflect.KFunction + */ public fun findAsJava(kotlinDri: DRI): DRI? } diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/SymbolsAnalysisPlugin.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/SymbolsAnalysisPlugin.kt index ef795145..122e0b10 100644 --- a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/SymbolsAnalysisPlugin.kt +++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/SymbolsAnalysisPlugin.kt @@ -92,6 +92,7 @@ public class SymbolsAnalysisPlugin : DokkaPlugin() { internal val symbolAnalyzerImpl by extending { plugin<InternalKotlinAnalysisPlugin>().documentableSourceLanguageParser providing { KotlinDocumentableSourceLanguageParser() } } + internal val symbolFullClassHierarchyBuilder by extending { plugin<InternalKotlinAnalysisPlugin>().fullClassHierarchyBuilder providing ::SymbolFullClassHierarchyBuilder } @@ -104,14 +105,14 @@ public class SymbolsAnalysisPlugin : DokkaPlugin() { plugin<InternalKotlinAnalysisPlugin>().moduleAndPackageDocumentationReader providing ::ModuleAndPackageDocumentationReader } - /* internal val kotlinToJavaMapper by extending { - plugin<InternalKotlinAnalysisPlugin>().kotlinToJavaService providing { DescriptorKotlinToJavaMapper() } + internal val kotlinToJavaMapper by extending { + plugin<InternalKotlinAnalysisPlugin>().kotlinToJavaService providing { SymbolKotlinToJavaMapper() } } - intern val descriptorInheritanceBuilder by extending { - plugin<InternalKotlinAnalysisPlugin>().inheritanceBuilder providing { DescriptorInheritanceBuilder() } + internal val symbolInheritanceBuilder by extending { + plugin<InternalKotlinAnalysisPlugin>().inheritanceBuilder providing ::SymbolInheritanceBuilder } - */ + internal val symbolExternalDocumentablesProvider by extending { plugin<InternalKotlinAnalysisPlugin>().externalDocumentablesProvider providing ::SymbolExternalDocumentablesProvider } diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolFullClassHierarchyBuilder.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolFullClassHierarchyBuilder.kt index 497d7946..0b68ac81 100644 --- a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolFullClassHierarchyBuilder.kt +++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolFullClassHierarchyBuilder.kt @@ -5,6 +5,7 @@ package org.jetbrains.dokka.analysis.kotlin.symbols.services import com.intellij.psi.PsiClass +import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.analysis.java.util.PsiDocumentableSource import org.jetbrains.dokka.analysis.java.util.from import org.jetbrains.dokka.analysis.kotlin.symbols.translators.getDRIFromClassLike @@ -17,6 +18,8 @@ import org.jetbrains.dokka.analysis.kotlin.internal.ClassHierarchy import org.jetbrains.dokka.analysis.kotlin.internal.FullClassHierarchyBuilder import org.jetbrains.dokka.analysis.kotlin.internal.Supertypes import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.SymbolsAnalysisPlugin +import org.jetbrains.dokka.analysis.kotlin.symbols.translators.AnnotationTranslator +import org.jetbrains.dokka.analysis.kotlin.symbols.translators.TypeTranslator import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.querySingle @@ -24,7 +27,7 @@ import org.jetbrains.kotlin.psi.KtClassOrObject import java.util.concurrent.ConcurrentHashMap -internal class SymbolFullClassHierarchyBuilder(val context: DokkaContext) : FullClassHierarchyBuilder { +internal class SymbolFullClassHierarchyBuilder(context: DokkaContext) : FullClassHierarchyBuilder { private val kotlinAnalysis = context.plugin<SymbolsAnalysisPlugin>().querySingle { kotlinAnalysis } override suspend fun build(module: DModule): ClassHierarchy { @@ -38,14 +41,13 @@ internal class SymbolFullClassHierarchyBuilder(val context: DokkaContext) : Full supersMap: MutableMap<DRI, Supertypes> ) { val (dri, kotlinType) = driWithKType - val supertypes = kotlinType.getDirectSuperTypes().filterNot { it.isAny } - val supertypesDriWithKType = supertypes.mapNotNull { supertype -> - supertype.expandedClassSymbol?.let { - getDRIFromClassLike(it) to supertype - } - } - if (supersMap[dri] == null) { + val supertypes = kotlinType.getDirectSuperTypes(shouldApproximate = true).filterNot { it.isAny } + val supertypesDriWithKType = supertypes.mapNotNull { supertype -> + supertype.expandedClassSymbol?.let { + getDRIFromClassLike(it) to supertype + } + } supersMap[dri] = supertypesDriWithKType.map { it.first } supertypesDriWithKType.forEach { collectSupertypesFromKtType(it, supersMap) } } @@ -92,4 +94,55 @@ internal class SymbolFullClassHierarchyBuilder(val context: DokkaContext) : Full } } + internal class SuperclassesWithKind( + val typeConstructorWithKind: TypeConstructorWithKind, + val superclasses: List<TypeConstructorWithKind> + ) + + /** + * Currently, it works only for Symbols + */ + internal fun collectKotlinSupertypesWithKind( + documentable: Iterable<Documentable>, + sourceSet: DokkaConfiguration.DokkaSourceSet + ): Map<DRI, SuperclassesWithKind> { + val typeTranslator = TypeTranslator(sourceSet, AnnotationTranslator()) + val hierarchy = mutableMapOf<DRI, SuperclassesWithKind>() + + analyze(kotlinAnalysis.getModule(sourceSet)) { + documentable.filterIsInstance<DClasslike>().forEach { + val source = it.sources[sourceSet] + if (source is KtPsiDocumentableSource) { + (source.psi as? KtClassOrObject)?.let { psi -> + val type = psi.getNamedClassOrObjectSymbol()?.buildSelfClassType() ?: return@analyze + collectSupertypesWithKindFromKtType(typeTranslator, with(typeTranslator) { + toTypeConstructorWithKindFrom(type) + } to type, hierarchy) + } + } // else if (source is PsiDocumentableSource) TODO val psi = source.psi as? PsiClass + } + } + return hierarchy + } + + private fun KtAnalysisSession.collectSupertypesWithKindFromKtType( + typeTranslator: TypeTranslator, + typeConstructorWithKindWithKType: Pair<TypeConstructorWithKind, KtType>, + supersMap: MutableMap<DRI, SuperclassesWithKind> + ) { + val (typeConstructorWithKind, kotlinType) = typeConstructorWithKindWithKType + + if (supersMap[typeConstructorWithKind.typeConstructor.dri] == null) { + val supertypes = kotlinType.getDirectSuperTypes(shouldApproximate = true).filterNot { it.isAny } + + val supertypesDriWithKType = supertypes.map { supertype -> + with(typeTranslator) { + toTypeConstructorWithKindFrom(supertype) + } to supertype + } + supersMap[typeConstructorWithKind.typeConstructor.dri] = + SuperclassesWithKind(typeConstructorWithKind, supertypesDriWithKType.map { it.first }) + supertypesDriWithKType.forEach { collectSupertypesWithKindFromKtType(typeTranslator, it, supersMap) } + } + } } diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolInheritanceBuilder.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolInheritanceBuilder.kt new file mode 100644 index 00000000..540f08a7 --- /dev/null +++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolInheritanceBuilder.kt @@ -0,0 +1,89 @@ +/* + * 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.kotlin.symbols.services + +import com.intellij.psi.PsiClass +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.analysis.java.util.from +import org.jetbrains.dokka.analysis.java.util.PsiDocumentableSource +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.analysis.kotlin.internal.InheritanceBuilder +import org.jetbrains.dokka.analysis.kotlin.internal.InheritanceNode +import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle + +/** + * This is copy-pasted from org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.DescriptorInheritanceBuilder and adapted for symbols + */ +internal class SymbolInheritanceBuilder(context: DokkaContext) : InheritanceBuilder { + private val symbolFullClassHierarchyBuilder = + context.plugin<InternalKotlinAnalysisPlugin>().querySingle { fullClassHierarchyBuilder } + + override fun build(documentables: Map<DRI, Documentable>): List<InheritanceNode> { + + // this statement is copy-pasted from the version for Descriptors + val psiInheritanceTree = + documentables.flatMap { (_, v) -> (v as? WithSources)?.sources?.values.orEmpty() } + .filterIsInstance<PsiDocumentableSource>().mapNotNull { it.psi as? PsiClass } + .flatMap(::gatherPsiClasses) + .flatMap { entry -> entry.second.map { it to entry.first } } + .let { + it + it.map { it.second to null } + } + .groupBy({ it.first }) { it.second } + .map { it.key to it.value.filterNotNull().distinct() } + .map { (k, v) -> + InheritanceNode( + DRI.from(k), + v.map { InheritanceNode(DRI.from(it)) }, + k.supers.filter { it.isInterface }.map { DRI.from(it) }, + k.isInterface + ) + + } + + // copy-pasted from stdlib 1.5 + fun <T, R : Any> Iterable<T>.firstNotNullOfOrNull(transform: (T) -> R?): R? { + for (element in this) { + val result = transform(element) + if (result != null) { + return result + } + } + return null + } + + val jvmSourceSet = + documentables.values.firstNotNullOfOrNull { it.sourceSets.find { it.analysisPlatform == Platform.jvm } } + if (jvmSourceSet == null) + return psiInheritanceTree + + val typeConstructorsMap = + (symbolFullClassHierarchyBuilder as? SymbolFullClassHierarchyBuilder)?.collectKotlinSupertypesWithKind( + documentables.values, + jvmSourceSet + ) + ?: throw IllegalStateException("Unexpected symbolFullClassHierarchyBuildertype") // TODO: https://github.com/Kotlin/dokka/issues/3225 Unify FullClassHierarchyBuilder and InheritanceBuilder into one builder + + fun ClassKind.isInterface() = this == KotlinClassKindTypes.INTERFACE || this == JavaClassKindTypes.INTERFACE + val symbolsInheritanceTree = typeConstructorsMap.map { (dri, superclasses) -> + InheritanceNode( + dri, + superclasses.superclasses.map { InheritanceNode(it.typeConstructor.dri) }, + superclasses.superclasses.filter { it.kind.isInterface() }.map { it.typeConstructor.dri }, + isInterface = superclasses.typeConstructorWithKind.kind.isInterface() + ) + } + + return psiInheritanceTree + symbolsInheritanceTree + } + + private fun gatherPsiClasses(psi: PsiClass): List<Pair<PsiClass, List<PsiClass>>> = psi.supers.toList().let { l -> + listOf(psi to l) + l.flatMap { gatherPsiClasses(it) } + } +} diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolKotlinToJavaMapper.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolKotlinToJavaMapper.kt new file mode 100644 index 00000000..77ede87f --- /dev/null +++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolKotlinToJavaMapper.kt @@ -0,0 +1,38 @@ +/* + * 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.kotlin.symbols.services + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.PointingToDeclaration +import org.jetbrains.dokka.analysis.kotlin.internal.KotlinToJavaService +import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap // or import kotlin.reflect.jvm.internal.impl.builtins.jvm.JavaToKotlinClassMap see https://github.com/Kotlin/dokka/issues/3226 +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName + +/** + * This is copy-pasted from org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.DescriptorKotlinToJavaMapper + */ +internal class SymbolKotlinToJavaMapper : KotlinToJavaService { + + override fun findAsJava(kotlinDri: DRI): DRI? { + return kotlinDri.partialFqName().mapToJava()?.toDRI(kotlinDri) + } + + private fun DRI.partialFqName() = packageName?.let { "$it." } + classNames + + private fun String.mapToJava(): ClassId? = + JavaToKotlinClassMap.mapKotlinToJava(FqName(this).toUnsafe()) + + private fun ClassId.toDRI(dri: DRI?): DRI = DRI( + packageName = packageFqName.asString(), + classNames = classNames(), + callable = dri?.callable,//?.asJava(), TODO: check this + extra = null, + target = PointingToDeclaration + ) + + private fun ClassId.classNames(): String = + shortClassName.identifier + (outerClassId?.classNames()?.let { ".$it" } ?: "") +} diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt index faae08e2..c9882487 100644 --- a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt +++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt @@ -11,6 +11,7 @@ import org.jetbrains.dokka.model.ClassValue import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.annotations.* import org.jetbrains.kotlin.analysis.api.base.KtConstantValue +import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolOrigin import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget @@ -34,12 +35,16 @@ internal class AnnotationTranslator { annotated.annotations.map { toDokkaAnnotation(it) } /** - * @return direct annotations and file-level annotations + * The examples of annotations from backing field are [JvmField], [JvmSynthetic]. + * + * @return direct annotations, annotations from backing field and file-level annotations */ fun KtAnalysisSession.getAllAnnotationsFrom(annotated: KtAnnotated): List<Annotations.Annotation> { val directAnnotations = getDirectAnnotationsFrom(annotated) - val fileLevelAnnotations = (annotated as? KtSymbol)?.let { getFileLevelAnnotationsFrom(it) } ?: emptyList() - return directAnnotations + fileLevelAnnotations + val backingFieldAnnotations = + (annotated as? KtPropertySymbol)?.backingFieldSymbol?.let { getDirectAnnotationsFrom(it) }.orEmpty() + val fileLevelAnnotations = (annotated as? KtSymbol)?.let { getFileLevelAnnotationsFrom(it) }.orEmpty() + return directAnnotations + backingFieldAnnotations + fileLevelAnnotations } private fun KtAnnotationApplication.isNoExistedInSource() = psi == null 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 6c7071f5..298d0182 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 @@ -212,7 +212,15 @@ internal class DokkaSymbolVisitor( val isActual = namedClassOrObjectSymbol.isActual val documentation = getDocumentation(namedClassOrObjectSymbol)?.toSourceSetDependent() ?: emptyMap() - val (constructors, functions, properties, classlikes) = getDokkaScopeFrom(namedClassOrObjectSymbol, dri) + val (constructors, functions, properties, classlikesWithoutCompanion) = getDokkaScopeFrom(namedClassOrObjectSymbol, dri) + + val companionObject = namedClassOrObjectSymbol.companionObject?.let { + visitNamedClassOrObjectSymbol( + it, + dri + ) + } as? DObject + val classlikes = if (companionObject == null) classlikesWithoutCompanion else classlikesWithoutCompanion + companionObject val generics = namedClassOrObjectSymbol.typeParameters.mapIndexed { index, symbol -> visitVariantTypeParameter( @@ -229,7 +237,6 @@ internal class DokkaSymbolVisitor( namedClassOrObjectSymbol.superTypes.filterNot { it.isAny } .map { with(typeTranslator) { toTypeConstructorWithKindFrom(it) } } .toSourceSetDependent() - return@withExceptionCatcher when (namedClassOrObjectSymbol.classKind) { KtClassKind.OBJECT, KtClassKind.COMPANION_OBJECT -> DObject( @@ -268,12 +275,7 @@ internal class DokkaSymbolVisitor( generics = generics, documentation = documentation, modifier = namedClassOrObjectSymbol.getDokkaModality().toSourceSetDependent(), - companion = namedClassOrObjectSymbol.companionObject?.let { - visitNamedClassOrObjectSymbol( - it, - dri - ) - } as? DObject, + companion = companionObject, sourceSets = setOf(sourceSet), isExpectActual = (isExpect || isActual), extra = PropertyContainer.withAll( @@ -296,12 +298,7 @@ internal class DokkaSymbolVisitor( supertypes = supertypes, generics = generics, documentation = documentation, - companion = namedClassOrObjectSymbol.companionObject?.let { - visitNamedClassOrObjectSymbol( - it, - dri - ) - } as? DObject, + companion = companionObject, sourceSets = setOf(sourceSet), isExpectActual = (isExpect || isActual), extra = PropertyContainer.withAll( @@ -322,12 +319,7 @@ internal class DokkaSymbolVisitor( expectPresentInSet = sourceSet.takeIf { isExpect }, sourceSets = setOf(sourceSet), isExpectActual = (isExpect || isActual), - companion = namedClassOrObjectSymbol.companionObject?.let { - visitNamedClassOrObjectSymbol( - it, - dri - ) - } as? DObject, + companion = companionObject, visibility = namedClassOrObjectSymbol.getDokkaVisibility().toSourceSetDependent(), generics = generics, constructors = constructors, @@ -401,7 +393,7 @@ internal class DokkaSymbolVisitor( val constructors: List<DFunction>, val functions: List<DFunction>, val properties: List<DProperty>, - val classlikes: List<DClasslike> + val classlikesWithoutCompanion: List<DClasslike> ) /** @@ -445,13 +437,10 @@ internal class DokkaSymbolVisitor( javaFields.map { visitJavaFieldSymbol(it, dri) } - // hack, by default, compiler adds an empty companion object for enum - // TODO check if it is empty - fun List<KtNamedClassOrObjectSymbol>.filterOutEnumCompanion() = - if (namedClassOrObjectSymbol.classKind == KtClassKind.ENUM_CLASS) + fun List<KtNamedClassOrObjectSymbol>.filterOutCompanion() = filterNot { - it.name.asString() == "Companion" && it.classKind == KtClassKind.COMPANION_OBJECT - } else this + it.classKind == KtClassKind.COMPANION_OBJECT + } fun List<KtNamedClassOrObjectSymbol>.filterOutAndMarkAlreadyVisited() = filterNot { symbol -> visitedNamedClassOrObjectSymbol.contains(symbol.classIdIfNonLocal) @@ -465,7 +454,7 @@ internal class DokkaSymbolVisitor( } val classlikes = classifiers.filterIsInstance<KtNamedClassOrObjectSymbol>() - .filterOutEnumCompanion() // hack to filter out companion for enum + .filterOutCompanion() // also, this is a hack to filter out companion for enum .filterOutAndMarkAlreadyVisited() .map { visitNamedClassOrObjectSymbol(it, dri) } @@ -473,7 +462,7 @@ internal class DokkaSymbolVisitor( constructors = constructors, functions = functions, properties = properties, - classlikes = classlikes + classlikesWithoutCompanion = classlikes ) } diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt index 7127cbdf..f7366294 100644 --- a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt +++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt @@ -44,7 +44,7 @@ internal class TypeTranslator( typeAlias = GenericTypeConstructor( dri = getDRIFromNonErrorClassType(classType), projections = classType.ownTypeArguments.map { toProjection(it) }), - inner = toBoundFrom(classSymbol.expandedType), + inner = toBoundFrom(classType.fullyExpandedType), extra = PropertyContainer.withAll( getDokkaAnnotationsFrom(classType)?.toSourceSetDependent()?.toAnnotations() ) |