[LL API] Move PSI element context collection to 'ContextCollector'

The new API is going to be reused in 'KtFirScopeProvider'.
This commit is contained in:
Yan Zhulanow
2023-08-01 14:57:50 +09:00
committed by Space Team
parent 6413294f3f
commit fcefdcc3e9
2 changed files with 51 additions and 33 deletions
@@ -5,15 +5,11 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers package org.jetbrains.kotlin.analysis.low.level.api.fir.transformers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignationWithFile import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getFirResolveSession import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getFirResolveSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirResolveTarget import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirWholeFileResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.asResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.throwUnexpectedFirElementError import org.jetbrains.kotlin.analysis.low.level.api.fir.api.throwUnexpectedFirElementError
import org.jetbrains.kotlin.analysis.low.level.api.fir.compile.codeFragmentScopeProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.compile.codeFragmentScopeProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.getNonLocalContainingOrThisDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LLFirLockProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LLFirLockProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyBodiesCalculator import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyBodiesCalculator
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData
@@ -53,9 +49,8 @@ import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.psi.KtCodeFragment import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.utils.exceptions.buildErrorWithAttachment
import org.jetbrains.kotlin.utils.exceptions.requireWithAttachment import org.jetbrains.kotlin.utils.exceptions.requireWithAttachment
import org.jetbrains.kotlin.utils.exceptions.withPsiEntry
import org.jetbrains.kotlin.utils.findIsInstanceAnd import org.jetbrains.kotlin.utils.findIsInstanceAnd
internal object LLFirBodyLazyResolver : LLFirLazyResolver(FirResolvePhase.BODY_RESOLVE) { internal object LLFirBodyLazyResolver : LLFirLazyResolver(FirResolvePhase.BODY_RESOLVE) {
@@ -180,20 +175,11 @@ private class LLFirBodyTargetResolver(
firCodeFragment.codeFragmentContext = if (contextKtFile != null) { firCodeFragment.codeFragmentContext = if (contextKtFile != null) {
val contextFirFile = resolveSession.getOrBuildFirFile(contextKtFile) val contextFirFile = resolveSession.getOrBuildFirFile(contextKtFile)
val target = computeCodeFragmentContextResolveTarget(contextFirFile, contextPsiElement)
val contextParentKtElements = contextPsiElement.parentsWithSelf.toSet() val elementContext = ContextCollector.process(contextFirFile, transformer.components, contextPsiElement, preferBody = true)
?: errorWithAttachment("Cannot find enclosing context for ${contextPsiElement::class}") {
val contextProvider = ContextCollector.process(target, transformer.components) { candidate -> withPsiEntry("contextPsiElement", contextPsiElement)
when (candidate) {
contextPsiElement -> ContextCollector.FilterResponse.STOP
in contextParentKtElements -> ContextCollector.FilterResponse.CONTINUE
else -> ContextCollector.FilterResponse.SKIP
} }
}
val elementContext = contextProvider[contextPsiElement, ContextCollector.ContextKind.BODY]
?: contextParentKtElements.firstNotNullOf { contextProvider[it, ContextCollector.ContextKind.SELF] }
LLFirCodeFragmentContext(elementContext.towerDataContext.withExtraScopes(), elementContext.smartCasts) LLFirCodeFragmentContext(elementContext.towerDataContext.withExtraScopes(), elementContext.smartCasts)
} else { } else {
@@ -202,18 +188,6 @@ private class LLFirBodyTargetResolver(
} }
} }
private fun computeCodeFragmentContextResolveTarget(contextFirFile: FirFile, contextPsiElement: PsiElement): LLFirResolveTarget {
val contextKtDeclaration = contextPsiElement.getNonLocalContainingOrThisDeclaration()
if (contextKtDeclaration != null) {
val designationPath = FirElementFinder.collectDesignationPath(contextFirFile, contextKtDeclaration)
if (designationPath != null) {
return FirDesignationWithFile(designationPath.path, designationPath.target, contextFirFile).asResolveTarget()
}
}
return LLFirWholeFileResolveTarget(contextFirFile)
}
override fun doLazyResolveUnderLock(target: FirElementWithResolveState) { override fun doLazyResolveUnderLock(target: FirElementWithResolveState) {
when (target) { when (target) {
is FirRegularClass, is FirCodeFragment -> error("Should have been resolved in ${::doResolveWithoutLock.name}") is FirRegularClass, is FirCodeFragment -> error("Should have been resolved in ${::doResolveWithoutLock.name}")
@@ -9,7 +9,11 @@ import com.intellij.openapi.progress.ProgressManager
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import kotlinx.collections.immutable.PersistentMap import kotlinx.collections.immutable.PersistentMap
import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.persistentMapOf
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirResolveTarget import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirWholeFileResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.asResolveTarget
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.ContextKind
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ContextCollector.Context import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ContextCollector.Context
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ContextCollector.FilterResponse import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ContextCollector.FilterResponse
@@ -29,6 +33,7 @@ import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
internal object ContextCollector { internal object ContextCollector {
enum class ContextKind { enum class ContextKind {
@@ -55,6 +60,45 @@ internal object ContextCollector {
SKIP SKIP
} }
/**
* Process the [file], collecting contexts for the [targetElement] and all its PSI tree parents.
*/
fun process(file: FirFile, holder: SessionHolder, targetElement: PsiElement, preferBody: Boolean): Context? {
val acceptedElements = targetElement.parentsWithSelf.toSet()
val contextProvider = process(computeResolveTarget(file, targetElement), holder) { candidate ->
when (candidate) {
targetElement -> FilterResponse.STOP
in acceptedElements -> FilterResponse.CONTINUE
else -> FilterResponse.SKIP
}
}
if (preferBody) {
val bodyContext = contextProvider[targetElement, ContextKind.BODY]
if (bodyContext != null) {
return bodyContext
}
}
return acceptedElements.firstNotNullOfOrNull { contextProvider[it, ContextKind.SELF] }
}
private fun computeResolveTarget(file: FirFile, targetElement: PsiElement): LLFirResolveTarget {
val contextKtDeclaration = targetElement.getNonLocalContainingOrThisDeclaration()
if (contextKtDeclaration != null) {
val designationPath = FirElementFinder.collectDesignationPath(file, contextKtDeclaration)
if (designationPath != null) {
return FirDesignationWithFile(designationPath.path, designationPath.target, file).asResolveTarget()
}
}
return LLFirWholeFileResolveTarget(file)
}
/**
* Processes the [FirFile] that owns the [target], collecting contexts for elements matching the [filter].
*/
fun process(target: LLFirResolveTarget, holder: SessionHolder, filter: (PsiElement) -> FilterResponse): ContextProvider { fun process(target: LLFirResolveTarget, holder: SessionHolder, filter: (PsiElement) -> FilterResponse): ContextProvider {
val pathIterator = target.path.iterator() val pathIterator = target.path.iterator()
@@ -67,10 +111,10 @@ internal object ContextCollector {
return ContextProvider { element, kind -> visitor[element, kind] } return ContextProvider { element, kind -> visitor[element, kind] }
} }
}
internal fun interface ContextProvider { fun interface ContextProvider {
operator fun get(element: PsiElement, kind: ContextKind): Context? operator fun get(element: PsiElement, kind: ContextKind): Context?
}
} }
private class ContextCollectorVisitor( private class ContextCollectorVisitor(