K2: Add extension point for additional KDoc resolution

Similar to K1 KDocLinkResolutionService used by Fe10KDocReference (to
support additional KDoc resolution), this commit adds K2 counterpart
AdditionalKDocResolutionProvider and uses it for KDocReferenceResolver.

^KT-62187
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KDocReferenceResolver.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KDocReferenceResolver.kt
index 4b8f637..b2069f5 100644
--- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KDocReferenceResolver.kt
+++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/KDocReferenceResolver.kt
@@ -10,12 +10,13 @@
 import org.jetbrains.kotlin.analysis.api.components.KtScopeKind
 import org.jetbrains.kotlin.analysis.api.scopes.KtScope
 import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
-import org.jetbrains.kotlin.analysis.api.symbols.KtDeclarationSymbol
-import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol
-import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
+import org.jetbrains.kotlin.analysis.api.symbols.*
 import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithMembers
 import org.jetbrains.kotlin.analysis.utils.printer.parentsOfType
-import org.jetbrains.kotlin.name.*
+import org.jetbrains.kotlin.name.CallableId
+import org.jetbrains.kotlin.name.ClassId
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.Name
 import org.jetbrains.kotlin.psi.*
 import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
 import kotlin.reflect.KClass
@@ -82,6 +83,7 @@
         getExtensionReceiverSymbolByThisQualifier(fqName, contextElement).ifNotEmpty { return this }
         (getSymbolsFromScopes(fqName, contextElement) + listOfNotNull(getPackageSymbolIfPackageExists(fqName))).ifNotEmpty { return this }
         getNonImportedSymbolsByFullyQualifiedName(fqName).ifNotEmpty { return this }
+        AdditionalKDocResolutionProvider.resolveKdocFqName(fqName, contextElement).ifNotEmpty { return this }
         return emptyList()
     }
 
diff --git a/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt
index 3f5fe0c..f5f7e3e 100644
--- a/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt
+++ b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/StandaloneProjectFactory.kt
@@ -33,6 +33,7 @@
 import org.jetbrains.kotlin.analysis.api.impl.base.references.HLApiReferenceProviderService
 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.symbols.AdditionalKDocResolutionProvider
 import org.jetbrains.kotlin.analysis.decompiler.psi.BuiltInsVirtualFileProvider
 import org.jetbrains.kotlin.analysis.decompiler.psi.BuiltInsVirtualFileProviderCliImpl
 import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache
@@ -141,15 +142,26 @@
     ) {
         val applicationArea = applicationEnvironment.application.extensionArea
 
-        if (applicationArea.hasExtensionPoint(ClassTypePointerFactory.EP_NAME)) return
-        KotlinCoreEnvironment.underApplicationLock {
-            if (applicationArea.hasExtensionPoint(ClassTypePointerFactory.EP_NAME)) return@underApplicationLock
-            CoreApplicationEnvironment.registerApplicationExtensionPoint(
-                ClassTypePointerFactory.EP_NAME,
-                ClassTypePointerFactory::class.java
-            )
-            applicationArea.getExtensionPoint(ClassTypePointerFactory.EP_NAME)
-                .registerExtension(PsiClassReferenceTypePointerFactory(), applicationDisposable)
+        if (!applicationArea.hasExtensionPoint(AdditionalKDocResolutionProvider.EP_NAME)) {
+            KotlinCoreEnvironment.underApplicationLock {
+                if (applicationArea.hasExtensionPoint(AdditionalKDocResolutionProvider.EP_NAME)) return@underApplicationLock
+                CoreApplicationEnvironment.registerApplicationExtensionPoint(
+                    AdditionalKDocResolutionProvider.EP_NAME,
+                    AdditionalKDocResolutionProvider::class.java
+                )
+            }
+        }
+
+        if (!applicationArea.hasExtensionPoint(ClassTypePointerFactory.EP_NAME)) {
+            KotlinCoreEnvironment.underApplicationLock {
+                if (applicationArea.hasExtensionPoint(ClassTypePointerFactory.EP_NAME)) return@underApplicationLock
+                CoreApplicationEnvironment.registerApplicationExtensionPoint(
+                    ClassTypePointerFactory.EP_NAME,
+                    ClassTypePointerFactory::class.java
+                )
+                applicationArea.getExtensionPoint(ClassTypePointerFactory.EP_NAME)
+                    .registerExtension(PsiClassReferenceTypePointerFactory(), applicationDisposable)
+            }
         }
     }
 
diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/AdditionalKDocResolutionProvider.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/AdditionalKDocResolutionProvider.kt
new file mode 100644
index 0000000..81270e6
--- /dev/null
+++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/AdditionalKDocResolutionProvider.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.analysis.api.symbols
+
+import com.intellij.openapi.extensions.ExtensionPointName
+import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtElement
+
+/**
+ * An extension point to provide additional symbols for a KDoc reference. KDoc link resolution will use symbols returned by this EP
+ * only if the real resolution was unsuccessful. You can use this EP by creating a class implementing this interface.
+ * For example, let's assume that you want to return symbol `fun foo() = 3` for the following KDoc resolution:
+ *
+ * ```
+ *   package com.example
+ *   fun foo() = 3
+ *   /**
+ *    * [this.is.not.com.example.fo<caret>o] is not the above `com.example.foo`, but you want to resolve it to the above `com.example.foo`.
+ *    */
+ *   fun bar() = 7
+ * ```
+ *
+ * You can create
+ *
+ * ```
+ *   class AdditionalKDocResolutionProviderBasedOnNameMatch : AdditionalKDocResolutionProvider {
+ *     context(KtAnalysisSession)
+ *     override fun resolveKdocFqName(fqName: FqName, contextElement: KtElement): Collection<KtSymbol> =
+ *       contextElement.containingKtFile.declarations.filter { it.name == fqName.shortName().asString() }.map { it.getSymbol() }
+ *   }
+ * ```
+ */
+public interface AdditionalKDocResolutionProvider {
+    /**
+     * This function must return additional symbols for [contextElement] in KDoc.
+     */
+    context(KtAnalysisSession)
+    public fun resolveKdocFqName(fqName: FqName, contextElement: KtElement): Collection<KtSymbol>
+
+    public companion object {
+        public val EP_NAME: ExtensionPointName<AdditionalKDocResolutionProvider> =
+            ExtensionPointName<AdditionalKDocResolutionProvider>("org.jetbrains.kotlin.analysis.additionalKDocResolutionProvider")
+
+        context(KtAnalysisSession)
+        public fun resolveKdocFqName(fqName: FqName, contextElement: KtElement): Collection<KtSymbol> =
+            EP_NAME.extensions.flatMap { it.resolveKdocFqName(fqName, contextElement) }
+    }
+}
\ No newline at end of file