aboutsummaryrefslogtreecommitdiff
path: root/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains
diff options
context:
space:
mode:
authorIgnat Beresnev <ignat.beresnev@jetbrains.com>2023-11-10 11:46:54 +0100
committerGitHub <noreply@github.com>2023-11-10 11:46:54 +0100
commit8e5c63d035ef44a269b8c43430f43f5c8eebfb63 (patch)
tree1b915207b2b9f61951ddbf0ff2e687efd053d555 /dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains
parenta44efd4ba0c2e4ab921ff75e0f53fc9335aa79db (diff)
downloaddokka-8e5c63d035ef44a269b8c43430f43f5c8eebfb63.tar.gz
dokka-8e5c63d035ef44a269b8c43430f43f5c8eebfb63.tar.bz2
dokka-8e5c63d035ef44a269b8c43430f43f5c8eebfb63.zip
Restructure the project to utilize included builds (#3174)
* Refactor and simplify artifact publishing * Update Gradle to 8.4 * Refactor and simplify convention plugins and build scripts Fixes #3132 --------- Co-authored-by: Adam <897017+aSemy@users.noreply.github.com> Co-authored-by: Oleg Yukhnevich <whyoleg@gmail.com>
Diffstat (limited to 'dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains')
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KDocProvider.kt174
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KdocMarkdownParser.kt101
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/ResolveKDocLink.kt61
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/SyntheticKDocProvider.kt65
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorDocumentationContent.kt19
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorKotlinDocCommentCreator.kt20
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocComment.kt85
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocCommentParser.kt52
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinInheritDocTagContentProvider.kt35
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/IllegalModuleAndPackageDocumentation.kt11
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentation.kt15
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationFragment.kt13
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationParsingContext.kt63
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationReader.kt114
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationSource.kt18
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentation.kt16
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentationFragments.kt59
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/AnalysisContext.kt53
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/KotlinAnalysis.kt175
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/SymbolsAnalysisPlugin.kt126
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisProjectProvider.kt20
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisSourceRootsExtractor.kt16
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinDocumentableSourceLanguageParser.kt30
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinSampleProvider.kt100
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KtPsiDocumentableSource.kt24
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolExternalDocumentablesProvider.kt37
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolFullClassHierarchyBuilder.kt148
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolInheritanceBuilder.kt89
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolKotlinToJavaMapper.kt38
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolSyntheticDocumentableDetector.kt45
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt147
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DRIFactory.kt144
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt957
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TranslatorError.kt33
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeReferenceFactory.kt82
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt201
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/utils/isException.kt22
37 files changed, 3408 insertions, 0 deletions
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KDocProvider.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KDocProvider.kt
new file mode 100644
index 00000000..d8a4e476
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KDocProvider.kt
@@ -0,0 +1,174 @@
+/*
+ * 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.kdoc
+
+import com.intellij.psi.PsiNamedElement
+import com.intellij.psi.util.PsiTreeUtil
+import org.jetbrains.dokka.analysis.java.parsers.JavadocParser
+import org.jetbrains.dokka.model.doc.DocumentationNode
+import org.jetbrains.dokka.utilities.DokkaLogger
+import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
+import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
+import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
+import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
+import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolOrigin
+import org.jetbrains.kotlin.analysis.api.symbols.markers.KtNamedSymbol
+import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
+import org.jetbrains.kotlin.kdoc.psi.api.KDoc
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
+import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
+import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
+import org.jetbrains.kotlin.psi.psiUtil.isPropertyParameter
+import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
+
+internal fun KtAnalysisSession.getJavaDocDocumentationFrom(
+ symbol: KtSymbol,
+ javadocParser: JavadocParser
+): DocumentationNode? {
+ if (symbol.origin == KtSymbolOrigin.JAVA) {
+ return (symbol.psi as? PsiNamedElement)?.let {
+ javadocParser.parseDocumentation(it)
+ }
+ } else if (symbol.origin == KtSymbolOrigin.SOURCE && symbol is KtCallableSymbol) {
+ // Note: javadocParser searches in overridden JAVA declarations for JAVA method, not Kotlin
+ symbol.getAllOverriddenSymbols().forEach { overrider ->
+ if (overrider.origin == KtSymbolOrigin.JAVA)
+ return@getJavaDocDocumentationFrom (overrider.psi as? PsiNamedElement)?.let {
+ javadocParser.parseDocumentation(it)
+ }
+ }
+ }
+ return null
+}
+
+internal fun KtAnalysisSession.getKDocDocumentationFrom(symbol: KtSymbol, logger: DokkaLogger) = findKDoc(symbol)?.let { kDocContent ->
+
+ val ktElement = symbol.psi
+ val kdocLocation = ktElement?.containingFile?.name?.let {
+ val name = when(symbol) {
+ is KtCallableSymbol -> symbol.callableIdIfNonLocal?.toString()
+ is KtClassOrObjectSymbol -> symbol.classIdIfNonLocal?.toString()
+ is KtNamedSymbol -> symbol.name.asString()
+ else -> null
+ }?.replace('/', '.') // replace to be compatible with K1
+
+ if (name != null) "$it/$name"
+ else it
+ }
+
+
+ parseFromKDocTag(
+ kDocTag = kDocContent.contentTag,
+ externalDri = { link -> resolveKDocLink(link).ifUnresolved { logger.logUnresolvedLink(link.getLinkText(), kdocLocation) } },
+ kdocLocation = kdocLocation
+ )
+}
+
+
+
+
+// ----------- copy-paste from IDE ----------------------------------------------------------------------------
+
+internal data class KDocContent(
+ val contentTag: KDocTag,
+ val sections: List<KDocSection>
+)
+
+internal fun KtAnalysisSession.findKDoc(symbol: KtSymbol): KDocContent? {
+ // for generated function (e.g. `copy`) psi returns class, see test `data class kdocs over generated methods`
+ if (symbol.origin != KtSymbolOrigin.SOURCE) return null
+ val ktElement = symbol.psi as? KtElement
+ ktElement?.findKDoc()?.let {
+ return it
+ }
+
+ if (symbol is KtCallableSymbol) {
+ symbol.getAllOverriddenSymbols().forEach { overrider ->
+ findKDoc(overrider)?.let {
+ return it
+ }
+ }
+ }
+ return null
+}
+
+
+internal fun KtElement.findKDoc(): KDocContent? = this.lookupOwnedKDoc()
+ ?: this.lookupKDocInContainer()
+
+
+
+private fun KtElement.lookupOwnedKDoc(): KDocContent? {
+ // KDoc for primary constructor is located inside of its class KDoc
+ val psiDeclaration = when (this) {
+ is KtPrimaryConstructor -> getContainingClassOrObject()
+ else -> this
+ }
+
+ if (psiDeclaration is KtDeclaration) {
+ val kdoc = psiDeclaration.docComment
+ if (kdoc != null) {
+ if (this is KtConstructor<*>) {
+ // ConstructorDescriptor resolves to the same JetDeclaration
+ val constructorSection = kdoc.findSectionByTag(KDocKnownTag.CONSTRUCTOR)
+ if (constructorSection != null) {
+ // if annotated with @constructor tag and the caret is on constructor definition,
+ // then show @constructor description as the main content, and additional sections
+ // that contain @param tags (if any), as the most relatable ones
+ // practical example: val foo = Fo<caret>o("argument") -- show @constructor and @param content
+ val paramSections = kdoc.findSectionsContainingTag(KDocKnownTag.PARAM)
+ return KDocContent(constructorSection, paramSections)
+ }
+ }
+ return KDocContent(kdoc.getDefaultSection(), kdoc.getAllSections())
+ }
+ }
+
+ return null
+}
+
+/**
+ * Looks for sections that have a deeply nested [tag],
+ * as opposed to [KDoc.findSectionByTag], which only looks among the top level
+ */
+private fun KDoc.findSectionsContainingTag(tag: KDocKnownTag): List<KDocSection> {
+ return getChildrenOfType<KDocSection>()
+ .filter { it.findTagByName(tag.name.toLowerCaseAsciiOnly()) != null }
+}
+
+private fun KtElement.lookupKDocInContainer(): KDocContent? {
+ val subjectName = name
+ val containingDeclaration =
+ PsiTreeUtil.findFirstParent(this, true) {
+ it is KtDeclarationWithBody && it !is KtPrimaryConstructor
+ || it is KtClassOrObject
+ }
+
+ val containerKDoc = containingDeclaration?.getChildOfType<KDoc>()
+ if (containerKDoc == null || subjectName == null) return null
+ val propertySection = containerKDoc.findSectionByTag(KDocKnownTag.PROPERTY, subjectName)
+ val paramTag =
+ containerKDoc.findDescendantOfType<KDocTag> { it.knownTag == KDocKnownTag.PARAM && it.getSubjectName() == subjectName }
+
+ val primaryContent = when {
+ // class Foo(val <caret>s: String)
+ this is KtParameter && this.isPropertyParameter() -> propertySection ?: paramTag
+ // fun some(<caret>f: String) || class Some<<caret>T: Base> || Foo(<caret>s = "argument")
+ this is KtParameter || this is KtTypeParameter -> paramTag
+ // if this property is declared separately (outside primary constructor), but it's for some reason
+ // annotated as @property in class's description, instead of having its own KDoc
+ this is KtProperty && containingDeclaration is KtClassOrObject -> propertySection
+ else -> null
+ }
+ return primaryContent?.let {
+ // makes little sense to include any other sections, since we found
+ // documentation for a very specific element, like a property/param
+ KDocContent(it, sections = emptyList())
+ }
+}
+
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KdocMarkdownParser.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KdocMarkdownParser.kt
new file mode 100644
index 00000000..ad29f5f0
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KdocMarkdownParser.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.kdoc
+
+import com.intellij.psi.PsiElement
+import org.jetbrains.dokka.analysis.markdown.jb.MarkdownParser
+import org.jetbrains.dokka.analysis.markdown.jb.MarkdownParser.Companion.fqDeclarationName
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.doc.*
+import org.jetbrains.dokka.model.doc.Suppress
+import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
+import org.jetbrains.kotlin.psi.psiUtil.allChildren
+
+internal fun parseFromKDocTag(
+ kDocTag: KDocTag?,
+ externalDri: (KDocLink) -> DRI?,
+ kdocLocation: String?,
+ parseWithChildren: Boolean = true
+): DocumentationNode {
+ return if (kDocTag == null) {
+ DocumentationNode(emptyList())
+ } else {
+ fun parseStringToDocNode(text: String, externalDRIProvider: (String) -> DRI?) =
+ MarkdownParser(externalDRIProvider, kdocLocation).parseStringToDocNode(text)
+
+ fun pointedLink(tag: KDocTag): DRI? = tag.getSubjectLink()?.let(externalDri)
+
+ val allTags =
+ listOf(kDocTag) + if (kDocTag.canHaveParent() && parseWithChildren) getAllKDocTags(findParent(kDocTag)) else emptyList()
+ DocumentationNode(
+ allTags.map { tag ->
+ val links = tag.allChildren.filterIsInstance<KDocLink>().associate { it.getLinkText() to externalDri(it) }
+ val externalDRIProvider = { linkText: String -> links[linkText] }
+
+ when (tag.knownTag) {
+ null -> if (tag.name == null) Description(parseStringToDocNode(tag.getContent(), externalDRIProvider)) else CustomTagWrapper(
+ parseStringToDocNode(tag.getContent(), externalDRIProvider),
+ tag.name!!
+ )
+ KDocKnownTag.AUTHOR -> Author(parseStringToDocNode(tag.getContent(), externalDRIProvider))
+ KDocKnownTag.THROWS -> {
+ val dri = pointedLink(tag)
+ Throws(
+ parseStringToDocNode(tag.getContent(), externalDRIProvider),
+ dri?.fqDeclarationName() ?: tag.getSubjectName().orEmpty(),
+ dri,
+ )
+ }
+ KDocKnownTag.EXCEPTION -> {
+ val dri = pointedLink(tag)
+ Throws(
+ parseStringToDocNode(tag.getContent(), externalDRIProvider),
+ dri?.fqDeclarationName() ?: tag.getSubjectName().orEmpty(),
+ dri
+ )
+ }
+ KDocKnownTag.PARAM -> Param(
+ parseStringToDocNode(tag.getContent(), externalDRIProvider),
+ tag.getSubjectName().orEmpty()
+ )
+ KDocKnownTag.RECEIVER -> Receiver(parseStringToDocNode(tag.getContent(), externalDRIProvider))
+ KDocKnownTag.RETURN -> Return(parseStringToDocNode(tag.getContent(), externalDRIProvider))
+ KDocKnownTag.SEE -> {
+ val dri = pointedLink(tag)
+ See(
+ parseStringToDocNode(tag.getContent(), externalDRIProvider),
+ dri?.fqDeclarationName() ?: tag.getSubjectName().orEmpty(),
+ dri,
+ )
+ }
+ KDocKnownTag.SINCE -> Since(parseStringToDocNode(tag.getContent(), externalDRIProvider))
+ KDocKnownTag.CONSTRUCTOR -> Constructor(parseStringToDocNode(tag.getContent(), externalDRIProvider))
+ KDocKnownTag.PROPERTY -> Property(
+ parseStringToDocNode(tag.getContent(), externalDRIProvider),
+ tag.getSubjectName().orEmpty()
+ )
+ KDocKnownTag.SAMPLE -> Sample(
+ parseStringToDocNode(tag.getContent(), externalDRIProvider),
+ tag.getSubjectName().orEmpty()
+ )
+ KDocKnownTag.SUPPRESS -> Suppress(parseStringToDocNode(tag.getContent(), externalDRIProvider))
+ }
+ }
+ )
+ }
+}
+
+private fun findParent(kDoc: PsiElement): PsiElement =
+ if (kDoc.canHaveParent()) findParent(kDoc.parent) else kDoc
+
+private fun PsiElement.canHaveParent(): Boolean = this is KDocSection && knownTag != KDocKnownTag.PROPERTY
+
+private fun getAllKDocTags(kDocImpl: PsiElement): List<KDocTag> =
+ kDocImpl.children.filterIsInstance<KDocTag>().filterNot { it is KDocSection } + kDocImpl.children.flatMap {
+ getAllKDocTags(it)
+ }
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/ResolveKDocLink.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/ResolveKDocLink.kt
new file mode 100644
index 00000000..9a0b81bd
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/ResolveKDocLink.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.kdoc
+
+import com.intellij.psi.PsiElement
+import org.jetbrains.dokka.analysis.kotlin.symbols.translators.getDRIFromSymbol
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.utilities.DokkaLogger
+import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
+import org.jetbrains.kotlin.idea.references.mainReference
+import org.jetbrains.kotlin.kdoc.psi.api.KDoc
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
+import org.jetbrains.kotlin.psi.KtPsiFactory
+
+/**
+ * Util to print a message about unresolved [link]
+ */
+internal fun DokkaLogger.logUnresolvedLink(link: String, location: String?) {
+ warn("Couldn't resolve link for $link" + if (location != null) " in $location" else "")
+}
+
+internal inline fun DRI?.ifUnresolved(action: () -> Unit): DRI? = this ?: run {
+ action()
+ null
+}
+
+/**
+ * Resolves KDoc link via creating PSI.
+ * If the [link] is ambiguous, i.e. leads to more than one declaration,
+ * it returns deterministically any declaration.
+ *
+ * @return [DRI] or null if the [link] is unresolved
+ */
+internal fun KtAnalysisSession.resolveKDocTextLink(link: String, context: PsiElement? = null): DRI? {
+ val psiFactory = context?.let { KtPsiFactory.contextual(it) } ?: KtPsiFactory(this.useSiteModule.project)
+ val kDoc = psiFactory.createComment(
+ """
+ /**
+ * [$link]
+ */
+ """.trimIndent()
+ ) as? KDoc
+ val kDocLink = kDoc?.getDefaultSection()?.children?.filterIsInstance<KDocLink>()?.singleOrNull()
+ return kDocLink?.let { resolveKDocLink(it) }
+}
+
+/**
+ * If the [link] is ambiguous, i.e. leads to more than one declaration,
+ * it returns deterministically any declaration.
+ *
+ * @return [DRI] or null if the [link] is unresolved
+ */
+internal fun KtAnalysisSession.resolveKDocLink(link: KDocLink): DRI? {
+ val lastNameSegment = link.children.filterIsInstance<KDocName>().lastOrNull()
+ val linkedSymbol = lastNameSegment?.mainReference?.resolveToSymbols()?.firstOrNull()
+ return if (linkedSymbol == null) null
+ else getDRIFromSymbol(linkedSymbol)
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/SyntheticKDocProvider.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/SyntheticKDocProvider.kt
new file mode 100644
index 00000000..4622ffd8
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/SyntheticKDocProvider.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.kdoc
+
+import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.SymbolsAnalysisPlugin
+import org.jetbrains.dokka.analysis.markdown.jb.MarkdownParser
+import org.jetbrains.dokka.model.doc.DocumentationNode
+import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
+import org.jetbrains.kotlin.analysis.api.symbols.*
+import org.jetbrains.kotlin.analysis.api.symbols.markers.KtPossibleMemberSymbol
+import org.jetbrains.kotlin.builtins.StandardNames
+
+private const val ENUM_ENTRIES_TEMPLATE_PATH = "/dokka/docs/kdoc/EnumEntries.kt.template"
+private const val ENUM_VALUEOF_TEMPLATE_PATH = "/dokka/docs/kdoc/EnumValueOf.kt.template"
+private const val ENUM_VALUES_TEMPLATE_PATH = "/dokka/docs/kdoc/EnumValues.kt.template"
+
+internal fun KtAnalysisSession.hasGeneratedKDocDocumentation(symbol: KtSymbol): Boolean =
+ getDocumentationTemplatePath(symbol) != null
+
+private fun KtAnalysisSession.getDocumentationTemplatePath(symbol: KtSymbol): String? =
+ when (symbol) {
+ is KtPropertySymbol -> if (isEnumEntriesProperty(symbol)) ENUM_ENTRIES_TEMPLATE_PATH else null
+ is KtFunctionSymbol -> {
+ when {
+ isEnumValuesMethod(symbol) -> ENUM_VALUES_TEMPLATE_PATH
+ isEnumValueOfMethod(symbol) -> ENUM_VALUEOF_TEMPLATE_PATH
+ else -> null
+ }
+ }
+
+ else -> null
+ }
+
+private fun KtAnalysisSession.isEnumSpecialMember(symbol: KtPossibleMemberSymbol): Boolean =
+ symbol.origin == KtSymbolOrigin.SOURCE_MEMBER_GENERATED
+ && (symbol.getContainingSymbol() as? KtClassOrObjectSymbol)?.classKind == KtClassKind.ENUM_CLASS
+
+private fun KtAnalysisSession.isEnumEntriesProperty(symbol: KtPropertySymbol): Boolean =
+ symbol.name == StandardNames.ENUM_ENTRIES && isEnumSpecialMember(symbol)
+
+private fun KtAnalysisSession.isEnumValuesMethod(symbol: KtFunctionSymbol): Boolean =
+ symbol.name == StandardNames.ENUM_VALUES && isEnumSpecialMember(symbol)
+
+private fun KtAnalysisSession.isEnumValueOfMethod(symbol: KtFunctionSymbol): Boolean =
+ symbol.name == StandardNames.ENUM_VALUE_OF && isEnumSpecialMember(symbol)
+
+internal fun KtAnalysisSession.getGeneratedKDocDocumentationFrom(symbol: KtSymbol): DocumentationNode? {
+ val templatePath = getDocumentationTemplatePath(symbol) ?: return null
+ return loadTemplate(templatePath)
+}
+
+private fun KtAnalysisSession.loadTemplate(filePath: String): DocumentationNode? {
+ val kdoc = loadContent(filePath) ?: return null
+ val externalDriProvider = { link: String ->
+ resolveKDocTextLink(link)
+ }
+
+ val parser = MarkdownParser(externalDriProvider, filePath)
+ return parser.parse(kdoc)
+}
+
+private fun loadContent(filePath: String): String? =
+ SymbolsAnalysisPlugin::class.java.getResource(filePath)?.readText()
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorDocumentationContent.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorDocumentationContent.kt
new file mode 100644
index 00000000..4db17b28
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorDocumentationContent.kt
@@ -0,0 +1,19 @@
+/*
+ * 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.kdoc.java
+
+import org.jetbrains.dokka.analysis.java.doccomment.DocumentationContent
+import org.jetbrains.dokka.analysis.java.JavadocTag
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
+
+internal data class DescriptorDocumentationContent(
+ val resolveDocContext: ResolveDocContext,
+ val element: KDocTag,
+ override val tag: JavadocTag,
+) : DocumentationContent {
+ override fun resolveSiblings(): List<DocumentationContent> {
+ return listOf(this)
+ }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorKotlinDocCommentCreator.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorKotlinDocCommentCreator.kt
new file mode 100644
index 00000000..7a43972c
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorKotlinDocCommentCreator.kt
@@ -0,0 +1,20 @@
+/*
+ * 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.kdoc.java
+
+import com.intellij.psi.PsiNamedElement
+import org.jetbrains.dokka.analysis.java.doccomment.DocComment
+import org.jetbrains.dokka.analysis.java.doccomment.DocCommentCreator
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.findKDoc
+import org.jetbrains.kotlin.psi.KtElement
+
+internal class DescriptorKotlinDocCommentCreator : DocCommentCreator {
+ override fun create(element: PsiNamedElement): DocComment? {
+ val ktElement = element.navigationElement as? KtElement ?: return null
+ val kdoc = ktElement.findKDoc() ?: return null
+
+ return KotlinDocComment(kdoc.contentTag, ResolveDocContext(ktElement))
+ }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocComment.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocComment.kt
new file mode 100644
index 00000000..63c55869
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocComment.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.kdoc.java
+
+import org.jetbrains.dokka.analysis.java.*
+import org.jetbrains.dokka.analysis.java.doccomment.DocComment
+import org.jetbrains.dokka.analysis.java.doccomment.DocumentationContent
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
+import org.jetbrains.kotlin.psi.KtElement
+
+internal class ResolveDocContext(val ktElement: KtElement)
+
+internal class KotlinDocComment(
+ val comment: KDocTag,
+ val resolveDocContext: ResolveDocContext
+) : DocComment {
+
+ private val tagsWithContent: List<KDocTag> = comment.children.mapNotNull { (it as? KDocTag) }
+
+ override fun hasTag(tag: JavadocTag): Boolean {
+ return when (tag) {
+ is DescriptionJavadocTag -> comment.getContent().isNotEmpty()
+ is ThrowingExceptionJavadocTag -> tagsWithContent.any { it.hasException(tag) }
+ else -> tagsWithContent.any { it.text.startsWith("@${tag.name}") }
+ }
+ }
+
+ private fun KDocTag.hasException(tag: ThrowingExceptionJavadocTag) =
+ text.startsWith("@${tag.name}") && getSubjectName() == tag.exceptionQualifiedName
+
+ override fun resolveTag(tag: JavadocTag): List<DocumentationContent> {
+ return when (tag) {
+ is DescriptionJavadocTag -> listOf(DescriptorDocumentationContent(resolveDocContext, comment, tag))
+ is ParamJavadocTag -> {
+ val resolvedContent = resolveGeneric(tag)
+ listOf(resolvedContent[tag.paramIndex])
+ }
+
+ is ThrowsJavadocTag -> resolveThrowingException(tag)
+ is ExceptionJavadocTag -> resolveThrowingException(tag)
+ else -> resolveGeneric(tag)
+ }
+ }
+
+ private fun resolveThrowingException(tag: ThrowingExceptionJavadocTag): List<DescriptorDocumentationContent> {
+ val exceptionName = tag.exceptionQualifiedName ?: return resolveGeneric(tag)
+
+ return comment.children
+ .filterIsInstance<KDocTag>()
+ .filter { it.name == tag.name && it.getSubjectName() == exceptionName }
+ .map { DescriptorDocumentationContent(resolveDocContext, it, tag) }
+ }
+
+ private fun resolveGeneric(tag: JavadocTag): List<DescriptorDocumentationContent> {
+ return comment.children.mapNotNull { element ->
+ if (element is KDocTag && element.name == tag.name) {
+ DescriptorDocumentationContent(resolveDocContext, element, tag)
+ } else {
+ null
+ }
+ }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as KotlinDocComment
+
+ if (comment != other.comment) return false
+ //if (resolveDocContext.name != other.resolveDocContext.name) return false
+ if (tagsWithContent != other.tagsWithContent) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = comment.hashCode()
+ // result = 31 * result + resolveDocContext.name.hashCode()
+ result = 31 * result + tagsWithContent.hashCode()
+ return result
+ }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocCommentParser.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocCommentParser.kt
new file mode 100644
index 00000000..0ee95e45
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocCommentParser.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.kdoc.java
+
+import com.intellij.psi.PsiNamedElement
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.analysis.java.doccomment.DocComment
+import org.jetbrains.dokka.analysis.java.parsers.DocCommentParser
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.*
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.logUnresolvedLink
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.parseFromKDocTag
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.resolveKDocLink
+import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.SymbolsAnalysisPlugin
+import org.jetbrains.dokka.model.doc.DocumentationNode
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.querySingle
+import org.jetbrains.kotlin.analysis.api.analyze
+
+internal class KotlinDocCommentParser(
+ private val context: DokkaContext
+) : DocCommentParser {
+
+ override fun canParse(docComment: DocComment): Boolean {
+ return docComment is KotlinDocComment
+ }
+
+ override fun parse(docComment: DocComment, context: PsiNamedElement): DocumentationNode {
+ val kotlinDocComment = docComment as KotlinDocComment
+ return parseDocumentation(kotlinDocComment)
+ }
+
+ fun parseDocumentation(element: KotlinDocComment, parseWithChildren: Boolean = true): DocumentationNode {
+ val sourceSet = context.configuration.sourceSets.let { sourceSets ->
+ sourceSets.firstOrNull { it.sourceSetID.sourceSetName == "jvmMain" }
+ ?: sourceSets.first { it.analysisPlatform == Platform.jvm }
+ }
+ val kotlinAnalysis = context.plugin<SymbolsAnalysisPlugin>().querySingle { kotlinAnalysis }
+ val elementName = element.resolveDocContext.ktElement.name
+ return analyze(kotlinAnalysis.getModule(sourceSet)) {
+ parseFromKDocTag(
+ kDocTag = element.comment,
+ externalDri = { link -> resolveKDocLink(link).ifUnresolved { context.logger.logUnresolvedLink(link.getLinkText(), elementName) } },
+ kdocLocation = null,
+ parseWithChildren = parseWithChildren
+ )
+ }
+ }
+}
+
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinInheritDocTagContentProvider.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinInheritDocTagContentProvider.kt
new file mode 100644
index 00000000..4c2b7afd
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinInheritDocTagContentProvider.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.kdoc.java
+
+import org.jetbrains.dokka.analysis.java.doccomment.DocumentationContent
+import org.jetbrains.dokka.analysis.java.JavaAnalysisPlugin
+import org.jetbrains.dokka.analysis.java.parsers.doctag.DocTagParserContext
+import org.jetbrains.dokka.analysis.java.parsers.doctag.InheritDocTagContentProvider
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.query
+
+internal class KotlinInheritDocTagContentProvider(
+ context: DokkaContext
+) : InheritDocTagContentProvider {
+
+ val parser: KotlinDocCommentParser by lazy {
+ context.plugin<JavaAnalysisPlugin>().query { docCommentParsers }
+ .single { it is KotlinDocCommentParser } as KotlinDocCommentParser
+ }
+
+ override fun canConvert(content: DocumentationContent): Boolean = content is DescriptorDocumentationContent
+
+ override fun convertToHtml(content: DocumentationContent, docTagParserContext: DocTagParserContext): String {
+ val descriptorContent = content as DescriptorDocumentationContent
+ val inheritedDocNode = parser.parseDocumentation(
+ KotlinDocComment(descriptorContent.element, descriptorContent.resolveDocContext),
+ parseWithChildren = false
+ )
+ val id = docTagParserContext.store(inheritedDocNode)
+ return """<inheritdoc id="$id"/>"""
+ }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/IllegalModuleAndPackageDocumentation.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/IllegalModuleAndPackageDocumentation.kt
new file mode 100644
index 00000000..a090e4d5
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/IllegalModuleAndPackageDocumentation.kt
@@ -0,0 +1,11 @@
+/*
+ * 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.kdoc.moduledocs
+
+import org.jetbrains.dokka.DokkaException
+
+internal class IllegalModuleAndPackageDocumentation(
+ source: ModuleAndPackageDocumentationSource, message: String
+) : DokkaException("[$source] $message")
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentation.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentation.kt
new file mode 100644
index 00000000..4036d3d0
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentation.kt
@@ -0,0 +1,15 @@
+/*
+ * 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.kdoc.moduledocs
+
+import org.jetbrains.dokka.model.doc.DocumentationNode
+
+internal data class ModuleAndPackageDocumentation(
+ val name: String,
+ val classifier: Classifier,
+ val documentation: DocumentationNode
+) {
+ enum class Classifier { Module, Package }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationFragment.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationFragment.kt
new file mode 100644
index 00000000..be7c915c
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationFragment.kt
@@ -0,0 +1,13 @@
+/*
+ * 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.kdoc.moduledocs
+
+
+internal data class ModuleAndPackageDocumentationFragment(
+ val name: String,
+ val classifier: ModuleAndPackageDocumentation.Classifier,
+ val documentation: String,
+ val source: ModuleAndPackageDocumentationSource
+)
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationParsingContext.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationParsingContext.kt
new file mode 100644
index 00000000..f5cfbdb9
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationParsingContext.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.kdoc.moduledocs
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.ifUnresolved
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.logUnresolvedLink
+import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.KotlinAnalysis
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.moduledocs.ModuleAndPackageDocumentation.Classifier.Module
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.moduledocs.ModuleAndPackageDocumentation.Classifier.Package
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.resolveKDocTextLink
+import org.jetbrains.dokka.analysis.markdown.jb.MarkdownParser
+import org.jetbrains.dokka.model.doc.DocumentationNode
+import org.jetbrains.dokka.utilities.DokkaLogger
+import org.jetbrains.kotlin.analysis.api.analyze
+import org.jetbrains.kotlin.name.FqName
+
+internal fun interface ModuleAndPackageDocumentationParsingContext {
+ fun markdownParserFor(fragment: ModuleAndPackageDocumentationFragment, location: String): MarkdownParser
+}
+
+internal fun ModuleAndPackageDocumentationParsingContext.parse(
+ fragment: ModuleAndPackageDocumentationFragment
+): DocumentationNode {
+ return markdownParserFor(fragment, fragment.source.sourceDescription).parse(fragment.documentation)
+}
+
+internal fun ModuleAndPackageDocumentationParsingContext(
+ logger: DokkaLogger,
+ kotlinAnalysis: KotlinAnalysis? = null,
+ sourceSet: DokkaConfiguration.DokkaSourceSet? = null
+) = ModuleAndPackageDocumentationParsingContext { fragment, sourceLocation ->
+
+ if (kotlinAnalysis == null || sourceSet == null) {
+ MarkdownParser(externalDri = { null }, sourceLocation)
+ } else {
+ val sourceModule = kotlinAnalysis.getModule(sourceSet)
+ val contextPsi = analyze(sourceModule) {
+ val contextSymbol = when (fragment.classifier) {
+ Module -> ROOT_PACKAGE_SYMBOL
+ Package -> getPackageSymbolIfPackageExists(FqName(fragment.name))
+ }
+ contextSymbol?.psi
+ }
+ MarkdownParser(
+ externalDri = { link ->
+ analyze(sourceModule) {
+ resolveKDocTextLink(
+ link,
+ contextPsi
+ ).ifUnresolved {
+ logger.logUnresolvedLink(link, fragment.name.ifBlank { "module documentation" })
+ }
+
+ }
+ },
+ sourceLocation
+ )
+
+ }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationReader.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationReader.kt
new file mode 100644
index 00000000..ef79e885
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationReader.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.kdoc.moduledocs
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.moduledocs.ModuleAndPackageDocumentation.Classifier
+import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.SymbolsAnalysisPlugin
+import org.jetbrains.dokka.model.DModule
+import org.jetbrains.dokka.model.DPackage
+import org.jetbrains.dokka.model.SourceSetDependent
+import org.jetbrains.dokka.model.doc.*
+import org.jetbrains.dokka.model.doc.Deprecated
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.querySingle
+import org.jetbrains.dokka.utilities.associateWithNotNull
+import org.jetbrains.dokka.analysis.kotlin.internal.ModuleAndPackageDocumentationReader
+
+internal fun ModuleAndPackageDocumentationReader(context: DokkaContext): ModuleAndPackageDocumentationReader =
+ ContextModuleAndPackageDocumentationReader(context)
+
+private class ContextModuleAndPackageDocumentationReader(
+ private val context: DokkaContext
+) : ModuleAndPackageDocumentationReader {
+
+ private val kotlinAnalysis = context.plugin<SymbolsAnalysisPlugin>().querySingle { kotlinAnalysis }
+
+ private val documentationFragments: SourceSetDependent<List<ModuleAndPackageDocumentationFragment>> =
+ context.configuration.sourceSets.associateWith { sourceSet ->
+ sourceSet.includes.flatMap { include -> parseModuleAndPackageDocumentationFragments(include) }
+ }
+
+ private fun findDocumentationNodes(
+ sourceSets: Set<DokkaConfiguration.DokkaSourceSet>,
+ predicate: (ModuleAndPackageDocumentationFragment) -> Boolean
+ ): SourceSetDependent<DocumentationNode> {
+ return sourceSets.associateWithNotNull { sourceSet ->
+ val fragments = documentationFragments[sourceSet].orEmpty().filter(predicate)
+ kotlinAnalysis.getModule(sourceSet)// test: to throw exception for unknown sourceSet
+ val documentations = fragments.map { fragment ->
+ parseModuleAndPackageDocumentation(
+ context = ModuleAndPackageDocumentationParsingContext(context.logger, kotlinAnalysis, sourceSet),
+ fragment = fragment
+ )
+ }
+ when (documentations.size) {
+ 0 -> null
+ 1 -> documentations.single().documentation
+ else -> DocumentationNode(documentations.flatMap { it.documentation.children }
+ .mergeDocumentationNodes())
+ }
+ }
+ }
+
+ private val ModuleAndPackageDocumentationFragment.canonicalPackageName: String
+ get() {
+ check(classifier == Classifier.Package)
+ if (name == "[root]") return ""
+ return name
+ }
+
+ override fun read(module: DModule): SourceSetDependent<DocumentationNode> {
+ return findDocumentationNodes(module.sourceSets) { fragment ->
+ fragment.classifier == Classifier.Module && (fragment.name == module.name)
+ }
+ }
+
+ override fun read(pkg: DPackage): SourceSetDependent<DocumentationNode> {
+ return findDocumentationNodes(pkg.sourceSets) { fragment ->
+ fragment.classifier == Classifier.Package && fragment.canonicalPackageName == pkg.dri.packageName
+ }
+ }
+
+ override fun read(module: DokkaConfiguration.DokkaModuleDescription): DocumentationNode? {
+ val parsingContext = ModuleAndPackageDocumentationParsingContext(context.logger)
+
+ val documentationFragment = module.includes
+ .flatMap { include -> parseModuleAndPackageDocumentationFragments(include) }
+ .firstOrNull { fragment -> fragment.classifier == Classifier.Module && fragment.name == module.name }
+ ?: return null
+
+ val moduleDocumentation = parseModuleAndPackageDocumentation(parsingContext, documentationFragment)
+ return moduleDocumentation.documentation
+ }
+
+ private fun List<TagWrapper>.mergeDocumentationNodes(): List<TagWrapper> =
+ groupBy { it::class }.values.map {
+ it.reduce { acc, tagWrapper ->
+ val newRoot = CustomDocTag(
+ acc.children + tagWrapper.children,
+ name = (tagWrapper as? NamedTagWrapper)?.name.orEmpty()
+ )
+ when (acc) {
+ is See -> acc.copy(newRoot)
+ is Param -> acc.copy(newRoot)
+ is Throws -> acc.copy(newRoot)
+ is Sample -> acc.copy(newRoot)
+ is Property -> acc.copy(newRoot)
+ is CustomTagWrapper -> acc.copy(newRoot)
+ is Description -> acc.copy(newRoot)
+ is Author -> acc.copy(newRoot)
+ is Version -> acc.copy(newRoot)
+ is Since -> acc.copy(newRoot)
+ is Return -> acc.copy(newRoot)
+ is Receiver -> acc.copy(newRoot)
+ is Constructor -> acc.copy(newRoot)
+ is Deprecated -> acc.copy(newRoot)
+ is org.jetbrains.dokka.model.doc.Suppress -> acc.copy(newRoot)
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationSource.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationSource.kt
new file mode 100644
index 00000000..f994eb36
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationSource.kt
@@ -0,0 +1,18 @@
+/*
+ * 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.kdoc.moduledocs
+
+import java.io.File
+
+internal abstract class ModuleAndPackageDocumentationSource {
+ abstract val sourceDescription: String
+ abstract val documentation: String
+ override fun toString(): String = sourceDescription
+}
+
+internal data class ModuleAndPackageDocumentationFile(private val file: File) : ModuleAndPackageDocumentationSource() {
+ override val sourceDescription: String = file.path
+ override val documentation: String by lazy(LazyThreadSafetyMode.PUBLICATION) { file.readText() }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentation.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentation.kt
new file mode 100644
index 00000000..176a487b
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentation.kt
@@ -0,0 +1,16 @@
+/*
+ * 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.kdoc.moduledocs
+
+internal fun parseModuleAndPackageDocumentation(
+ context: ModuleAndPackageDocumentationParsingContext,
+ fragment: ModuleAndPackageDocumentationFragment
+): ModuleAndPackageDocumentation {
+ return ModuleAndPackageDocumentation(
+ name = fragment.name,
+ classifier = fragment.classifier,
+ documentation = context.parse(fragment)
+ )
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentationFragments.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentationFragments.kt
new file mode 100644
index 00000000..621b06f0
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentationFragments.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.kdoc.moduledocs
+
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.moduledocs.ModuleAndPackageDocumentation.Classifier.Module
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.moduledocs.ModuleAndPackageDocumentation.Classifier.Package
+import java.io.File
+
+internal fun parseModuleAndPackageDocumentationFragments(source: File): List<ModuleAndPackageDocumentationFragment> {
+ return parseModuleAndPackageDocumentationFragments(ModuleAndPackageDocumentationFile(source))
+}
+
+internal fun parseModuleAndPackageDocumentationFragments(
+ source: ModuleAndPackageDocumentationSource
+): List<ModuleAndPackageDocumentationFragment> {
+ val fragmentStrings = source.documentation.split(Regex("(|^)#\\s*(?=(Module|Package))"))
+ return fragmentStrings
+ .filter(String::isNotBlank)
+ .map { fragmentString -> parseModuleAndPackageDocFragment(source, fragmentString) }
+}
+
+private fun parseModuleAndPackageDocFragment(
+ source: ModuleAndPackageDocumentationSource,
+ fragment: String
+): ModuleAndPackageDocumentationFragment {
+ val firstLineAndDocumentation = fragment.split("\r\n", "\n", "\r", limit = 2)
+ val firstLine = firstLineAndDocumentation[0]
+
+ val classifierAndName = firstLine.split(Regex("\\s+"), limit = 2)
+
+ val classifier = when (classifierAndName[0].trim()) {
+ "Module" -> Module
+ "Package" -> Package
+ else -> throw IllegalStateException(
+ """Unexpected classifier: "${classifierAndName[0]}", expected either "Module" or "Package".
+ |For more information consult the specification: https://kotlinlang.org/docs/dokka-module-and-package-docs.html""".trimMargin()
+ )
+ }
+
+ if (classifierAndName.size != 2 && classifier == Module) {
+ throw IllegalModuleAndPackageDocumentation(source, "Missing Module name")
+ }
+
+ val name = classifierAndName.getOrNull(1)?.trim().orEmpty()
+ if (classifier == Package && name.contains(Regex("\\s"))) {
+ throw IllegalModuleAndPackageDocumentation(
+ source, "Package name cannot contain whitespace in '$firstLine'"
+ )
+ }
+
+ return ModuleAndPackageDocumentationFragment(
+ name = name,
+ classifier = classifier,
+ documentation = firstLineAndDocumentation.getOrNull(1)?.trim().orEmpty(),
+ source = source
+ )
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/AnalysisContext.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/AnalysisContext.kt
new file mode 100644
index 00000000..191c5f92
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/AnalysisContext.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.plugin
+
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.util.Disposer
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.model.SourceSetDependent
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.kotlin.analysis.api.standalone.StandaloneAnalysisAPISession
+import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
+import java.io.Closeable
+
+internal fun SamplesKotlinAnalysis(
+ sourceSets: List<DokkaConfiguration.DokkaSourceSet>,
+ context: DokkaContext,
+): KotlinAnalysis = createAnalysisSession(
+ sourceSets = sourceSets,
+ logger = context.logger,
+ isSampleProject = true
+)
+
+internal fun ProjectKotlinAnalysis(
+ sourceSets: List<DokkaConfiguration.DokkaSourceSet>,
+ context: DokkaContext,
+): KotlinAnalysis = createAnalysisSession(
+ sourceSets = sourceSets,
+ logger = context.logger
+)
+
+internal class KotlinAnalysis(
+ private val sourceModules: SourceSetDependent<KtSourceModule>,
+ private val analysisSession: StandaloneAnalysisAPISession,
+ private val applicationDisposable: Disposable,
+ private val projectDisposable: Disposable
+) : Closeable {
+
+ fun getModule(sourceSet: DokkaConfiguration.DokkaSourceSet) =
+ sourceModules[sourceSet] ?: error("Missing a source module for sourceSet ${sourceSet.displayName} with id ${sourceSet.sourceSetID}")
+
+ fun getModuleOrNull(sourceSet: DokkaConfiguration.DokkaSourceSet) =
+ sourceModules[sourceSet]
+
+ val modulesWithFiles
+ get() = analysisSession.modulesWithFiles
+
+ override fun close() {
+ Disposer.dispose(applicationDisposable)
+ Disposer.dispose(projectDisposable)
+ }
+} \ No newline at end of file
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/KotlinAnalysis.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/KotlinAnalysis.kt
new file mode 100644
index 00000000..e074a142
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/KotlinAnalysis.kt
@@ -0,0 +1,175 @@
+/*
+ * 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.plugin
+
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.util.Disposer
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.DokkaSourceSetID
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.utilities.DokkaLogger
+import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals
+import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeTokenProvider
+import org.jetbrains.kotlin.analysis.api.standalone.KtAlwaysAccessibleLifetimeTokenProvider
+import org.jetbrains.kotlin.analysis.api.standalone.buildStandaloneAnalysisAPISession
+import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
+import org.jetbrains.kotlin.analysis.project.structure.builder.KtModuleBuilder
+import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtLibraryModule
+import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSdkModule
+import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSourceModule
+import org.jetbrains.kotlin.config.*
+import org.jetbrains.kotlin.platform.CommonPlatforms
+import org.jetbrains.kotlin.platform.js.JsPlatforms
+import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
+import org.jetbrains.kotlin.platform.konan.NativePlatforms
+import java.io.File
+
+internal fun Platform.toTargetPlatform() = when (this) {
+ Platform.js, Platform.wasm -> JsPlatforms.defaultJsPlatform
+ Platform.common -> CommonPlatforms.defaultCommonPlatform
+ Platform.native -> NativePlatforms.unspecifiedNativePlatform
+ Platform.jvm -> JvmPlatforms.defaultJvmPlatform
+}
+
+private fun getJdkHomeFromSystemProperty(logger: DokkaLogger): File? {
+ val javaHome = File(System.getProperty("java.home"))
+ if (!javaHome.exists()) {
+ logger.error("Set existed java.home to use JDK")
+ return null
+ }
+ return javaHome
+}
+
+internal fun getLanguageVersionSettings(
+ languageVersionString: String?,
+ apiVersionString: String?
+): LanguageVersionSettingsImpl {
+ val languageVersion = LanguageVersion.fromVersionString(languageVersionString) ?: LanguageVersion.LATEST_STABLE
+ val apiVersion =
+ apiVersionString?.let { ApiVersion.parse(it) } ?: ApiVersion.createByLanguageVersion(languageVersion)
+ return LanguageVersionSettingsImpl(
+ languageVersion = languageVersion,
+ apiVersion = apiVersion, analysisFlags = hashMapOf(
+ // special flag for Dokka
+ // force to resolve light classes (lazily by default)
+ AnalysisFlags.eagerResolveOfLightClasses to true
+ )
+ )
+}
+
+@OptIn(KtAnalysisApiInternals::class)
+internal fun createAnalysisSession(
+ sourceSets: List<DokkaConfiguration.DokkaSourceSet>,
+ logger: DokkaLogger,
+ applicationDisposable: Disposable = Disposer.newDisposable("StandaloneAnalysisAPISession.application"),
+ projectDisposable: Disposable = Disposer.newDisposable("StandaloneAnalysisAPISession.project"),
+ isSampleProject: Boolean = false
+): KotlinAnalysis {
+ val sourcesModule = mutableMapOf<DokkaConfiguration.DokkaSourceSet, KtSourceModule>()
+
+ val analysisSession = buildStandaloneAnalysisAPISession(
+ applicationDisposable = applicationDisposable,
+ projectDisposable = projectDisposable,
+ withPsiDeclarationFromBinaryModuleProvider = false
+ ) {
+ registerProjectService(KtLifetimeTokenProvider::class.java, KtAlwaysAccessibleLifetimeTokenProvider())
+
+ val sortedSourceSets = topologicalSortByDependantSourceSets(sourceSets, logger)
+
+ val sourcesModuleBySourceSetId = mutableMapOf<DokkaSourceSetID, KtSourceModule>()
+
+ buildKtModuleProvider {
+ val jdkModule = getJdkHomeFromSystemProperty(logger)?.let { jdkHome ->
+ buildKtSdkModule {
+ this.platform = Platform.jvm.toTargetPlatform()
+ addBinaryRootsFromJdkHome(jdkHome.toPath(), isJre = true)
+ sdkName = "JDK"
+ }
+ }
+
+ fun KtModuleBuilder.addModuleDependencies(sourceSet: DokkaConfiguration.DokkaSourceSet) {
+ val targetPlatform = sourceSet.analysisPlatform.toTargetPlatform()
+ addRegularDependency(
+ buildKtLibraryModule {
+ this.platform = targetPlatform
+ addBinaryRoots(sourceSet.classpath.map { it.toPath() })
+ libraryName = "Library for ${sourceSet.displayName}"
+ }
+ )
+ if (sourceSet.analysisPlatform == Platform.jvm) {
+ jdkModule?.let { addRegularDependency(it) }
+ }
+ sourceSet.dependentSourceSets.forEach {
+ addRegularDependency(
+ sourcesModuleBySourceSetId[it]
+ ?: error("There is no source module for $it")
+ )
+ }
+ }
+
+ for (sourceSet in sortedSourceSets) {
+ val targetPlatform = sourceSet.analysisPlatform.toTargetPlatform()
+ val sourceModule = buildKtSourceModule {
+ languageVersionSettings =
+ getLanguageVersionSettings(sourceSet.languageVersion, sourceSet.apiVersion)
+ platform = targetPlatform
+ moduleName = "<module ${sourceSet.displayName}>"
+ if (isSampleProject)
+ addSourceRoots(sourceSet.samples.map { it.toPath() })
+ else
+ addSourceRoots(sourceSet.sourceRoots.map { it.toPath() })
+ addModuleDependencies(
+ sourceSet,
+ )
+ }
+ sourcesModule[sourceSet] = sourceModule
+ sourcesModuleBySourceSetId[sourceSet.sourceSetID] = sourceModule
+ addModule(sourceModule)
+ }
+ platform = sourceSets.map { it.analysisPlatform }.distinct().singleOrNull()?.toTargetPlatform()
+ ?: Platform.common.toTargetPlatform()
+ }
+ }
+ return KotlinAnalysis(sourcesModule, analysisSession, applicationDisposable, projectDisposable)
+}
+
+private enum class State {
+ UNVISITED,
+ VISITING,
+ VISITED;
+}
+
+internal fun topologicalSortByDependantSourceSets(
+ sourceSets: List<DokkaConfiguration.DokkaSourceSet>,
+ logger: DokkaLogger
+): List<DokkaConfiguration.DokkaSourceSet> {
+ val result = mutableListOf<DokkaConfiguration.DokkaSourceSet>()
+
+ val verticesAssociatedWithState = sourceSets.associateWithTo(mutableMapOf()) { State.UNVISITED }
+ fun dfs(souceSet: DokkaConfiguration.DokkaSourceSet) {
+ when (verticesAssociatedWithState[souceSet]) {
+ State.VISITED -> return
+ State.VISITING -> {
+ logger.error("Detected cycle in source set graph")
+ return
+ }
+
+ else -> {
+ val dependentSourceSets =
+ souceSet.dependentSourceSets.mapNotNull { dependentSourceSetId ->
+ sourceSets.find { it.sourceSetID == dependentSourceSetId }
+ // just skip
+ ?: null.also { logger.error("Unknown source set Id $dependentSourceSetId in dependencies of ${souceSet.sourceSetID}") }
+ }
+ verticesAssociatedWithState[souceSet] = State.VISITING
+ dependentSourceSets.forEach(::dfs)
+ verticesAssociatedWithState[souceSet] = State.VISITED
+ result += souceSet
+ }
+ }
+ }
+ sourceSets.forEach(::dfs)
+ return result
+} \ No newline at end of file
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/SymbolsAnalysisPlugin.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/SymbolsAnalysisPlugin.kt
new file mode 100644
index 00000000..122e0b10
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/SymbolsAnalysisPlugin.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.plugin
+
+import com.intellij.lang.jvm.annotation.JvmAnnotationAttribute
+import com.intellij.psi.PsiAnnotation
+import org.jetbrains.dokka.CoreExtensions
+import org.jetbrains.dokka.analysis.java.BreakingAbstractionKotlinLightMethodChecker
+import org.jetbrains.dokka.analysis.java.JavaAnalysisPlugin
+import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.java.KotlinInheritDocTagContentProvider
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.java.DescriptorKotlinDocCommentCreator
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.java.KotlinDocCommentParser
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.moduledocs.ModuleAndPackageDocumentationReader
+import org.jetbrains.dokka.analysis.kotlin.symbols.services.KotlinAnalysisSourceRootsExtractor
+import org.jetbrains.dokka.analysis.kotlin.symbols.services.*
+import org.jetbrains.dokka.analysis.kotlin.symbols.services.KotlinDocumentableSourceLanguageParser
+import org.jetbrains.dokka.analysis.kotlin.symbols.services.SymbolExternalDocumentablesProvider
+import org.jetbrains.dokka.analysis.kotlin.symbols.translators.DefaultSymbolToDocumentableTranslator
+import org.jetbrains.dokka.plugability.DokkaPlugin
+import org.jetbrains.dokka.plugability.DokkaPluginApiPreview
+import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement
+import org.jetbrains.dokka.plugability.querySingle
+import org.jetbrains.dokka.renderers.PostAction
+import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation
+
+@Suppress("unused")
+public class SymbolsAnalysisPlugin : DokkaPlugin() {
+
+ internal val kotlinAnalysis by extensionPoint<KotlinAnalysis>()
+
+ internal val defaultKotlinAnalysis by extending {
+ kotlinAnalysis providing { ctx ->
+ ProjectKotlinAnalysis(
+ sourceSets = ctx.configuration.sourceSets,
+ context = ctx
+ )
+ }
+ }
+
+ internal val disposeKotlinAnalysisPostAction by extending {
+ CoreExtensions.postActions with PostAction { querySingle { kotlinAnalysis }.close() }
+ }
+
+ internal val symbolToDocumentableTranslator by extending {
+ CoreExtensions.sourceToDocumentableTranslator providing ::DefaultSymbolToDocumentableTranslator
+ }
+
+ private val javaAnalysisPlugin by lazy { plugin<JavaAnalysisPlugin>() }
+
+ internal val projectProvider by extending {
+ javaAnalysisPlugin.projectProvider providing { KotlinAnalysisProjectProvider() }
+ }
+
+ internal val sourceRootsExtractor by extending {
+ javaAnalysisPlugin.sourceRootsExtractor providing { KotlinAnalysisSourceRootsExtractor() }
+ }
+
+ internal val kotlinDocCommentCreator by extending {
+ javaAnalysisPlugin.docCommentCreators providing {
+ DescriptorKotlinDocCommentCreator()
+ }
+ }
+
+ internal val kotlinDocCommentParser by extending {
+ javaAnalysisPlugin.docCommentParsers providing { context ->
+ KotlinDocCommentParser(
+ context
+ )
+ }
+ }
+ internal val inheritDocTagProvider by extending {
+ javaAnalysisPlugin.inheritDocTagContentProviders providing ::KotlinInheritDocTagContentProvider
+ }
+ internal val kotlinLightMethodChecker by extending {
+ javaAnalysisPlugin.kotlinLightMethodChecker providing {
+ object : BreakingAbstractionKotlinLightMethodChecker {
+ override fun isLightAnnotation(annotation: PsiAnnotation): Boolean {
+ return annotation is KtLightAbstractAnnotation
+ }
+
+ override fun isLightAnnotationAttribute(attribute: JvmAnnotationAttribute): Boolean {
+ return attribute is KtLightAbstractAnnotation
+ }
+ }
+ }
+ }
+
+
+ internal val symbolAnalyzerImpl by extending {
+ plugin<InternalKotlinAnalysisPlugin>().documentableSourceLanguageParser providing { KotlinDocumentableSourceLanguageParser() }
+ }
+
+ internal val symbolFullClassHierarchyBuilder by extending {
+ plugin<InternalKotlinAnalysisPlugin>().fullClassHierarchyBuilder providing ::SymbolFullClassHierarchyBuilder
+ }
+
+ internal val symbolSyntheticDocumentableDetector by extending {
+ plugin<InternalKotlinAnalysisPlugin>().syntheticDocumentableDetector providing { SymbolSyntheticDocumentableDetector() }
+ }
+
+ internal val moduleAndPackageDocumentationReader by extending {
+ plugin<InternalKotlinAnalysisPlugin>().moduleAndPackageDocumentationReader providing ::ModuleAndPackageDocumentationReader
+ }
+
+ internal val kotlinToJavaMapper by extending {
+ plugin<InternalKotlinAnalysisPlugin>().kotlinToJavaService providing { SymbolKotlinToJavaMapper() }
+ }
+
+ internal val symbolInheritanceBuilder by extending {
+ plugin<InternalKotlinAnalysisPlugin>().inheritanceBuilder providing ::SymbolInheritanceBuilder
+ }
+
+ internal val symbolExternalDocumentablesProvider by extending {
+ plugin<InternalKotlinAnalysisPlugin>().externalDocumentablesProvider providing ::SymbolExternalDocumentablesProvider
+ }
+
+ internal val kotlinSampleProviderFactory by extending {
+ plugin<InternalKotlinAnalysisPlugin>().sampleProviderFactory providing ::KotlinSampleProviderFactory
+ }
+
+ @OptIn(DokkaPluginApiPreview::class)
+ override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisProjectProvider.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisProjectProvider.kt
new file mode 100644
index 00000000..398d48ee
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisProjectProvider.kt
@@ -0,0 +1,20 @@
+/*
+ * 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.openapi.project.Project
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.analysis.java.ProjectProvider
+import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.SymbolsAnalysisPlugin
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.querySingle
+
+internal class KotlinAnalysisProjectProvider : ProjectProvider {
+ override fun getProject(sourceSet: DokkaConfiguration.DokkaSourceSet, context: DokkaContext): Project {
+ val kotlinAnalysis = context.plugin<SymbolsAnalysisPlugin>().querySingle { kotlinAnalysis }
+ return kotlinAnalysis.getModule(sourceSet).project
+ }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisSourceRootsExtractor.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisSourceRootsExtractor.kt
new file mode 100644
index 00000000..01084eab
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisSourceRootsExtractor.kt
@@ -0,0 +1,16 @@
+/*
+ * 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.DokkaConfiguration
+import org.jetbrains.dokka.analysis.java.SourceRootsExtractor
+import org.jetbrains.dokka.plugability.DokkaContext
+import java.io.File
+
+internal class KotlinAnalysisSourceRootsExtractor : SourceRootsExtractor {
+ override fun extract(sourceSet: DokkaConfiguration.DokkaSourceSet, context: DokkaContext): List<File> {
+ return sourceSet.sourceRoots.filter { directory -> directory.isDirectory || directory.extension == "java" }
+ }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinDocumentableSourceLanguageParser.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinDocumentableSourceLanguageParser.kt
new file mode 100644
index 00000000..ae0d79ee
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinDocumentableSourceLanguageParser.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.DokkaConfiguration
+import org.jetbrains.dokka.analysis.java.util.PsiDocumentableSource
+import org.jetbrains.dokka.model.Documentable
+import org.jetbrains.dokka.model.WithSources
+import org.jetbrains.dokka.analysis.kotlin.internal.DocumentableLanguage
+import org.jetbrains.dokka.analysis.kotlin.internal.DocumentableSourceLanguageParser
+
+internal class KotlinDocumentableSourceLanguageParser : DocumentableSourceLanguageParser {
+
+ /**
+ * For members inherited from Java in Kotlin - it returns [DocumentableLanguage.KOTLIN]
+ */
+ override fun getLanguage(
+ documentable: Documentable,
+ sourceSet: DokkaConfiguration.DokkaSourceSet,
+ ): DocumentableLanguage? {
+ val documentableSource = (documentable as? WithSources)?.sources?.get(sourceSet) ?: return null
+ return when (documentableSource) {
+ is PsiDocumentableSource -> DocumentableLanguage.JAVA
+ is KtPsiDocumentableSource -> DocumentableLanguage.KOTLIN
+ else -> error("Unknown language sources: ${documentableSource::class}")
+ }
+ }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinSampleProvider.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinSampleProvider.kt
new file mode 100644
index 00000000..e453c72d
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinSampleProvider.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.PsiElement
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.InternalDokkaApi
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.querySingle
+import org.jetbrains.dokka.analysis.kotlin.internal.SampleProvider
+import org.jetbrains.dokka.analysis.kotlin.internal.SampleProviderFactory
+import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.SamplesKotlinAnalysis
+import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.SymbolsAnalysisPlugin
+import org.jetbrains.kotlin.analysis.api.analyze
+import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.KtBlockExpression
+import org.jetbrains.kotlin.psi.KtDeclarationWithBody
+import org.jetbrains.kotlin.psi.KtFile
+
+public class KotlinSampleProviderFactory(
+ public val context: DokkaContext
+): SampleProviderFactory {
+ override fun build(): SampleProvider {
+ return KotlinSampleProvider(context)
+ }
+
+}
+/**
+ * It's declared as open since StdLib has its own sample transformer
+ * with [processBody] and [processImports]
+ */
+@InternalDokkaApi
+public open class KotlinSampleProvider(
+ public val context: DokkaContext
+): SampleProvider {
+ private val kotlinAnalysisOfRegularSources = context.plugin<SymbolsAnalysisPlugin>().querySingle { kotlinAnalysis }
+
+ private val kotlinAnalysisOfSamples = SamplesKotlinAnalysis(
+ sourceSets = context.configuration.sourceSets, context = context
+ )
+
+ protected open fun processBody(psiElement: PsiElement): String {
+ val text = processSampleBody(psiElement).trim { it == '\n' || it == '\r' }.trimEnd()
+ val lines = text.split("\n")
+ val indent = lines.filter(String::isNotBlank).minOfOrNull { it.takeWhile(Char::isWhitespace).count() } ?: 0
+ return lines.joinToString("\n") { it.drop(indent) }
+ }
+
+ private fun processSampleBody(psiElement: PsiElement): String = when (psiElement) {
+ is KtDeclarationWithBody -> {
+ when (val bodyExpression = psiElement.bodyExpression) {
+ is KtBlockExpression -> bodyExpression.text.removeSurrounding("{", "}")
+ else -> bodyExpression!!.text
+ }
+ }
+ else -> psiElement.text
+ }
+
+ protected open fun processImports(psiElement: PsiElement): String {
+ val psiFile = psiElement.containingFile
+ return when(val text = (psiFile as? KtFile)?.importList?.text) {
+ is String -> text
+ else -> ""
+ }
+ }
+
+ /**
+ * @return [SampleProvider.SampleSnippet] or null if it has not found by [fqLink]
+ */
+ override fun getSample(sourceSet: DokkaConfiguration.DokkaSourceSet, fqLink: String): SampleProvider.SampleSnippet? {
+ return kotlinAnalysisOfSamples.getModuleOrNull(sourceSet)?.let { getSampleFromModule(it, fqLink) }
+ ?: getSampleFromModule(
+ kotlinAnalysisOfRegularSources.getModule(sourceSet), fqLink
+ )
+ }
+ private fun getSampleFromModule(module: KtSourceModule, fqLink: String): SampleProvider.SampleSnippet? {
+ val psiElement = analyze(module) {
+ val lastDotIndex = fqLink.lastIndexOf('.')
+
+ val functionName = if (lastDotIndex == -1) fqLink else fqLink.substring(lastDotIndex + 1, fqLink.length)
+ val packageName = if (lastDotIndex == -1) "" else fqLink.substring(0, lastDotIndex)
+ getTopLevelCallableSymbols(FqName(packageName), Name.identifier(functionName)).firstOrNull()?.psi
+ }
+ ?: return null.also { context.logger.warn("Cannot find PsiElement corresponding to $fqLink") }
+ val imports =
+ processImports(psiElement)
+ val body = processBody(psiElement)
+
+ return SampleProvider.SampleSnippet(imports, body)
+ }
+
+ override fun close() {
+ kotlinAnalysisOfSamples.close()
+ }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KtPsiDocumentableSource.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KtPsiDocumentableSource.kt
new file mode 100644
index 00000000..284da189
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KtPsiDocumentableSource.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.PsiDocumentManager
+import com.intellij.psi.PsiElement
+import org.jetbrains.dokka.model.DocumentableSource
+import org.jetbrains.kotlin.lexer.KtTokens
+
+
+internal class KtPsiDocumentableSource(val psi: PsiElement?) : DocumentableSource {
+ override val path = psi?.containingFile?.virtualFile?.path ?: ""
+
+ override fun computeLineNumber(): Int? {
+ return psi?.let {
+ val range = it.node?.findChildByType(KtTokens.IDENTIFIER)?.textRange ?: it.textRange
+ val doc = PsiDocumentManager.getInstance(it.project).getDocument(it.containingFile)
+ // IJ uses 0-based line-numbers; external source browsers use 1-based
+ doc?.getLineNumber(range.startOffset)?.plus(1)
+ }
+ }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolExternalDocumentablesProvider.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolExternalDocumentablesProvider.kt
new file mode 100644
index 00000000..1473a7da
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolExternalDocumentablesProvider.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.DokkaConfiguration.DokkaSourceSet
+import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.SymbolsAnalysisPlugin
+import org.jetbrains.dokka.analysis.kotlin.symbols.translators.DokkaSymbolVisitor
+import org.jetbrains.dokka.analysis.kotlin.symbols.translators.getClassIdFromDRI
+import org.jetbrains.dokka.analysis.kotlin.symbols.translators.getDRIFromSymbol
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.DClasslike
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.querySingle
+import org.jetbrains.kotlin.analysis.api.analyze
+import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol
+import org.jetbrains.dokka.analysis.kotlin.internal.ExternalDocumentablesProvider
+
+internal class SymbolExternalDocumentablesProvider(val context: DokkaContext) : ExternalDocumentablesProvider {
+ private val kotlinAnalysis = context.plugin<SymbolsAnalysisPlugin>().querySingle { kotlinAnalysis }
+
+ override fun findClasslike(dri: DRI, sourceSet: DokkaSourceSet): DClasslike? {
+ val classId = getClassIdFromDRI(dri)
+
+ return analyze(kotlinAnalysis.getModule(sourceSet)) {
+ val symbol = getClassOrObjectSymbolByClassId(classId) as? KtNamedClassOrObjectSymbol?: return@analyze null
+ val translator = DokkaSymbolVisitor(sourceSet, sourceSet.displayName, kotlinAnalysis, logger = context.logger)
+
+ val parentDRI = symbol.getContainingSymbol()?.let { getDRIFromSymbol(it) } ?: /* top level */ DRI(dri.packageName)
+ with(translator) {
+ return@analyze visitNamedClassOrObjectSymbol(symbol, parentDRI)
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolFullClassHierarchyBuilder.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolFullClassHierarchyBuilder.kt
new file mode 100644
index 00000000..0b68ac81
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolFullClassHierarchyBuilder.kt
@@ -0,0 +1,148 @@
+/*
+ * 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.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
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.*
+import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
+import org.jetbrains.kotlin.analysis.api.analyze
+import org.jetbrains.kotlin.analysis.api.types.KtType
+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(context: DokkaContext) : FullClassHierarchyBuilder {
+ private val kotlinAnalysis = context.plugin<SymbolsAnalysisPlugin>().querySingle { kotlinAnalysis }
+
+ override suspend fun build(module: DModule): ClassHierarchy {
+ val map = module.sourceSets.associateWith { ConcurrentHashMap<DRI, List<DRI>>() }
+ module.packages.forEach { visitDocumentable(it, map) }
+ return map
+ }
+
+ private fun KtAnalysisSession.collectSupertypesFromKtType(
+ driWithKType: Pair<DRI, KtType>,
+ supersMap: MutableMap<DRI, Supertypes>
+ ) {
+ val (dri, kotlinType) = driWithKType
+ 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) }
+ }
+ }
+
+ private fun collectSupertypesFromPsiClass(
+ driWithPsiClass: Pair<DRI, PsiClass>,
+ supersMap: MutableMap<DRI, Supertypes>
+ ) {
+ val (dri, psiClass) = driWithPsiClass
+ val supertypes = psiClass.superTypes.mapNotNull { it.resolve() }
+ .filterNot { it.qualifiedName == "java.lang.Object" }
+ val supertypesDriWithPsiClass = supertypes.map { DRI.from(it) to it }
+
+ if (supersMap[dri] == null) {
+ supersMap[dri] = supertypesDriWithPsiClass.map { it.first }
+ supertypesDriWithPsiClass.forEach { collectSupertypesFromPsiClass(it, supersMap) }
+ }
+ }
+
+ private fun visitDocumentable(
+ documentable: Documentable,
+ hierarchy: SourceSetDependent<MutableMap<DRI, List<DRI>>>
+ ) {
+ if (documentable is WithScope) {
+ documentable.classlikes.forEach { visitDocumentable(it, hierarchy) }
+ }
+ if (documentable is DClasslike) {
+ // to build a full class graph,
+ // using supertypes from Documentable is not enough since it keeps only one level of hierarchy
+ documentable.sources.forEach { (sourceSet, source) ->
+ if (source is KtPsiDocumentableSource) {
+ (source.psi as? KtClassOrObject)?.let { psi ->
+ analyze(kotlinAnalysis.getModule(sourceSet)) {
+ val type = psi.getNamedClassOrObjectSymbol()?.buildSelfClassType() ?: return@analyze
+ hierarchy[sourceSet]?.let { collectSupertypesFromKtType(documentable.dri to type, it) }
+ }
+ }
+ } else if (source is PsiDocumentableSource) {
+ val psi = source.psi as PsiClass
+ hierarchy[sourceSet]?.let { collectSupertypesFromPsiClass(documentable.dri to psi, it) }
+ }
+ }
+ }
+ }
+
+ 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/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolInheritanceBuilder.kt b/dokka-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/dokka-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/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolKotlinToJavaMapper.kt b/dokka-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/dokka-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/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolSyntheticDocumentableDetector.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolSyntheticDocumentableDetector.kt
new file mode 100644
index 00000000..d281e9c0
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolSyntheticDocumentableDetector.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.PsiElement
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.analysis.java.util.PsiDocumentableSource
+import org.jetbrains.dokka.model.Documentable
+import org.jetbrains.dokka.model.InheritedMember
+import org.jetbrains.dokka.model.WithSources
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+import org.jetbrains.dokka.analysis.kotlin.internal.SyntheticDocumentableDetector
+
+internal class SymbolSyntheticDocumentableDetector : SyntheticDocumentableDetector {
+
+ /**
+ * Currently, it's used only for [org.jetbrains.dokka.base.transformers.documentables.ReportUndocumentedTransformer]
+ *
+ * For so-called fake-ovveride declarations - we have [InheritedMember] extra.
+ * For synthesized declaration - we do not have PSI source.
+ *
+ * @see org.jetbrains.kotlin.analysis.api.symbols.KtSymbolOrigin.SOURCE_MEMBER_GENERATED
+ */
+ override fun isSynthetic(documentable: Documentable, sourceSet: DokkaConfiguration.DokkaSourceSet): Boolean {
+ @Suppress("UNCHECKED_CAST")
+ val extra = (documentable as? WithExtraProperties<Documentable>)?.extra
+ val isInherited = extra?.get(InheritedMember)?.inheritedFrom?.get(sourceSet) != null
+ // TODO the same for JAVA?
+ val isSynthesized = documentable.getPsi(sourceSet) == null
+ return isInherited || isSynthesized
+ }
+
+ private fun Documentable.getPsi(sourceSet: DokkaConfiguration.DokkaSourceSet): PsiElement? {
+ val documentableSource = (this as? WithSources)?.sources?.get(sourceSet) ?: return null
+ return when (documentableSource) {
+ is PsiDocumentableSource -> documentableSource.psi
+ is KtPsiDocumentableSource -> documentableSource.psi
+ else -> error("Unknown language sources: ${documentableSource::class}")
+ }
+ }
+
+
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt
new file mode 100644
index 00000000..c9882487
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt
@@ -0,0 +1,147 @@
+/*
+ * 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.translators
+
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.links.withEnumEntryExtra
+import org.jetbrains.dokka.model.*
+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
+import org.jetbrains.kotlin.name.ClassId
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.KtFile
+
+/**
+ * Map [KtAnnotationApplication] to Dokka [Annotations.Annotation]
+ */
+internal class AnnotationTranslator {
+ private fun KtAnalysisSession.getFileLevelAnnotationsFrom(symbol: KtSymbol) =
+ if (symbol.origin != KtSymbolOrigin.SOURCE)
+ null
+ else
+ (symbol.psi?.containingFile as? KtFile)?.getFileSymbol()?.annotations
+ ?.map { toDokkaAnnotation(it) }
+
+ private fun KtAnalysisSession.getDirectAnnotationsFrom(annotated: KtAnnotated) =
+ annotated.annotations.map { toDokkaAnnotation(it) }
+
+ /**
+ * 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 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
+ private fun AnnotationUseSiteTarget.toDokkaAnnotationScope(): Annotations.AnnotationScope = when (this) {
+ AnnotationUseSiteTarget.PROPERTY_GETTER -> Annotations.AnnotationScope.DIRECT // due to compatibility with Dokka K1
+ AnnotationUseSiteTarget.PROPERTY_SETTER -> Annotations.AnnotationScope.DIRECT // due to compatibility with Dokka K1
+ AnnotationUseSiteTarget.FILE -> Annotations.AnnotationScope.FILE
+ else -> Annotations.AnnotationScope.DIRECT
+ }
+
+ private fun KtAnalysisSession.mustBeDocumented(annotationApplication: KtAnnotationApplication): Boolean {
+ if (annotationApplication.isNoExistedInSource()) return false
+ val annotationClass = getClassOrObjectSymbolByClassId(annotationApplication.classId ?: return false)
+ return annotationClass?.hasAnnotation(mustBeDocumentedAnnotation)
+ ?: false
+ }
+
+ private fun KtAnalysisSession.toDokkaAnnotation(annotationApplication: KtAnnotationApplication) =
+ Annotations.Annotation(
+ dri = annotationApplication.classId?.createDRI()
+ ?: DRI(packageName = "", classNames = ERROR_CLASS_NAME), // classId might be null on a non-existing annotation call,
+ params = if (annotationApplication is KtAnnotationApplicationWithArgumentsInfo) annotationApplication.arguments.associate {
+ it.name.asString() to toDokkaAnnotationValue(
+ it.expression
+ )
+ } else emptyMap(),
+ mustBeDocumented = mustBeDocumented(annotationApplication),
+ scope = annotationApplication.useSiteTarget?.toDokkaAnnotationScope() ?: Annotations.AnnotationScope.DIRECT
+ )
+
+ @OptIn(ExperimentalUnsignedTypes::class)
+ private fun KtAnalysisSession.toDokkaAnnotationValue(annotationValue: KtAnnotationValue): AnnotationParameterValue =
+ when (annotationValue) {
+ is KtConstantAnnotationValue -> {
+ when (val value = annotationValue.constantValue) {
+ is KtConstantValue.KtNullConstantValue -> NullValue
+ is KtConstantValue.KtFloatConstantValue -> FloatValue(value.value)
+ is KtConstantValue.KtDoubleConstantValue -> DoubleValue(value.value)
+ is KtConstantValue.KtLongConstantValue -> LongValue(value.value)
+ is KtConstantValue.KtIntConstantValue -> IntValue(value.value)
+ is KtConstantValue.KtBooleanConstantValue -> BooleanValue(value.value)
+ is KtConstantValue.KtByteConstantValue -> IntValue(value.value.toInt())
+ is KtConstantValue.KtCharConstantValue -> StringValue(value.value.toString())
+ is KtConstantValue.KtErrorConstantValue -> StringValue(value.renderAsKotlinConstant())
+ is KtConstantValue.KtShortConstantValue -> IntValue(value.value.toInt())
+ is KtConstantValue.KtStringConstantValue -> StringValue(value.value)
+ is KtConstantValue.KtUnsignedByteConstantValue -> IntValue(value.value.toInt())
+ is KtConstantValue.KtUnsignedIntConstantValue -> IntValue(value.value.toInt())
+ is KtConstantValue.KtUnsignedLongConstantValue -> LongValue(value.value.toLong())
+ is KtConstantValue.KtUnsignedShortConstantValue -> IntValue(value.value.toInt())
+ }
+ }
+
+ is KtEnumEntryAnnotationValue -> EnumValue(
+ with(annotationValue.callableId) { this?.className?.asString() + "." + this?.callableName?.asString() },
+ getDRIFrom(annotationValue)
+ )
+
+ is KtArrayAnnotationValue -> ArrayValue(annotationValue.values.map { toDokkaAnnotationValue(it) })
+ is KtAnnotationApplicationValue -> AnnotationValue(toDokkaAnnotation(annotationValue.annotationValue))
+ is KtKClassAnnotationValue.KtNonLocalKClassAnnotationValue -> ClassValue(
+ annotationValue.classId.relativeClassName.asString(),
+ annotationValue.classId.createDRI()
+ )
+
+ is KtKClassAnnotationValue.KtLocalKClassAnnotationValue -> throw IllegalStateException("Unexpected a local class in annotation")
+ is KtKClassAnnotationValue.KtErrorClassAnnotationValue -> ClassValue(
+ annotationValue.unresolvedQualifierName ?: "",
+ DRI(packageName = "", classNames = ERROR_CLASS_NAME)
+ )
+
+ KtUnsupportedAnnotationValue -> ClassValue(
+ "<Unsupported Annotation Value>",
+ DRI(packageName = "", classNames = ERROR_CLASS_NAME)
+ )
+ }
+
+ private fun getDRIFrom(enumEntry: KtEnumEntryAnnotationValue): DRI {
+ val callableId =
+ enumEntry.callableId ?: throw IllegalStateException("Can't get `callableId` for enum entry from annotation")
+ return DRI(
+ packageName = callableId.packageName.asString(),
+ classNames = callableId.className?.asString() + "." + callableId.callableName.asString(),
+ ).withEnumEntryExtra()
+ }
+
+ companion object {
+ val mustBeDocumentedAnnotation = ClassId(FqName("kotlin.annotation"), FqName("MustBeDocumented"), false)
+ private val parameterNameAnnotation = ClassId(FqName("kotlin"), FqName("ParameterName"), false)
+
+ /**
+ * Functional types can have **generated** [ParameterName] annotation
+ * @see ParameterName
+ */
+ internal fun KtAnnotated.getPresentableName(): String? =
+ this.annotationsByClassId(parameterNameAnnotation)
+ .firstOrNull()?.arguments?.firstOrNull { it.name == Name.identifier("name") }?.expression?.let { it as? KtConstantAnnotationValue }
+ ?.let { it.constantValue.value.toString() }
+ }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DRIFactory.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DRIFactory.kt
new file mode 100644
index 00000000..a2cb423a
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DRIFactory.kt
@@ -0,0 +1,144 @@
+/*
+ * 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.translators
+
+import org.jetbrains.dokka.links.*
+import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
+import org.jetbrains.kotlin.analysis.api.symbols.*
+import org.jetbrains.kotlin.analysis.api.symbols.markers.KtNamedSymbol
+import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolKind
+import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithKind
+import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithTypeParameters
+import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType
+import org.jetbrains.kotlin.name.CallableId
+import org.jetbrains.kotlin.name.ClassId
+import org.jetbrains.kotlin.name.FqName
+
+internal fun ClassId.createDRI(): DRI = DRI(
+ packageName = this.packageFqName.asString(), classNames = this.relativeClassName.asString()
+)
+
+private fun CallableId.createDRI(receiver: TypeReference?, params: List<TypeReference>): DRI = DRI(
+ packageName = this.packageName.asString(),
+ classNames = this.className?.asString(),
+ callable = Callable(
+ this.callableName.asString(),
+ params = params,
+ receiver = receiver
+ )
+)
+
+internal fun getDRIFromNonErrorClassType(nonErrorClassType: KtNonErrorClassType): DRI =
+ nonErrorClassType.classId.createDRI()
+
+private val KtCallableSymbol.callableId
+ get() = this.callableIdIfNonLocal ?: throw IllegalStateException("Can not get callable Id due to it is local")
+
+// because of compatibility with Dokka K1, DRI of entry is kept as non-callable
+internal fun getDRIFromEnumEntry(symbol: KtEnumEntrySymbol): DRI =
+ symbol.callableId.let {
+ DRI(
+ packageName = it.packageName.asString(),
+ classNames = it.className?.asString() + "." + it.callableName.asString(),
+ )
+ }.withEnumEntryExtra()
+
+
+internal fun KtAnalysisSession.getDRIFromTypeParameter(symbol: KtTypeParameterSymbol): DRI {
+ val containingSymbol =
+ (symbol.getContainingSymbol() as? KtSymbolWithTypeParameters)
+ ?: throw IllegalStateException("Containing symbol is null for type parameter")
+ val typeParameters = containingSymbol.typeParameters
+ val index = typeParameters.indexOfFirst { symbol.name == it.name }
+ return getDRIFromSymbol(containingSymbol).copy(target = PointingToGenericParameters(index))
+}
+
+internal fun KtAnalysisSession.getDRIFromConstructor(symbol: KtConstructorSymbol): DRI =
+ (symbol.containingClassIdIfNonLocal
+ ?: throw IllegalStateException("Can not get class Id due to it is local")).createDRI().copy(
+ callable = Callable(
+ name = symbol.containingClassIdIfNonLocal?.relativeClassName?.asString() ?: "",
+ params = symbol.valueParameters.map { getTypeReferenceFrom(it.returnType) })
+ )
+
+internal fun KtAnalysisSession.getDRIFromVariableLike(symbol: KtVariableLikeSymbol): DRI {
+ val receiver = symbol.receiverType?.let {
+ getTypeReferenceFrom(it)
+ }
+ return symbol.callableId.createDRI(receiver, emptyList())
+}
+
+internal fun KtAnalysisSession.getDRIFromFunctionLike(symbol: KtFunctionLikeSymbol): DRI {
+ val params = symbol.valueParameters.map { getTypeReferenceFrom(it.returnType) }
+ val receiver = symbol.receiverType?.let {
+ getTypeReferenceFrom(it)
+ }
+ return symbol.callableIdIfNonLocal?.createDRI(receiver, params)
+ ?: getDRIFromLocalFunction(symbol)
+}
+
+internal fun getDRIFromClassLike(symbol: KtClassLikeSymbol): DRI =
+ symbol.classIdIfNonLocal?.createDRI() ?: throw IllegalStateException("Can not get class Id due to it is local")
+
+internal fun getDRIFromPackage(symbol: KtPackageSymbol): DRI =
+ DRI(packageName = symbol.fqName.asString())
+
+internal fun KtAnalysisSession.getDRIFromValueParameter(symbol: KtValueParameterSymbol): DRI {
+ val function = (symbol.getContainingSymbol() as? KtFunctionLikeSymbol)
+ ?: throw IllegalStateException("Containing symbol is null for type parameter")
+ val index = function.valueParameters.indexOfFirst { it.name == symbol.name }
+ val funDRI = getDRIFromFunctionLike(function)
+ return funDRI.copy(target = PointingToCallableParameters(index))
+}
+
+internal fun KtAnalysisSession.getDRIFromSymbol(symbol: KtSymbol): DRI =
+ when (symbol) {
+ is KtEnumEntrySymbol -> getDRIFromEnumEntry(symbol)
+ is KtTypeParameterSymbol -> getDRIFromTypeParameter(symbol)
+ is KtConstructorSymbol -> getDRIFromConstructor(symbol)
+ is KtValueParameterSymbol -> getDRIFromValueParameter(symbol)
+ is KtVariableLikeSymbol -> getDRIFromVariableLike(symbol)
+ is KtFunctionLikeSymbol -> getDRIFromFunctionLike(symbol)
+ is KtClassLikeSymbol -> getDRIFromClassLike(symbol)
+ is KtPackageSymbol -> getDRIFromPackage(symbol)
+ else -> throw IllegalStateException("Unknown symbol while creating DRI $symbol")
+ }
+
+private fun KtAnalysisSession.getDRIFromNonCallablePossibleLocalSymbol(symbol: KtSymbol): DRI {
+ if ((symbol as? KtSymbolWithKind)?.symbolKind == KtSymbolKind.LOCAL) {
+ return symbol.getContainingSymbol()?.let { getDRIFromNonCallablePossibleLocalSymbol(it) }
+ ?: throw IllegalStateException("Can't get containing symbol for local symbol")
+ }
+ return getDRIFromSymbol(symbol)
+}
+
+/**
+ * Currently, it's used only for functions from enum entry,
+ * For its members: `memberSymbol.callableIdIfNonLocal=null`
+ */
+private fun KtAnalysisSession.getDRIFromLocalFunction(symbol: KtFunctionLikeSymbol): DRI {
+ /**
+ * A function is inside local object
+ */
+ val containingSymbolDRI = symbol.getContainingSymbol()?.let { getDRIFromNonCallablePossibleLocalSymbol(it) }
+ ?: throw IllegalStateException("Can't get containing symbol for local function")
+ return containingSymbolDRI.copy(
+ callable = Callable(
+ (symbol as? KtNamedSymbol)?.name?.asString() ?: "",
+ params = symbol.valueParameters.map { getTypeReferenceFrom(it.returnType) },
+ receiver = symbol.receiverType?.let {
+ getTypeReferenceFrom(it)
+ }
+ )
+ )
+}
+
+// ----------- DRI => compiler identifiers ----------------------------------------------------------------------------
+internal fun getClassIdFromDRI(dri: DRI) = ClassId(
+ FqName(dri.packageName ?: ""),
+ FqName(dri.classNames ?: throw IllegalStateException("DRI must have `classNames` to get ClassID")),
+ false
+)
+
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt
new file mode 100644
index 00000000..298d0182
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt
@@ -0,0 +1,957 @@
+/*
+ * 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.translators
+
+
+import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.*
+import com.intellij.psi.util.PsiLiteralUtil
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.analysis.java.JavaAnalysisPlugin
+import org.jetbrains.dokka.analysis.java.parsers.JavadocParser
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.getGeneratedKDocDocumentationFrom
+import org.jetbrains.dokka.analysis.kotlin.symbols.services.KtPsiDocumentableSource
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.getJavaDocDocumentationFrom
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.getKDocDocumentationFrom
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.hasGeneratedKDocDocumentation
+import org.jetbrains.dokka.analysis.kotlin.symbols.translators.AnnotationTranslator.Companion.getPresentableName
+import org.jetbrains.dokka.analysis.kotlin.symbols.utils.typeConstructorsBeingExceptions
+import org.jetbrains.dokka.links.*
+import org.jetbrains.dokka.links.Callable
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.Visibility
+import org.jetbrains.dokka.model.doc.*
+import org.jetbrains.dokka.model.properties.PropertyContainer
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.query
+import org.jetbrains.dokka.plugability.querySingle
+import org.jetbrains.dokka.transformers.sources.AsyncSourceToDocumentableTranslator
+import org.jetbrains.dokka.utilities.DokkaLogger
+import org.jetbrains.kotlin.KtNodeTypes
+import org.jetbrains.kotlin.analysis.api.*
+import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotated
+import org.jetbrains.kotlin.analysis.api.symbols.*
+import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithModality
+import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithVisibility
+import org.jetbrains.kotlin.analysis.api.types.*
+import org.jetbrains.kotlin.descriptors.Modality
+import org.jetbrains.kotlin.descriptors.Visibilities
+import org.jetbrains.kotlin.descriptors.java.JavaVisibilities
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.name.ClassId
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
+import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
+import java.nio.file.Paths
+
+internal class DefaultSymbolToDocumentableTranslator(context: DokkaContext) : AsyncSourceToDocumentableTranslator {
+ private val kotlinAnalysis = context.plugin<SymbolsAnalysisPlugin>().querySingle { kotlinAnalysis }
+ private val javadocParser = JavadocParser(
+ docCommentParsers = context.plugin<JavaAnalysisPlugin>().query { docCommentParsers },
+ docCommentFinder = context.plugin<JavaAnalysisPlugin>().docCommentFinder
+ )
+
+ override suspend fun invokeSuspending(
+ sourceSet: DokkaConfiguration.DokkaSourceSet,
+ context: DokkaContext
+ ): DModule {
+ val analysisContext = kotlinAnalysis
+ @Suppress("unused")
+ return DokkaSymbolVisitor(
+ sourceSet = sourceSet,
+ moduleName = context.configuration.moduleName,
+ analysisContext = analysisContext,
+ logger = context.logger,
+ javadocParser = javadocParser
+ ).visitModule()
+ }
+}
+
+internal fun <T : Bound> T.wrapWithVariance(variance: org.jetbrains.kotlin.types.Variance) =
+ when (variance) {
+ org.jetbrains.kotlin.types.Variance.INVARIANT -> Invariance(this)
+ org.jetbrains.kotlin.types.Variance.IN_VARIANCE -> Contravariance(this)
+ org.jetbrains.kotlin.types.Variance.OUT_VARIANCE -> Covariance(this)
+ }
+
+/**
+ * Maps [KtSymbol] to Documentable model [Documentable]
+ */
+internal class DokkaSymbolVisitor(
+ private val sourceSet: DokkaConfiguration.DokkaSourceSet,
+ private val moduleName: String,
+ private val analysisContext: KotlinAnalysis,
+ private val logger: DokkaLogger,
+ private val javadocParser: JavadocParser? = null
+) {
+ private var annotationTranslator = AnnotationTranslator()
+ private var typeTranslator = TypeTranslator(sourceSet, annotationTranslator)
+
+ /**
+ * To avoid recursive classes
+ * e.g.
+ * open class Klass() {
+ * object Default : Klass()
+ * }
+ */
+ private val visitedNamedClassOrObjectSymbol: MutableSet<ClassId> =
+ mutableSetOf()
+
+ private fun <T> T.toSourceSetDependent() = if (this != null) mapOf(sourceSet to this) else emptyMap()
+
+ // KT-54846 will replace
+ private val KtDeclarationSymbol.isActual
+ get() = (psi as? KtModifierListOwner)?.hasActualModifier() == true
+ private val KtDeclarationSymbol.isExpect
+ get() = (psi as? KtModifierListOwner)?.hasExpectModifier() == true
+
+ private fun <T : KtSymbol> Collection<T>.filterSymbolsInSourceSet() = filter {
+ it.psi?.containingFile?.virtualFile?.path?.let { path ->
+ path.isNotBlank() && sourceSet.sourceRoots.any { root ->
+ Paths.get(path).startsWith(root.toPath())
+ }
+ } == true
+ }
+
+ fun visitModule(): DModule {
+ val sourceModule = analysisContext.getModule(sourceSet)
+ val ktFiles = analysisContext.modulesWithFiles[sourceModule]?.filterIsInstance<KtFile>() ?: throw IllegalStateException("No source files for a source module ${sourceModule.moduleName} of source set ${sourceSet.sourceSetID}")
+ val processedPackages: MutableSet<FqName> = mutableSetOf()
+ return analyze(sourceModule) {
+ val packageSymbols: List<DPackage> = ktFiles
+ .mapNotNull {
+ if (processedPackages.contains(it.packageFqName))
+ return@mapNotNull null
+ processedPackages.add(it.packageFqName)
+ getPackageSymbolIfPackageExists(it.packageFqName)?.let { it1 ->
+ visitPackageSymbol(
+ it1
+ )
+ }
+ }
+
+ DModule(
+ name = moduleName,
+ packages = packageSymbols,
+ documentation = emptyMap(),
+ expectPresentInSet = null,
+ sourceSets = setOf(sourceSet)
+ )
+ }
+ }
+
+ private fun KtAnalysisSession.visitPackageSymbol(packageSymbol: KtPackageSymbol): DPackage {
+ val dri = getDRIFromPackage(packageSymbol)
+ val scope = packageSymbol.getPackageScope()
+ val callables = scope.getCallableSymbols().toList().filterSymbolsInSourceSet()
+ val classifiers = scope.getClassifierSymbols().toList().filterSymbolsInSourceSet()
+
+ val functions = callables.filterIsInstance<KtFunctionSymbol>().map { visitFunctionSymbol(it, dri) }
+ val properties = callables.filterIsInstance<KtPropertySymbol>().map { visitPropertySymbol(it, dri) }
+ val classlikes =
+ classifiers.filterIsInstance<KtNamedClassOrObjectSymbol>()
+ .map { visitNamedClassOrObjectSymbol(it, dri) }
+ val typealiases = classifiers.filterIsInstance<KtTypeAliasSymbol>().map { visitTypeAliasSymbol(it, dri) }
+
+ return DPackage(
+ dri = dri,
+ functions = functions,
+ properties = properties,
+ classlikes = classlikes,
+ typealiases = typealiases,
+ documentation = emptyMap(),
+ sourceSets = setOf(sourceSet)
+ )
+ }
+
+ private fun KtAnalysisSession.visitTypeAliasSymbol(
+ typeAliasSymbol: KtTypeAliasSymbol,
+ parent: DRI
+ ): DTypeAlias = withExceptionCatcher(typeAliasSymbol) {
+ val name = typeAliasSymbol.name.asString()
+ val dri = parent.withClass(name)
+
+ val ancestryInfo = with(typeTranslator) { buildAncestryInformationFrom(typeAliasSymbol.expandedType) }
+
+ val generics =
+ typeAliasSymbol.typeParameters.mapIndexed { index, symbol -> visitVariantTypeParameter(index, symbol, dri) }
+
+ return DTypeAlias(
+ dri = dri,
+ name = name,
+ type = GenericTypeConstructor(
+ dri = dri,
+ projections = generics.map { it.variantTypeParameter }), // this property can be removed in DTypeAlias
+ expectPresentInSet = null,
+ underlyingType = toBoundFrom(typeAliasSymbol.expandedType).toSourceSetDependent(),
+ visibility = typeAliasSymbol.getDokkaVisibility().toSourceSetDependent(),
+ documentation = getDocumentation(typeAliasSymbol)?.toSourceSetDependent() ?: emptyMap(),
+ sourceSets = setOf(sourceSet),
+ generics = generics,
+ sources = typeAliasSymbol.getSource(),
+ extra = PropertyContainer.withAll(
+ getDokkaAnnotationsFrom(typeAliasSymbol)?.toSourceSetDependent()?.toAnnotations(),
+ ancestryInfo.exceptionInSupertypesOrNull(),
+ )
+ )
+ }
+
+ fun KtAnalysisSession.visitNamedClassOrObjectSymbol(
+ namedClassOrObjectSymbol: KtNamedClassOrObjectSymbol,
+ parent: DRI
+ ): DClasslike = withExceptionCatcher(namedClassOrObjectSymbol) {
+ namedClassOrObjectSymbol.classIdIfNonLocal?.let { visitedNamedClassOrObjectSymbol.add(it) }
+
+ val name = namedClassOrObjectSymbol.name.asString()
+ val dri = parent.withClass(name)
+
+ val isExpect = namedClassOrObjectSymbol.isExpect
+ val isActual = namedClassOrObjectSymbol.isActual
+ val documentation = getDocumentation(namedClassOrObjectSymbol)?.toSourceSetDependent() ?: emptyMap()
+
+ 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(
+ index,
+ symbol,
+ dri
+ )
+ }
+
+ val ancestryInfo =
+ with(typeTranslator) { buildAncestryInformationFrom(namedClassOrObjectSymbol.buildSelfClassType()) }
+ val supertypes =
+ //(ancestryInfo.interfaces.map{ it.typeConstructor } + listOfNotNull(ancestryInfo.superclass?.typeConstructor))
+ namedClassOrObjectSymbol.superTypes.filterNot { it.isAny }
+ .map { with(typeTranslator) { toTypeConstructorWithKindFrom(it) } }
+ .toSourceSetDependent()
+ return@withExceptionCatcher when (namedClassOrObjectSymbol.classKind) {
+ KtClassKind.OBJECT, KtClassKind.COMPANION_OBJECT ->
+ DObject(
+ dri = dri,
+ name = name,
+ functions = functions,
+ properties = properties,
+ classlikes = classlikes,
+ sources = namedClassOrObjectSymbol.getSource(),
+ expectPresentInSet = sourceSet.takeIf { isExpect },
+ visibility = namedClassOrObjectSymbol.getDokkaVisibility().toSourceSetDependent(),
+ supertypes = supertypes,
+ documentation = documentation,
+ sourceSets = setOf(sourceSet),
+ isExpectActual = (isExpect || isActual),
+ extra = PropertyContainer.withAll(
+ namedClassOrObjectSymbol.additionalExtras()?.toSourceSetDependent()
+ ?.toAdditionalModifiers(),
+ getDokkaAnnotationsFrom(namedClassOrObjectSymbol)?.toSourceSetDependent()?.toAnnotations(),
+ ImplementedInterfaces(ancestryInfo.allImplementedInterfaces().toSourceSetDependent()),
+ ancestryInfo.exceptionInSupertypesOrNull()
+ )
+ )
+
+ KtClassKind.CLASS -> DClass(
+ dri = dri,
+ name = name,
+ constructors = constructors,
+ functions = functions,
+ properties = properties,
+ classlikes = classlikes,
+ sources = namedClassOrObjectSymbol.getSource(),
+ expectPresentInSet = sourceSet.takeIf { isExpect },
+ visibility = namedClassOrObjectSymbol.getDokkaVisibility().toSourceSetDependent(),
+ supertypes = supertypes,
+ generics = generics,
+ documentation = documentation,
+ modifier = namedClassOrObjectSymbol.getDokkaModality().toSourceSetDependent(),
+ companion = companionObject,
+ sourceSets = setOf(sourceSet),
+ isExpectActual = (isExpect || isActual),
+ extra = PropertyContainer.withAll(
+ namedClassOrObjectSymbol.additionalExtras()?.toSourceSetDependent()?.toAdditionalModifiers(),
+ getDokkaAnnotationsFrom(namedClassOrObjectSymbol)?.toSourceSetDependent()?.toAnnotations(),
+ ImplementedInterfaces(ancestryInfo.allImplementedInterfaces().toSourceSetDependent()),
+ ancestryInfo.exceptionInSupertypesOrNull()
+ )
+ )
+
+ KtClassKind.INTERFACE -> DInterface(
+ dri = dri,
+ name = name,
+ functions = functions,
+ properties = properties,
+ classlikes = classlikes,
+ sources = namedClassOrObjectSymbol.getSource(), //
+ expectPresentInSet = sourceSet.takeIf { isExpect },
+ visibility = namedClassOrObjectSymbol.getDokkaVisibility().toSourceSetDependent(),
+ supertypes = supertypes,
+ generics = generics,
+ documentation = documentation,
+ companion = companionObject,
+ sourceSets = setOf(sourceSet),
+ isExpectActual = (isExpect || isActual),
+ extra = PropertyContainer.withAll(
+ namedClassOrObjectSymbol.additionalExtras()?.toSourceSetDependent()?.toAdditionalModifiers(),
+ getDokkaAnnotationsFrom(namedClassOrObjectSymbol)?.toSourceSetDependent()?.toAnnotations(),
+ ImplementedInterfaces(ancestryInfo.allImplementedInterfaces().toSourceSetDependent()),
+ ancestryInfo.exceptionInSupertypesOrNull()
+ )
+ )
+
+ KtClassKind.ANNOTATION_CLASS -> DAnnotation(
+ dri = dri,
+ name = name,
+ documentation = documentation,
+ functions = functions,
+ properties = properties,
+ classlikes = classlikes,
+ expectPresentInSet = sourceSet.takeIf { isExpect },
+ sourceSets = setOf(sourceSet),
+ isExpectActual = (isExpect || isActual),
+ companion = companionObject,
+ visibility = namedClassOrObjectSymbol.getDokkaVisibility().toSourceSetDependent(),
+ generics = generics,
+ constructors = constructors,
+ sources = namedClassOrObjectSymbol.getSource(),
+ extra = PropertyContainer.withAll(
+ namedClassOrObjectSymbol.additionalExtras()?.toSourceSetDependent()?.toAdditionalModifiers(),
+ getDokkaAnnotationsFrom(namedClassOrObjectSymbol)?.toSourceSetDependent()?.toAnnotations(),
+ )
+ )
+
+ KtClassKind.ANONYMOUS_OBJECT -> throw NotImplementedError("ANONYMOUS_OBJECT does not support")
+ KtClassKind.ENUM_CLASS -> {
+ /**
+ * See https://github.com/Kotlin/dokka/issues/3129
+ *
+ * e.g. the `A` enum entry in the `enum E` is
+ * ```
+ * static val A: E = object : E() {
+ * val x: Int = 5
+ * }
+ * ```
+ * it needs to exclude all static members like `values` and `valueOf` from the enum class's scope
+ */
+ val enumEntryScope = lazy {
+ getDokkaScopeFrom(namedClassOrObjectSymbol, dri, includeStaticScope = false).let {
+ it.copy(
+ functions = it.functions.map { it.withNewExtras( it.extra + InheritedMember(dri.copy(callable = null).toSourceSetDependent())) },
+ properties = it.properties.map { it.withNewExtras( it.extra + InheritedMember(dri.copy(callable = null).toSourceSetDependent())) }
+ )
+ }
+ }
+
+ val entries =
+ namedClassOrObjectSymbol.getEnumEntries().map {
+ visitEnumEntrySymbol(it, enumEntryScope.value)
+ }
+
+ DEnum(
+ dri = dri,
+ name = name,
+ entries = entries,
+ constructors = constructors,
+ functions = functions,
+ properties = properties,
+ classlikes = classlikes,
+ sources = namedClassOrObjectSymbol.getSource(),
+ expectPresentInSet = sourceSet.takeIf { isExpect },
+ visibility = namedClassOrObjectSymbol.getDokkaVisibility().toSourceSetDependent(),
+ supertypes = supertypes,
+ documentation = documentation,
+ companion = namedClassOrObjectSymbol.companionObject?.let {
+ visitNamedClassOrObjectSymbol(
+ it,
+ dri
+ )
+ } as? DObject,
+ sourceSets = setOf(sourceSet),
+ isExpectActual = (isExpect || isActual),
+ extra = PropertyContainer.withAll(
+ namedClassOrObjectSymbol.additionalExtras()?.toSourceSetDependent()
+ ?.toAdditionalModifiers(),
+ getDokkaAnnotationsFrom(namedClassOrObjectSymbol)?.toSourceSetDependent()?.toAnnotations(),
+ ImplementedInterfaces(ancestryInfo.allImplementedInterfaces().toSourceSetDependent())
+ )
+ )
+ }
+ }
+ }
+
+ private data class DokkaScope(
+ val constructors: List<DFunction>,
+ val functions: List<DFunction>,
+ val properties: List<DProperty>,
+ val classlikesWithoutCompanion: List<DClasslike>
+ )
+
+ /**
+ * @param includeStaticScope flag to add static members, e.g. `valueOf`, `values` and `entries` members for Enum
+ */
+ private fun KtAnalysisSession.getDokkaScopeFrom(
+ namedClassOrObjectSymbol: KtNamedClassOrObjectSymbol,
+ dri: DRI,
+ includeStaticScope: Boolean = true
+ ): DokkaScope {
+ // e.g. getStaticMemberScope contains `valueOf`, `values` and `entries` members for Enum
+ val scope = if(includeStaticScope) listOf(namedClassOrObjectSymbol.getMemberScope(), namedClassOrObjectSymbol.getStaticMemberScope()).asCompositeScope() else namedClassOrObjectSymbol.getMemberScope()
+ val constructors = scope.getConstructors().map { visitConstructorSymbol(it) }.toList()
+
+ val callables = scope.getCallableSymbols().toList()
+ val classifiers = scope.getClassifierSymbols().toList()
+
+ val syntheticJavaProperties =
+ namedClassOrObjectSymbol.buildSelfClassType().getSyntheticJavaPropertiesScope()?.getCallableSignatures()
+ ?.map { it.symbol }
+ ?.filterIsInstance<KtSyntheticJavaPropertySymbol>() ?: emptySequence()
+
+ fun List<KtJavaFieldSymbol>.filterOutSyntheticJavaPropBackingField() =
+ filterNot { javaField -> syntheticJavaProperties.any { it.hasBackingField && javaField.name == it.name } }
+
+ val javaFields = callables.filterIsInstance<KtJavaFieldSymbol>()
+ .filterOutSyntheticJavaPropBackingField()
+
+ fun List<KtFunctionSymbol>.filterOutSyntheticJavaPropAccessors() = filterNot { fn ->
+ if (fn.origin == KtSymbolOrigin.JAVA && fn.callableIdIfNonLocal != null)
+ syntheticJavaProperties.any { fn.callableIdIfNonLocal == it.javaGetterSymbol.callableIdIfNonLocal || fn.callableIdIfNonLocal == it.javaSetterSymbol?.callableIdIfNonLocal }
+ else false
+ }
+
+ val functions = callables.filterIsInstance<KtFunctionSymbol>()
+ .filterOutSyntheticJavaPropAccessors().map { visitFunctionSymbol(it, dri) }
+
+
+ val properties = callables.filterIsInstance<KtPropertySymbol>().map { visitPropertySymbol(it, dri) } +
+ syntheticJavaProperties.map { visitPropertySymbol(it, dri) } +
+ javaFields.map { visitJavaFieldSymbol(it, dri) }
+
+
+ fun List<KtNamedClassOrObjectSymbol>.filterOutCompanion() =
+ filterNot {
+ it.classKind == KtClassKind.COMPANION_OBJECT
+ }
+
+ fun List<KtNamedClassOrObjectSymbol>.filterOutAndMarkAlreadyVisited() = filterNot { symbol ->
+ visitedNamedClassOrObjectSymbol.contains(symbol.classIdIfNonLocal)
+ .also {
+ if (!it) symbol.classIdIfNonLocal?.let { classId ->
+ visitedNamedClassOrObjectSymbol.add(
+ classId
+ )
+ }
+ }
+ }
+
+ val classlikes = classifiers.filterIsInstance<KtNamedClassOrObjectSymbol>()
+ .filterOutCompanion() // also, this is a hack to filter out companion for enum
+ .filterOutAndMarkAlreadyVisited()
+ .map { visitNamedClassOrObjectSymbol(it, dri) }
+
+ return DokkaScope(
+ constructors = constructors,
+ functions = functions,
+ properties = properties,
+ classlikesWithoutCompanion = classlikes
+ )
+ }
+
+ private fun KtAnalysisSession.visitEnumEntrySymbol(
+ enumEntrySymbol: KtEnumEntrySymbol, scope: DokkaScope
+ ): DEnumEntry = withExceptionCatcher(enumEntrySymbol) {
+ val dri = getDRIFromEnumEntry(enumEntrySymbol)
+ val isExpect = false
+
+ return DEnumEntry(
+ dri = dri,
+ name = enumEntrySymbol.name.asString(),
+ documentation = getDocumentation(enumEntrySymbol)?.toSourceSetDependent() ?: emptyMap(),
+ functions = scope.functions,
+ properties = scope.properties,
+ classlikes = emptyList(), // always empty, see https://github.com/Kotlin/dokka/issues/3129
+ sourceSets = setOf(sourceSet),
+ expectPresentInSet = sourceSet.takeIf { isExpect },
+ extra = PropertyContainer.withAll(
+ getDokkaAnnotationsFrom(enumEntrySymbol)?.toSourceSetDependent()?.toAnnotations()
+ )
+ )
+ }
+
+ private fun KtAnalysisSession.visitPropertySymbol(propertySymbol: KtPropertySymbol, parent: DRI): DProperty =
+ withExceptionCatcher(propertySymbol) {
+ val dri = createDRIWithOverridden(propertySymbol).origin
+ val inheritedFrom = dri.getInheritedFromDRI(parent)
+ val isExpect = propertySymbol.isExpect
+ val isActual = propertySymbol.isActual
+ val generics =
+ propertySymbol.typeParameters.mapIndexed { index, symbol ->
+ visitVariantTypeParameter(
+ index,
+ symbol,
+ dri
+ )
+ }
+
+ return DProperty(
+ dri = dri,
+ name = propertySymbol.name.asString(),
+ receiver = propertySymbol.receiverParameter?.let {
+ visitReceiverParameter(
+ it,
+ dri
+ )
+ },
+ sources = propertySymbol.getSource(),
+ getter = propertySymbol.getter?.let { visitPropertyAccessor(it, propertySymbol, dri) },
+ setter = propertySymbol.setter?.let { visitPropertyAccessor(it, propertySymbol, dri) },
+ visibility = propertySymbol.visibility.toDokkaVisibility().toSourceSetDependent(),
+ documentation = getDocumentation(propertySymbol)?.toSourceSetDependent() ?: emptyMap(), // TODO
+ modifier = propertySymbol.modality.toDokkaModifier().toSourceSetDependent(),
+ type = toBoundFrom(propertySymbol.returnType),
+ expectPresentInSet = sourceSet.takeIf { isExpect },
+ sourceSets = setOf(sourceSet),
+ generics = generics,
+ isExpectActual = (isExpect || isActual),
+ extra = PropertyContainer.withAll(
+ propertySymbol.additionalExtras()?.toSourceSetDependent()?.toAdditionalModifiers(),
+ getDokkaAnnotationsFrom(propertySymbol)?.toSourceSetDependent()?.toAnnotations(),
+ propertySymbol.getDefaultValue()?.let { DefaultValue(it.toSourceSetDependent()) },
+ inheritedFrom?.let { InheritedMember(it.toSourceSetDependent()) },
+ takeUnless { propertySymbol.isVal }?.let { IsVar },
+ takeIf { propertySymbol.psi is KtParameter }?.let { IsAlsoParameter(listOf(sourceSet)) }
+ )
+ )
+ }
+
+ private fun KtAnalysisSession.visitJavaFieldSymbol(
+ javaFieldSymbol: KtJavaFieldSymbol,
+ parent: DRI
+ ): DProperty =
+ withExceptionCatcher(javaFieldSymbol) {
+ val dri = createDRIWithOverridden(javaFieldSymbol).origin
+ val inheritedFrom = dri.getInheritedFromDRI(parent)
+ val isExpect = false
+ val isActual = false
+ val generics =
+ javaFieldSymbol.typeParameters.mapIndexed { index, symbol ->
+ visitVariantTypeParameter(
+ index,
+ symbol,
+ dri
+ )
+ }
+
+ return DProperty(
+ dri = dri,
+ name = javaFieldSymbol.name.asString(),
+ receiver = javaFieldSymbol.receiverParameter?.let {
+ visitReceiverParameter(
+ it,
+ dri
+ )
+ },
+ sources = javaFieldSymbol.getSource(),
+ getter = null,
+ setter = null,
+ visibility = javaFieldSymbol.getDokkaVisibility().toSourceSetDependent(),
+ documentation = getDocumentation(javaFieldSymbol)?.toSourceSetDependent() ?: emptyMap(), // TODO
+ modifier = javaFieldSymbol.modality.toDokkaModifier().toSourceSetDependent(),
+ type = toBoundFrom(javaFieldSymbol.returnType),
+ expectPresentInSet = sourceSet.takeIf { isExpect },
+ sourceSets = setOf(sourceSet),
+ generics = generics,
+ isExpectActual = (isExpect || isActual),
+ extra = PropertyContainer.withAll(
+ javaFieldSymbol.additionalExtras()?.toSourceSetDependent()?.toAdditionalModifiers(),
+ getDokkaAnnotationsFrom(javaFieldSymbol)?.toSourceSetDependent()?.toAnnotations(),
+ //javaFieldSymbol.getDefaultValue()?.let { DefaultValue(it.toSourceSetDependent()) },
+ inheritedFrom?.let { InheritedMember(it.toSourceSetDependent()) },
+ // non-final java property should be var
+ takeUnless { javaFieldSymbol.isVal }?.let { IsVar }
+ )
+ )
+ }
+
+ private fun KtAnalysisSession.visitPropertyAccessor(
+ propertyAccessorSymbol: KtPropertyAccessorSymbol,
+ propertySymbol: KtPropertySymbol,
+ propertyDRI: DRI
+ ): DFunction = withExceptionCatcher(propertyAccessorSymbol) {
+ val isGetter = propertyAccessorSymbol is KtPropertyGetterSymbol
+ // it also covers @JvmName annotation
+ val name = (if (isGetter) propertySymbol.javaGetterName else propertySymbol.javaSetterName)?.asString() ?: ""
+
+ // SyntheticJavaProperty has callableIdIfNonLocal, propertyAccessorSymbol.origin = JAVA_SYNTHETIC_PROPERTY
+ // For Kotlin properties callableIdIfNonLocal=null
+ val dri = if (propertyAccessorSymbol.callableIdIfNonLocal != null)
+ getDRIFromFunctionLike(propertyAccessorSymbol)
+ else
+ propertyDRI.copy(
+ callable = Callable(name, null, propertyAccessorSymbol.valueParameters.map { getTypeReferenceFrom(it.returnType) })
+ )
+ // for SyntheticJavaProperty
+ val inheritedFrom = if(propertyAccessorSymbol.origin == KtSymbolOrigin.JAVA_SYNTHETIC_PROPERTY) dri.copy(callable = null) else null
+
+ val isExpect = propertyAccessorSymbol.isExpect
+ val isActual = propertyAccessorSymbol.isActual
+
+ val generics = propertyAccessorSymbol.typeParameters.mapIndexed { index, symbol ->
+ visitVariantTypeParameter(
+ index,
+ symbol,
+ dri
+ )
+ }
+
+ return DFunction(
+ dri = dri,
+ name = name,
+ isConstructor = false,
+ receiver = propertyAccessorSymbol.receiverParameter?.let {
+ visitReceiverParameter(
+ it,
+ dri
+ )
+ },
+ parameters = propertyAccessorSymbol.valueParameters.mapIndexed { index, symbol ->
+ visitValueParameter(index, symbol, dri)
+ },
+ expectPresentInSet = sourceSet.takeIf { isExpect },
+ sources = propertyAccessorSymbol.getSource(),
+ visibility = propertyAccessorSymbol.visibility.toDokkaVisibility().toSourceSetDependent(),
+ generics = generics,
+ documentation = getDocumentation(propertyAccessorSymbol)?.toSourceSetDependent() ?: emptyMap(),
+ modifier = propertyAccessorSymbol.modality.toDokkaModifier().toSourceSetDependent(),
+ type = toBoundFrom(propertyAccessorSymbol.returnType),
+ sourceSets = setOf(sourceSet),
+ isExpectActual = (isExpect || isActual),
+ extra = PropertyContainer.withAll(
+ propertyAccessorSymbol.additionalExtras()?.toSourceSetDependent()?.toAdditionalModifiers(),
+ inheritedFrom?.let { InheritedMember(it.toSourceSetDependent()) },
+ getDokkaAnnotationsFrom(propertyAccessorSymbol)?.toSourceSetDependent()?.toAnnotations()
+ )
+ )
+ }
+
+ private fun KtAnalysisSession.visitConstructorSymbol(
+ constructorSymbol: KtConstructorSymbol
+ ): DFunction = withExceptionCatcher(constructorSymbol) {
+ val name = constructorSymbol.containingClassIdIfNonLocal?.shortClassName?.asString()
+ ?: throw IllegalStateException("Unknown containing class of constructor")
+ val dri = createDRIWithOverridden(constructorSymbol).origin
+ val isExpect = false // TODO
+ val isActual = false // TODO
+
+ val generics = constructorSymbol.typeParameters.mapIndexed { index, symbol ->
+ visitVariantTypeParameter(
+ index,
+ symbol,
+ dri
+ )
+ }
+
+ val documentation = getDocumentation(constructorSymbol)?.let { docNode ->
+ if (constructorSymbol.isPrimary) {
+ docNode.copy(children = (docNode.children.find { it is Constructor }?.root?.let { constructor ->
+ listOf(Description(constructor))
+ } ?: emptyList<TagWrapper>()) + docNode.children.filterIsInstance<Param>())
+ } else {
+ docNode
+ }
+ }?.toSourceSetDependent()
+
+ return DFunction(
+ dri = dri,
+ name = name,
+ isConstructor = true,
+ receiver = constructorSymbol.receiverParameter?.let {
+ visitReceiverParameter(
+ it,
+ dri
+ )
+ },
+ parameters = constructorSymbol.valueParameters.mapIndexed { index, symbol ->
+ visitValueParameter(index, symbol, dri)
+ },
+ expectPresentInSet = sourceSet.takeIf { isExpect },
+ sources = constructorSymbol.getSource(),
+ visibility = constructorSymbol.visibility.toDokkaVisibility().toSourceSetDependent(),
+ generics = generics,
+ documentation = documentation ?: emptyMap(),
+ modifier = KotlinModifier.Empty.toSourceSetDependent(),
+ type = toBoundFrom(constructorSymbol.returnType),
+ sourceSets = setOf(sourceSet),
+ isExpectActual = (isExpect || isActual),
+ extra = PropertyContainer.withAll(
+ getDokkaAnnotationsFrom(constructorSymbol)?.toSourceSetDependent()?.toAnnotations(),
+ takeIf { constructorSymbol.isPrimary }?.let { PrimaryConstructorExtra }
+ )
+ )
+ }
+
+ private fun KtAnalysisSession.visitFunctionSymbol(functionSymbol: KtFunctionSymbol, parent: DRI): DFunction =
+ withExceptionCatcher(functionSymbol) {
+ val dri = createDRIWithOverridden(functionSymbol).origin
+ val inheritedFrom = dri.getInheritedFromDRI(parent)
+ val isExpect = functionSymbol.isExpect
+ val isActual = functionSymbol.isActual
+
+ val generics =
+ functionSymbol.typeParameters.mapIndexed { index, symbol ->
+ visitVariantTypeParameter(
+ index,
+ symbol,
+ dri
+ )
+ }
+
+ return DFunction(
+ dri = dri,
+ name = functionSymbol.name.asString(),
+ isConstructor = false,
+ receiver = functionSymbol.receiverParameter?.let {
+ visitReceiverParameter(
+ it,
+ dri
+ )
+ },
+ parameters = functionSymbol.valueParameters.mapIndexed { index, symbol ->
+ visitValueParameter(index, symbol, dri)
+ },
+ expectPresentInSet = sourceSet.takeIf { isExpect },
+ sources = functionSymbol.getSource(),
+ visibility = functionSymbol.getDokkaVisibility().toSourceSetDependent(),
+ generics = generics,
+ documentation = getDocumentation(functionSymbol)?.toSourceSetDependent() ?: emptyMap(),
+ modifier = functionSymbol.getDokkaModality().toSourceSetDependent(),
+ type = toBoundFrom(functionSymbol.returnType),
+ sourceSets = setOf(sourceSet),
+ isExpectActual = (isExpect || isActual),
+ extra = PropertyContainer.withAll(
+ inheritedFrom?.let { InheritedMember(it.toSourceSetDependent()) },
+ functionSymbol.additionalExtras()?.toSourceSetDependent()?.toAdditionalModifiers(),
+ getDokkaAnnotationsFrom(functionSymbol)
+ ?.toSourceSetDependent()?.toAnnotations(),
+ ObviousMember.takeIf { isObvious(functionSymbol) },
+ )
+ )
+ }
+
+ private fun KtAnalysisSession.visitValueParameter(
+ index: Int, valueParameterSymbol: KtValueParameterSymbol, dri: DRI
+ ) = DParameter(
+ dri = dri.copy(target = PointingToCallableParameters(index)),
+ name = valueParameterSymbol.name.asString(),
+ type = toBoundFrom(valueParameterSymbol.returnType),
+ expectPresentInSet = null,
+ documentation = getDocumentation(valueParameterSymbol)?.toSourceSetDependent() ?: emptyMap(),
+ sourceSets = setOf(sourceSet),
+ extra = PropertyContainer.withAll(
+ valueParameterSymbol.additionalExtras()?.toSourceSetDependent()?.toAdditionalModifiers(),
+ getDokkaAnnotationsFrom(valueParameterSymbol)?.toSourceSetDependent()?.toAnnotations(),
+ valueParameterSymbol.getDefaultValue()?.let { DefaultValue(it.toSourceSetDependent()) }
+ )
+ )
+
+ private fun KtAnalysisSession.visitReceiverParameter(
+ receiverParameterSymbol: KtReceiverParameterSymbol, dri: DRI
+ ) = DParameter(
+ dri = dri.copy(target = PointingToDeclaration),
+ name = null,
+ type = toBoundFrom(receiverParameterSymbol.type),
+ expectPresentInSet = null,
+ documentation = getDocumentation(receiverParameterSymbol)?.toSourceSetDependent() ?: emptyMap(),
+ sourceSets = setOf(sourceSet),
+ extra = PropertyContainer.withAll(
+ getDokkaAnnotationsFrom(receiverParameterSymbol)?.toSourceSetDependent()?.toAnnotations()
+ )
+ )
+
+ private fun KtValueParameterSymbol.getDefaultValue(): Expression? =
+ if (origin == KtSymbolOrigin.SOURCE) (psi as? KtParameter)?.defaultValue?.toDefaultValueExpression()
+ else null
+
+ private fun KtPropertySymbol.getDefaultValue(): Expression? =
+ (initializer?.initializerPsi as? KtConstantExpression)?.toDefaultValueExpression() // TODO consider [KtConstantInitializerValue], but should we keep an original format, e.g. 0xff or 0b101?
+
+ private fun KtExpression.toDefaultValueExpression(): Expression? = when (node?.elementType) {
+ KtNodeTypes.INTEGER_CONSTANT -> PsiLiteralUtil.parseLong(node?.text)?.let { IntegerConstant(it) }
+ KtNodeTypes.FLOAT_CONSTANT -> if (node?.text?.toLowerCase()?.endsWith('f') == true)
+ PsiLiteralUtil.parseFloat(node?.text)?.let { FloatConstant(it) }
+ else PsiLiteralUtil.parseDouble(node?.text)?.let { DoubleConstant(it) }
+
+ KtNodeTypes.BOOLEAN_CONSTANT -> BooleanConstant(node?.text == "true")
+ KtNodeTypes.STRING_TEMPLATE -> StringConstant(node.findChildByType(KtNodeTypes.LITERAL_STRING_TEMPLATE_ENTRY)?.text.orEmpty())
+ else -> node?.text?.let { ComplexExpression(it) }
+ }
+
+ private fun KtAnalysisSession.visitVariantTypeParameter(
+ index: Int,
+ typeParameterSymbol: KtTypeParameterSymbol,
+ dri: DRI
+ ): DTypeParameter {
+ val upperBoundsOrNullableAny =
+ typeParameterSymbol.upperBounds.takeIf { it.isNotEmpty() } ?: listOf(this.builtinTypes.NULLABLE_ANY)
+ return DTypeParameter(
+ variantTypeParameter = TypeParameter(
+ dri = dri.copy(target = PointingToGenericParameters(index)),
+ name = typeParameterSymbol.name.asString(),
+ presentableName = typeParameterSymbol.getPresentableName()
+ ).wrapWithVariance(typeParameterSymbol.variance),
+ documentation = getDocumentation(typeParameterSymbol)?.toSourceSetDependent() ?: emptyMap(),
+ expectPresentInSet = null,
+ bounds = upperBoundsOrNullableAny.map { toBoundFrom(it) },
+ sourceSets = setOf(sourceSet),
+ extra = PropertyContainer.withAll(
+ getDokkaAnnotationsFrom(typeParameterSymbol)?.toSourceSetDependent()?.toAnnotations()
+ )
+ )
+ }
+ // ----------- Utils ----------------------------------------------------------------------------
+
+ private fun KtAnalysisSession.getDokkaAnnotationsFrom(annotated: KtAnnotated): List<Annotations.Annotation>? =
+ with(annotationTranslator) { getAllAnnotationsFrom(annotated) }.takeUnless { it.isEmpty() }
+
+ private fun KtAnalysisSession.toBoundFrom(type: KtType) =
+ with(typeTranslator) { toBoundFrom(type) }
+
+ /**
+ * `createDRI` returns the DRI of the exact element and potential DRI of an element that is overriding it
+ * (It can be also FAKE_OVERRIDE which is in fact just inheritance of the symbol)
+ *
+ * Looking at what PSIs do, they give the DRI of the element within the classnames where it is actually
+ * declared and inheritedFrom as the same DRI but truncated callable part.
+ * Therefore, we set callable to null and take the DRI only if it is indeed coming from different class.
+ */
+ private fun DRI.getInheritedFromDRI(dri: DRI): DRI? {
+ return this.copy(callable = null)
+ .takeIf { dri.classNames != this.classNames || dri.packageName != this.packageName }
+ }
+
+ data class DRIWithOverridden(val origin: DRI, val overridden: DRI? = null)
+
+ private fun KtAnalysisSession.createDRIWithOverridden(
+ callableSymbol: KtCallableSymbol,
+ wasOverriddenBy: DRI? = null
+ ): DRIWithOverridden {
+ if (callableSymbol is KtPropertySymbol && callableSymbol.isOverride
+ || callableSymbol is KtFunctionSymbol && callableSymbol.isOverride
+ ) {
+ return DRIWithOverridden(getDRIFromSymbol(callableSymbol), wasOverriddenBy)
+ }
+
+ val overriddenSymbols = callableSymbol.getAllOverriddenSymbols()
+ // For already
+ return if (overriddenSymbols.isEmpty()) {
+ DRIWithOverridden(getDRIFromSymbol(callableSymbol), wasOverriddenBy)
+ } else {
+ createDRIWithOverridden(overriddenSymbols.first())
+ }
+ }
+
+ private fun KtAnalysisSession.getDocumentation(symbol: KtSymbol) =
+ if (symbol.origin == KtSymbolOrigin.SOURCE_MEMBER_GENERATED)
+ getGeneratedKDocDocumentationFrom(symbol)
+ else
+ getKDocDocumentationFrom(symbol, logger) ?: javadocParser?.let { getJavaDocDocumentationFrom(symbol, it) }
+
+ private fun KtAnalysisSession.isObvious(functionSymbol: KtFunctionSymbol): Boolean {
+ return functionSymbol.origin == KtSymbolOrigin.SOURCE_MEMBER_GENERATED && !hasGeneratedKDocDocumentation(functionSymbol) ||
+ !functionSymbol.isOverride && functionSymbol.callableIdIfNonLocal?.classId?.isObvious() == true
+ }
+
+ private fun ClassId.isObvious(): Boolean = with(asString()) {
+ return this == "kotlin/Any" || this == "kotlin/Enum"
+ || this == "java.lang/Object" || this == "java.lang/Enum"
+ }
+
+ private fun KtSymbol.getSource() = KtPsiDocumentableSource(psi).toSourceSetDependent()
+
+ private fun AncestryNode.exceptionInSupertypesOrNull(): ExceptionInSupertypes? =
+ typeConstructorsBeingExceptions().takeIf { it.isNotEmpty() }
+ ?.let { ExceptionInSupertypes(it.toSourceSetDependent()) }
+
+
+ // ----------- Translators of modifiers ----------------------------------------------------------------------------
+ private fun KtSymbolWithModality.getDokkaModality() = modality.toDokkaModifier()
+ private fun KtSymbolWithVisibility.getDokkaVisibility() = visibility.toDokkaVisibility()
+ private fun KtValueParameterSymbol.additionalExtras() = listOfNotNull(
+ ExtraModifiers.KotlinOnlyModifiers.NoInline.takeIf { isNoinline },
+ ExtraModifiers.KotlinOnlyModifiers.CrossInline.takeIf { isCrossinline },
+ ExtraModifiers.KotlinOnlyModifiers.VarArg.takeIf { isVararg }
+ ).toSet().takeUnless { it.isEmpty() }
+
+ private fun KtPropertyAccessorSymbol.additionalExtras() = listOfNotNull(
+ ExtraModifiers.KotlinOnlyModifiers.Inline.takeIf { isInline },
+//ExtraModifiers.JavaOnlyModifiers.Static.takeIf { isJvmStaticInObjectOrClassOrInterface() },
+ ExtraModifiers.KotlinOnlyModifiers.Override.takeIf { isOverride }
+ ).toSet().takeUnless { it.isEmpty() }
+
+ private fun KtPropertySymbol.additionalExtras() = listOfNotNull(
+ ExtraModifiers.KotlinOnlyModifiers.Const.takeIf { (this as? KtKotlinPropertySymbol)?.isConst == true },
+ ExtraModifiers.KotlinOnlyModifiers.LateInit.takeIf { (this as? KtKotlinPropertySymbol)?.isLateInit == true },
+ //ExtraModifiers.JavaOnlyModifiers.Static.takeIf { isJvmStaticInObjectOrClassOrInterface() },
+ //ExtraModifiers.KotlinOnlyModifiers.External.takeIf { isExternal },
+ //ExtraModifiers.KotlinOnlyModifiers.Static.takeIf { isStatic },
+ ExtraModifiers.KotlinOnlyModifiers.Override.takeIf { isOverride }
+ ).toSet().takeUnless { it.isEmpty() }
+
+ private fun KtJavaFieldSymbol.additionalExtras() = listOfNotNull(
+ ExtraModifiers.JavaOnlyModifiers.Static.takeIf { isStatic }
+ ).toSet().takeUnless { it.isEmpty() }
+
+ private fun KtFunctionSymbol.additionalExtras() = listOfNotNull(
+ ExtraModifiers.KotlinOnlyModifiers.Infix.takeIf { isInfix },
+ ExtraModifiers.KotlinOnlyModifiers.Inline.takeIf { isInline },
+ ExtraModifiers.KotlinOnlyModifiers.Suspend.takeIf { isSuspend },
+ ExtraModifiers.KotlinOnlyModifiers.Operator.takeIf { isOperator },
+//ExtraModifiers.JavaOnlyModifiers.Static.takeIf { isJvmStaticInObjectOrClassOrInterface() },
+ ExtraModifiers.KotlinOnlyModifiers.TailRec.takeIf { (psi as? KtNamedFunction)?.hasModifier(KtTokens.TAILREC_KEYWORD) == true },
+ ExtraModifiers.KotlinOnlyModifiers.External.takeIf { isExternal },
+ ExtraModifiers.KotlinOnlyModifiers.Override.takeIf { isOverride }
+ ).toSet().takeUnless { it.isEmpty() }
+
+ private fun KtNamedClassOrObjectSymbol.additionalExtras() = listOfNotNull(
+ ExtraModifiers.KotlinOnlyModifiers.Inline.takeIf { (this.psi as? KtClass)?.isInline() == true },
+ ExtraModifiers.KotlinOnlyModifiers.Value.takeIf { (this.psi as? KtClass)?.isValue() == true },
+ ExtraModifiers.KotlinOnlyModifiers.External.takeIf { isExternal },
+ ExtraModifiers.KotlinOnlyModifiers.Inner.takeIf { isInner },
+ ExtraModifiers.KotlinOnlyModifiers.Data.takeIf { isData },
+ ExtraModifiers.KotlinOnlyModifiers.Fun.takeIf { isFun },
+ ).toSet().takeUnless { it.isEmpty() }
+
+ private fun Modality.toDokkaModifier() = when (this) {
+ Modality.FINAL -> KotlinModifier.Final
+ Modality.SEALED -> KotlinModifier.Sealed
+ Modality.OPEN -> KotlinModifier.Open
+ Modality.ABSTRACT -> KotlinModifier.Abstract
+ }
+
+
+ private fun org.jetbrains.kotlin.descriptors.Visibility.toDokkaVisibility(): Visibility = when (this) {
+ Visibilities.Public -> KotlinVisibility.Public
+ Visibilities.Protected -> KotlinVisibility.Protected
+ Visibilities.Internal -> KotlinVisibility.Internal
+ Visibilities.Private, Visibilities.PrivateToThis -> KotlinVisibility.Private
+ JavaVisibilities.ProtectedAndPackage -> KotlinVisibility.Protected
+ JavaVisibilities.ProtectedStaticVisibility -> KotlinVisibility.Protected
+ JavaVisibilities.PackageVisibility -> JavaVisibility.Default
+ else -> KotlinVisibility.Public
+ }
+}
+
+
+
+
+
+
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TranslatorError.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TranslatorError.kt
new file mode 100644
index 00000000..eceb7a38
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TranslatorError.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.translators
+
+import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
+import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
+import org.jetbrains.kotlin.analysis.api.symbols.markers.KtNamedSymbol
+
+internal class TranslatorError(message: String, cause: Throwable?) : IllegalStateException(message, cause)
+
+internal inline fun <R> KtAnalysisSession.withExceptionCatcher(symbol: KtSymbol, action: KtAnalysisSession.() -> R): R =
+ try {
+ action()
+ } catch (e: TranslatorError) {
+ throw e
+ } catch (e: Throwable) {
+ val file = try {
+ symbol.psi?.containingFile?.virtualFile?.path
+ } catch (e: Throwable) {
+ "[$e]"
+ }
+ val textRange = try {
+ symbol.psi?.textRange.toString()
+ } catch (e: Throwable) {
+ "[$e]"
+ }
+ throw TranslatorError(
+ "Error in translating of symbol (${(symbol as? KtNamedSymbol)?.name}) $symbol in file: $file, $textRange",
+ e
+ )
+ }
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeReferenceFactory.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeReferenceFactory.kt
new file mode 100644
index 00000000..ea9a18c9
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeReferenceFactory.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.translators
+
+import org.jetbrains.dokka.links.*
+import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
+import org.jetbrains.kotlin.analysis.api.KtStarTypeProjection
+import org.jetbrains.kotlin.analysis.api.KtTypeArgumentWithVariance
+import org.jetbrains.kotlin.analysis.api.KtTypeProjection
+import org.jetbrains.kotlin.analysis.api.types.*
+
+internal fun KtAnalysisSession.getTypeReferenceFrom(type: KtType): TypeReference =
+ getTypeReferenceFromPossiblyRecursive(type, emptyList())
+
+
+// see `deep recursive typebound #1342` test
+private fun KtAnalysisSession.getTypeReferenceFromPossiblyRecursive(
+ type: KtType,
+ paramTrace: List<KtType>
+): TypeReference {
+ if (type is KtTypeParameterType) {
+ // compare by symbol since, e.g. T? and T have the different KtType, but the same type parameter
+ paramTrace.indexOfFirst { it is KtTypeParameterType && type.symbol == it.symbol }
+ .takeIf { it >= 0 }
+ ?.let { return RecursiveType(it) }
+ }
+
+ return when (type) {
+ is KtNonErrorClassType -> TypeConstructor(
+ type.classId.asFqNameString(),
+ type.ownTypeArguments.map {
+ getTypeReferenceFromTypeProjection(
+ it,
+ paramTrace
+ )
+ }
+ )
+
+ is KtTypeParameterType -> {
+ val upperBoundsOrNullableAny =
+ type.symbol.upperBounds.takeIf { it.isNotEmpty() } ?: listOf(this.builtinTypes.NULLABLE_ANY)
+
+ TypeParam(bounds = upperBoundsOrNullableAny.map {
+ getTypeReferenceFromPossiblyRecursive(
+ it,
+ paramTrace + type
+ )
+ })
+ }
+
+ is KtClassErrorType -> TypeConstructor("$ERROR_CLASS_NAME $type", emptyList())
+ is KtFlexibleType -> getTypeReferenceFromPossiblyRecursive(
+ type.upperBound,
+ paramTrace
+ )
+
+ is KtDefinitelyNotNullType -> getTypeReferenceFromPossiblyRecursive(
+ type.original,
+ paramTrace
+ )
+
+ is KtDynamicType -> TypeConstructor("[dynamic]", emptyList())
+ is KtTypeErrorType -> TypeConstructor("$ERROR_CLASS_NAME $type", emptyList())
+ is KtCapturedType -> throw NotImplementedError()
+ is KtIntegerLiteralType -> throw NotImplementedError()
+ is KtIntersectionType -> throw NotImplementedError()
+ }.let {
+ if (type.isMarkedNullable) Nullable(it) else it
+ }
+
+}
+
+private fun KtAnalysisSession.getTypeReferenceFromTypeProjection(
+ typeProjection: KtTypeProjection,
+ paramTrace: List<KtType>
+): TypeReference =
+ when (typeProjection) {
+ is KtStarTypeProjection -> StarProjection
+ is KtTypeArgumentWithVariance -> getTypeReferenceFromPossiblyRecursive(typeProjection.type, paramTrace)
+ }
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt
new file mode 100644
index 00000000..f7366294
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt
@@ -0,0 +1,201 @@
+/*
+ * 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.translators
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.analysis.kotlin.symbols.translators.AnnotationTranslator.Companion.getPresentableName
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.properties.PropertyContainer
+import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
+import org.jetbrains.kotlin.analysis.api.KtStarTypeProjection
+import org.jetbrains.kotlin.analysis.api.KtTypeArgumentWithVariance
+import org.jetbrains.kotlin.analysis.api.KtTypeProjection
+import org.jetbrains.kotlin.analysis.api.annotations.*
+import org.jetbrains.kotlin.analysis.api.symbols.*
+import org.jetbrains.kotlin.analysis.api.types.*
+
+internal const val ERROR_CLASS_NAME = "<ERROR CLASS>"
+
+/**
+ * Maps [KtType] to Dokka [Bound] or [TypeConstructorWithKind].
+ *
+ * Also, build [AncestryNode] tree from [KtType]
+ */
+internal class TypeTranslator(
+ private val sourceSet: DokkaConfiguration.DokkaSourceSet,
+ private val annotationTranslator: AnnotationTranslator
+) {
+
+ private fun <T> T.toSourceSetDependent() = if (this != null) mapOf(sourceSet to this) else emptyMap()
+
+ private fun KtAnalysisSession.toProjection(typeProjection: KtTypeProjection): Projection =
+ when (typeProjection) {
+ is KtStarTypeProjection -> Star
+ is KtTypeArgumentWithVariance -> toBoundFrom(typeProjection.type).wrapWithVariance(typeProjection.variance)
+ }
+
+ private fun KtAnalysisSession.toTypeConstructorFromTypeAliased(classType: KtUsualClassType): TypeAliased {
+ val classSymbol = classType.classSymbol
+ return if (classSymbol is KtTypeAliasSymbol)
+ TypeAliased(
+ typeAlias = GenericTypeConstructor(
+ dri = getDRIFromNonErrorClassType(classType),
+ projections = classType.ownTypeArguments.map { toProjection(it) }),
+ inner = toBoundFrom(classType.fullyExpandedType),
+ extra = PropertyContainer.withAll(
+ getDokkaAnnotationsFrom(classType)?.toSourceSetDependent()?.toAnnotations()
+ )
+ ) else
+ throw IllegalStateException("Expected type alias symbol in type")
+ }
+
+ private fun KtAnalysisSession.toTypeConstructorFrom(classType: KtNonErrorClassType) =
+ GenericTypeConstructor(
+ dri = getDRIFromNonErrorClassType(classType),
+ projections = classType.ownTypeArguments.map { toProjection(it) },
+ presentableName = classType.getPresentableName(),
+ extra = PropertyContainer.withAll(
+ getDokkaAnnotationsFrom(classType)?.toSourceSetDependent()?.toAnnotations()
+ )
+ )
+
+ private fun KtAnalysisSession.toFunctionalTypeConstructorFrom(functionalType: KtFunctionalType) =
+ FunctionalTypeConstructor(
+ dri = getDRIFromNonErrorClassType(functionalType),
+ projections = functionalType.ownTypeArguments.map { toProjection(it) },
+ isExtensionFunction = functionalType.receiverType != null,
+ isSuspendable = functionalType.isSuspend,
+ presentableName = functionalType.getPresentableName(),
+ extra = PropertyContainer.withAll(
+ getDokkaAnnotationsFrom(functionalType)?.toSourceSetDependent()?.toAnnotations()
+ )
+ )
+
+ fun KtAnalysisSession.toBoundFrom(type: KtType): Bound =
+ when (type) {
+ is KtUsualClassType -> {
+ if (type.classSymbol is KtTypeAliasSymbol) toTypeConstructorFromTypeAliased(type)
+ else toTypeConstructorFrom(type)
+ }
+
+ is KtTypeParameterType -> TypeParameter(
+ dri = getDRIFromTypeParameter(type.symbol),
+ name = type.name.asString(),
+ presentableName = type.getPresentableName(),
+ extra = PropertyContainer.withAll(
+ getDokkaAnnotationsFrom(type)?.toSourceSetDependent()?.toAnnotations()
+ )
+ )
+
+ is KtClassErrorType -> UnresolvedBound(type.toString())
+ is KtFunctionalType -> {
+ /**
+ * For example
+ * `typealias CompletionHandler = (cause: Throwable?) -> Unit`
+ * has functional type with no type arguments in K2
+ * In K1 we have a usual generic type
+ */
+ if (type.ownTypeArguments.isEmpty())
+ toTypeConstructorFrom(type)
+ else
+ toFunctionalTypeConstructorFrom(type)
+ }
+ is KtDynamicType -> Dynamic
+ is KtDefinitelyNotNullType -> DefinitelyNonNullable(
+ toBoundFrom(type.original)
+ )
+
+ is KtFlexibleType -> TypeAliased(
+ toBoundFrom(type.upperBound),
+ toBoundFrom(type.lowerBound),
+ extra = PropertyContainer.withAll(
+ getDokkaAnnotationsFrom(type)?.toSourceSetDependent()?.toAnnotations()
+ )
+ )
+
+ is KtTypeErrorType -> UnresolvedBound(type.toString())
+ is KtCapturedType -> throw NotImplementedError()
+ is KtIntegerLiteralType -> throw NotImplementedError()
+ is KtIntersectionType -> throw NotImplementedError()
+ }.let {
+ if (type.isMarkedNullable) Nullable(it) else it
+ }
+
+ fun KtAnalysisSession.buildAncestryInformationFrom(
+ type: KtType
+ ): AncestryNode {
+ val (interfaces, superclass) = type.getDirectSuperTypes().filterNot { it.isAny }
+ .partition {
+ val typeConstructorWithKind = toTypeConstructorWithKindFrom(it)
+ typeConstructorWithKind.kind == KotlinClassKindTypes.INTERFACE ||
+ typeConstructorWithKind.kind == JavaClassKindTypes.INTERFACE
+ }
+
+ return AncestryNode(
+ typeConstructor = toTypeConstructorWithKindFrom(type).typeConstructor,
+ superclass = superclass.map { buildAncestryInformationFrom(it) }.singleOrNull(),
+ interfaces = interfaces.map { buildAncestryInformationFrom(it) }
+ )
+ }
+
+ internal fun KtAnalysisSession.toTypeConstructorWithKindFrom(type: KtType): TypeConstructorWithKind = when (type) {
+ is KtUsualClassType ->
+ when (val classSymbol = type.classSymbol) {
+ is KtNamedClassOrObjectSymbol -> TypeConstructorWithKind(
+ toTypeConstructorFrom(type),
+ classSymbol.classKind.toDokkaClassKind()
+ )
+
+ is KtAnonymousObjectSymbol -> throw NotImplementedError()
+ is KtTypeAliasSymbol -> toTypeConstructorWithKindFrom(classSymbol.expandedType)
+ }
+
+ is KtClassErrorType -> TypeConstructorWithKind(
+ GenericTypeConstructor(
+ dri = DRI(packageName = "", classNames = "$ERROR_CLASS_NAME $type"),
+ projections = emptyList(),
+
+ ),
+ KotlinClassKindTypes.CLASS
+ )
+
+ is KtTypeErrorType -> TypeConstructorWithKind(
+ GenericTypeConstructor(
+ dri = DRI(packageName = "", classNames = "$ERROR_CLASS_NAME $type"),
+ projections = emptyList(),
+
+ ),
+ KotlinClassKindTypes.CLASS
+ )
+
+ is KtFunctionalType -> TypeConstructorWithKind(
+ toFunctionalTypeConstructorFrom(type),
+ KotlinClassKindTypes.CLASS
+ )
+
+ is KtDefinitelyNotNullType -> toTypeConstructorWithKindFrom(type.original)
+
+ is KtCapturedType -> throw NotImplementedError()
+ is KtDynamicType -> throw NotImplementedError()
+ is KtFlexibleType -> throw NotImplementedError()
+ is KtIntegerLiteralType -> throw NotImplementedError()
+ is KtIntersectionType -> throw NotImplementedError()
+ is KtTypeParameterType -> throw NotImplementedError()
+ }
+
+ private fun KtAnalysisSession.getDokkaAnnotationsFrom(annotated: KtAnnotated): List<Annotations.Annotation>? =
+ with(annotationTranslator) { getAllAnnotationsFrom(annotated) }.takeUnless { it.isEmpty() }
+
+ private fun KtClassKind.toDokkaClassKind() = when (this) {
+ KtClassKind.CLASS -> KotlinClassKindTypes.CLASS
+ KtClassKind.ENUM_CLASS -> KotlinClassKindTypes.ENUM_CLASS
+ KtClassKind.ANNOTATION_CLASS -> KotlinClassKindTypes.ANNOTATION_CLASS
+ KtClassKind.OBJECT -> KotlinClassKindTypes.OBJECT
+ KtClassKind.COMPANION_OBJECT -> KotlinClassKindTypes.OBJECT
+ KtClassKind.INTERFACE -> KotlinClassKindTypes.INTERFACE
+ KtClassKind.ANONYMOUS_OBJECT -> KotlinClassKindTypes.OBJECT
+ }
+}
diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/utils/isException.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/utils/isException.kt
new file mode 100644
index 00000000..08a2aaad
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/utils/isException.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.utils
+
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.AncestryNode
+import org.jetbrains.dokka.model.TypeConstructor
+
+internal fun AncestryNode.typeConstructorsBeingExceptions(): List<TypeConstructor> {
+ fun traverseSupertypes(ancestry: AncestryNode): List<TypeConstructor> =
+ listOf(ancestry.typeConstructor) + (ancestry.superclass?.let(::traverseSupertypes) ?: emptyList())
+
+ return traverseSupertypes(this).filter { type -> type.dri.isDirectlyAnException() }
+}
+
+internal fun DRI.isDirectlyAnException(): Boolean =
+ toString().let { stringed ->
+ stringed == "kotlin/Exception///PointingToDeclaration/" ||
+ stringed == "java.lang/Exception///PointingToDeclaration/"
+ }