From 9b07435773831874d4d80066a2227596198af988 Mon Sep 17 00:00:00 2001 From: Vadim Mishenev Date: Thu, 26 Oct 2023 14:39:00 +0300 Subject: [K2] Support kotlin-as-java and javadoc plugins and update version of Analysis API (#3227) * Implement InheritanceBuilder and for symbols * Enable tests for as-java and javadoc plugins and fix some * Update the version of Analysis API and enable test * Use assert from kotlin.test * Rename `descriptorInheritanceBuilder` * Fix after rebasing * Mute test `two classes from different packages` --- .../kotlin/symbols/plugin/SymbolsAnalysisPlugin.kt | 11 +-- .../services/SymbolFullClassHierarchyBuilder.kt | 69 +++++++++++++++-- .../symbols/services/SymbolInheritanceBuilder.kt | 89 ++++++++++++++++++++++ .../symbols/services/SymbolKotlinToJavaMapper.kt | 38 +++++++++ .../symbols/translators/AnnotationTranslator.kt | 11 ++- .../DefaultSymbolToDocumentableTranslator.kt | 47 +++++------- .../kotlin/symbols/translators/TypeTranslator.kt | 2 +- 7 files changed, 221 insertions(+), 46 deletions(-) create mode 100644 subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolInheritanceBuilder.kt create mode 100644 subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolKotlinToJavaMapper.kt (limited to 'subprojects/analysis-kotlin-symbols') 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().documentableSourceLanguageParser providing { KotlinDocumentableSourceLanguageParser() } } + internal val symbolFullClassHierarchyBuilder by extending { plugin().fullClassHierarchyBuilder providing ::SymbolFullClassHierarchyBuilder } @@ -104,14 +105,14 @@ public class SymbolsAnalysisPlugin : DokkaPlugin() { plugin().moduleAndPackageDocumentationReader providing ::ModuleAndPackageDocumentationReader } - /* internal val kotlinToJavaMapper by extending { - plugin().kotlinToJavaService providing { DescriptorKotlinToJavaMapper() } + internal val kotlinToJavaMapper by extending { + plugin().kotlinToJavaService providing { SymbolKotlinToJavaMapper() } } - intern val descriptorInheritanceBuilder by extending { - plugin().inheritanceBuilder providing { DescriptorInheritanceBuilder() } + internal val symbolInheritanceBuilder by extending { + plugin().inheritanceBuilder providing ::SymbolInheritanceBuilder } - */ + internal val symbolExternalDocumentablesProvider by extending { plugin().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().querySingle { kotlinAnalysis } override suspend fun build(module: DModule): ClassHierarchy { @@ -38,14 +41,13 @@ internal class SymbolFullClassHierarchyBuilder(val context: DokkaContext) : Full supersMap: MutableMap ) { 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 + ) + + /** + * Currently, it works only for Symbols + */ + internal fun collectKotlinSupertypesWithKind( + documentable: Iterable, + sourceSet: DokkaConfiguration.DokkaSourceSet + ): Map { + val typeTranslator = TypeTranslator(sourceSet, AnnotationTranslator()) + val hierarchy = mutableMapOf() + + analyze(kotlinAnalysis.getModule(sourceSet)) { + documentable.filterIsInstance().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, + supersMap: MutableMap + ) { + 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().querySingle { fullClassHierarchyBuilder } + + override fun build(documentables: Map): List { + + // this statement is copy-pasted from the version for Descriptors + val psiInheritanceTree = + documentables.flatMap { (_, v) -> (v as? WithSources)?.sources?.values.orEmpty() } + .filterIsInstance().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 Iterable.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>> = 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 { 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, val functions: List, val properties: List, - val classlikes: List + val classlikesWithoutCompanion: List ) /** @@ -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.filterOutEnumCompanion() = - if (namedClassOrObjectSymbol.classKind == KtClassKind.ENUM_CLASS) + fun List.filterOutCompanion() = filterNot { - it.name.asString() == "Companion" && it.classKind == KtClassKind.COMPANION_OBJECT - } else this + it.classKind == KtClassKind.COMPANION_OBJECT + } fun List.filterOutAndMarkAlreadyVisited() = filterNot { symbol -> visitedNamedClassOrObjectSymbol.contains(symbol.classIdIfNonLocal) @@ -465,7 +454,7 @@ internal class DokkaSymbolVisitor( } val classlikes = classifiers.filterIsInstance() - .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() ) -- cgit