aboutsummaryrefslogtreecommitdiff
path: root/subprojects/analysis-java-psi/src
diff options
context:
space:
mode:
authorIgnat Beresnev <ignat.beresnev@jetbrains.com>2023-07-05 10:04:55 +0200
committerGitHub <noreply@github.com>2023-07-05 10:04:55 +0200
commit9559158bfeeb274e9ccf1b4563f1b23b42afc493 (patch)
tree3ece0887623cfe2b7148af23001867a1dd5e6597 /subprojects/analysis-java-psi/src
parentcbd9733d3dd2f52992e98e7cebd072091a572529 (diff)
downloaddokka-9559158bfeeb274e9ccf1b4563f1b23b42afc493.tar.gz
dokka-9559158bfeeb274e9ccf1b4563f1b23b42afc493.tar.bz2
dokka-9559158bfeeb274e9ccf1b4563f1b23b42afc493.zip
Decompose Kotlin/Java analysis (#3034)
* Extract analysis into separate modules
Diffstat (limited to 'subprojects/analysis-java-psi/src')
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/DefaultPsiToDocumentableTranslator.kt83
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavaAnalysisPlugin.kt106
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavadocTag.kt48
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/SynheticElementDocumentationProvider.kt42
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocComment.kt14
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentCreator.kt9
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFactory.kt20
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFinder.kt64
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocumentationContent.kt11
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocComment.kt84
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocCommentCreator.kt11
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/PsiDocumentationContent.kt22
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/CommentResolutionContext.kt9
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/DocCommentParser.kt12
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/DokkaPsiParser.kt797
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/JavaDocCommentParser.kt228
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/JavadocParser.kt24
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/DocTagParserContext.kt47
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/HtmlToDocTagConverter.kt114
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/InheritDocTagContentProvider.kt10
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/InheritDocTagResolver.kt114
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/PsiDocTagParser.kt39
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/PsiElementToHtmlConverter.kt214
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/CoreCopyPaste.kt20
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/NoopIntellijLogger.kt43
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PropertiesConventionUtil.kt101
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiAccessorConventionUtil.kt98
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiCommentsUtils.kt49
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiUtil.kt119
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/StdlibUtil.kt33
-rw-r--r--subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/resolveToGetDri.kt7
-rw-r--r--subprojects/analysis-java-psi/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin1
32 files changed, 2593 insertions, 0 deletions
diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/DefaultPsiToDocumentableTranslator.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/DefaultPsiToDocumentableTranslator.kt
new file mode 100644
index 00000000..72cf45d9
--- /dev/null
+++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/DefaultPsiToDocumentableTranslator.kt
@@ -0,0 +1,83 @@
+package org.jetbrains.dokka.analysis.java
+
+import com.intellij.openapi.vfs.VirtualFileManager
+import com.intellij.psi.PsiJavaFile
+import com.intellij.psi.PsiKeyword
+import com.intellij.psi.PsiManager
+import com.intellij.psi.PsiModifierListOwner
+import kotlinx.coroutines.coroutineScope
+import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
+import org.jetbrains.dokka.analysis.java.parsers.DokkaPsiParser
+import org.jetbrains.dokka.analysis.java.parsers.JavaPsiDocCommentParser
+import org.jetbrains.dokka.analysis.java.parsers.JavadocParser
+import org.jetbrains.dokka.model.DModule
+import org.jetbrains.dokka.model.JavaVisibility
+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.parallelMap
+import org.jetbrains.dokka.utilities.parallelMapNotNull
+
+internal class DefaultPsiToDocumentableTranslator : AsyncSourceToDocumentableTranslator {
+
+ override suspend fun invokeSuspending(sourceSet: DokkaSourceSet, context: DokkaContext): DModule {
+ return coroutineScope {
+ val projectProvider = context.plugin<JavaAnalysisPlugin>().querySingle { projectProvider }
+ val project = projectProvider.getProject(sourceSet, context)
+
+ val sourceRootsExtractor = context.plugin<JavaAnalysisPlugin>().querySingle { sourceRootsExtractor }
+ val sourceRoots = sourceRootsExtractor.extract(sourceSet, context)
+
+ val localFileSystem = VirtualFileManager.getInstance().getFileSystem("file")
+
+ val psiFiles = sourceRoots.parallelMap { sourceRoot ->
+ sourceRoot.absoluteFile.walkTopDown().mapNotNull {
+ localFileSystem.findFileByPath(it.path)?.let { vFile ->
+ PsiManager.getInstance(project).findFile(vFile) as? PsiJavaFile
+ }
+ }.toList()
+ }.flatten()
+
+ val docParser = createPsiParser(sourceSet, context)
+
+ DModule(
+ name = context.configuration.moduleName,
+ packages = psiFiles.parallelMapNotNull { it }.groupBy { it.packageName }.toList()
+ .parallelMap { (packageName: String, psiFiles: List<PsiJavaFile>) ->
+ docParser.parsePackage(packageName, psiFiles)
+ },
+ documentation = emptyMap(),
+ expectPresentInSet = null,
+ sourceSets = setOf(sourceSet)
+ )
+ }
+ }
+
+ private fun createPsiParser(sourceSet: DokkaSourceSet, context: DokkaContext): DokkaPsiParser {
+ val projectProvider = context.plugin<JavaAnalysisPlugin>().querySingle { projectProvider }
+ val docCommentParsers = context.plugin<JavaAnalysisPlugin>().query { docCommentParsers }
+ return DokkaPsiParser(
+ sourceSetData = sourceSet,
+ project = projectProvider.getProject(sourceSet, context),
+ logger = context.logger,
+ javadocParser = JavadocParser(
+ docCommentParsers = docCommentParsers,
+ docCommentFinder = context.plugin<JavaAnalysisPlugin>().docCommentFinder
+ ),
+ javaPsiDocCommentParser = docCommentParsers.single { it is JavaPsiDocCommentParser } as JavaPsiDocCommentParser,
+ lightMethodChecker = context.plugin<JavaAnalysisPlugin>().querySingle { kotlinLightMethodChecker }
+ )
+ }
+}
+
+internal fun PsiModifierListOwner.getVisibility() = modifierList?.let {
+ val ml = it.children.toList()
+ when {
+ ml.any { it.text == PsiKeyword.PUBLIC } || it.hasModifierProperty("public") -> JavaVisibility.Public
+ ml.any { it.text == PsiKeyword.PROTECTED } || it.hasModifierProperty("protected") -> JavaVisibility.Protected
+ ml.any { it.text == PsiKeyword.PRIVATE } || it.hasModifierProperty("private") -> JavaVisibility.Private
+ else -> JavaVisibility.Default
+ }
+} ?: JavaVisibility.Default
diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavaAnalysisPlugin.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavaAnalysisPlugin.kt
new file mode 100644
index 00000000..8884d444
--- /dev/null
+++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavaAnalysisPlugin.kt
@@ -0,0 +1,106 @@
+package org.jetbrains.dokka.analysis.java
+
+import com.intellij.lang.jvm.annotation.JvmAnnotationAttribute
+import com.intellij.openapi.diagnostic.Logger
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiAnnotation
+import org.jetbrains.dokka.CoreExtensions
+import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
+import org.jetbrains.dokka.InternalDokkaApi
+import org.jetbrains.dokka.analysis.java.doccomment.DocCommentCreator
+import org.jetbrains.dokka.analysis.java.doccomment.DocCommentFactory
+import org.jetbrains.dokka.analysis.java.doccomment.DocCommentFinder
+import org.jetbrains.dokka.analysis.java.doccomment.JavaDocCommentCreator
+import org.jetbrains.dokka.analysis.java.parsers.DocCommentParser
+import org.jetbrains.dokka.analysis.java.parsers.doctag.InheritDocTagContentProvider
+import org.jetbrains.dokka.analysis.java.parsers.JavaPsiDocCommentParser
+import org.jetbrains.dokka.analysis.java.parsers.doctag.InheritDocTagResolver
+import org.jetbrains.dokka.analysis.java.parsers.doctag.PsiDocTagParser
+import org.jetbrains.dokka.analysis.java.util.NoopIntellijLoggerFactory
+import org.jetbrains.dokka.plugability.*
+import java.io.File
+
+
+@InternalDokkaApi
+interface ProjectProvider {
+ fun getProject(sourceSet: DokkaSourceSet, context: DokkaContext): Project
+}
+
+@InternalDokkaApi
+interface SourceRootsExtractor {
+ fun extract(sourceSet: DokkaSourceSet, context: DokkaContext): List<File>
+}
+
+@InternalDokkaApi
+interface BreakingAbstractionKotlinLightMethodChecker {
+ // TODO [beresnev] not even sure it's needed, but left for compatibility and to preserve behaviour
+ fun isLightAnnotation(annotation: PsiAnnotation): Boolean
+ fun isLightAnnotationAttribute(attribute: JvmAnnotationAttribute): Boolean
+}
+
+@InternalDokkaApi
+class JavaAnalysisPlugin : DokkaPlugin() {
+
+ // single
+ val projectProvider by extensionPoint<ProjectProvider>()
+
+ // single
+ val sourceRootsExtractor by extensionPoint<SourceRootsExtractor>()
+
+ // multiple
+ val docCommentCreators by extensionPoint<DocCommentCreator>()
+
+ // multiple
+ val docCommentParsers by extensionPoint<DocCommentParser>()
+
+ // none or more
+ val inheritDocTagContentProviders by extensionPoint<InheritDocTagContentProvider>()
+
+ // TODO [beresnev] figure out a better way depending on what it's used for
+ val kotlinLightMethodChecker by extensionPoint<BreakingAbstractionKotlinLightMethodChecker>()
+
+ private val docCommentFactory by lazy {
+ DocCommentFactory(query { docCommentCreators }.reversed())
+ }
+
+ val docCommentFinder by lazy {
+ DocCommentFinder(logger, docCommentFactory)
+ }
+
+ internal val javaDocCommentCreator by extending {
+ docCommentCreators providing { JavaDocCommentCreator() }
+ }
+
+ private val psiDocTagParser by lazy {
+ PsiDocTagParser(
+ inheritDocTagResolver = InheritDocTagResolver(
+ docCommentFactory = docCommentFactory,
+ docCommentFinder = docCommentFinder,
+ contentProviders = query { inheritDocTagContentProviders }
+ )
+ )
+ }
+
+ internal val javaDocCommentParser by extending {
+ docCommentParsers providing {
+ JavaPsiDocCommentParser(
+ psiDocTagParser
+ )
+ }
+ }
+
+ internal val psiToDocumentableTranslator by extending {
+ CoreExtensions.sourceToDocumentableTranslator providing { DefaultPsiToDocumentableTranslator() }
+ }
+
+ @OptIn(DokkaPluginApiPreview::class)
+ override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement
+
+ private companion object {
+ init {
+ // Suppress messages emitted by the IntelliJ logger since
+ // there's not much the end user can do about it
+ Logger.setFactory(NoopIntellijLoggerFactory())
+ }
+ }
+}
diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavadocTag.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavadocTag.kt
new file mode 100644
index 00000000..f5cd550f
--- /dev/null
+++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavadocTag.kt
@@ -0,0 +1,48 @@
+package org.jetbrains.dokka.analysis.java
+
+import com.intellij.psi.PsiMethod
+import org.jetbrains.dokka.InternalDokkaApi
+
+@InternalDokkaApi
+sealed class JavadocTag(val name: String)
+
+object AuthorJavadocTag : JavadocTag("author")
+object DeprecatedJavadocTag : JavadocTag("deprecated")
+object DescriptionJavadocTag : JavadocTag("description")
+object ReturnJavadocTag : JavadocTag("return")
+object SinceJavadocTag : JavadocTag("since")
+
+class ParamJavadocTag(
+ val method: PsiMethod,
+ val paramName: String,
+ val paramIndex: Int
+) : JavadocTag(name) {
+ companion object {
+ const val name: String = "param"
+ }
+}
+
+class SeeJavadocTag(
+ val qualifiedReference: String
+) : JavadocTag(name) {
+ companion object {
+ const val name: String = "see"
+ }
+}
+
+sealed class ThrowingExceptionJavadocTag(
+ name: String,
+ val exceptionQualifiedName: String?
+) : JavadocTag(name)
+
+class ThrowsJavadocTag(exceptionQualifiedName: String?) : ThrowingExceptionJavadocTag(name, exceptionQualifiedName) {
+ companion object {
+ const val name: String = "throws"
+ }
+}
+
+class ExceptionJavadocTag(exceptionQualifiedName: String?) : ThrowingExceptionJavadocTag(name, exceptionQualifiedName) {
+ companion object {
+ const val name: String = "exception"
+ }
+}
diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/SynheticElementDocumentationProvider.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/SynheticElementDocumentationProvider.kt
new file mode 100644
index 00000000..d780bb40
--- /dev/null
+++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/SynheticElementDocumentationProvider.kt
@@ -0,0 +1,42 @@
+package org.jetbrains.dokka.analysis.java
+
+import com.intellij.openapi.project.Project
+import com.intellij.psi.JavaPsiFacade
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiMethod
+import com.intellij.psi.SyntheticElement
+import com.intellij.psi.javadoc.PsiDocComment
+import org.jetbrains.dokka.analysis.java.parsers.JavaPsiDocCommentParser
+import org.jetbrains.dokka.model.doc.DocumentationNode
+
+private const val ENUM_VALUEOF_TEMPLATE_PATH = "/dokka/docs/javadoc/EnumValueOf.java.template"
+private const val ENUM_VALUES_TEMPLATE_PATH = "/dokka/docs/javadoc/EnumValues.java.template"
+
+internal class SyntheticElementDocumentationProvider(
+ private val javadocParser: JavaPsiDocCommentParser,
+ private val project: Project
+) {
+ fun isDocumented(psiElement: PsiElement): Boolean = psiElement is PsiMethod
+ && (psiElement.isSyntheticEnumValuesMethod() || psiElement.isSyntheticEnumValueOfMethod())
+
+ fun getDocumentation(psiElement: PsiElement): DocumentationNode? {
+ val psiMethod = psiElement as? PsiMethod ?: return null
+ val templatePath = when {
+ psiMethod.isSyntheticEnumValuesMethod() -> ENUM_VALUES_TEMPLATE_PATH
+ psiMethod.isSyntheticEnumValueOfMethod() -> ENUM_VALUEOF_TEMPLATE_PATH
+ else -> return null
+ }
+ val docComment = loadSyntheticDoc(templatePath) ?: return null
+ return javadocParser.parsePsiDocComment(docComment, psiElement)
+ }
+
+ private fun loadSyntheticDoc(path: String): PsiDocComment? {
+ val text = javaClass.getResource(path)?.readText() ?: return null
+ return JavaPsiFacade.getElementFactory(project).createDocCommentFromText(text)
+ }
+}
+
+private fun PsiMethod.isSyntheticEnumValuesMethod() = this.isSyntheticEnumFunction() && this.name == "values"
+private fun PsiMethod.isSyntheticEnumValueOfMethod() = this.isSyntheticEnumFunction() && this.name == "valueOf"
+private fun PsiMethod.isSyntheticEnumFunction() = this is SyntheticElement && this.containingClass?.isEnum == true
+
diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocComment.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocComment.kt
new file mode 100644
index 00000000..6cc32233
--- /dev/null
+++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocComment.kt
@@ -0,0 +1,14 @@
+package org.jetbrains.dokka.analysis.java.doccomment
+
+import org.jetbrains.dokka.InternalDokkaApi
+import org.jetbrains.dokka.analysis.java.JavadocTag
+
+/**
+ * MUST override equals and hashcode
+ */
+@InternalDokkaApi
+interface DocComment {
+ fun hasTag(tag: JavadocTag): Boolean
+
+ fun resolveTag(tag: JavadocTag): List<DocumentationContent>
+}
diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentCreator.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentCreator.kt
new file mode 100644
index 00000000..3d7d4247
--- /dev/null
+++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentCreator.kt
@@ -0,0 +1,9 @@
+package org.jetbrains.dokka.analysis.java.doccomment
+
+import com.intellij.psi.PsiNamedElement
+import org.jetbrains.dokka.InternalDokkaApi
+
+@InternalDokkaApi
+interface DocCommentCreator {
+ fun create(element: PsiNamedElement): DocComment?
+}
diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFactory.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFactory.kt
new file mode 100644
index 00000000..96245ac2
--- /dev/null
+++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFactory.kt
@@ -0,0 +1,20 @@
+package org.jetbrains.dokka.analysis.java.doccomment
+
+import com.intellij.psi.PsiNamedElement
+import org.jetbrains.dokka.InternalDokkaApi
+
+@InternalDokkaApi
+class DocCommentFactory(
+ private val docCommentCreators: List<DocCommentCreator>
+) {
+ fun fromElement(element: PsiNamedElement): DocComment? {
+ docCommentCreators.forEach { creator ->
+ val comment = creator.create(element)
+ if (comment != null) {
+ return comment
+ }
+ }
+ return null
+ }
+}
+
diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFinder.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFinder.kt
new file mode 100644
index 00000000..32c8dc65
--- /dev/null
+++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFinder.kt
@@ -0,0 +1,64 @@
+package org.jetbrains.dokka.analysis.java.doccomment
+
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiMethod
+import com.intellij.psi.PsiNamedElement
+import com.intellij.psi.javadoc.PsiDocComment
+import org.jetbrains.dokka.InternalDokkaApi
+import org.jetbrains.dokka.analysis.java.util.from
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.utilities.DokkaLogger
+
+@InternalDokkaApi
+class DocCommentFinder(
+ private val logger: DokkaLogger,
+ private val docCommentFactory: DocCommentFactory,
+) {
+ fun findClosestToElement(element: PsiNamedElement): DocComment? {
+ val docComment = docCommentFactory.fromElement(element)
+ if (docComment != null) {
+ return docComment
+ }
+
+ return if (element is PsiMethod) {
+ findClosestToMethod(element)
+ } else {
+ element.children
+ .filterIsInstance<PsiDocComment>()
+ .firstOrNull()
+ ?.let { JavaDocComment(it) }
+ }
+ }
+
+ private fun findClosestToMethod(method: PsiMethod): DocComment? {
+ val superMethods = method.findSuperMethods()
+ if (superMethods.isEmpty()) return null
+
+ if (superMethods.size == 1) {
+ return findClosestToElement(superMethods.single())
+ }
+
+ val superMethodDocumentation = superMethods.map { superMethod -> findClosestToElement(superMethod) }.distinct()
+ if (superMethodDocumentation.size == 1) {
+ return superMethodDocumentation.single()
+ }
+
+ logger.debug(
+ "Conflicting documentation for ${DRI.from(method)}" +
+ "${superMethods.map { DRI.from(it) }}"
+ )
+
+ /* Prioritize super class over interface */
+ val indexOfSuperClass = superMethods.indexOfFirst { superMethod ->
+ val parent = superMethod.parent
+ if (parent is PsiClass) !parent.isInterface
+ else false
+ }
+
+ return if (indexOfSuperClass >= 0) {
+ superMethodDocumentation[indexOfSuperClass]
+ } else {
+ superMethodDocumentation.first()
+ }
+ }
+}
diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocumentationContent.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocumentationContent.kt
new file mode 100644
index 00000000..c06ed496
--- /dev/null
+++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocumentationContent.kt
@@ -0,0 +1,11 @@
+package org.jetbrains.dokka.analysis.java.doccomment
+
+import org.jetbrains.dokka.InternalDokkaApi
+import org.jetbrains.dokka.analysis.java.JavadocTag
+
+@InternalDokkaApi
+interface DocumentationContent {
+ val tag: JavadocTag
+
+ fun resolveSiblings(): List<DocumentationContent>
+}
diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocComment.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocComment.kt
new file mode 100644
index 00000000..5c9be887
--- /dev/null
+++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocComment.kt
@@ -0,0 +1,84 @@
+package org.jetbrains.dokka.analysis.java.doccomment
+
+import com.intellij.psi.PsiElement
+import com.intellij.psi.javadoc.PsiDocComment
+import com.intellij.psi.javadoc.PsiDocTag
+import org.jetbrains.dokka.analysis.java.*
+import org.jetbrains.dokka.analysis.java.util.contentElementsWithSiblingIfNeeded
+import org.jetbrains.dokka.analysis.java.util.getKotlinFqName
+import org.jetbrains.dokka.analysis.java.util.hasTag
+import org.jetbrains.dokka.analysis.java.util.resolveToElement
+import org.jetbrains.dokka.utilities.firstIsInstanceOrNull
+
+internal class JavaDocComment(val comment: PsiDocComment) : DocComment {
+ override fun hasTag(tag: JavadocTag): Boolean {
+ return when (tag) {
+ is ThrowingExceptionJavadocTag -> hasTag(tag)
+ else -> comment.hasTag(tag)
+ }
+ }
+
+ private fun hasTag(tag: ThrowingExceptionJavadocTag): Boolean =
+ comment.hasTag(tag) && comment.resolveTag(tag).firstIsInstanceOrNull<PsiDocTag>()
+ ?.resolveToElement()
+ ?.getKotlinFqName() == tag.exceptionQualifiedName
+
+ override fun resolveTag(tag: JavadocTag): List<DocumentationContent> {
+ return when (tag) {
+ is ParamJavadocTag -> resolveParamTag(tag)
+ is ThrowingExceptionJavadocTag -> resolveThrowingTag(tag)
+ else -> comment.resolveTag(tag).map { PsiDocumentationContent(it, tag) }
+ }
+ }
+
+ private fun resolveParamTag(tag: ParamJavadocTag): List<DocumentationContent> {
+ val resolvedParamElements = comment.resolveTag(tag)
+ .filterIsInstance<PsiDocTag>()
+ .map { it.contentElementsWithSiblingIfNeeded() }
+ .firstOrNull {
+ it.firstOrNull()?.text == tag.method.parameterList.parameters[tag.paramIndex].name
+ }.orEmpty()
+
+ return resolvedParamElements
+ .withoutReferenceLink()
+ .map { PsiDocumentationContent(it, tag) }
+ }
+
+ private fun resolveThrowingTag(tag: ThrowingExceptionJavadocTag): List<DocumentationContent> {
+ val resolvedElements = comment.resolveTag(tag)
+ .flatMap {
+ when (it) {
+ is PsiDocTag -> it.contentElementsWithSiblingIfNeeded()
+ else -> listOf(it)
+ }
+ }
+
+ return resolvedElements
+ .withoutReferenceLink()
+ .map { PsiDocumentationContent(it, tag) }
+ }
+
+ private fun PsiDocComment.resolveTag(tag: JavadocTag): List<PsiElement> {
+ return when (tag) {
+ DescriptionJavadocTag -> this.descriptionElements.toList()
+ else -> this.findTagsByName(tag.name).toList()
+ }
+ }
+
+ private fun List<PsiElement>.withoutReferenceLink(): List<PsiElement> = drop(1)
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as JavaDocComment
+
+ if (comment != other.comment) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ return comment.hashCode()
+ }
+}
diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocCommentCreator.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocCommentCreator.kt
new file mode 100644
index 00000000..00efeb0a
--- /dev/null
+++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocCommentCreator.kt
@@ -0,0 +1,11 @@
+package org.jetbrains.dokka.analysis.java.doccomment
+
+import com.intellij.psi.PsiDocCommentOwner
+import com.intellij.psi.PsiNamedElement
+
+internal class JavaDocCommentCreator : DocCommentCreator {
+ override fun create(element: PsiNamedElement): DocComment? {
+ val psiDocComment = (element as? PsiDocCommentOwner)?.docComment ?: return null
+ return JavaDocComment(psiDocComment)
+ }
+}
diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/PsiDocumentationContent.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/PsiDocumentationContent.kt
new file mode 100644
index 00000000..c36ce50d
--- /dev/null
+++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/PsiDocumentationContent.kt
@@ -0,0 +1,22 @@
+package org.jetbrains.dokka.analysis.java.doccomment
+
+import com.intellij.psi.PsiElement
+import com.intellij.psi.javadoc.PsiDocTag
+import org.jetbrains.dokka.analysis.java.JavadocTag
+import org.jetbrains.dokka.analysis.java.util.contentElementsWithSiblingIfNeeded
+
+internal data class PsiDocumentationContent(
+ val psiElement: PsiElement,
+ override val tag: JavadocTag
+) : DocumentationContent {
+
+ override fun resolveSiblings(): List<DocumentationContent> {
+ return if (psiElement is PsiDocTag) {
+ psiElement.contentElementsWithSiblingIfNeeded()
+ .map { content -> PsiDocumentationContent(content, tag) }
+ } else {
+ listOf(this)
+ }
+ }
+
+}
diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/CommentResolutionContext.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/CommentResolutionContext.kt
new file mode 100644
index 00000000..1e193648
--- /dev/null
+++ b/