aboutsummaryrefslogtreecommitdiff
path: root/dokka-subprojects/analysis-kotlin-symbols
diff options
context:
space:
mode:
Diffstat (limited to 'dokka-subprojects/analysis-kotlin-symbols')
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/README.md18
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/api/analysis-kotlin-symbols.api19
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/build.gradle.kts91
-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
-rw-r--r--dokka-subprojects/analysis-kotlin-symbols/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin5
41 files changed, 3541 insertions, 0 deletions
diff --git a/dokka-subprojects/analysis-kotlin-symbols/README.md b/dokka-subprojects/analysis-kotlin-symbols/README.md
new file mode 100644
index 00000000..7c84ffc5
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/README.md
@@ -0,0 +1,18 @@
+# Analysis: Kotlin symbols
+
+An internal symbols-based implementation for [analysis-kotlin-api](../analysis-kotlin-api). This implementation is
+also known as K2 or "the new compiler".
+
+Contains no stable public API and must not be used by anyone directly, only via [analysis-kotlin-api](../analysis-kotlin-api).
+
+Can be added as a runtime dependency by the runner.
+
+## Shadowing
+
+The `.jar` produced by this project shadows all dependencies. There are several reasons for it:
+
+1. Some of the artifacts Dokka depends on, like `com.jetbrains.intellij.java:java-psi`, are not
+ published to Maven Central, so the users would need to add custom repositories to their build scripts.
+2. There are many intertwining transitive dependencies of different versions, as well as direct copy-paste,
+ that can lead to runtime errors due to classpath conflicts, so it's best to let Gradle take care of
+ dependency resolution, and then pack everything into a single jar in a single place that can be tuned.
diff --git a/dokka-subprojects/analysis-kotlin-symbols/api/analysis-kotlin-symbols.api b/dokka-subprojects/analysis-kotlin-symbols/api/analysis-kotlin-symbols.api
new file mode 100644
index 00000000..4bddfcf1
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/api/analysis-kotlin-symbols.api
@@ -0,0 +1,19 @@
+public final class org/jetbrains/dokka/analysis/kotlin/symbols/plugin/SymbolsAnalysisPlugin : org/jetbrains/dokka/plugability/DokkaPlugin {
+ public fun <init> ()V
+}
+
+public class org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinSampleProvider : org/jetbrains/dokka/analysis/kotlin/internal/SampleProvider {
+ public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V
+ public fun close ()V
+ public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext;
+ public fun getSample (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Ljava/lang/String;)Lorg/jetbrains/dokka/analysis/kotlin/internal/SampleProvider$SampleSnippet;
+ protected fun processBody (Lcom/intellij/psi/PsiElement;)Ljava/lang/String;
+ protected fun processImports (Lcom/intellij/psi/PsiElement;)Ljava/lang/String;
+}
+
+public final class org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinSampleProviderFactory : org/jetbrains/dokka/analysis/kotlin/internal/SampleProviderFactory {
+ public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V
+ public fun build ()Lorg/jetbrains/dokka/analysis/kotlin/internal/SampleProvider;
+ public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext;
+}
+
diff --git a/dokka-subprojects/analysis-kotlin-symbols/build.gradle.kts b/dokka-subprojects/analysis-kotlin-symbols/build.gradle.kts
new file mode 100644
index 00000000..4aae4628
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-symbols/build.gradle.kts
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+import dokkabuild.overridePublicationArtifactId
+
+plugins {
+ id("dokkabuild.kotlin-jvm")
+ id("dokkabuild.publish-shadow")
+}
+
+overridePublicationArtifactId("analysis-kotlin-symbols")
+
+dependencies {
+ compileOnly(projects.dokkaSubprojects.dokkaCore)
+ compileOnly(projects.dokkaSubprojects.analysisKotlinApi)
+
+ implementation(projects.dokkaSubprojects.analysisMarkdownJb)
+ implementation(projects.dokkaSubprojects.analysisJavaPsi)
+
+
+ // ----------- IDE dependencies ----------------------------------------------------------------------------
+
+ listOf(
+ libs.intellij.platform.util.rt,
+ libs.intellij.platform.util.api,
+ libs.intellij.java.psi.api,
+ libs.intellij.java.psi.impl
+ ).forEach {
+ runtimeOnly(it) { isTransitive = false }
+ }
+
+ implementation(libs.intellij.java.psi.api) { isTransitive = false }
+
+
+ // TODO move to toml
+ listOf(
+ "com.jetbrains.intellij.platform:util-class-loader",
+ "com.jetbrains.intellij.platform:util-text-matching",
+ "com.jetbrains.intellij.platform:util-base",
+ "com.jetbrains.intellij.platform:util-xml-dom",
+ "com.jetbrains.intellij.platform:core-impl",
+ "com.jetbrains.intellij.platform:extensions",
+ ).forEach {
+ runtimeOnly("$it:213.7172.25") { isTransitive = false }
+ }
+
+ implementation("com.jetbrains.intellij.platform:core:213.7172.25") {
+ isTransitive = false
+ } // for Standalone prototype
+
+ // ----------- Analysis dependencies ----------------------------------------------------------------------------
+
+ listOf(
+ libs.kotlin.high.level.api.api,
+ libs.kotlin.analysis.api.standalone,
+ ).forEach {
+ implementation(it) {
+ isTransitive = false // see KTIJ-19820
+ }
+ }
+ listOf(
+ libs.kotlin.high.level.api.impl,
+ libs.kotlin.high.level.api.fir,
+ libs.kotlin.high.level.api.fe10,
+ libs.kotlin.low.level.api.fir,
+ libs.kotlin.analysis.project.structure,
+ libs.kotlin.analysis.api.providers,
+ libs.kotlin.symbol.light.classes,
+ ).forEach {
+ runtimeOnly(it) {
+ isTransitive = false // see KTIJ-19820
+ }
+ }
+ // copy-pasted from Analysis API https://github.com/JetBrains/kotlin/blob/a10042f9099e20a656dec3ecf1665eea340a3633/analysis/low-level-api-fir/build.gradle.kts#L37
+ runtimeOnly("com.github.ben-manes.caffeine:caffeine:2.9.3")
+
+ runtimeOnly(libs.kotlinx.collections.immutable)
+ implementation(libs.kotlin.compiler.k2) {
+ isTransitive = false
+ }
+
+ // TODO [beresnev] get rid of it
+ compileOnly(libs.kotlinx.coroutines.core)
+}
+
+tasks.shadowJar {
+ // service files are merged to make sure all Dokka plugins
+ // from the dependencies are loaded, and not just a single one.
+ mergeServiceFiles()
+}
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/