Dirty attempt of custom on-air designation
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt
index 6bc12dd..938c2f4 100644
--- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt
+++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt
@@ -256,17 +256,16 @@
originalFile: KtFile,
positionInFakeFile: KtElement
): KtScopeContext {
- val fakeFile = positionInFakeFile.containingKtFile
-
// If the position is in KDoc, we want to pass the owning declaration to the ContextCollector.
// That way, the resulting scope will contain all the nested declarations which can be references by KDoc.
val parentKDoc = positionInFakeFile.parentOfType<KDoc>()
val correctedPosition = parentKDoc?.owner ?: positionInFakeFile
- val context = ContextCollector.process(
- fakeFile.getOrBuildFirFile(firResolveSession),
+ val context = ContextCollector.processFake(
+ originalFile.getOrBuildFirFile(firResolveSession),
SessionHolderImpl(analysisSession.useSiteSession, getScopeSession()),
correctedPosition,
+ firResolveSession,
)
val towerDataContext =
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LowLevelFirApiFacadeForResolveOnAir.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LowLevelFirApiFacadeForResolveOnAir.kt
index 07cf3ca..2709efa 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LowLevelFirApiFacadeForResolveOnAir.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LowLevelFirApiFacadeForResolveOnAir.kt
@@ -278,6 +278,15 @@
return fileAnnotationsContainer.annotations.single()
}
+ fun getDeclarationForOnAirResolve(originalPlace: PsiElement): KtElement? {
+ val minimalOriginalDeclarationToReplace = originalPlace.getNonLocalContainingOrThisDeclarationCodeFragmentAware {
+ it.isApplicableForOnAirResolve()
+ }
+
+ val originalDeclaration = minimalOriginalDeclarationToReplace?.onAirGetNonLocalContainingOrThisDeclaration()
+ return originalDeclaration
+ }
+
/**
* Creates a new fir declaration from closer non-local declaration based on [originalPlace] position.
* The resulted [FirResolvePhase] depends on [forcedResolvePhase] or the position of [originalPlace] inside the non-local declaration.
@@ -355,7 +364,7 @@
session.moduleComponents.firModuleLazyDeclarationResolver.runLazyDesignatedOnAirResolve(
FirDesignationWithFile(originalDesignationPath.path, newFirDeclaration, originalFirFile),
collector,
- forcedResolvePhase ?: requiredResolvePhase(minimalOriginalDeclarationToReplace, originalPlace),
+ resolvePhase = forcedResolvePhase ?: requiredResolvePhase(minimalOriginalDeclarationToReplace, originalPlace),
)
return newFirDeclaration
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt
index dfef535..4baeb17 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt
@@ -7,8 +7,12 @@
import com.intellij.openapi.progress.ProgressManager
import com.intellij.psi.PsiElement
+import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignation
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignationWithScript
+import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession
+import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LowLevelFirApiFacadeForResolveOnAir
+import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirSafe
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.getNonLocalContainingOrThisDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ContextCollector.ContextKind
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ContextCollector.Context
@@ -36,6 +40,8 @@
import org.jetbrains.kotlin.fir.types.typeContext
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.util.PrivateForInline
import org.jetbrains.kotlin.utils.yieldIfNotNull
@@ -106,6 +112,53 @@
return null
}
+ fun processFake(file: FirFile, holder: SessionHolder, targetElement: PsiElement, resolveSession: LLFirResolveSession, bodyElement: PsiElement? = targetElement): Context? {
+ val isBodyContextCollected = bodyElement != null
+ val acceptedElements = targetElement.parentsWithSelf.flatMap {
+ val originalDeclarations = when (it) {
+ is KtDeclaration -> it.originalDeclaration
+ is KtFile -> it.originalFile
+ else -> null
+ }
+ listOfNotNull(it, originalDeclarations)
+ }.toSet()
+
+ val contextProvider = process(file, holder, computeFakeDesignation(file, targetElement, resolveSession), isBodyContextCollected) { candidate ->
+ when (candidate) {
+ targetElement -> FilterResponse.STOP
+ in acceptedElements -> FilterResponse.CONTINUE
+ else -> FilterResponse.SKIP
+ }
+ }
+
+ for (acceptedElement in acceptedElements) {
+ if (acceptedElement === bodyElement) {
+ val bodyContext = contextProvider[acceptedElement, ContextKind.BODY]
+ if (bodyContext != null) {
+ return bodyContext
+ }
+ }
+
+ val elementContext = contextProvider[acceptedElement, ContextKind.SELF]
+ if (elementContext != null) {
+ return elementContext
+ }
+
+ val original = when (acceptedElement) {
+ is KtDeclaration -> acceptedElement.originalDeclaration
+ is KtFile -> acceptedElement.originalFile
+ else -> null
+ } ?: continue
+
+ val originalElementContext = contextProvider[original, ContextKind.SELF]
+ if (originalElementContext != null) {
+ return originalElementContext
+ }
+ }
+
+ return null
+ }
+
fun computeDesignation(file: FirFile, targetElement: PsiElement): FirDesignation? {
val contextKtDeclaration = targetElement.getNonLocalContainingOrThisDeclaration()
if (contextKtDeclaration != null) {
@@ -124,6 +177,26 @@
return null
}
+ fun computeFakeDesignation(file: FirFile, targetElement: PsiElement, resolveSession: LLFirResolveSession): FirDesignation? {
+ val fakeContextKtDeclaration = LowLevelFirApiFacadeForResolveOnAir.getDeclarationForOnAirResolve(targetElement) as? KtDeclaration
+ val fakeFirDeclaration = fakeContextKtDeclaration?.getOrBuildFirSafe<FirDeclaration>(resolveSession) ?: return null
+ val contextKtDeclaration = PsiTreeUtil.findSameElementInCopy(fakeContextKtDeclaration, file.psi as KtFile)
+ if (contextKtDeclaration != null) {
+ val designationPath = FirElementFinder.collectDesignationPath(file, contextKtDeclaration)
+ if (designationPath != null) {
+ val script = file.declarations.singleOrNull() as? FirScript
+
+ return if (script == null || script === designationPath.target) {
+ FirDesignation(designationPath.path, fakeFirDeclaration)
+ } else {
+ FirDesignationWithScript(designationPath.path, designationPath.target, script)
+ }
+ }
+ }
+
+ return null
+ }
+
/**
* Processes the [FirFile], collecting contexts for elements matching the [filter].
*