Skip to content

Commit

Permalink
Implement InheritanceBuilder and for symbols
Browse files Browse the repository at this point in the history
  • Loading branch information
vmishenev committed Oct 17, 2023
1 parent 9bbcc21 commit b7c093f
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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?
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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 descriptorInheritanceBuilder by extending {
plugin<InternalKotlinAnalysisPlugin>().inheritanceBuilder providing ::SymbolInheritanceBuilder
}
*/

internal val symbolExternalDocumentablesProvider by extending {
plugin<InternalKotlinAnalysisPlugin>().externalDocumentablesProvider providing ::SymbolExternalDocumentablesProvider
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -17,14 +18,16 @@ 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
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 {
Expand All @@ -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) }
}
Expand Down Expand Up @@ -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[sourceSet].mainModule) {
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) }
}
}
}
Original file line number Diff line number Diff line change
@@ -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) }
}
}
Original file line number Diff line number Diff line change
@@ -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" } ?: "")
}

0 comments on commit b7c093f

Please sign in to comment.