aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Mishenev <vad-mishenev@yandex.ru>2023-08-28 19:42:21 +0300
committerGitHub <noreply@github.com>2023-08-28 19:42:21 +0300
commit0e00edc6fcd406fcf38673ef6a2f8f59e8374de2 (patch)
tree697b0de0d44b421c922f1f5e6a7c1352f17c68a6
parentbec2cac91726e52884329e7997207e9777abaab7 (diff)
downloaddokka-0e00edc6fcd406fcf38673ef6a2f8f59e8374de2.tar.gz
dokka-0e00edc6fcd406fcf38673ef6a2f8f59e8374de2.tar.bz2
dokka-0e00edc6fcd406fcf38673ef6a2f8f59e8374de2.zip
Support Dokka K2 analysis (#3094)
Dokka has its own documentable model to represent analyzed code. The analysis is performed by a compiler frontend. In K1 the compiler frontend has descriptors that use the underlying Binding Context (global shared stateful structure). Dokka just maps descriptors to Documentable by DefaultDescriptorToDocumentableTranslator. K2 compiler has FIR tree, which means “Frontend Intermediate Representation”, instead of Binding Context. But we do not use FIR in Dokka directly, since it is too low-level for analysis. The Kotlin compiler provides high-level Analysis API for this case. The API is used by KSP too. Analysis API represent elements of FIR (declarations, parameters and so on) as Symbols. For more details see KtSymbolByFirBuilder, KtSymbol. For Dokka symbol is the replacement of descriptor in K2. Also, to set up the environment of project analysis in K1 we use idea dependencies (or copy-past from there). In K2 for these aims, there is a Standalone mode for Analysis API.
-rw-r--r--build-logic/src/main/kotlin/org/jetbrains/conventions/base-unit-test.gradle.kts39
-rw-r--r--gradle/libs.versions.toml16
-rw-r--r--plugins/android-documentation/build.gradle.kts8
-rw-r--r--plugins/base/base-test-utils/build.gradle.kts1
-rw-r--r--plugins/base/build.gradle.kts13
-rw-r--r--plugins/base/src/test/kotlin/basic/DRITest.kt1
-rw-r--r--plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt6
-rw-r--r--plugins/base/src/test/kotlin/content/annotations/JavaDeprecatedTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/content/exceptions/ContentForExceptions.kt5
-rw-r--r--plugins/base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt10
-rw-r--r--plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt4
-rw-r--r--plugins/base/src/test/kotlin/content/signatures/ConstructorsSignaturesTest.kt3
-rw-r--r--plugins/base/src/test/kotlin/filter/JavaVisibilityFilterTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt3
-rw-r--r--plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt4
-rw-r--r--plugins/base/src/test/kotlin/linking/EnumValuesLinkingTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/markdown/LinkTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/markdown/ParserTest.kt8
-rw-r--r--plugins/base/src/test/kotlin/model/ClassesTest.kt7
-rw-r--r--plugins/base/src/test/kotlin/model/ExtensionsTest.kt4
-rw-r--r--plugins/base/src/test/kotlin/model/FunctionsTest.kt7
-rw-r--r--plugins/base/src/test/kotlin/model/JavaTest.kt5
-rw-r--r--plugins/base/src/test/kotlin/model/MultiLanguageInheritanceTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsForParametersTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/parsers/JavadocParserTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/renderers/html/NavigationIconTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt3
-rw-r--r--plugins/base/src/test/kotlin/signatures/FunctionalTypeConstructorsSignatureTest.kt7
-rw-r--r--plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt13
-rw-r--r--plugins/base/src/test/kotlin/signatures/SignatureTest.kt4
-rw-r--r--plugins/base/src/test/kotlin/superFields/DescriptorSuperPropertiesTest.kt3
-rw-r--r--plugins/base/src/test/kotlin/superFields/PsiSuperFieldsTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/transformers/InheritedEntriesDocumentableFilterTransfromerTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/transformers/ModuleAndPackageDocumentationTransformerFunctionalTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/transformers/ObviousAndInheritedFunctionsDocumentableFilterTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/transformers/SourceLinkTransformerTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/transformers/isExceptionTest.kt8
-rw-r--r--plugins/base/src/test/kotlin/translators/Bug1341.kt2
-rw-r--r--plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt7
-rw-r--r--plugins/base/src/test/kotlin/translators/JavadocInheritDocsTest.kt3
-rw-r--r--plugins/base/src/test/kotlin/translators/JavadocInheritedDocTagsTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/translators/JavadocParserTest.kt2
-rw-r--r--plugins/base/src/test/kotlin/utils/TagsAnnotations.kt71
-rw-r--r--plugins/mathjax/build.gradle.kts8
-rw-r--r--settings.gradle.kts2
-rw-r--r--subprojects/analysis-java-psi/api/analysis-java-psi.api4
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiUtil.kt3
-rw-r--r--subprojects/analysis-kotlin-symbols/api/analysis-kotlin-symbols.api19
-rw-r--r--subprojects/analysis-kotlin-symbols/build.gradle.kts71
-rw-r--r--subprojects/analysis-kotlin-symbols/compiler/api/compiler.api4
-rw-r--r--subprojects/analysis-kotlin-symbols/compiler/build.gradle.kts13
-rw-r--r--subprojects/analysis-kotlin-symbols/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/compiler/CompilerSymbolsAnalysisPlugin.kt11
-rw-r--r--subprojects/analysis-kotlin-symbols/compiler/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin1
-rw-r--r--subprojects/analysis-kotlin-symbols/ide/api/ide.api4
-rw-r--r--subprojects/analysis-kotlin-symbols/ide/build.gradle.kts13
-rw-r--r--subprojects/analysis-kotlin-symbols/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/ide/IdeSymbolsAnalysisPlugin.kt11
-rw-r--r--subprojects/analysis-kotlin-symbols/ide/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin1
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KDocProvider.kt170
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KdocMarkdownParser.kt97
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/ResolveKDocLink.kt42
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/SyntheticKDocProvider.kt61
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorDocumentationContent.kt15
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorKotlinDocCommentCreator.kt16
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocComment.kt81
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocCommentParser.kt46
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinInheritDocTagContentProvider.kt31
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/IllegalModuleAndPackageDocumentation.kt7
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentation.kt11
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationFragment.kt9
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationParsingContext.kt47
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationReader.kt110
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationSource.kt14
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentation.kt12
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentationFragments.kt55
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/AnalysisContext.kt139
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/KotlinAnalysis.kt245
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/SymbolsAnalysisPlugin.kt121
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisProjectProvider.kt16
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisSourceRootsExtractor.kt12
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinDocumentableSourceLanguageParser.kt26
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinSampleProvider.kt85
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KtPsiDocumentableSource.kt20
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolExternalDocumentablesProvider.kt34
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolFullClassHierarchyBuilder.kt85
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolSyntheticDocumentableDetector.kt41
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt134
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DRIFactory.kt140
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt949
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TranslatorError.kt29
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeReferenceFactory.kt78
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt186
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/utils/isException.kt18
-rw-r--r--subprojects/analysis-kotlin-symbols/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin1
98 files changed, 3555 insertions, 98 deletions
diff --git a/build-logic/src/main/kotlin/org/jetbrains/conventions/base-unit-test.gradle.kts b/build-logic/src/main/kotlin/org/jetbrains/conventions/base-unit-test.gradle.kts
new file mode 100644
index 00000000..3ece2e8d
--- /dev/null
+++ b/build-logic/src/main/kotlin/org/jetbrains/conventions/base-unit-test.gradle.kts
@@ -0,0 +1,39 @@
+package org.jetbrains.conventions
+
+/**
+ * Utility to run ynit tests for K1 and K2 (analysis API).
+ */
+
+plugins {
+ id("org.jetbrains.conventions.base")
+ id("org.jetbrains.conventions.base-java")
+}
+
+val descriptorsTestConfiguration: Configuration by configurations.creating {
+ extendsFrom(configurations.testImplementation.get())
+}
+val symbolsTestConfiguration: Configuration by configurations.creating {
+ extendsFrom(configurations.testImplementation.get())
+}
+
+val symbolsTest = tasks.register<Test>("symbolsTest") {
+ useJUnitPlatform {
+ excludeTags("onlyDescriptors", "onlyDescriptorsMPP", "javaCode", "usingJDK")
+ }
+ classpath += symbolsTestConfiguration
+}
+// run symbols and descriptors tests
+tasks.test {
+ //enabled = false
+ classpath += descriptorsTestConfiguration
+ dependsOn(symbolsTest)
+}
+
+val descriptorsTest = tasks.register<Test>("descriptorsTest") {
+ classpath += descriptorsTestConfiguration
+}
+
+tasks.check {
+ dependsOn(symbolsTest)
+}
+
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index a09f1413..9170c40b 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -6,10 +6,12 @@ gradlePlugin-android = "4.2.2"
gradlePlugin-dokka = "1.8.20"
kotlinx-coroutines = "1.6.3"
+kotlinx-collections-immutable = "0.3.4"
kotlinx-bcv = "0.12.1"
## Analysis
kotlin-compiler = "1.9.0"
+kotlin-compiler-k2 = "1.9.0-release-358"
# MUST match the version of the intellij platform used in the kotlin compiler,
# otherwise this will lead to different versions of psi API and implementations
@@ -56,6 +58,7 @@ eclipse-jgit = "5.12.0.202106070339-r"
[libraries]
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
+kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm", version.ref = "kotlinx-collections-immutable" }
#### Gradle plugins ####
# The Maven coordinates of Gradle plugins that are either used in convention plugins, or in Dokka subprojects
@@ -68,6 +71,19 @@ gradlePlugin-gradlePublish= { module = "com.gradle.publish:plugin-publish-plugin
#### Kotlin analysis ####
kotlin-compiler = { module = "org.jetbrains.kotlin:kotlin-compiler", version.ref = "kotlin-compiler" }
+###### K2 analysis ######
+kotlin-compiler-k2 = { module = "org.jetbrains.kotlin:kotlin-compiler", version.ref = "kotlin-compiler-k2" }
+kotlin-high-level-api-api = { module = "org.jetbrains.kotlin:high-level-api-for-ide", version.ref = "kotlin-compiler-k2" }
+kotlin-high-level-api-impl = { module = "org.jetbrains.kotlin:high-level-api-impl-base-for-ide", version.ref = "kotlin-compiler-k2" }
+kotlin-high-level-api-fir = { module = "org.jetbrains.kotlin:high-level-api-fir-for-ide", version.ref = "kotlin-compiler-k2" }
+kotlin-high-level-api-fe10 = { module = "org.jetbrains.kotlin:high-level-api-fe10-for-ide", version.ref = "kotlin-compiler-k2" }
+kotlin-low-level-api-fir = { module = "org.jetbrains.kotlin:low-level-api-fir-for-ide", version.ref = "kotlin-compiler-k2" }
+kotlin-analysis-project-structure = { module = "org.jetbrains.kotlin:analysis-project-structure-for-ide", version.ref = "kotlin-compiler-k2" }
+kotlin-analysis-api-standalone = { module = "org.jetbrains.kotlin:analysis-api-standalone-for-ide", version.ref = "kotlin-compiler-k2" }
+kotlin-analysis-api-providers = { module = "org.jetbrains.kotlin:analysis-api-providers-for-ide", version.ref = "kotlin-compiler-k2" }
+kotlin-symbol-light-classes = { module = "org.jetbrains.kotlin:symbol-light-classes-for-ide", version.ref = "kotlin-compiler-k2" }
+
+
#### Java analysis ####
intellij-java-psi-api = { module = "com.jetbrains.intellij.java:java-psi", version.ref = "intellij-platform" }
intellij-java-psi-impl = { module = "com.jetbrains.intellij.java:java-psi-impl", version.ref = "intellij-platform" }
diff --git a/plugins/android-documentation/build.gradle.kts b/plugins/android-documentation/build.gradle.kts
index 4dfc972d..545f8435 100644
--- a/plugins/android-documentation/build.gradle.kts
+++ b/plugins/android-documentation/build.gradle.kts
@@ -3,6 +3,7 @@ import org.jetbrains.registerDokkaArtifactPublication
plugins {
id("org.jetbrains.conventions.kotlin-jvm")
id("org.jetbrains.conventions.maven-publish")
+ id("org.jetbrains.conventions.base-unit-test")
}
dependencies {
@@ -13,10 +14,15 @@ dependencies {
implementation(kotlin("reflect"))
testImplementation(projects.plugins.base)
- testImplementation(projects.plugins.base.baseTestUtils)
testImplementation(projects.core.testApi)
testImplementation(platform(libs.junit.bom))
testImplementation(libs.junit.jupiter)
+
+ symbolsTestConfiguration(project(path = ":subprojects:analysis-kotlin-symbols", configuration = "shadow"))
+ descriptorsTestConfiguration(project(path = ":subprojects:analysis-kotlin-descriptors", configuration = "shadow"))
+ testImplementation(projects.plugins.base.baseTestUtils) {
+ exclude(module = "analysis-kotlin-descriptors")
+ }
}
registerDokkaArtifactPublication("androidDocumentationPlugin") {
diff --git a/plugins/base/base-test-utils/build.gradle.kts b/plugins/base/base-test-utils/build.gradle.kts
index ef4f9f7b..20c3b727 100644
--- a/plugins/base/base-test-utils/build.gradle.kts
+++ b/plugins/base/base-test-utils/build.gradle.kts
@@ -12,6 +12,7 @@ dependencies {
api(projects.subprojects.analysisKotlinApi)
// TODO [beresnev] analysis switcher
+ //runtimeOnly(project(path = ":subprojects:analysis-kotlin-symbols", configuration = "shadow"))
runtimeOnly(project(path = ":subprojects:analysis-kotlin-descriptors", configuration = "shadow"))
implementation(kotlin("reflect"))
diff --git a/plugins/base/build.gradle.kts b/plugins/base/build.gradle.kts
index 8bea63e8..2f9f5863 100644
--- a/plugins/base/build.gradle.kts
+++ b/plugins/base/build.gradle.kts
@@ -4,6 +4,7 @@ plugins {
id("org.jetbrains.conventions.kotlin-jvm")
id("org.jetbrains.conventions.maven-publish")
id("org.jetbrains.conventions.dokka-html-frontend-files")
+ id("org.jetbrains.conventions.base-unit-test")
}
dependencies {
@@ -26,7 +27,11 @@ dependencies {
}
// Test only
- testImplementation(projects.plugins.base.baseTestUtils)
+ symbolsTestConfiguration(project(path = ":subprojects:analysis-kotlin-symbols", configuration = "shadow"))
+ descriptorsTestConfiguration(project(path = ":subprojects:analysis-kotlin-descriptors", configuration = "shadow"))
+ testImplementation(projects.plugins.base.baseTestUtils) {
+ exclude(module = "analysis-kotlin-descriptors")
+ }
testImplementation(projects.core.contentMatcherTestUtils)
testImplementation(projects.core.testApi)
testImplementation(platform(libs.junit.bom))
@@ -37,6 +42,12 @@ dependencies {
}
}
+
+
+
+
+
+
// access the frontend files via the dependency on :plugins:base:frontend
val dokkaHtmlFrontendFiles: Provider<FileCollection> =
configurations.dokkaHtmlFrontendFiles.map { frontendFiles ->
diff --git a/plugins/base/src/test/kotlin/basic/DRITest.kt b/plugins/base/src/test/kotlin/basic/DRITest.kt
index 9c443567..c70c1b0b 100644
--- a/plugins/base/src/test/kotlin/basic/DRITest.kt
+++ b/plugins/base/src/test/kotlin/basic/DRITest.kt
@@ -10,6 +10,7 @@ import org.jetbrains.dokka.pages.ClasslikePageNode
import org.jetbrains.dokka.pages.ContentPage
import org.jetbrains.dokka.pages.MemberPageNode
import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
class DRITest : BaseAbstractTest() {
diff --git a/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt b/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt
index 46239baa..82159e0d 100644
--- a/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt
+++ b/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt
@@ -10,10 +10,7 @@ import org.jetbrains.dokka.pages.ContentText
import org.jetbrains.dokka.pages.MemberPageNode
import org.jetbrains.dokka.pages.PackagePageNode
import org.junit.jupiter.api.Test
-import utils.ParamAttributes
-import utils.assertNotNull
-import utils.bareSignature
-import utils.propertySignature
+import utils.*
import kotlin.test.assertEquals
import kotlin.test.assertTrue
@@ -318,6 +315,7 @@ class ContentForAnnotationsTest : BaseAbstractTest() {
}
}
+ @JavaCode
@Test
fun `annotated bounds in Java`() {
testInline(
diff --git a/plugins/base/src/test/kotlin/content/annotations/JavaDeprecatedTest.kt b/plugins/base/src/test/kotlin/content/annotations/JavaDeprecatedTest.kt
index 961ce5f5..c25c1a1b 100644
--- a/plugins/base/src/test/kotlin/content/annotations/JavaDeprecatedTest.kt
+++ b/plugins/base/src/test/kotlin/content/annotations/JavaDeprecatedTest.kt
@@ -9,10 +9,12 @@ import org.jetbrains.dokka.model.properties.WithExtraProperties
import org.jetbrains.dokka.pages.ContentPage
import org.jetbrains.dokka.pages.ContentStyle
import org.junit.jupiter.api.Test
+import utils.JavaCode
import utils.pWrapped
import kotlin.test.assertEquals
import kotlin.test.assertTrue
+@JavaCode
class JavaDeprecatedTest : BaseAbstractTest() {
private val testConfiguration = dokkaConfiguration {
diff --git a/plugins/base/src/test/kotlin/content/exceptions/ContentForExceptions.kt b/plugins/base/src/test/kotlin/content/exceptions/ContentForExceptions.kt
index 14a36611..17bd209e 100644
--- a/plugins/base/src/test/kotlin/content/exceptions/ContentForExceptions.kt
+++ b/plugins/base/src/test/kotlin/content/exceptions/ContentForExceptions.kt
@@ -7,9 +7,7 @@ import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.model.DisplaySourceSet
import org.junit.jupiter.api.Test
-import utils.ParamAttributes
-import utils.bareSignature
-import utils.findTestType
+import utils.*
import kotlin.test.assertEquals
class ContentForExceptions : BaseAbstractTest() {
@@ -55,6 +53,7 @@ class ContentForExceptions : BaseAbstractTest() {
)
}
+ @OnlyDescriptors("Fixed in 1.9.20 (IMPORT STAR)")
@Test
fun `function with navigatable thrown exception`() {
testInline(
diff --git a/plugins/base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt b/plugins/base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt
index e5059073..2994fb42 100644
--- a/plugins/base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt
+++ b/plugins/base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt
@@ -6,6 +6,7 @@ import org.jetbrains.dokka.PluginConfigurationImpl
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.junit.jupiter.api.Test
+import utils.OnlyDescriptors
import utils.classSignature
import utils.findTestType
import kotlin.test.assertEquals
@@ -128,6 +129,7 @@ class ContentForInheritorsTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptors("Order of inheritors is different in K2")
@Test
fun `interface with few inheritors has table in description`() {
testInline(
diff --git a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt
index e74cb49d..df5efd03 100644
--- a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt
+++ b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt
@@ -242,6 +242,7 @@ class ContentForParamsTest : BaseAbstractTest() {
}
}
+ @JavaCode
@Test
fun `deprecated with multiple links inside`() {
testInline(
@@ -346,6 +347,7 @@ class ContentForParamsTest : BaseAbstractTest() {
}
}
+ @JavaCode
@Test
fun `deprecated with an multiple inline links`() {
testInline(
@@ -410,6 +412,7 @@ class ContentForParamsTest : BaseAbstractTest() {
}
}
+ @JavaCode
@Test
fun `multiline throws with comment`() {
testInline(
@@ -473,6 +476,7 @@ class ContentForParamsTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptors("Fixed in 1.9.20 (IMPORT STAR)")
@Test
fun `multiline kotlin throws with comment`() {
testInline(
@@ -590,6 +594,7 @@ class ContentForParamsTest : BaseAbstractTest() {
}
}
+ @JavaCode
@Test
fun `multiline throws where exception is not in the same line as description`() {
testInline(
@@ -673,6 +678,7 @@ class ContentForParamsTest : BaseAbstractTest() {
}
+ @JavaCode
@Test
fun `documentation splitted in 2 using enters`() {
testInline(
@@ -718,6 +724,7 @@ class ContentForParamsTest : BaseAbstractTest() {
}
}
+ @JavaCode
@Test
fun `multiline return tag with param`() {
testInline(
@@ -783,6 +790,7 @@ class ContentForParamsTest : BaseAbstractTest() {
}
}
+ @UsingJDK
@Test
fun `return tag in kotlin`() {
testInline(
@@ -830,6 +838,7 @@ class ContentForParamsTest : BaseAbstractTest() {
}
}
+ @JavaCode
@Test
fun `list with links and description`() {
testInline(
@@ -1476,6 +1485,7 @@ class ContentForParamsTest : BaseAbstractTest() {
}
}
+ @JavaCode
@Test
fun javaDocCommentWithDocumentedParameters() {
testInline(
diff --git a/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt b/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt
index 79c1e1ad..82a04b89 100644
--- a/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt
+++ b/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt
@@ -171,6 +171,7 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptors("No link for `abc` in K1")
@Test
fun `undocumented seealso with reference to parameter for class`() {
testInline(
@@ -201,7 +202,7 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
header(4) { +"See also" }
table {
group {
- +"abc"
+ +"abc" // link { +"abc" }
}
}
}
@@ -751,6 +752,7 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptorsMPP
@Test
fun `multiplatform class with seealso in few platforms`() {
testInline(
diff --git a/plugins/base/src/test/kotlin/content/signatures/ConstructorsSignaturesTest.kt b/plugins/base/src/test/kotlin/content/signatures/ConstructorsSignaturesTest.kt
index 0cf94c18..e57e6a8c 100644
--- a/plugins/base/src/test/kotlin/content/signatures/ConstructorsSignaturesTest.kt
+++ b/plugins/base/src/test/kotlin/content/signatures/ConstructorsSignaturesTest.kt
@@ -5,6 +5,7 @@ import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.pages.BasicTabbedContentType
import org.jetbrains.dokka.pages.ContentPage
import org.junit.jupiter.api.Test
+import utils.OnlyDescriptors
class ConstructorsSignaturesTest : BaseAbstractTest() {
private val testConfiguration = dokkaConfiguration {
@@ -157,6 +158,7 @@ class ConstructorsSignaturesTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptors("Order of constructors is different in K2")
@Test
fun `class with a parameterless secondary constructor`() {
testInline(
@@ -227,6 +229,7 @@ class ConstructorsSignaturesTest : BaseAbstractTest() {
}
+ @OnlyDescriptors("Order of constructors is different in K2")
@Test
fun `class with a few documented constructors`() {
testInline(
diff --git a/plugins/base/src/test/kotlin/filter/JavaVisibilityFilterTest.kt b/plugins/base/src/test/kotlin/filter/JavaVisibilityFilterTest.kt
index efab9aba..6423655a 100644
--- a/plugins/base/src/test/kotlin/filter/JavaVisibilityFilterTest.kt
+++ b/plugins/base/src/test/kotlin/filter/JavaVisibilityFilterTest.kt
@@ -11,8 +11,10 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import testApi.testRunner.dokkaConfiguration
+import utils.JavaCode
import kotlin.test.assertEquals
+@JavaCode
class JavaVisibilityFilterTest : BaseAbstractTest() {
@Test
diff --git a/plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt b/plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt
index f4ef85b9..af77d61c 100644
--- a/plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt
+++ b/plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt
@@ -8,6 +8,7 @@ import org.jetbrains.dokka.model.GenericTypeConstructor
import org.jetbrains.dokka.model.Invariance
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
+import utils.OnlyDescriptors
class KotlinArrayDocumentableReplacerTest : BaseAbstractTest() {
private val configuration = dokkaConfiguration {
@@ -157,6 +158,8 @@ class KotlinArrayDocumentableReplacerTest : BaseAbstractTest() {
}
}
}
+
+ @OnlyDescriptors("Fix module.contentScope in new Standalone API") // TODO fix module.contentScope [getKtModuleForKtElement]
@Test
fun `no jvm source set`() {
val configurationWithNoJVM = dokkaConfiguration {
diff --git a/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt
index be75e01f..ccf62641 100644
--- a/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt
+++ b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt
@@ -10,6 +10,8 @@ import org.jetbrains.dokka.pages.*
import org.jsoup.Jsoup
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
+import utils.OnlyDescriptors
+import utils.OnlyDescriptorsMPP
import utils.TestOutputWriterPlugin
import utils.assertNotNull
import java.net.URL
@@ -18,6 +20,7 @@ import kotlin.test.assertEquals
class LinkableContentTest : BaseAbstractTest() {
+ @OnlyDescriptorsMPP
@Test
fun `Include module and package documentation`() {
@@ -143,6 +146,7 @@ class LinkableContentTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptorsMPP
@Test
fun `Samples multiplatform documentation`() {
diff --git a/plugins/base/src/test/kotlin/linking/EnumValuesLinkingTest.kt b/plugins/base/src/test/kotlin/linking/EnumValuesLinkingTest.kt
index 14875832..9ba428d6 100644
--- a/plugins/base/src/test/kotlin/linking/EnumValuesLinkingTest.kt
+++ b/plugins/base/src/test/kotlin/linking/EnumValuesLinkingTest.kt
@@ -11,11 +11,13 @@ import org.jsoup.Jsoup
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Test
+import utils.OnlyDescriptors
import utils.TestOutputWriterPlugin
import java.nio.file.Paths
class EnumValuesLinkingTest : BaseAbstractTest() {
+ @OnlyDescriptors // TODO
@Test
fun `check if enum values are correctly linked`() {
val writerPlugin = TestOutputWriterPlugin()
diff --git a/plugins/base/src/test/kotlin/markdown/LinkTest.kt b/plugins/base/src/test/kotlin/markdown/LinkTest.kt
index 526ff0eb..8ee5a20d 100644
--- a/plugins/base/src/test/kotlin/markdown/LinkTest.kt
+++ b/plugins/base/src/test/kotlin/markdown/LinkTest.kt
@@ -11,8 +11,10 @@ import org.jetbrains.dokka.pages.MemberPageNode
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Test
+import utils.UsingJDK
class LinkTest : BaseAbstractTest() {
+ @UsingJDK
@Test
fun linkToClassLoader() {
val configuration = dokkaConfiguration {
diff --git a/plugins/base/src/test/kotlin/markdown/ParserTest.kt b/plugins/base/src/test/kotlin/markdown/ParserTest.kt
index 41b086ee..e10a2260 100644
--- a/plugins/base/src/test/kotlin/markdown/ParserTest.kt
+++ b/plugins/base/src/test/kotlin/markdown/ParserTest.kt
@@ -1473,6 +1473,7 @@ class ParserTest : KDocTest() {
executeTest(kdoc, expectedDocumentationNode)
}
+
@Test
fun `exception thrown by empty header should point to location of a file`() {
val kdoc = """
@@ -1481,9 +1482,10 @@ class ParserTest : KDocTest() {
val expectedDocumentationNode = DocumentationNode(emptyList())
val exception = runCatching { executeTest(kdoc, expectedDocumentationNode) }.exceptionOrNull()
- assertEquals(
- "Wrong AST Tree. Header does not contain expected content in Test.kt/example.Test, element starts from offset 0 and ends 3: ###",
- exception?.message
+ val expectedMessage = "Wrong AST Tree. Header does not contain expected content in Test.kt/example.Test, element starts from offset 0 and ends 3: ###"
+ assert(
+ exception?.message == expectedMessage
+ || /* for K2 */ exception?.cause?.cause?.message == expectedMessage
)
}
diff --git a/plugins/base/src/test/kotlin/model/ClassesTest.kt b/plugins/base/src/test/kotlin/model/ClassesTest.kt
index 807ede78..e358945e 100644
--- a/plugins/base/src/test/kotlin/model/ClassesTest.kt
+++ b/plugins/base/src/test/kotlin/model/ClassesTest.kt
@@ -7,10 +7,7 @@ import org.jetbrains.dokka.model.*
import org.jetbrains.dokka.model.KotlinModifier.*
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
-import utils.AbstractModelTest
-import utils.assertNotNull
-import utils.name
-import utils.supers
+import utils.*
class ClassesTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "classes") {
@@ -411,6 +408,7 @@ class ClassesTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "class
}
}
+ @OnlyDescriptors("Bug in descriptors, DRI of entry should have [EnumEntryDRIExtra]")
@Test
fun javaAnnotationClass() {
inlineModelTest(
@@ -523,6 +521,7 @@ class ClassesTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "class
}
}
+ @UsingJDK
@Test
fun doublyTypealiasedException() {
inlineModelTest(
diff --git a/plugins/base/src/test/kotlin/model/ExtensionsTest.kt b/plugins/base/src/test/kotlin/model/ExtensionsTest.kt
index e28b442f..b54f8deb 100644
--- a/plugins/base/src/test/kotlin/model/ExtensionsTest.kt
+++ b/plugins/base/src/test/kotlin/model/ExtensionsTest.kt
@@ -8,6 +8,7 @@ import org.jetbrains.dokka.model.Documentable
import org.jetbrains.dokka.model.properties.WithExtraProperties
import org.junit.jupiter.api.Test
import utils.AbstractModelTest
+import utils.UsingJDK
class ExtensionsTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "classes") {
private fun <T : WithExtraProperties<R>, R : Documentable> T.checkExtension(name: String = "extension") =
@@ -66,6 +67,7 @@ class ExtensionsTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "cl
}
}
+ @UsingJDK
@Test
fun `should be extension for external classes`() {
inlineModelTest(
@@ -85,7 +87,7 @@ class ExtensionsTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "cl
}
}
}
-
+
@Test
fun `should be extension for typealias`() {
inlineModelTest(
diff --git a/plugins/base/src/test/kotlin/model/FunctionsTest.kt b/plugins/base/src/test/kotlin/model/FunctionsTest.kt
index fa65a477..3410f9ef 100644
--- a/plugins/base/src/test/kotlin/model/FunctionsTest.kt
+++ b/plugins/base/src/test/kotlin/model/FunctionsTest.kt
@@ -3,10 +3,7 @@ package model
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.*
import org.junit.jupiter.api.Test
-import utils.AbstractModelTest
-import utils.assertNotNull
-import utils.comments
-import utils.name
+import utils.*
class FunctionTest : AbstractModelTest("/src/main/kotlin/function/Test.kt", "function") {
@@ -213,6 +210,7 @@ class FunctionTest : AbstractModelTest("/src/main/kotlin/function/Test.kt", "fun
}
}
+ @OnlyDescriptors("Bug in descriptors, DRI of entry should have [EnumEntryDRIExtra]")
@Test
fun functionWithAnnotatedParam() {
inlineModelTest(
@@ -274,6 +272,7 @@ class FunctionTest : AbstractModelTest("/src/main/kotlin/function/Test.kt", "fun
}
}
+ @OnlyDescriptors("Bug in descriptors, DRI of entry should have [EnumEntryDRIExtra]")
@Test
fun annotatedFunctionWithAnnotationParameters() {
inlineModelTest(
diff --git a/plugins/base/src/test/kotlin/model/JavaTest.kt b/plugins/base/src/test/kotlin/model/JavaTest.kt
index f57c3c8c..a0605a5e 100644
--- a/plugins/base/src/test/kotlin/model/JavaTest.kt
+++ b/plugins/base/src/test/kotlin/model/JavaTest.kt
@@ -9,12 +9,11 @@ import org.jetbrains.dokka.model.doc.Param
import org.jetbrains.dokka.model.doc.Text
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
-import utils.AbstractModelTest
+import utils.*
import utils.assertContains
-import utils.assertNotNull
-import utils.name
import kotlin.test.assertEquals
+@JavaCode
class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") {
val configuration = dokkaConfiguration {
sourceSets {
diff --git a/plugins/base/src/test/kotlin/model/MultiLanguageInheritanceTest.kt b/plugins/base/src/test/kotlin/model/MultiLanguageInheritanceTest.kt
index 5fe17fc8..6f08c89d 100644
--- a/plugins/base/src/test/kotlin/model/MultiLanguageInheritanceTest.kt
+++ b/plugins/base/src/test/kotlin/model/MultiLanguageInheritanceTest.kt
@@ -9,9 +9,11 @@ import org.jetbrains.dokka.model.withDescendants
import org.jetbrains.dokka.utilities.firstIsInstanceOrNull
import org.junit.jupiter.api.Test
import translators.documentationOf
+import utils.JavaCode
import utils.docs
import kotlin.test.assertEquals
+@JavaCode
class MultiLanguageInheritanceTest : BaseAbstractTest() {
val configuration = dokkaConfiguration {
suppressObviousFunctions = false
diff --git a/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsForParametersTest.kt b/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsForParametersTest.kt
index d6564343..35997681 100644
--- a/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsForParametersTest.kt
+++ b/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsForParametersTest.kt
@@ -6,9 +6,11 @@ import org.jetbrains.dokka.model.*
import org.jetbrains.dokka.utilities.cast
import org.junit.jupiter.api.Test
import utils.AbstractModelTest
+import utils.JavaCode
import kotlin.test.assertEquals
import kotlin.test.assertTrue
+@JavaCode
class JavaAnnotationsForParametersTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") {
@Test
diff --git a/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsTest.kt b/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsTest.kt
index e704bf71..0abf504e 100644
--- a/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsTest.kt
+++ b/plugins/base/src/test/kotlin/model/annotations/JavaAnnotationsTest.kt
@@ -4,11 +4,13 @@ import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.model.*
import org.junit.jupiter.api.Test
import translators.findClasslike
+import utils.JavaCode
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
+@JavaCode
class JavaAnnotationsTest : BaseAbstractTest() {
val configuration = dokkaConfiguration {
diff --git a/plugins/base/src/test/kotlin/parsers/JavadocParserTest.kt b/plugins/base/src/test/kotlin/parsers/JavadocParserTest.kt
index f4615216..4b376c73 100644
--- a/plugins/base/src/test/kotlin/parsers/JavadocParserTest.kt
+++ b/plugins/base/src/test/kotlin/parsers/JavadocParserTest.kt
@@ -10,10 +10,12 @@ import org.jetbrains.dokka.model.doc.*
import org.jetbrains.dokka.utilities.firstIsInstanceOrNull
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
+import utils.JavaCode
import utils.docs
import utils.text
import kotlin.test.assertNotNull
+@JavaCode
class JavadocParserTest : BaseAbstractTest() {
private val configuration = dokkaConfiguration {
diff --git a/plugins/base/src/test/kotlin/renderers/html/NavigationIconTest.kt b/plugins/base/src/test/kotlin/renderers/html/NavigationIconTest.kt
index 5e2560bf..fb2c53cd 100644
--- a/plugins/base/src/test/kotlin/renderers/html/NavigationIconTest.kt
+++ b/plugins/base/src/test/kotlin/renderers/html/NavigationIconTest.kt
@@ -3,6 +3,7 @@ package renderers.html
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.junit.jupiter.api.Test
import utils.TestOutputWriterPlugin
+import utils.UsingJDK
import utils.navigationHtml
import utils.selectNavigationGrid
import kotlin.test.assertEquals
@@ -182,6 +183,7 @@ class NavigationIconTest : BaseAbstractTest() {
)
}
+ @UsingJDK
@Test
fun `should add icon styles to kotlin exception class navigation item`() {
assertNavigationIcon(
diff --git a/plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt b/plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt
index ca287216..af10cbee 100644
--- a/plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt
+++ b/plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt
@@ -1,8 +1,11 @@
package signatures
import org.junit.jupiter.api.Test
+import utils.OnlyDescriptors
+import utils.OnlyDescriptorsMPP
import utils.TestOutputWriterPlugin
+@OnlyDescriptorsMPP
class DivergentSignatureTest : AbstractRenderingTest() {
@Test
diff --git a/plugins/base/src/test/kotlin/signatures/FunctionalTypeConstructorsSignatureTest.kt b/plugins/base/src/test/kotlin/signatures/FunctionalTypeConstructorsSignatureTest.kt
index c9787b67..588b3d50 100644
--- a/plugins/base/src/test/kotlin/signatures/FunctionalTypeConstructorsSignatureTest.kt
+++ b/plugins/base/src/test/kotlin/signatures/FunctionalTypeConstructorsSignatureTest.kt
@@ -5,10 +5,7 @@ import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.jdk
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
-import utils.A
-import utils.Span
-import utils.TestOutputWriterPlugin
-import utils.match
+import utils.*
class FunctionalTypeConstructorsSignatureTest : BaseAbstractTest() {
private val configuration = dokkaConfiguration {
@@ -254,6 +251,7 @@ class FunctionalTypeConstructorsSignatureTest : BaseAbstractTest() {
}
}
+ @JavaCode
@Test
fun `java with java function`() {
val source = """
@@ -280,6 +278,7 @@ class FunctionalTypeConstructorsSignatureTest : BaseAbstractTest() {
}
}
+ @JavaCode
@Test
fun `java with kotlin function`() {
val source = """
diff --git a/plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt b/plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt
index 0767b2df..4cd9a94d 100644
--- a/plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt
+++ b/plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt
@@ -3,12 +3,10 @@ package signatures
import org.jetbrains.dokka.DokkaConfiguration
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.junit.jupiter.api.Test
-import utils.A
-import utils.Span
-import utils.TestOutputWriterPlugin
-import utils.match
+import utils.*
import kotlin.test.assertEquals
+@JavaCode
class InheritedAccessorsSignatureTest : BaseAbstractTest() {
private val configuration = dokkaConfiguration {
@@ -24,6 +22,7 @@ class InheritedAccessorsSignatureTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptors("'var' expected but found: 'open var'")
@Test
fun `should collapse accessor functions inherited from java into the property`() {
val writerPlugin = TestOutputWriterPlugin()
@@ -76,6 +75,7 @@ class InheritedAccessorsSignatureTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptors("'var' expected but found: 'open var'")
@Test
fun `should render as val if inherited java property has no setter`() {
val writerPlugin = TestOutputWriterPlugin()
@@ -178,6 +178,7 @@ class InheritedAccessorsSignatureTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptors("'var' expected but found: 'open var'")
@Test
fun `should keep inherited java accessor lookalikes if underlying function is public`() {
val writerPlugin = TestOutputWriterPlugin()
@@ -228,6 +229,7 @@ class InheritedAccessorsSignatureTest : BaseAbstractTest() {
}
}
+ @JavaCode
@Test
fun `should keep kotlin property with no accessors when java inherits kotlin a var`() {
val writerPlugin = TestOutputWriterPlugin()
@@ -265,6 +267,7 @@ class InheritedAccessorsSignatureTest : BaseAbstractTest() {
}
}
+ @JavaCode
@Test
fun `kotlin property with compute get and set`() {
val writerPlugin = TestOutputWriterPlugin()
@@ -326,6 +329,7 @@ class InheritedAccessorsSignatureTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptors("'var' expected but found: 'open var'")
@Test
fun `inherited property should inherit getter's visibility`() {
val configWithProtectedVisibility = dokkaConfiguration {
@@ -399,6 +403,7 @@ class InheritedAccessorsSignatureTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptors("'var' expected but found: 'open var'")
@Test
fun `should resolve protected java property as protected`() {
val configWithProtectedVisibility = dokkaConfiguration {
diff --git a/plugins/base/src/test/kotlin/signatures/SignatureTest.kt b/plugins/base/src/test/kotlin/signatures/SignatureTest.kt
index 38ae2be3..00d98102 100644
--- a/plugins/base/src/test/kotlin/signatures/SignatureTest.kt
+++ b/plugins/base/src/test/kotlin/signatures/SignatureTest.kt
@@ -549,6 +549,7 @@ class SignatureTest : BaseAbstractTest() {
}
}
}
+ @OnlyDescriptorsMPP
@Test
fun `actual typealias should have generic parameters and fully qualified name of the expansion type`() {
val writerPlugin = TestOutputWriterPlugin()
@@ -583,6 +584,7 @@ class SignatureTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptorsMPP
@Test
fun `type with an actual typealias`() {
val writerPlugin = TestOutputWriterPlugin()
@@ -763,6 +765,7 @@ class SignatureTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptors("Order of constructors is different in K2")
@Test
fun `generic constructor params`() {
val writerPlugin = TestOutputWriterPlugin()
@@ -977,6 +980,7 @@ class SignatureTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptors("'var' expected but found: 'open var'")
@Test
fun `java property without accessors should be var`() {
val writerPlugin = TestOutputWriterPlugin()
diff --git a/plugins/base/src/test/kotlin/superFields/DescriptorSuperPropertiesTest.kt b/plugins/base/src/test/kotlin/superFields/DescriptorSuperPropertiesTest.kt
index a189894c..14c2752a 100644
--- a/plugins/base/src/test/kotlin/superFields/DescriptorSuperPropertiesTest.kt
+++ b/plugins/base/src/test/kotlin/superFields/DescriptorSuperPropertiesTest.kt
@@ -7,10 +7,12 @@ import org.jetbrains.dokka.model.InheritedMember
import org.jetbrains.dokka.model.IsVar
import org.jetbrains.dokka.model.KotlinVisibility
import org.junit.jupiter.api.Test
+import utils.JavaCode
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
+@JavaCode
class DescriptorSuperPropertiesTest : BaseAbstractTest() {
private val commonTestConfiguration = dokkaConfiguration {
@@ -173,6 +175,7 @@ class DescriptorSuperPropertiesTest : BaseAbstractTest() {
}
}
+ // incorrect test https://github.com/Kotlin/dokka/issues/3128
@Test
fun `kotlin inheriting java should not append anything since field is public api`() {
val configuration = dokkaConfiguration {
diff --git a/plugins/base/src/test/kotlin/superFields/PsiSuperFieldsTest.kt b/plugins/base/src/test/kotlin/superFields/PsiSuperFieldsTest.kt
index 9c1265a6..5c7124cd 100644
--- a/plugins/base/src/test/kotlin/superFields/PsiSuperFieldsTest.kt
+++ b/plugins/base/src/test/kotlin/superFields/PsiSuperFieldsTest.kt
@@ -9,9 +9,11 @@ import org.jetbrains.dokka.model.isJvmField
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
+import utils.JavaCode
import kotlin.test.assertEquals
+@JavaCode
class PsiSuperFieldsTest : BaseAbstractTest() {
private val commonTestConfiguration = dokkaConfiguration {
diff --git a/plugins/base/src/test/kotlin/transformers/InheritedEntriesDocumentableFilterTransfromerTest.kt b/plugins/base/src/test/kotlin/transformers/InheritedEntriesDocumentableFilterTransfromerTest.kt
index 9cde40a5..826df64e 100644
--- a/plugins/base/src/test/kotlin/transformers/InheritedEntriesDocumentableFilterTransfromerTest.kt
+++ b/plugins/base/src/test/kotlin/transformers/InheritedEntriesDocumentableFilterTransfromerTest.kt
@@ -3,6 +3,7 @@ package transformers
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.model.DEnum
import org.junit.jupiter.api.Test
+import utils.OnlyDescriptors
import kotlin.test.assertEquals
import kotlin.test.assertTrue
@@ -133,6 +134,7 @@ class InheritedEntriesDocumentableFilterTransformerTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptors("Entry does not have `name` and `ordinal`") // TODO
@Test
fun `should work with enum entries when not suppressing`(){
testInline(
diff --git a/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt b/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt
index 39d725bb..e6cc393d 100644
--- a/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt
+++ b/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt
@@ -9,6 +9,7 @@ import org.jetbrains.dokka.model.dfs
import org.jetbrains.dokka.model.firstChildOfType
import org.jetbrains.dokka.pages.*
import org.junit.jupiter.api.Test
+import utils.OnlyDescriptors
import utils.assertNotNull
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
@@ -269,6 +270,7 @@ class MergeImplicitExpectActualDeclarationsTest : BaseAbstractTest() {
fun PageNode.childrenRec(): List<PageNode> = listOf(this) + children.flatMap { it.childrenRec() }
+ @OnlyDescriptors("Enum entry [SMTH] does not have functions") // TODO
@Test
fun `should merge enum entries`() {
testInline(
diff --git a/plugins/base/src/test/kotlin/transformers/ModuleAndPackageDocumentationTransformerFunctionalTest.kt b/plugins/base/src/test/kotlin/transformers/ModuleAndPackageDocumentationTransformerFunctionalTest.kt
index 85db2d27..134e7f59 100644
--- a/plugins/base/src/test/kotlin/transformers/ModuleAndPackageDocumentationTransformerFunctionalTest.kt
+++ b/plugins/base/src/test/kotlin/transformers/ModuleAndPackageDocumentationTransformerFunctionalTest.kt
@@ -5,11 +5,13 @@ import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import transformers.AbstractContextModuleAndPackageDocumentationReaderTest.Companion.texts
+import utils.OnlyDescriptorsMPP
import java.nio.file.Path
import kotlin.test.assertEquals
class ModuleAndPackageDocumentationTransformerFunctionalTest : BaseAbstractTest() {
+ @OnlyDescriptorsMPP
@Test
fun `multiplatform project`(@TempDir tempDir: Path) {
val include = tempDir.resolve("include.md").toFile()
diff --git a/plugins/base/src/test/kotlin/transformers/ObviousAndInheritedFunctionsDocumentableFilterTest.kt b/plugins/base/src/test/kotlin/transformers/ObviousAndInheritedFunctionsDocumentableFilterTest.kt
index 3618c8fb..ffb5c9c8 100644
--- a/plugins/base/src/test/kotlin/transformers/ObviousAndInheritedFunctionsDocumentableFilterTest.kt
+++ b/plugins/base/src/test/kotlin/transformers/ObviousAndInheritedFunctionsDocumentableFilterTest.kt
@@ -5,6 +5,7 @@ import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import testApi.testRunner.dokkaConfiguration
+import utils.JavaCode
import kotlin.test.assertEquals
class ObviousAndInheritedFunctionsDocumentableFilterTest : BaseAbstractTest() {
@@ -196,6 +197,7 @@ class ObviousAndInheritedFunctionsDocumentableFilterTest : BaseAbstractTest() {
@ParameterizedTest
@MethodSource(value = ["nonSuppressingObviousConfiguration", "nonSuppressingInheritedConfiguration"])
+ @JavaCode
fun `should not suppress toString, equals and hashcode if custom config is provided in Java`(nonSuppressingConfiguration: DokkaConfigurationImpl) {
testInline(
"""
diff --git a/plugins/base/src/test/kotlin/transformers/SourceLinkTransformerTest.kt b/plugins/base/src/test/kotlin/transformers/SourceLinkTransformerTest.kt
index 469c1a1e..532c9e81 100644
--- a/plugins/base/src/test/kotlin/transformers/SourceLinkTransformerTest.kt
+++ b/plugins/base/src/test/kotlin/transformers/SourceLinkTransformerTest.kt
@@ -6,6 +6,7 @@ import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jsoup.nodes.Element
import org.junit.jupiter.api.Test
import signatures.renderedContent
+import utils.OnlyDescriptorsMPP
import utils.TestOutputWriterPlugin
import java.net.URL
import kotlin.test.assertEquals
@@ -66,6 +67,7 @@ class SourceLinkTransformerTest : BaseAbstractTest() {
}
}
+ @OnlyDescriptorsMPP
@Test
fun `source link should be for actual typealias`() {
val mppConfiguration = dokkaConfiguration {
diff --git a/plugins/base/src/test/kotlin/transformers/isExceptionTest.kt b/plugins/base/src/test/kotlin/transformers/isExceptionTest.kt
index ba00cf15..dd888ad6 100644
--- a/plugins/base/src/test/kotlin/transformers/isExceptionTest.kt
+++ b/plugins/base/src/test/kotlin/transformers/isExceptionTest.kt
@@ -5,8 +5,11 @@ import org.jetbrains.dokka.model.DClass
import org.jetbrains.dokka.model.DTypeAlias
import org.junit.jupiter.api.Test
import utils.AbstractModelTest
+import utils.JavaCode
+import utils.UsingJDK
class IsExceptionKotlinTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "classes") {
+ @UsingJDK
@Test
fun `isException should work for kotlin exception`(){
inlineModelTest(
@@ -20,6 +23,7 @@ class IsExceptionKotlinTest : AbstractModelTest("/src/main/kotlin/classes/Test.k
}
}
+ @UsingJDK
@Test
fun `isException should work for java exceptions`(){
inlineModelTest(
@@ -33,6 +37,7 @@ class IsExceptionKotlinTest : AbstractModelTest("/src/main/kotlin/classes/Test.k
}
}
+ @UsingJDK
@Test
fun `isException should work for RuntimeException`(){
inlineModelTest(
@@ -46,6 +51,7 @@ class IsExceptionKotlinTest : AbstractModelTest("/src/main/kotlin/classes/Test.k
}
}
+ @UsingJDK
@Test
fun `isException should work if exception is typealiased`(){
inlineModelTest(
@@ -59,6 +65,7 @@ class IsExceptionKotlinTest : AbstractModelTest("/src/main/kotlin/classes/Test.k
}
}
+ @UsingJDK
@Test
fun `isException should work if exception is extending a typaliased class`(){
inlineModelTest(
@@ -100,6 +107,7 @@ class IsExceptionKotlinTest : AbstractModelTest("/src/main/kotlin/classes/Test.k
}
}
+@JavaCode
class IsExceptionJavaTest: AbstractModelTest("/src/main/kotlin/java/Test.java", "java") {
@Test
fun `isException should work for java exceptions`(){
diff --git a/plugins/base/src/test/kotlin/translators/Bug1341.kt b/plugins/base/src/test/kotlin/translators/Bug1341.kt
index a8c9e342..3e1b403d 100644
--- a/plugins/base/src/test/kotlin/translators/Bug1341.kt
+++ b/plugins/base/src/test/kotlin/translators/Bug1341.kt
@@ -4,8 +4,10 @@ import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.links.DRI
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
+import utils.JavaCode
class Bug1341 : BaseAbstractTest() {
+ @JavaCode
@Test
fun `reproduce bug #1341`() {
val configuration = dokkaConfiguration {
diff --git a/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt b/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt
index 57165fd1..4ae8f7ac 100644
--- a/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt
+++ b/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt
@@ -10,6 +10,7 @@ import org.jetbrains.dokka.model.doc.*
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
+import utils.OnlyDescriptors
import utils.text
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
@@ -980,6 +981,7 @@ val soapXml = node("soap-env:Envelope", soapAttrs,
}
}
+ @OnlyDescriptors("Fix kdoc link") // TODO
@Test
fun `should have documentation for synthetic Enum valueOf functions`() {
testInline(
diff --git a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt
index a763cbd2..b3f83d79 100644
--- a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt
+++ b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt
@@ -12,7 +12,9 @@ import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement
import org.jetbrains.dokka.DokkaConfiguration.Visibility
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
+import utils.JavaCode
+@JavaCode
class DefaultPsiToDocumentableTranslatorTest : BaseAbstractTest() {
val configuration = dokkaConfiguration {
sourceSets {
diff --git a/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt b/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt
index a9c865b4..c4087b20 100644
--- a/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt
+++ b/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt
@@ -10,8 +10,11 @@ import org.jetbrains.dokka.analysis.kotlin.internal.ExternalDocumentablesProvide
import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
+import utils.OnlyDescriptors
+import utils.UsingJDK
class ExternalDocumentablesTest : BaseAbstractTest() {
+ @UsingJDK
@Test
fun `external documentable from java stdlib`() {
val configuration = dokkaConfiguration {
@@ -54,6 +57,10 @@ class ExternalDocumentablesTest : BaseAbstractTest() {
}
}
+
+ // typealias CompletionHandler = (cause: Throwable?) -> Unit
+ // FunctionalTypeConstructor(dri=kotlinx.coroutines/CompletionHandler///PointingToDeclaration/, projections=[], isExtensionFunction=false, isSuspendable=false, presentableName=null, extra=PropertyContainer(map={}))
+ @OnlyDescriptors(reason = "FunctionType has not parameters") // TODO
@Test
fun `external documentable from dependency`() {
val coroutinesPath =
diff --git a/plugins/base/src/test/kotlin/translators/JavadocInheritDocsTest.kt b/plugins/base/src/test/kotlin/translators/JavadocInheritDocsTest.kt
index 7fc6b7fa..a357491f 100644
--- a/plugins/base/src/test/kotlin/translators/JavadocInheritDocsTest.kt
+++ b/plugins/base/src/test/kotlin/translators/JavadocInheritDocsTest.kt
@@ -8,7 +8,9 @@ import org.jetbrains.dokka.model.doc.Text
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
+import utils.JavaCode
+@JavaCode
class JavadocInheritDocsTest : BaseAbstractTest() {
val configuration = dokkaConfiguration {
sourceSets {
@@ -211,6 +213,7 @@ class JavadocInheritDocsTest : BaseAbstractTest() {
}
+ @JavaCode
@Test
fun `work with multiple supertypes`() {
testInline(
diff --git a/plugins/base/src/test/kotlin/translators/JavadocInheritedDocTagsTest.kt b/plugins/base/src/test/kotlin/translators/JavadocInheritedDocTagsTest.kt
index ba0d95d5..1e3d784a 100644
--- a/plugins/base/src/test/kotlin/translators/JavadocInheritedDocTagsTest.kt
+++ b/plugins/base/src/test/kotlin/translators/JavadocInheritedDocTagsTest.kt
@@ -7,9 +7,11 @@ import org.jetbrains.dokka.model.DModule
import org.jetbrains.dokka.model.doc.*
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
+import utils.JavaCode
import org.jetbrains.dokka.model.doc.Deprecated as DokkaDeprecatedTag
import org.jetbrains.dokka.model.doc.Throws as DokkaThrowsTag
+@JavaCode
class JavadocInheritedDocTagsTest : BaseAbstractTest() {
@Suppress("DEPRECATION") // for includeNonPublic
private val configuration = dokkaConfiguration {
diff --git a/plugins/base/src/test/kotlin/translators/JavadocParserTest.kt b/plugins/base/src/test/kotlin/translators/JavadocParserTest.kt
index 2c1173c0..8bc307df 100644
--- a/plugins/base/src/test/kotlin/translators/JavadocParserTest.kt
+++ b/plugins/base/src/test/kotlin/translators/JavadocParserTest.kt
@@ -9,8 +9,10 @@ import org.jetbrains.dokka.model.firstChildOfType
import org.jetbrains.dokka.model.firstMemberOfType
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
+import utils.JavaCode
import utils.text
+@JavaCode
class JavadocParserTest : BaseAbstractTest() {
private fun performJavadocTest(testOperation: (DModule) -> Unit) {
diff --git a/plugins/base/src/test/kotlin/utils/TagsAnnotations.kt b/plugins/base/src/test/kotlin/utils/TagsAnnotations.kt
new file mode 100644
index 00000000..181d1f1d
--- /dev/null
+++ b/plugins/base/src/test/kotlin/utils/TagsAnnotations.kt
@@ -0,0 +1,71 @@
+package utils
+
+import org.junit.jupiter.api.Tag
+
+
+/**
+ * Run a test only for descriptors, not symbols.
+ *
+ * In theory, these tests can be fixed
+ */
+@Target(
+ AnnotationTarget.CLASS,
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.PROPERTY_GETTER,
+ AnnotationTarget.PROPERTY_SETTER
+)
+@Retention(
+ AnnotationRetention.RUNTIME
+)
+@Tag("onlyDescriptors")
+annotation class OnlyDescriptors(val reason: String = "")
+
+/**
+ * Run a test only for descriptors, not symbols.
+ *
+ * These tests cannot be fixed until Analysis API does not support MPP
+ */
+@Target(
+ AnnotationTarget.CLASS,
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.PROPERTY_GETTER,
+ AnnotationTarget.PROPERTY_SETTER
+)
+@Retention(
+ AnnotationRetention.RUNTIME
+)
+@Tag("onlyDescriptorsMPP")
+annotation class OnlyDescriptorsMPP(val reason: String = "")
+
+
+/**
+ * For test containing .java code
+ * These tests are disabled in K2 due to Standlone prototype. https://github.com/Kotlin/dokka/issues/3114
+ */
+@Target(
+ AnnotationTarget.CLASS,
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.PROPERTY_GETTER,
+ AnnotationTarget.PROPERTY_SETTER
+)
+@Retention(
+ AnnotationRetention.RUNTIME
+)
+@Tag("javaCode")
+annotation class JavaCode
+
+/**
+ * For Kotlin test using JDK
+ * These tests are disabled in K2 due to Standlone prototype. https://github.com/Kotlin/dokka/issues/3114
+ */
+@Target(
+ AnnotationTarget.CLASS,
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.PROPERTY_GETTER,
+ AnnotationTarget.PROPERTY_SETTER
+)
+@Retention(
+ AnnotationRetention.RUNTIME
+)
+@Tag("usingJDK")
+annotation class UsingJDK \ No newline at end of file
diff --git a/plugins/mathjax/build.gradle.kts b/plugins/mathjax/build.gradle.kts
index 4c572450..1d10d838 100644
--- a/plugins/mathjax/build.gradle.kts
+++ b/plugins/mathjax/build.gradle.kts
@@ -3,6 +3,7 @@ import org.jetbrains.registerDokkaArtifactPublication
plugins {
id("org.jetbrains.conventions.kotlin-jvm")
id("org.jetbrains.conventions.maven-publish")
+ id("org.jetbrains.conventions.base-unit-test")
}
dependencies {
@@ -13,12 +14,17 @@ dependencies {
implementation(kotlin("reflect"))
testImplementation(libs.jsoup)
- testImplementation(projects.plugins.base.baseTestUtils)
testImplementation(projects.core.contentMatcherTestUtils)
testImplementation(kotlin("test-junit"))
testImplementation(projects.core.testApi)
testImplementation(platform(libs.junit.bom))
testImplementation(libs.junit.jupiter)
+
+ symbolsTestConfiguration(project(path = ":subprojects:analysis-kotlin-symbols", configuration = "shadow"))
+ descriptorsTestConfiguration(project(path = ":subprojects:analysis-kotlin-descriptors", configuration = "shadow"))
+ testImplementation(projects.plugins.base.baseTestUtils) {
+ exclude(module = "analysis-kotlin-descriptors")
+ }
}
registerDokkaArtifactPublication("mathjaxPlugin") {
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 2320ca69..d3e2cba2 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -67,8 +67,6 @@ include(
":subprojects:analysis-kotlin-descriptors:compiler",
":subprojects:analysis-kotlin-descriptors:ide",
":subprojects:analysis-kotlin-symbols",
- ":subprojects:analysis-kotlin-symbols:compiler",
- ":subprojects:analysis-kotlin-symbols:ide",
":subprojects:analysis-markdown-jb",
":runners:gradle-plugin",
diff --git a/subprojects/analysis-java-psi/api/analysis-java-psi.api b/subprojects/analysis-java-psi/api/analysis-java-psi.api
index 404249f8..6b4d444f 100644
--- a/subprojects/analysis-java-psi/api/analysis-java-psi.api
+++ b/subprojects/analysis-java-psi/api/analysis-java-psi.api
@@ -146,3 +146,7 @@ public final class org/jetbrains/dokka/analysis/java/util/PsiDocumentableSource
public final fun getPsi ()Lcom/intellij/psi/PsiNamedElement;
}
+public final class org/jetbrains/dokka/analysis/java/util/PsiUtilKt {
+ public static final fun from (Lorg/jetbrains/dokka/links/DRI$Companion;Lcom/intellij/psi/PsiElement;)Lorg/jetbrains/dokka/links/DRI;
+}
+
diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiUtil.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiUtil.kt
index ed58eb56..eb2058d8 100644
--- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiUtil.kt
+++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiUtil.kt
@@ -12,7 +12,8 @@ import org.jetbrains.dokka.utilities.firstIsInstanceOrNull
internal val PsiElement.parentsWithSelf: Sequence<PsiElement>
get() = generateSequence(this) { if (it is PsiFile) null else it.parent }
-internal fun DRI.Companion.from(psi: PsiElement) = psi.parentsWithSelf.run {
+@InternalDokkaApi
+fun DRI.Companion.from(psi: PsiElement) = psi.parentsWithSelf.run {
val psiMethod = firstIsInstanceOrNull<PsiMethod>()
val psiField = firstIsInstanceOrNull<PsiField>()
val classes = filterIsInstance<PsiClass>().filterNot { it is PsiTypeParameter }
diff --git a/subprojects/analysis-kotlin-symbols/api/analysis-kotlin-symbols.api b/subprojects/analysis-kotlin-symbols/api/analysis-kotlin-symbols.api
index e69de29b..4bddfcf1 100644
--- a/subprojects/analysis-kotlin-symbols/api/analysis-kotlin-symbols.api
+++ b/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/subprojects/analysis-kotlin-symbols/build.gradle.kts b/subprojects/analysis-kotlin-symbols/build.gradle.kts
index c000df58..9fddcde1 100644
--- a/subprojects/analysis-kotlin-symbols/build.gradle.kts
+++ b/subprojects/analysis-kotlin-symbols/build.gradle.kts
@@ -8,9 +8,74 @@ plugins {
}
dependencies {
- implementation(projects.subprojects.analysisKotlinApi)
- implementation(projects.subprojects.analysisKotlinSymbols.compiler)
- implementation(projects.subprojects.analysisKotlinSymbols.ide)
+ compileOnly(projects.core)
+ compileOnly(projects.subprojects.analysisKotlinApi)
+
+ implementation(projects.subprojects.analysisMarkdownJb)
+ implementation(projects.subprojects.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,
+ libs.kotlin.high.level.api.impl // for Standalone prototype
+ ).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
+ }
+ }
+ runtimeOnly(libs.kotlinx.collections.immutable)
+ implementation(libs.kotlin.compiler.k2) {
+ isTransitive = false
+ }
+
+ // TODO [beresnev] get rid of it
+ compileOnly(libs.kotlinx.coroutines.core)
}
tasks {
diff --git a/subprojects/analysis-kotlin-symbols/compiler/api/compiler.api b/subprojects/analysis-kotlin-symbols/compiler/api/compiler.api
deleted file mode 100644
index 39870f57..00000000
--- a/subprojects/analysis-kotlin-symbols/compiler/api/compiler.api
+++ /dev/null
@@ -1,4 +0,0 @@
-public final class org/jetbrains/dokka/analysis/kotlin/symbols/compiler/CompilerSymbolsAnalysisPlugin : org/jetbrains/dokka/plugability/DokkaPlugin {
- public fun <init> ()V
-}
-
diff --git a/subprojects/analysis-kotlin-symbols/compiler/build.gradle.kts b/subprojects/analysis-kotlin-symbols/compiler/build.gradle.kts
deleted file mode 100644
index 876d87ca..00000000
--- a/subprojects/analysis-kotlin-symbols/compiler/build.gradle.kts
+++ /dev/null
@@ -1,13 +0,0 @@
-plugins {
- id("org.jetbrains.conventions.kotlin-jvm")
-}
-
-dependencies {
- compileOnly(projects.core)
- compileOnly(projects.subprojects.analysisKotlinApi)
-
- // TODO
-
- // TODO [beresnev] get rid of it
- compileOnly(libs.kotlinx.coroutines.core)
-}
diff --git a/subprojects/analysis-kotlin-symbols/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/compiler/CompilerSymbolsAnalysisPlugin.kt b/subprojects/analysis-kotlin-symbols/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/compiler/CompilerSymbolsAnalysisPlugin.kt
deleted file mode 100644
index 4a39e0d8..00000000
--- a/subprojects/analysis-kotlin-symbols/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/compiler/CompilerSymbolsAnalysisPlugin.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.jetbrains.dokka.analysis.kotlin.symbols.compiler
-
-import org.jetbrains.dokka.plugability.DokkaPlugin
-import org.jetbrains.dokka.plugability.DokkaPluginApiPreview
-import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement
-
-class CompilerSymbolsAnalysisPlugin : DokkaPlugin() {
-
- @OptIn(DokkaPluginApiPreview::class)
- override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement
-}
diff --git a/subprojects/analysis-kotlin-symbols/compiler/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/subprojects/analysis-kotlin-symbols/compiler/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
deleted file mode 100644
index 47163f6e..00000000
--- a/subprojects/analysis-kotlin-symbols/compiler/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
+++ /dev/null
@@ -1 +0,0 @@
-org.jetbrains.dokka.analysis.kotlin.symbols.compiler.CompilerSymbolsAnalysisPlugin
diff --git a/subprojects/analysis-kotlin-symbols/ide/api/ide.api b/subprojects/analysis-kotlin-symbols/ide/api/ide.api
deleted file mode 100644
index 9be46c0b..00000000
--- a/subprojects/analysis-kotlin-symbols/ide/api/ide.api
+++ /dev/null
@@ -1,4 +0,0 @@
-public final class org/jetbrains/dokka/analysis/kotlin/symbols/ide/IdeSymbolsAnalysisPlugin : org/jetbrains/dokka/plugability/DokkaPlugin {
- public fun <init> ()V
-}
-
diff --git a/subprojects/analysis-kotlin-symbols/ide/build.gradle.kts b/subprojects/analysis-kotlin-symbols/ide/build.gradle.kts
deleted file mode 100644
index 876d87ca..00000000
--- a/subprojects/analysis-kotlin-symbols/ide/build.gradle.kts
+++ /dev/null
@@ -1,13 +0,0 @@
-plugins {
- id("org.jetbrains.conventions.kotlin-jvm")
-}
-
-dependencies {
- compileOnly(projects.core)
- compileOnly(projects.subprojects.analysisKotlinApi)
-
- // TODO
-
- // TODO [beresnev] get rid of it
- compileOnly(libs.kotlinx.coroutines.core)
-}
diff --git a/subprojects/analysis-kotlin-symbols/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/ide/IdeSymbolsAnalysisPlugin.kt b/subprojects/analysis-kotlin-symbols/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/ide/IdeSymbolsAnalysisPlugin.kt
deleted file mode 100644
index 33f555cb..00000000
--- a/subprojects/analysis-kotlin-symbols/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/ide/IdeSymbolsAnalysisPlugin.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.jetbrains.dokka.analysis.kotlin.symbols.ide
-
-import org.jetbrains.dokka.plugability.DokkaPlugin
-import org.jetbrains.dokka.plugability.DokkaPluginApiPreview
-import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement
-
-class IdeSymbolsAnalysisPlugin : DokkaPlugin() {
-
- @OptIn(DokkaPluginApiPreview::class)
- override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement
-}
diff --git a/subprojects/analysis-kotlin-symbols/ide/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/subprojects/analysis-kotlin-symbols/ide/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
deleted file mode 100644
index 59245578..00000000
--- a/subprojects/analysis-kotlin-symbols/ide/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
+++ /dev/null
@@ -1 +0,0 @@
-org.jetbrains.dokka.analysis.kotlin.symbols.ide.IdeSymbolsAnalysisPlugin
diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KDocProvider.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KDocProvider.kt
new file mode 100644
index 00000000..c1b8651a
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KDocProvider.kt
@@ -0,0 +1,170 @@
+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).logIfNotResolved(link.getLinkText(), logger) },
+ 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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KdocMarkdownParser.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KdocMarkdownParser.kt
new file mode 100644
index 00000000..ec23b0c8
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KdocMarkdownParser.kt
@@ -0,0 +1,97 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/ResolveKDocLink.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/ResolveKDocLink.kt
new file mode 100644
index 00000000..9d26d917
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/ResolveKDocLink.kt
@@ -0,0 +1,42 @@
+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
+
+internal fun DRI?.logIfNotResolved(link: String, logger: DokkaLogger): DRI? {
+ if(this == null)
+ logger.warn("Couldn't resolve link for $link")
+ return this
+}
+
+/**
+ * It resolves KDoc link via creating PSI.
+ *
+ */
+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) }
+}
+
+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 // logger.warn("Couldn't resolve link for $link")
+ else getDRIFromSymbol(linkedSymbol)
+} \ No newline at end of file
diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/SyntheticKDocProvider.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/SyntheticKDocProvider.kt
new file mode 100644
index 00000000..9b108f80
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/SyntheticKDocProvider.kt
@@ -0,0 +1,61 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorDocumentationContent.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorDocumentationContent.kt
new file mode 100644
index 00000000..a3651603
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorDocumentationContent.kt
@@ -0,0 +1,15 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorKotlinDocCommentCreator.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorKotlinDocCommentCreator.kt
new file mode 100644
index 00000000..399d005a
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/DescriptorKotlinDocCommentCreator.kt
@@ -0,0 +1,16 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocComment.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocComment.kt
new file mode 100644
index 00000000..385b5328
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocComment.kt
@@ -0,0 +1,81 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocCommentParser.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocCommentParser.kt
new file mode 100644
index 00000000..c8ecc786
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocCommentParser.kt
@@ -0,0 +1,46 @@
+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.logIfNotResolved
+import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.SymbolsAnalysisPlugin
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.parseFromKDocTag
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.resolveKDocLink
+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 }
+ return analyze(kotlinAnalysis[sourceSet].mainModule) {
+ parseFromKDocTag(
+ kDocTag = element.comment,
+ externalDri = { link -> resolveKDocLink(link).logIfNotResolved(link.getLinkText(), context.logger) },
+ kdocLocation = null,
+ parseWithChildren = parseWithChildren
+ )
+ }
+ }
+}
+
diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinInheritDocTagContentProvider.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinInheritDocTagContentProvider.kt
new file mode 100644
index 00000000..6d2bfff4
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinInheritDocTagContentProvider.kt
@@ -0,0 +1,31 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/IllegalModuleAndPackageDocumentation.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/IllegalModuleAndPackageDocumentation.kt
new file mode 100644
index 00000000..6e3f215a
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/IllegalModuleAndPackageDocumentation.kt
@@ -0,0 +1,7 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentation.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentation.kt
new file mode 100644
index 00000000..46f714cb
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentation.kt
@@ -0,0 +1,11 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationFragment.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationFragment.kt
new file mode 100644
index 00000000..1eafc688
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationFragment.kt
@@ -0,0 +1,9 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationParsingContext.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationParsingContext.kt
new file mode 100644
index 00000000..cf627e77
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationParsingContext.kt
@@ -0,0 +1,47 @@
+package org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.moduledocs
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.logIfNotResolved
+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 analysisContext = kotlinAnalysis[sourceSet]
+ analyze(analysisContext.mainModule) {
+ val contextSymbol = when (fragment.classifier) {
+ Module -> ROOT_PACKAGE_SYMBOL
+ Package -> getPackageSymbolIfPackageExists(FqName(fragment.name))
+ }
+
+ MarkdownParser(
+ externalDri = { resolveKDocTextLink(it, contextSymbol?.psi).logIfNotResolved(it, logger) },
+ sourceLocation
+ )
+ }
+ }
+}
diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationReader.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationReader.kt
new file mode 100644
index 00000000..82259b89
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationReader.kt
@@ -0,0 +1,110 @@
+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[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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationSource.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationSource.kt
new file mode 100644
index 00000000..d7877559
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationSource.kt
@@ -0,0 +1,14 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentation.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentation.kt
new file mode 100644
index 00000000..b92adf4a
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentation.kt
@@ -0,0 +1,12 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentationFragments.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentationFragments.kt
new file mode 100644
index 00000000..ae728a28
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/parseModuleAndPackageDocumentationFragments.kt
@@ -0,0 +1,55 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/AnalysisContext.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/AnalysisContext.kt
new file mode 100644
index 00000000..eb7c5d70
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/AnalysisContext.kt
@@ -0,0 +1,139 @@
+package org.jetbrains.dokka.analysis.kotlin.symbols.plugin
+
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.util.Disposer
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.DokkaSourceSetID
+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
+import java.io.File
+
+@Suppress("FunctionName", "UNUSED_PARAMETER")
+internal fun SamplesKotlinAnalysis(
+ sourceSets: List<DokkaConfiguration.DokkaSourceSet>,
+ context: DokkaContext,
+ projectKotlinAnalysis: KotlinAnalysis
+): KotlinAnalysis {
+ val environments = sourceSets
+ .filter { it.samples.isNotEmpty() }
+ .associateWith { sourceSet ->
+ createAnalysisContext(
+ classpath = sourceSet.classpath,
+ sourceRoots = sourceSet.samples,
+ sourceSet = sourceSet
+ )
+ }
+
+ return EnvironmentKotlinAnalysis(environments, projectKotlinAnalysis)
+}
+
+internal fun ProjectKotlinAnalysis(
+ sourceSets: List<DokkaConfiguration.DokkaSourceSet>,
+ context: DokkaContext,
+): KotlinAnalysis {
+ val environments = sourceSets.associateWith { sourceSet ->
+ createAnalysisContext(
+ context = context,
+ sourceSets = sourceSets,
+ sourceSet = sourceSet
+ )
+ }
+ return EnvironmentKotlinAnalysis(environments)
+}
+
+
+@Suppress("UNUSED_PARAMETER")
+internal fun createAnalysisContext(
+ context: DokkaContext,
+ sourceSets: List<DokkaConfiguration.DokkaSourceSet>,
+ sourceSet: DokkaConfiguration.DokkaSourceSet
+): AnalysisContext {
+ val parentSourceSets = sourceSets.filter { it.sourceSetID in sourceSet.dependentSourceSets }
+ val classpath = sourceSet.classpath + parentSourceSets.flatMap { it.classpath }
+ val sources = sourceSet.sourceRoots + parentSourceSets.flatMap { it.sourceRoots }
+
+ return createAnalysisContext(classpath, sources, sourceSet)
+}
+
+internal fun createAnalysisContext(
+ classpath: List<File>,
+ sourceRoots: Set<File>,
+ sourceSet: DokkaConfiguration.DokkaSourceSet
+): AnalysisContext {
+ val applicationDisposable: Disposable = Disposer.newDisposable("StandaloneAnalysisAPISession.application")
+ val projectDisposable: Disposable = Disposer.newDisposable("StandaloneAnalysisAPISession.project")
+
+ val analysis= createAnalysisSession(
+ classpath = classpath,
+ sourceRoots = sourceRoots,
+ analysisPlatform = sourceSet.analysisPlatform,
+ languageVersion = sourceSet.languageVersion,
+ apiVersion = sourceSet.apiVersion,
+ applicationDisposable = applicationDisposable,
+ projectDisposable = projectDisposable
+ )
+ return AnalysisContextImpl(
+ mainModule = analysis.second,
+ analysisSession = analysis.first,
+ applicationDisposable = applicationDisposable,
+ projectDisposable = projectDisposable
+ )
+}
+
+
+/**
+ * First child delegation. It does not close [parent].
+ */
+internal abstract class KotlinAnalysis(
+ private val parent: KotlinAnalysis? = null
+) : Closeable {
+
+ operator fun get(key: DokkaConfiguration.DokkaSourceSet): AnalysisContext {
+ return get(key.sourceSetID)
+ }
+
+ internal operator fun get(key: DokkaSourceSetID): AnalysisContext {
+ return find(key)
+ ?: parent?.get(key)
+ ?: throw IllegalStateException("Missing EnvironmentAndFacade for sourceSet $key")
+ }
+
+ internal abstract fun find(sourceSetID: DokkaSourceSetID): AnalysisContext?
+}
+
+internal open class EnvironmentKotlinAnalysis(
+ private val environments: SourceSetDependent<AnalysisContext>,
+ parent: KotlinAnalysis? = null,
+) : KotlinAnalysis(parent = parent) {
+
+ override fun find(sourceSetID: DokkaSourceSetID): AnalysisContext? =
+ environments.entries.firstOrNull { (sourceSet, _) -> sourceSet.sourceSetID == sourceSetID }?.value
+
+ override fun close() {
+ environments.values.forEach(AnalysisContext::close)
+ }
+}
+
+internal interface AnalysisContext: Closeable {
+ val project: Project
+ val mainModule: KtSourceModule
+}
+
+private class AnalysisContextImpl(
+ override val mainModule: KtSourceModule,
+ private val analysisSession: StandaloneAnalysisAPISession,
+ private val applicationDisposable: Disposable,
+ private val projectDisposable: Disposable
+) : AnalysisContext {
+ override val project: Project
+ get() = analysisSession.project
+
+ override fun close() {
+ Disposer.dispose(applicationDisposable)
+ Disposer.dispose(projectDisposable)
+ }
+} \ No newline at end of file
diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/KotlinAnalysis.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/KotlinAnalysis.kt
new file mode 100644
index 00000000..8b9e552d
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/KotlinAnalysis.kt
@@ -0,0 +1,245 @@
+package org.jetbrains.dokka.analysis.kotlin.symbols.plugin
+
+import com.intellij.core.CoreApplicationEnvironment
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.vfs.StandardFileSystems
+import com.intellij.openapi.vfs.VirtualFileManager
+import com.intellij.psi.PsiFileSystemItem
+import com.intellij.psi.PsiManager
+import com.intellij.psi.search.GlobalSearchScope
+import com.intellij.psi.search.ProjectScope
+import com.intellij.util.io.URLUtil
+import org.jetbrains.dokka.Platform
+import org.jetbrains.kotlin.analysis.api.impl.base.util.LibraryUtils
+import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider
+import org.jetbrains.kotlin.analysis.api.standalone.StandaloneAnalysisAPISession
+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.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
+import org.jetbrains.kotlin.config.*
+import org.jetbrains.kotlin.idea.KotlinFileType
+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 org.jetbrains.kotlin.psi.KtFile
+import java.io.File
+import java.io.IOException
+import java.nio.file.*
+import java.nio.file.attribute.BasicFileAttributes
+
+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(): File? {
+ val javaHome = File(System.getProperty("java.home"))
+ if (!javaHome.exists()) {
+ // messageCollector.report(CompilerMessageSeverity.WARNING, "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
+ )
+ )
+}
+
+// it should be changed after https://github.com/Kotlin/dokka/issues/3114
+internal fun createAnalysisSession(
+ classpath: List<File>,
+ sourceRoots: Set<File>,
+ analysisPlatform: Platform,
+ languageVersion: String?,
+ apiVersion: String?,
+ applicationDisposable: Disposable,
+ projectDisposable: Disposable
+): Pair<StandaloneAnalysisAPISession, KtSourceModule> {
+
+ var sourceModule: KtSourceModule? = null
+ val analysisSession = buildStandaloneAnalysisAPISession(
+ applicationDisposable = applicationDisposable,
+ projectDisposable = projectDisposable,
+ withPsiDeclarationFromBinaryModuleProvider = false
+ ) {
+ val project = project
+ val targetPlatform = analysisPlatform.toTargetPlatform()
+ fun KtModuleBuilder.addModuleDependencies(moduleName: String) {
+ val libraryRoots = classpath
+ addRegularDependency(
+ buildKtLibraryModule {
+ contentScope = ProjectScope.getLibrariesScope(project)
+ this.platform = targetPlatform
+ this.project = project
+ binaryRoots = libraryRoots.map { it.toPath() }
+ libraryName = "Library for $moduleName"
+ }
+ )
+ getJdkHomeFromSystemProperty()?.let { jdkHome ->
+ val vfm = VirtualFileManager.getInstance()
+ val jdkHomePath = jdkHome.toPath()
+ val jdkHomeVirtualFile = vfm.findFileByNioPath(jdkHome.toPath())//vfm.findFileByPath(jdkHomePath)
+ val binaryRoots = LibraryUtils.findClassesFromJdkHome(jdkHomePath).map {
+ Paths.get(URLUtil.extractPath(it))
+ }
+ addRegularDependency(
+ buildKtSdkModule {
+ contentScope = GlobalSearchScope.fileScope(project, jdkHomeVirtualFile)
+ this.platform = targetPlatform
+ this.project = project
+ this.binaryRoots = binaryRoots
+ sdkName = "JDK for $moduleName"
+ }
+ )
+ }
+ }
+ sourceModule = buildKtSourceModule {
+ this.languageVersionSettings = getLanguageVersionSettings(languageVersion, apiVersion)
+
+ //val fs = StandardFileSystems.local()
+ //val psiManager = PsiManager.getInstance(project)
+ // TODO: We should handle (virtual) file changes announced via LSP with the VFS
+ /*val ktFiles = sources
+ .flatMap { Files.walk(it).toList() }
+ .mapNotNull { fs.findFileByPath(it.toString()) }
+ .mapNotNull { psiManager.findFile(it) }
+ .map { it as KtFile }*/
+ val sourcePaths = sourceRoots.map { it.absolutePath }
+ val (ktFilePath, javaFilePath) = getSourceFilePaths(sourcePaths).partition { it.endsWith(KotlinFileType.EXTENSION) }
+ val javaFiles: List<PsiFileSystemItem> = getPsiFilesFromPaths(project, javaFilePath)
+ val ktFiles: List<KtFile> = getPsiFilesFromPaths(project, getSourceFilePaths(ktFilePath))
+ addSourceRoots(ktFiles + javaFiles)
+ contentScope = TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, ktFiles)
+ platform = targetPlatform
+ moduleName = "<module>"
+ this.project = project
+ addModuleDependencies(moduleName)
+ }
+
+ buildKtModuleProvider {
+ platform = targetPlatform
+ this.project = project
+ addModule(sourceModule!!)
+ }
+ }
+ // TODO remove further
+ CoreApplicationEnvironment.registerExtensionPoint(
+ analysisSession.project.extensionArea,
+ KtResolveExtensionProvider.EP_NAME.name,
+ KtResolveExtensionProvider::class.java
+ )
+ return Pair(analysisSession, sourceModule ?: throw IllegalStateException())
+}
+
+// ----------- copy-paste from Analysis API ----------------------------------------------------------------------------
+/**
+ * Collect source file path from the given [root] store them in [result].
+ *
+ * E.g., for `project/app/src` as a [root], this will walk the file tree and
+ * collect all `.kt` and `.java` files under that folder.
+ *
+ * Note that this util gracefully skips [IOException] during file tree traversal.
+ */
+internal fun collectSourceFilePaths(
+ root: Path,
+ result: MutableSet<String>
+) {
+ // NB: [Files#walk] throws an exception if there is an issue during IO.
+ // With [Files#walkFileTree] with a custom visitor, we can take control of exception handling.
+ Files.walkFileTree(
+ root,
+ object : SimpleFileVisitor<Path>() {
+ override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult {
+ return if (Files.isReadable(dir))
+ FileVisitResult.CONTINUE
+ else
+ FileVisitResult.SKIP_SUBTREE
+ }
+
+ override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
+ if (!Files.isRegularFile(file) || !Files.isReadable(file))
+ return FileVisitResult.CONTINUE
+ val ext = file.toFile().extension
+ if (ext == KotlinFileType.EXTENSION || ext == "java"/*JavaFileType.DEFAULT_EXTENSION*/) {
+ result.add(file.toString())
+ }
+ return FileVisitResult.CONTINUE
+ }
+
+ override fun visitFileFailed(file: Path, exc: IOException?): FileVisitResult {
+ // TODO: report or log [IOException]?
+ // NB: this intentionally swallows the exception, hence fail-safe.
+ // Skipping subtree doesn't make any sense, since this is not a directory.
+ // Skipping sibling may drop valid file paths afterward, so we just continue.
+ return FileVisitResult.CONTINUE
+ }
+ }
+ )
+}
+
+/**
+ * Collect source file path as [String] from the given source roots in [sourceRoot].
+ *
+ * this util collects all `.kt` and `.java` files under source roots.
+ */
+internal fun getSourceFilePaths(
+ sourceRoot: Collection<String>,
+ includeDirectoryRoot: Boolean = false,
+): Set<String> {
+ val result = mutableSetOf<String>()
+ sourceRoot.forEach { srcRoot ->
+ val path = Paths.get(srcRoot)
+ if (Files.isDirectory(path)) {
+ // E.g., project/app/src
+ collectSourceFilePaths(path, result)
+ if (includeDirectoryRoot) {
+ result.add(srcRoot)
+ }
+ } else {
+ // E.g., project/app/src/some/pkg/main.kt
+ result.add(srcRoot)
+ }
+ }
+
+ return result
+}
+
+internal inline fun <reified T : PsiFileSystemItem> getPsiFilesFromPaths(
+ project: Project,
+ paths: Collection<String>,
+): List<T> {
+ val fs = StandardFileSystems.local()
+ val psiManager = PsiManager.getInstance(project)
+ val result = mutableListOf<T>()
+ for (path in paths) {
+ val vFile = fs.findFileByPath(path) ?: continue
+ val psiFileSystemItem =
+ if (vFile.isDirectory)
+ psiManager.findDirectory(vFile) as? T
+ else
+ psiManager.findFile(vFile) as? T
+ psiFileSystemItem?.let { result.add(it) }
+ }
+ return result
+} \ No newline at end of file
diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/SymbolsAnalysisPlugin.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/SymbolsAnalysisPlugin.kt
new file mode 100644
index 00000000..71fdfadb
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/plugin/SymbolsAnalysisPlugin.kt
@@ -0,0 +1,121 @@
+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 { DescriptorKotlinToJavaMapper() }
+ }
+
+ intern val descriptorInheritanceBuilder by extending {
+ plugin<InternalKotlinAnalysisPlugin>().inheritanceBuilder providing { DescriptorInheritanceBuilder() }
+ }
+ */
+ 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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisProjectProvider.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisProjectProvider.kt
new file mode 100644
index 00000000..47bcb156
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisProjectProvider.kt
@@ -0,0 +1,16 @@
+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[sourceSet].project
+ }
+}
diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisSourceRootsExtractor.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisSourceRootsExtractor.kt
new file mode 100644
index 00000000..43aceccd
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinAnalysisSourceRootsExtractor.kt
@@ -0,0 +1,12 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinDocumentableSourceLanguageParser.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinDocumentableSourceLanguageParser.kt
new file mode 100644
index 00000000..799cd7b6
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinDocumentableSourceLanguageParser.kt
@@ -0,0 +1,26 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinSampleProvider.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinSampleProvider.kt
new file mode 100644
index 00000000..dc61b35b
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KotlinSampleProvider.kt
@@ -0,0 +1,85 @@
+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.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
+
+class KotlinSampleProviderFactory(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
+open class KotlinSampleProvider(val context: DokkaContext): SampleProvider {
+ private val kotlinAnalysis = SamplesKotlinAnalysis(
+ sourceSets = context.configuration.sourceSets,
+ context = context,
+ projectKotlinAnalysis = context.plugin<SymbolsAnalysisPlugin>().querySingle { kotlinAnalysis }
+ )
+
+ 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? {
+ val analysisContext = kotlinAnalysis[sourceSet]
+ val psiElement = analyze(analysisContext.mainModule) {
+ 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() {
+ kotlinAnalysis.close()
+ }
+} \ No newline at end of file
diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KtPsiDocumentableSource.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KtPsiDocumentableSource.kt
new file mode 100644
index 00000000..3a819931
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/KtPsiDocumentableSource.kt
@@ -0,0 +1,20 @@
+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)
+ }
+ }
+} \ No newline at end of file
diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolExternalDocumentablesProvider.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolExternalDocumentablesProvider.kt
new file mode 100644
index 00000000..c328fe57
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolExternalDocumentablesProvider.kt
@@ -0,0 +1,34 @@
+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)
+
+ val analysisContext = kotlinAnalysis[sourceSet]
+ return analyze(analysisContext.mainModule) {
+ val symbol = getClassOrObjectSymbolByClassId(classId) as? KtNamedClassOrObjectSymbol?: return@analyze null
+ val translator = DokkaSymbolVisitor(sourceSet, sourceSet.displayName, analysisContext, 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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolFullClassHierarchyBuilder.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolFullClassHierarchyBuilder.kt
new file mode 100644
index 00000000..d4269b11
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolFullClassHierarchyBuilder.kt
@@ -0,0 +1,85 @@
+package org.jetbrains.dokka.analysis.kotlin.symbols.services
+
+import com.intellij.psi.PsiClass
+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.kotlin.psi.KtClassOrObject
+import java.util.concurrent.ConcurrentHashMap
+
+
+internal class SymbolFullClassHierarchyBuilder : FullClassHierarchyBuilder {
+ 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
+ val supertypes = kotlinType.getDirectSuperTypes().filterNot { it.isAny }
+ val supertypesDriWithKType = supertypes.mapNotNull { supertype ->
+ supertype.expandedClassSymbol?.let {
+ getDRIFromClassLike(it) to supertype
+ }
+ }
+
+ if (supersMap[dri] == null) {
+ 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(psi) {
+ 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) }
+ }
+ }
+ }
+ }
+
+}
diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolSyntheticDocumentableDetector.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolSyntheticDocumentableDetector.kt
new file mode 100644
index 00000000..20d2508b
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/services/SymbolSyntheticDocumentableDetector.kt
@@ -0,0 +1,41 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt
new file mode 100644
index 00000000..02198518
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt
@@ -0,0 +1,134 @@
+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.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) }
+
+ /**
+ * @return direct annotations and file-level annotations
+ */
+ fun KtAnalysisSession.getAllAnnotationsFrom(annotated: KtAnnotated): List<Annotations.Annotation> {
+ val directAnnotations = getDirectAnnotationsFrom(annotated)
+ val fileLevelAnnotations = (annotated as? KtSymbol)?.let { getFileLevelAnnotationsFrom(it) } ?: emptyList()
+ return directAnnotations + fileLevelAnnotations
+ }
+
+ 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 -> TODO()
+ }
+
+ 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
+ */
+ 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() }
+ }
+} \ No newline at end of file
diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DRIFactory.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DRIFactory.kt
new file mode 100644
index 00000000..ed5ed532
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DRIFactory.kt
@@ -0,0 +1,140 @@
+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 ")
+ }
+
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt
new file mode 100644
index 00000000..0c79b8a0
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt
@@ -0,0 +1,949 @@
+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.plugin.AnalysisContext
+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[sourceSet]
+ @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: AnalysisContext,
+ 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 ktFiles: List<KtFile> = getPsiFilesFromPaths(
+ analysisContext.project,
+ getSourceFilePaths(sourceSet.sourceRoots.map { it.canonicalPath })
+ )
+ val processedPackages: MutableSet<FqName> = mutableSetOf()
+ return analyze(analysisContext.mainModule) {
+ 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, classlikes) = getDokkaScopeFrom(namedClassOrObjectSymbol, dri)
+
+ 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 = 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()),
+ 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 = 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()),
+ 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 = namedClassOrObjectSymbol.companionObject?.let {
+ visitNamedClassOrObjectSymbol(
+ it,
+ dri
+ )
+ } as? DObject,
+ 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 -> {
+ val entries = namedClassOrObjectSymbol.getEnumEntries().map { visitEnumEntrySymbol(it) }
+
+ 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 classlikes: List<DClasslike>
+ )
+ private fun KtAnalysisSession.getDokkaScopeFrom(
+ namedClassOrObjectSymbol: KtNamedClassOrObjectSymbol,
+ dri: DRI
+ ): DokkaScope {
+ // e.g. getStaticMemberScope contains `valueOf`, `values` and `entries` members for Enum
+ val scope = listOf(namedClassOrObjectSymbol.getMemberScope(), namedClassOrObjectSymbol.getStaticMemberScope()).asCompositeScope()
+ 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) }
+
+
+ // hack, by default, compiler adds an empty companion object for enum
+ // TODO check if it is empty
+ fun List<KtNamedClassOrObjectSymbol>.filterOutEnumCompanion() =
+ if (namedClassOrObjectSymbol.classKind == KtClassKind.ENUM_CLASS)
+ filterNot {
+ it.name.asString() == "Companion" && it.classKind == KtClassKind.COMPANION_OBJECT
+ } else this
+
+ 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>()
+ .filterOutEnumCompanion() // hack to filter out companion for enum
+ .filterOutAndMarkAlreadyVisited()
+ .map { visitNamedClassOrObjectSymbol(it, dri) }
+
+ return DokkaScope(
+ constructors = constructors,
+ functions = functions,
+ properties = properties,
+ classlikes = classlikes
+ )
+ }
+
+ private fun KtAnalysisSession.visitEnumEntrySymbol(
+ enumEntrySymbol: KtEnumEntrySymbol
+ ): DEnumEntry = withExceptionCatcher(enumEntrySymbol) {
+ val dri = getDRIFromEnumEntry(enumEntrySymbol)
+ val isExpect = false
+
+ val scope = enumEntrySymbol.getMemberScope()
+ val callables = scope.getCallableSymbols().toList()
+ val classifiers = scope.getClassifierSymbols().toList()
+
+ 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) }
+
+ return DEnumEntry(
+ dri = dri,
+ name = enumEntrySymbol.name.asString(),
+ documentation = getDocumentation(enumEntrySymbol)?.toSourceSetDependent() ?: emptyMap(),
+ functions = functions,
+ properties = properties,
+ classlikes = classlikes,
+ 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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TranslatorError.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TranslatorError.kt
new file mode 100644
index 00000000..8a8e2261
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TranslatorError.kt
@@ -0,0 +1,29 @@
+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
+ )
+ } \ No newline at end of file
diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeReferenceFactory.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeReferenceFactory.kt
new file mode 100644
index 00000000..5acfeb9e
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeReferenceFactory.kt
@@ -0,0 +1,78 @@
+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/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt
new file mode 100644
index 00000000..2b79498d
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt
@@ -0,0 +1,186 @@
+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(classSymbol.expandedType),
+ extra = PropertyContainer.withAll(
+ getDokkaAnnotationsFrom(classType)?.toSourceSetDependent()?.toAnnotations()
+ )
+ ) else
+ throw IllegalStateException("Expected type alias symbol in type")
+ }
+
+ private fun KtAnalysisSession.toTypeConstructorFrom(classType: KtUsualClassType) =
+ 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 -> 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
+ }
+} \ No newline at end of file
diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/utils/isException.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/utils/isException.kt
new file mode 100644
index 00000000..9333878a
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/utils/isException.kt
@@ -0,0 +1,18 @@
+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/"
+ }
diff --git a/subprojects/analysis-kotlin-symbols/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/subprojects/analysis-kotlin-symbols/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
new file mode 100644
index 00000000..442f3148
--- /dev/null
+++ b/subprojects/analysis-kotlin-symbols/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
@@ -0,0 +1 @@
+org.jetbrains.dokka.analysis.kotlin.symbols.plugin.SymbolsAnalysisPlugin