[FIR IDE] Refactor low level resolve state for completion

Rename refactorings
Remove redundant methods and implementations
Increase code-logic locality
Removed redundant entities
This commit is contained in:
Igor Yakovlev
2021-04-21 18:26:36 +02:00
committed by TeamCityServer
parent aa13936cb2
commit aaefe94763
13 changed files with 179 additions and 288 deletions
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.idea.completion package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.* import com.intellij.codeInsight.completion.*
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.patterns.PlatformPatterns import com.intellij.patterns.PlatformPatterns
import com.intellij.patterns.PsiJavaPatterns import com.intellij.patterns.PsiJavaPatterns
import com.intellij.util.ProcessingContext import com.intellij.util.ProcessingContext
@@ -26,7 +25,6 @@ import org.jetbrains.kotlin.idea.frontend.api.scopes.KtCompositeScope
import org.jetbrains.kotlin.idea.frontend.api.scopes.KtScope import org.jetbrains.kotlin.idea.frontend.api.scopes.KtScope
import org.jetbrains.kotlin.idea.frontend.api.scopes.KtScopeNameFilter import org.jetbrains.kotlin.idea.frontend.api.scopes.KtScopeNameFilter
import org.jetbrains.kotlin.idea.frontend.api.symbols.* import org.jetbrains.kotlin.idea.frontend.api.symbols.*
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtNamedSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithVisibility import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithVisibility
import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType
import org.jetbrains.kotlin.idea.frontend.api.types.KtType import org.jetbrains.kotlin.idea.frontend.api.types.KtType
@@ -8,16 +8,13 @@ package org.jetbrains.kotlin.idea.completion.context
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.analyse import org.jetbrains.kotlin.idea.frontend.api.analyse
import org.jetbrains.kotlin.idea.frontend.api.analyseInFakeAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.analyseInDependedAnalysisSession
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtLabelReferenceExpression import org.jetbrains.kotlin.psi.KtLabelReferenceExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
internal sealed class FirPositionCompletionContext { internal sealed class FirPositionCompletionContext {
abstract val position: PsiElement abstract val position: PsiElement
@@ -51,7 +48,7 @@ internal object FirPositionCompletionContextDetector {
action: KtAnalysisSession.() -> Unit action: KtAnalysisSession.() -> Unit
) { ) {
return when (positionContext) { return when (positionContext) {
is FirNameReferencePositionContext -> analyseInFakeAnalysisSession( is FirNameReferencePositionContext -> analyseInDependedAnalysisSession(
basicContext.originalKtFile, basicContext.originalKtFile,
positionContext.nameExpression, positionContext.nameExpression,
action action
@@ -47,7 +47,7 @@ abstract class KtAnalysisSession(final override val token: ValidityToken) : Vali
override val analysisSession: KtAnalysisSession get() = this override val analysisSession: KtAnalysisSession get() = this
abstract fun createContextDependentCopy(originalKtFile: KtFile, fakeKtElement: KtElement): KtAnalysisSession abstract fun createContextDependentCopy(originalKtFile: KtFile, dependencyKtElement: KtElement): KtAnalysisSession
internal val smartCastProvider: KtSmartCastProvider get() = smartCastProviderImpl internal val smartCastProvider: KtSmartCastProvider get() = smartCastProviderImpl
protected abstract val smartCastProviderImpl: KtSmartCastProvider protected abstract val smartCastProviderImpl: KtSmartCastProvider
@@ -36,10 +36,14 @@ abstract class KtAnalysisSessionProvider : Disposable {
abstract fun getAnalysisSession(contextElement: KtElement, factory: ValidityTokenFactory): KtAnalysisSession abstract fun getAnalysisSession(contextElement: KtElement, factory: ValidityTokenFactory): KtAnalysisSession
@InvalidWayOfUsingAnalysisSession @InvalidWayOfUsingAnalysisSession
inline fun <R> analyseInFakeAnalysisSession(originalFile: KtFile, fakeExpresion: KtElement, action: KtAnalysisSession.() -> R): R { inline fun <R> analyseInDependedAnalysisSession(
val fakeAnalysisSession = getAnalysisSession(originalFile, ReadActionConfinementValidityTokenFactory) originalFile: KtFile,
.createContextDependentCopy(originalFile, fakeExpresion) dependencyExpression: KtElement,
return analyse(fakeAnalysisSession, ReadActionConfinementValidityTokenFactory, action) action: KtAnalysisSession.() -> R
): R {
val dependedAnalysisSession = getAnalysisSession(originalFile, ReadActionConfinementValidityTokenFactory)
.createContextDependentCopy(originalFile, dependencyExpression)
return analyse(dependedAnalysisSession, ReadActionConfinementValidityTokenFactory, action)
} }
@InvalidWayOfUsingAnalysisSession @InvalidWayOfUsingAnalysisSession
@@ -89,8 +93,12 @@ inline fun <R> analyseWithCustomToken(
contextElement.project.service<KtAnalysisSessionProvider>().analyse(contextElement, tokenFactory, action) contextElement.project.service<KtAnalysisSessionProvider>().analyse(contextElement, tokenFactory, action)
@OptIn(InvalidWayOfUsingAnalysisSession::class) @OptIn(InvalidWayOfUsingAnalysisSession::class)
inline fun <R> analyseInFakeAnalysisSession(originalFile: KtFile, fakeExpresion: KtElement, action: KtAnalysisSession.() -> R): R = inline fun <R> analyseInDependedAnalysisSession(
originalFile.project.service<KtAnalysisSessionProvider>().analyseInFakeAnalysisSession(originalFile, fakeExpresion, action) originalFile: KtFile,
dependencyExpression: KtElement,
action: KtAnalysisSession.() -> R
): R =
originalFile.project.service<KtAnalysisSessionProvider>().analyseInDependedAnalysisSession(originalFile, dependencyExpression, action)
/** /**
* Execute given [action] in [KtAnalysisSession] context like [analyse] does but execute it in read action * Execute given [action] in [KtAnalysisSession] context like [analyse] does but execute it in read action
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.idea.fir.low.level.api package org.jetbrains.kotlin.idea.fir.low.level.api
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
@@ -14,31 +13,34 @@ import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.InternalForInline import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.InternalForInline
import org.jetbrains.kotlin.idea.fir.low.level.api.api.DiagnosticCheckerFilter import org.jetbrains.kotlin.idea.fir.low.level.api.api.DiagnosticCheckerFilter
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirTowerDataContextCollector
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.getClosestAvailableParentContext
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.FirElementsRecorder import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.FirElementsRecorder
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider
import org.jetbrains.kotlin.idea.fir.low.level.api.util.containingKtFileIfAny import org.jetbrains.kotlin.idea.fir.low.level.api.util.containingKtFileIfAny
import org.jetbrains.kotlin.idea.fir.low.level.api.util.originalKtFile import org.jetbrains.kotlin.idea.fir.low.level.api.util.originalKtFile
import org.jetbrains.kotlin.idea.frontend.api.components.KtDiagnosticCheckerFilter
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtLambdaExpression import org.jetbrains.kotlin.psi.KtLambdaExpression
internal class FirModuleResolveStateForCompletion( internal class FirModuleResolveStateDepended(
override val project: Project, dependencyFirDeclaration: FirDeclaration,
private val originalState: FirModuleResolveStateImpl originalFirFile: FirFile,
private val originalState: FirModuleResolveStateImpl,
) : FirModuleResolveState() { ) : FirModuleResolveState() {
override val moduleInfo: IdeaModuleInfo get() = originalState.moduleInfo
override val project: Project get() = originalState.project
override val moduleInfo: IdeaModuleInfo get() = originalState.moduleInfo
override val rootModuleSession get() = originalState.rootModuleSession override val rootModuleSession get() = originalState.rootModuleSession
private val fileStructureCache = originalState.fileStructureCache private val fileStructureCache = originalState.fileStructureCache
private val completionMapping = mutableMapOf<KtElement, FirElement>() private val completionMapping = mutableMapOf<KtElement, FirElement>()
private val collector = FirTowerDataContextCollector()
override fun getSessionFor(moduleInfo: IdeaModuleInfo): FirSession = override fun getSessionFor(moduleInfo: IdeaModuleInfo): FirSession =
originalState.getSessionFor(moduleInfo) originalState.getSessionFor(moduleInfo)
@@ -57,24 +59,8 @@ internal class FirModuleResolveStateForCompletion(
override fun getFirFile(ktFile: KtFile): FirFile = override fun getFirFile(ktFile: KtFile): FirFile =
originalState.getFirFile(ktFile) originalState.getFirFile(ktFile)
override fun isFirFileBuilt(ktFile: KtFile): Boolean { override fun <D : FirDeclaration> resolvedFirToPhase(declaration: D, toPhase: FirResolvePhase): D =
error("Should not be called in in completion") originalState.resolvedFirToPhase(declaration, toPhase)
}
override fun recordPsiToFirMappingsForCompletionFrom(fir: FirDeclaration, firFile: FirFile, ktFile: KtFile) {
synchronized(completionMapping) { fir.accept(FirElementsRecorder(), completionMapping) }
}
override fun <D : FirDeclaration> resolvedFirToPhase(declaration: D, toPhase: FirResolvePhase): D {
return originalState.resolvedFirToPhase(declaration, toPhase)
}
override fun lazyResolveDeclarationForCompletion(
firDeclaration: FirDeclaration,
containerFirFile: FirFile,
) {
originalState.lazyResolveDeclarationForCompletion(firDeclaration, containerFirFile)
}
override fun getFirFile(declaration: FirDeclaration, cache: ModuleFileCache): FirFile? { override fun getFirFile(declaration: FirDeclaration, cache: ModuleFileCache): FirFile? {
val ktFile = declaration.containingKtFileIfAny ?: return null val ktFile = declaration.containingKtFileIfAny ?: return null
@@ -83,34 +69,37 @@ internal class FirModuleResolveStateForCompletion(
return null return null
} }
override fun getDiagnostics(element: KtElement, filter: DiagnosticCheckerFilter): List<FirPsiDiagnostic<*>> =
TODO("Diagnostics are not implemented for depended state")
override fun getDiagnostics(element: KtElement, filter: DiagnosticCheckerFilter): List<FirPsiDiagnostic<*>> { override fun collectDiagnosticsForFile(ktFile: KtFile, filter: DiagnosticCheckerFilter): Collection<FirPsiDiagnostic<*>> =
error("Diagnostics should not be retrieved in completion") TODO("Diagnostics are not implemented for depended state")
}
override fun collectDiagnosticsForFile(ktFile: KtFile, filter: DiagnosticCheckerFilter): Collection<FirPsiDiagnostic<*>> {
error("Diagnostics should not be retrieved in completion")
}
@OptIn(InternalForInline::class) @OptIn(InternalForInline::class)
override fun findNonLocalSourceFirDeclaration(ktDeclaration: KtDeclaration): FirDeclaration { override fun findSourceFirDeclaration(ktDeclaration: KtLambdaExpression): FirDeclaration =
error("Should not be used in completion") originalState.findSourceFirDeclaration(ktDeclaration)
}
@OptIn(InternalForInline::class) @OptIn(InternalForInline::class)
override fun findSourceFirDeclaration(ktDeclaration: KtDeclaration): FirDeclaration { override fun findSourceFirDeclaration(ktDeclaration: KtDeclaration): FirDeclaration =
return originalState.findSourceFirDeclaration(ktDeclaration) originalState.findSourceFirDeclaration(ktDeclaration)
}
@OptIn(InternalForInline::class)
override fun findSourceFirDeclaration(ktDeclaration: KtLambdaExpression): FirDeclaration {
error("Should not be used in completion")
}
override fun getBuiltFirFileOrNull(ktFile: KtFile): FirFile? {
error("Should not be used in completion")
}
override fun getTowerDataContextForElement(element: KtElement): FirTowerDataContext? = override fun getTowerDataContextForElement(element: KtElement): FirTowerDataContext? =
originalState.getTowerDataContextForElement(element) collector.getClosestAvailableParentContext(element) ?: originalState.getTowerDataContextForElement(element)
init {
originalState.firFileBuilder.runCustomResolveWithPCECheck(originalFirFile, rootModuleSession.cache) {
originalState.firLazyDeclarationResolver.runLazyResolveWithoutLock(
dependencyFirDeclaration,
rootModuleSession.cache,
originalFirFile,
originalFirFile.session.firIdeProvider,
fromPhase = dependencyFirDeclaration.resolvePhase,
toPhase = FirResolvePhase.BODY_RESOLVE,
towerDataContextCollector = collector,
checkPCE = true
)
}
synchronized(completionMapping) { dependencyFirDeclaration.accept(FirElementsRecorder(), completionMapping) }
}
} }
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.idea.fir.low.level.api package org.jetbrains.kotlin.idea.fir.low.level.api
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
@@ -74,24 +75,12 @@ internal class FirModuleResolveStateImpl(
override fun collectDiagnosticsForFile(ktFile: KtFile, filter: DiagnosticCheckerFilter): Collection<FirPsiDiagnostic<*>> = override fun collectDiagnosticsForFile(ktFile: KtFile, filter: DiagnosticCheckerFilter): Collection<FirPsiDiagnostic<*>> =
diagnosticsCollector.collectDiagnosticsForFile(ktFile, filter) diagnosticsCollector.collectDiagnosticsForFile(ktFile, filter)
override fun getBuiltFirFileOrNull(ktFile: KtFile): FirFile? { @TestOnly
internal fun getBuiltFirFileOrNull(ktFile: KtFile): FirFile? {
val cache = sessionProvider.getModuleCache(ktFile.getModuleInfo() as ModuleSourceInfo) val cache = sessionProvider.getModuleCache(ktFile.getModuleInfo() as ModuleSourceInfo)
return firFileBuilder.getBuiltFirFileOrNull(ktFile, cache) return firFileBuilder.getBuiltFirFileOrNull(ktFile, cache)
} }
override fun recordPsiToFirMappingsForCompletionFrom(fir: FirDeclaration, firFile: FirFile, ktFile: KtFile) {
error("Should be called only from FirModuleResolveStateForCompletion")
}
@OptIn(InternalForInline::class)
override fun findNonLocalSourceFirDeclaration(
ktDeclaration: KtDeclaration,
): FirDeclaration = ktDeclaration.findSourceNonLocalFirDeclaration(
firFileBuilder,
rootModuleSession.firIdeProvider.symbolProvider,
sessionProvider.getModuleCache(ktDeclaration.getModuleInfo() as ModuleSourceInfo)
)
@OptIn(InternalForInline::class) @OptIn(InternalForInline::class)
override fun findSourceFirDeclaration(ktDeclaration: KtDeclaration): FirDeclaration = override fun findSourceFirDeclaration(ktDeclaration: KtDeclaration): FirDeclaration =
findSourceFirDeclarationByExpression(ktDeclaration) findSourceFirDeclarationByExpression(ktDeclaration)
@@ -104,25 +93,28 @@ internal class FirModuleResolveStateImpl(
* [ktDeclaration] should be either [KtDeclaration] or [KtLambdaExpression] * [ktDeclaration] should be either [KtDeclaration] or [KtLambdaExpression]
*/ */
private fun findSourceFirDeclarationByExpression(ktDeclaration: KtExpression): FirDeclaration { private fun findSourceFirDeclarationByExpression(ktDeclaration: KtExpression): FirDeclaration {
val nonLocalFirDeclaration = ktDeclaration.getNonLocalContainingOrThisDeclaration() val nonLocalNamedDeclaration = ktDeclaration.getNonLocalContainingOrThisDeclaration()
?: error("Declaration should have non-local container${ktDeclaration.getElementTextInContext()}") ?: error("Declaration should have non-local container${ktDeclaration.getElementTextInContext()}")
if (ktDeclaration == nonLocalFirDeclaration) return findNonLocalSourceFirDeclaration(ktDeclaration as KtDeclaration)
val container = nonLocalFirDeclaration.findSourceNonLocalFirDeclaration( val nonLocalFirForNamedDeclaration = nonLocalNamedDeclaration.findSourceNonLocalFirDeclaration(
firFileBuilder, firFileBuilder,
rootModuleSession.firIdeProvider.symbolProvider, rootModuleSession.firIdeProvider.symbolProvider,
sessionProvider.getModuleCache(ktDeclaration.getModuleInfo() as ModuleSourceInfo) sessionProvider.getModuleCache(ktDeclaration.getModuleInfo() as ModuleSourceInfo)
) )
if (container.resolvePhase < FirResolvePhase.BODY_RESOLVE) {
val cache = (container.moduleData.session as FirIdeSourcesSession).cache if (ktDeclaration == nonLocalNamedDeclaration) return nonLocalFirForNamedDeclaration
if (nonLocalFirForNamedDeclaration.resolvePhase < FirResolvePhase.BODY_RESOLVE) {
val cache = (nonLocalFirForNamedDeclaration.moduleData.session as FirIdeSourcesSession).cache
firLazyDeclarationResolver.lazyResolveDeclaration( firLazyDeclarationResolver.lazyResolveDeclaration(
container, nonLocalFirForNamedDeclaration,
cache, cache,
FirResolvePhase.BODY_RESOLVE, FirResolvePhase.BODY_RESOLVE,
checkPCE = false, /*TODO*/ checkPCE = false, /*TODO*/
towerDataContextCollector = collector, towerDataContextCollector = collector,
) )
} }
val firDeclaration = FirElementFinder.findElementIn<FirDeclaration>(container) { firDeclaration -> val firDeclaration = FirElementFinder.findElementIn<FirDeclaration>(nonLocalFirForNamedDeclaration) { firDeclaration ->
when (val realPsi = firDeclaration.realPsi) { when (val realPsi = firDeclaration.realPsi) {
is KtObjectLiteralExpression -> realPsi.objectDeclaration == ktDeclaration is KtObjectLiteralExpression -> realPsi.objectDeclaration == ktDeclaration
is KtFunctionLiteral -> realPsi.parent == ktDeclaration is KtFunctionLiteral -> realPsi.parent == ktDeclaration
@@ -133,12 +125,6 @@ internal class FirModuleResolveStateImpl(
?: error("FirDeclaration was not found for\n${ktDeclaration.getElementTextInContext()}") ?: error("FirDeclaration was not found for\n${ktDeclaration.getElementTextInContext()}")
} }
override fun isFirFileBuilt(ktFile: KtFile): Boolean {
val moduleSourceInfo = ktFile.getModuleInfo() as? ModuleSourceInfo ?: return true
val cache = sessionProvider.getModuleCache(moduleSourceInfo)
return firFileBuilder.isFirFileBuilt(ktFile, cache)
}
override fun <D : FirDeclaration> resolvedFirToPhase(declaration: D, toPhase: FirResolvePhase): D { override fun <D : FirDeclaration> resolvedFirToPhase(declaration: D, toPhase: FirResolvePhase): D {
val fileCache = when (val session = declaration.moduleData.session) { val fileCache = when (val session = declaration.moduleData.session) {
is FirIdeSourcesSession -> session.cache is FirIdeSourcesSession -> session.cache
@@ -154,24 +140,6 @@ internal class FirModuleResolveStateImpl(
return declaration return declaration
} }
override fun lazyResolveDeclarationForCompletion(
firDeclaration: FirDeclaration,
containerFirFile: FirFile,
) {
firFileBuilder.runCustomResolveWithPCECheck(containerFirFile, rootModuleSession.cache) {
firLazyDeclarationResolver.runLazyResolveWithoutLock(
firDeclaration,
rootModuleSession.cache,
containerFirFile,
containerFirFile.moduleData.session.firIdeProvider,
fromPhase = firDeclaration.resolvePhase,
toPhase = FirResolvePhase.BODY_RESOLVE,
towerDataContextCollector = collector,
checkPCE = true
)
}
}
override fun getFirFile(declaration: FirDeclaration, cache: ModuleFileCache): FirFile? = override fun getFirFile(declaration: FirDeclaration, cache: ModuleFileCache): FirFile? =
cache.getContainerFirFile(declaration) cache.getContainerFirFile(declaration)
@@ -18,31 +18,31 @@ import org.jetbrains.kotlin.psi.*
object DeclarationCopyBuilder { object DeclarationCopyBuilder {
fun createDeclarationCopy( fun createDeclarationCopy(
originalFirDeclaration: FirDeclaration, originalFirDeclaration: FirDeclaration,
fakeKtDeclaration: KtDeclaration, copiedKtDeclaration: KtDeclaration,
state: FirModuleResolveState, state: FirModuleResolveState,
): FirDeclaration { ): FirDeclaration {
return when (fakeKtDeclaration) { return when (copiedKtDeclaration) {
is KtNamedFunction -> createFunctionCopy( is KtNamedFunction -> createFunctionCopy(
fakeKtDeclaration, copiedKtDeclaration,
originalFirDeclaration as FirSimpleFunction, originalFirDeclaration as FirSimpleFunction,
state state
) )
is KtProperty -> createPropertyCopy( is KtProperty -> createPropertyCopy(
fakeKtDeclaration, copiedKtDeclaration,
originalFirDeclaration as FirProperty, originalFirDeclaration as FirProperty,
state state
) )
is KtClassOrObject -> createClassCopy( is KtClassOrObject -> createClassCopy(
fakeKtDeclaration, copiedKtDeclaration,
originalFirDeclaration as FirRegularClass, originalFirDeclaration as FirRegularClass,
state state
) )
is KtTypeAlias -> createTypeAliasCopy( is KtTypeAlias -> createTypeAliasCopy(
fakeKtDeclaration, copiedKtDeclaration,
originalFirDeclaration as FirTypeAlias, originalFirDeclaration as FirTypeAlias,
state state
) )
else -> error("Unsupported declaration ${fakeKtDeclaration::class.simpleName}") else -> error("Unsupported declaration ${copiedKtDeclaration::class.simpleName}")
} }
} }
@@ -64,11 +64,11 @@ object DeclarationCopyBuilder {
} }
private fun createClassCopy( private fun createClassCopy(
fakeKtClassOrObject: KtClassOrObject, copiedKtClassOrObject: KtClassOrObject,
originalFirClass: FirRegularClass, originalFirClass: FirRegularClass,
state: FirModuleResolveState, state: FirModuleResolveState,
): FirRegularClass { ): FirRegularClass {
val builtClass = createCopy(fakeKtClassOrObject, originalFirClass) val builtClass = createCopy(copiedKtClassOrObject, originalFirClass)
return buildRegularClassCopy(originalFirClass) { return buildRegularClassCopy(originalFirClass) {
declarations.clear() declarations.clear()
@@ -79,11 +79,11 @@ object DeclarationCopyBuilder {
} }
private fun createTypeAliasCopy( private fun createTypeAliasCopy(
fakeKtTypeAlias: KtTypeAlias, copiedKtTypeAlias: KtTypeAlias,
originalFirTypeAlias: FirTypeAlias, originalFirTypeAlias: FirTypeAlias,
state: FirModuleResolveState, state: FirModuleResolveState,
): FirTypeAlias { ): FirTypeAlias {
val builtTypeAlias = createCopy(fakeKtTypeAlias, originalFirTypeAlias) val builtTypeAlias = createCopy(copiedKtTypeAlias, originalFirTypeAlias)
return buildTypeAliasCopy(originalFirTypeAlias) { return buildTypeAliasCopy(originalFirTypeAlias) {
expandedTypeRef = builtTypeAlias.expandedTypeRef expandedTypeRef = builtTypeAlias.expandedTypeRef
@@ -135,14 +135,14 @@ object DeclarationCopyBuilder {
} }
internal inline fun <reified T : FirDeclaration> createCopy( internal inline fun <reified T : FirDeclaration> createCopy(
fakeKtDeclaration: KtDeclaration, copiedKtDeclaration: KtDeclaration,
originalFirDeclaration: T, originalFirDeclaration: T,
): T { ): T {
return RawFirFragmentForLazyBodiesBuilder.build( return RawFirFragmentForLazyBodiesBuilder.build(
session = originalFirDeclaration.moduleData.session, session = originalFirDeclaration.moduleData.session,
baseScopeProvider = originalFirDeclaration.moduleData.session.firIdeProvider.kotlinScopeProvider, baseScopeProvider = originalFirDeclaration.moduleData.session.firIdeProvider.kotlinScopeProvider,
designation = originalFirDeclaration.collectDesignation().fullDesignation, designation = originalFirDeclaration.collectDesignation().fullDesignation,
declaration = fakeKtDeclaration declaration = copiedKtDeclaration
) as T ) as T
} }
@@ -34,8 +34,6 @@ abstract class FirModuleResolveState {
internal abstract fun getFirFile(ktFile: KtFile): FirFile internal abstract fun getFirFile(ktFile: KtFile): FirFile
internal abstract fun isFirFileBuilt(ktFile: KtFile): Boolean
internal abstract fun getDiagnostics(element: KtElement, filter: DiagnosticCheckerFilter): List<FirPsiDiagnostic<*>> internal abstract fun getDiagnostics(element: KtElement, filter: DiagnosticCheckerFilter): List<FirPsiDiagnostic<*>>
internal abstract fun collectDiagnosticsForFile(ktFile: KtFile, filter: DiagnosticCheckerFilter): Collection<FirPsiDiagnostic<*>> internal abstract fun collectDiagnosticsForFile(ktFile: KtFile, filter: DiagnosticCheckerFilter): Collection<FirPsiDiagnostic<*>>
@@ -56,14 +54,6 @@ abstract class FirModuleResolveState {
} }
} }
@TestOnly
internal abstract fun getBuiltFirFileOrNull(ktFile: KtFile): FirFile?
@InternalForInline
abstract fun findNonLocalSourceFirDeclaration(
ktDeclaration: KtDeclaration,
): FirDeclaration
@InternalForInline @InternalForInline
abstract fun findSourceFirDeclaration( abstract fun findSourceFirDeclaration(
ktDeclaration: KtDeclaration, ktDeclaration: KtDeclaration,
@@ -74,18 +64,8 @@ abstract class FirModuleResolveState {
ktDeclaration: KtLambdaExpression, ktDeclaration: KtLambdaExpression,
): FirDeclaration ): FirDeclaration
// todo temporary, used only in completion
internal abstract fun recordPsiToFirMappingsForCompletionFrom(fir: FirDeclaration, firFile: FirFile, ktFile: KtFile)
internal abstract fun <D : FirDeclaration> resolvedFirToPhase(declaration: D, toPhase: FirResolvePhase): D internal abstract fun <D : FirDeclaration> resolvedFirToPhase(declaration: D, toPhase: FirResolvePhase): D
// todo temporary, used only in completion
internal abstract fun lazyResolveDeclarationForCompletion(
firDeclaration: FirDeclaration,
containerFirFile: FirFile,
)
internal abstract fun getFirFile(declaration: FirDeclaration, cache: ModuleFileCache): FirFile? internal abstract fun getFirFile(declaration: FirDeclaration, cache: ModuleFileCache): FirFile?
abstract fun getTowerDataContextForElement(element: KtElement): FirTowerDataContext? abstract fun getTowerDataContextForElement(element: KtElement): FirTowerDataContext?
@@ -1,33 +0,0 @@
/*
* Copyright 2010-2021 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.idea.fir.low.level.api.api
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateForCompletion
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateImpl
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtFile
object LowLevelFirApiFacadeForCompletion {
fun getResolveStateForCompletion(originalState: FirModuleResolveState): FirModuleResolveState {
check(originalState is FirModuleResolveStateImpl)
return FirModuleResolveStateForCompletion(originalState.project, originalState)
}
fun recordCompletionContextForDeclaration(
firFile: FirFile,
fakeKtDeclaration: KtDeclaration,
originalKtDeclaration: KtDeclaration,
fakeContainingFile: KtFile,
state: FirModuleResolveState,
) {
val originalFirDeclaration = originalKtDeclaration.getOrBuildFirOfType<FirDeclaration>(state)
val fakeFirDeclaration = DeclarationCopyBuilder.createDeclarationCopy(originalFirDeclaration, fakeKtDeclaration, state)
state.lazyResolveDeclarationForCompletion(fakeFirDeclaration, firFile)
state.recordPsiToFirMappingsForCompletionFrom(fakeFirDeclaration, firFile, fakeContainingFile)
}
}
@@ -0,0 +1,78 @@
/*
* Copyright 2010-2021 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.idea.fir.low.level.api.api
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.parentsOfType
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateDepended
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateImpl
import org.jetbrains.kotlin.idea.fir.low.level.api.util.originalDeclaration
import org.jetbrains.kotlin.idea.util.getElementTextInContext
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
object LowLevelFirApiFacadeForDependentCopy {
private fun KtDeclaration.canBeEnclosingDeclaration(): Boolean = when (this) {
is KtNamedFunction -> isTopLevel || containingClassOrObject?.isLocal == false
is KtProperty -> isTopLevel || containingClassOrObject?.isLocal == false
is KtClassOrObject -> !isLocal
is KtTypeAlias -> isTopLevel() || containingClassOrObject?.isLocal == false
else -> false
}
private fun findEnclosingNonLocalDeclaration(position: KtElement): KtNamedDeclaration? =
position.parentsOfType<KtNamedDeclaration>().firstOrNull { ktDeclaration ->
ktDeclaration.canBeEnclosingDeclaration()
}
private fun <T : KtElement> locateDeclarationInFileByOffset(offsetElement: T, file: KtFile): T? {
val elementOffset = offsetElement.textOffset
val elementAtOffset = file.findElementAt(elementOffset) ?: return null
return PsiTreeUtil.getParentOfType(elementAtOffset, offsetElement::class.java, false)?.takeIf { it.textOffset == elementOffset }
}
private fun recordOriginalDeclaration(targetDeclaration: KtNamedDeclaration, originalDeclaration: KtNamedDeclaration) {
require(!targetDeclaration.isPhysical)
require(originalDeclaration.containingKtFile !== targetDeclaration.containingKtFile)
val originalDeclrationParents = originalDeclaration.parentsOfType<KtDeclaration>().toList()
val fakeDeclarationParents = targetDeclaration.parentsOfType<KtDeclaration>().toList()
originalDeclrationParents.zip(fakeDeclarationParents) { original, fake ->
fake.originalDeclaration = original
}
}
fun getResolveStateForDependedCopy(
originalState: FirModuleResolveState,
originalKtFile: KtFile,
dependencyKtElement: KtElement
): FirModuleResolveState {
require(originalState is FirModuleResolveStateImpl)
val dependencyNonLocalDeclaration = findEnclosingNonLocalDeclaration(dependencyKtElement)
?: error("Cannot find enclosing declaration for ${dependencyKtElement.getElementTextInContext()}")
val originalNonLocalDeclaration = locateDeclarationInFileByOffset(dependencyNonLocalDeclaration, originalKtFile)
?: error("Cannot find original function matching to ${dependencyNonLocalDeclaration.getElementTextInContext()} in $originalKtFile")
recordOriginalDeclaration(
targetDeclaration = dependencyNonLocalDeclaration,
originalDeclaration = originalNonLocalDeclaration
)
val originalFirDeclaration = originalNonLocalDeclaration.getOrBuildFirOfType<FirDeclaration>(originalState)
val copiedFirDeclaration = DeclarationCopyBuilder.createDeclarationCopy(
originalFirDeclaration = originalFirDeclaration,
copiedKtDeclaration = dependencyNonLocalDeclaration,
state = originalState
)
val originalFirFile = originalState.getFirFile(originalKtFile)
return FirModuleResolveStateDepended(copiedFirDeclaration, originalFirFile, originalState)
}
}
@@ -36,6 +36,7 @@ abstract class AbstractFirLazyDeclarationResolveTest : KotlinLightCodeInsightFix
val declarationToResolve = lazyDeclarations.firstOrNull { it.name?.lowercase() == "resolveme" } val declarationToResolve = lazyDeclarations.firstOrNull { it.name?.lowercase() == "resolveme" }
?: error("declaration with name `resolveMe` was not found") ?: error("declaration with name `resolveMe` was not found")
resolveWithClearCaches(ktFile) { firModuleResolveState -> resolveWithClearCaches(ktFile) { firModuleResolveState ->
check(firModuleResolveState is FirModuleResolveStateImpl)
val rendered = declarationToResolve.withFirDeclaration( val rendered = declarationToResolve.withFirDeclaration(
firModuleResolveState, firModuleResolveState,
FirResolvePhase.BODY_RESOLVE FirResolvePhase.BODY_RESOLVE
@@ -7,12 +7,9 @@ package org.jetbrains.kotlin.idea.frontend.api.fir
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.symbolProvider import org.jetbrains.kotlin.fir.resolve.symbolProvider
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState import org.jetbrains.kotlin.idea.fir.low.level.api.api.*
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacadeForCompletion
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getFirFile
import org.jetbrains.kotlin.idea.frontend.api.InvalidWayOfUsingAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.InvalidWayOfUsingAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.components.KtVisibilityChecker import org.jetbrains.kotlin.idea.frontend.api.components.KtVisibilityChecker
@@ -20,8 +17,6 @@ import org.jetbrains.kotlin.idea.frontend.api.components.KtSymbolDeclarationRend
import org.jetbrains.kotlin.idea.frontend.api.fir.components.* import org.jetbrains.kotlin.idea.frontend.api.fir.components.*
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirOverrideInfoProvider import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirOverrideInfoProvider
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbolProvider import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbolProvider
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.EnclosingDeclarationContext
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.recordCompletionContext
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.threadLocal import org.jetbrains.kotlin.idea.frontend.api.fir.utils.threadLocal
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
@@ -33,9 +28,14 @@ private constructor(
val firResolveState: FirModuleResolveState, val firResolveState: FirModuleResolveState,
internal val firSymbolBuilder: KtSymbolByFirBuilder, internal val firSymbolBuilder: KtSymbolByFirBuilder,
token: ValidityToken, token: ValidityToken,
val context: KtFirAnalysisSessionContext, private val mode: AnalysisSessionMode,
) : KtAnalysisSession(token) { ) : KtAnalysisSession(token) {
private enum class AnalysisSessionMode {
REGULAR,
DEPENDENT_COPY
}
override val smartCastProviderImpl = KtFirSmartcastProvider(this, token) override val smartCastProviderImpl = KtFirSmartcastProvider(this, token)
override val expressionTypeProviderImpl = KtFirExpressionTypeProvider(this, token) override val expressionTypeProviderImpl = KtFirExpressionTypeProvider(this, token)
@@ -73,19 +73,23 @@ private constructor(
override val subtypingComponentImpl = KtFirSubtypingComponent(this, token) override val subtypingComponentImpl = KtFirSubtypingComponent(this, token)
override fun createContextDependentCopy(originalKtFile: KtFile, fakeKtElement: KtElement): KtAnalysisSession { override fun createContextDependentCopy(originalKtFile: KtFile, dependencyKtElement: KtElement): KtAnalysisSession {
check(context == KtFirAnalysisSessionContext.DefaultContext) { check(mode == AnalysisSessionMode.REGULAR) {
"Cannot create context-dependent copy of KtAnalysis session from a context dependent one" "Cannot create context-dependent copy of KtAnalysis session from a context dependent one"
} }
val contextResolveState = LowLevelFirApiFacadeForCompletion.getResolveStateForCompletion(firResolveState)
val originalFirFile = originalKtFile.getFirFile(firResolveState) val contextResolveState = LowLevelFirApiFacadeForDependentCopy.getResolveStateForDependedCopy(
val context = KtFirAnalysisSessionContext.FakeFileContext(originalKtFile, originalFirFile, fakeKtElement, contextResolveState) originalState = firResolveState,
originalKtFile = originalKtFile,
dependencyKtElement = dependencyKtElement
)
return KtFirAnalysisSession( return KtFirAnalysisSession(
project, project,
contextResolveState, contextResolveState,
firSymbolBuilder.createReadOnlyCopy(contextResolveState), firSymbolBuilder.createReadOnlyCopy(contextResolveState),
token, token,
context AnalysisSessionMode.DEPENDENT_COPY
) )
} }
@@ -110,28 +114,8 @@ private constructor(
firResolveState, firResolveState,
firSymbolBuilder, firSymbolBuilder,
token, token,
KtFirAnalysisSessionContext.DefaultContext AnalysisSessionMode.REGULAR
) )
} }
} }
} }
internal sealed class KtFirAnalysisSessionContext {
object DefaultContext : KtFirAnalysisSessionContext()
class FakeFileContext(
originalFile: KtFile,
firFile: FirFile,
fakeContextElement: KtElement,
fakeModuleResolveState: FirModuleResolveState
) : KtFirAnalysisSessionContext() {
init {
require(!fakeContextElement.isPhysical)
val enclosingContext = EnclosingDeclarationContext.detect(originalFile, fakeContextElement)
enclosingContext.recordCompletionContext(firFile, fakeModuleResolveState)
}
val fakeKtFile = fakeContextElement.containingKtFile
}
}
@@ -1,79 +0,0 @@
/*
* Copyright 2010-2020 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.idea.frontend.api.fir.utils
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.parentsOfType
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacadeForCompletion
import org.jetbrains.kotlin.idea.fir.low.level.api.util.originalDeclaration
import org.jetbrains.kotlin.idea.util.getElementTextInContext
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import kotlin.reflect.KClass
internal sealed class EnclosingDeclarationContext {
abstract val fakeKtEnclosingDeclaration: KtDeclaration
abstract val originalKtEnclosingDeclaration: KtDeclaration
companion object {
private fun KtDeclaration.canBeEnclosingDeclaration(): Boolean = when (this) {
is KtNamedFunction -> isTopLevel || containingClassOrObject?.isLocal == false
is KtProperty -> isTopLevel || containingClassOrObject?.isLocal == false
is KtClassOrObject -> !isLocal
is KtTypeAlias -> isTopLevel() || containingClassOrObject?.isLocal == false
else -> false
}
fun detect(originalFile: KtFile, positionInFakeFile: KtElement): EnclosingDeclarationContext {
val fakeKtDeclaration = positionInFakeFile.parentsOfType<KtNamedDeclaration>().firstOrNull { ktDeclaration ->
ktDeclaration.canBeEnclosingDeclaration()
}
if (fakeKtDeclaration != null) {
val originalDeclaration = findMatchingElementInCopy(fakeKtDeclaration, originalFile)
?: error("Cannot find original declaration matching to ${fakeKtDeclaration.getElementTextInContext()} in $originalFile")
recordOriginalDeclaration(originalDeclaration, fakeKtDeclaration)
return EnclosingDeclarationContextImpl(fakeKtDeclaration, originalDeclaration)
}
error("Cannot find enclosing declaration for ${positionInFakeFile.getElementTextInContext()}")
}
private fun recordOriginalDeclaration(originalDeclaration: KtNamedDeclaration, fakeDeclaration: KtNamedDeclaration) {
require(!fakeDeclaration.isPhysical)
require(originalDeclaration.containingKtFile !== fakeDeclaration.containingKtFile)
val originalDeclrationParents = originalDeclaration.parentsOfType<KtDeclaration>().toList()
val fakeDeclarationParents = fakeDeclaration.parentsOfType<KtDeclaration>().toList()
originalDeclrationParents.zip(fakeDeclarationParents) { original, fake ->
fake.originalDeclaration = original
}
}
}
}
internal class EnclosingDeclarationContextImpl(
override val fakeKtEnclosingDeclaration: KtDeclaration,
override val originalKtEnclosingDeclaration: KtDeclaration
) : EnclosingDeclarationContext()
internal fun EnclosingDeclarationContext.recordCompletionContext(originalFirFile: FirFile, firResolveState: FirModuleResolveState) {
LowLevelFirApiFacadeForCompletion.recordCompletionContextForDeclaration(
originalFirFile,
fakeKtEnclosingDeclaration,
originalKtEnclosingDeclaration,
state = firResolveState,
fakeContainingFile = fakeKtEnclosingDeclaration.containingKtFile
)
}
fun <T : KtElement> findMatchingElementInCopy(element: T, copy: KtFile): T? {
val elementOffset = element.textOffset
val elementAtOffset = copy.findElementAt(elementOffset) ?: return null
return PsiTreeUtil.getParentOfType(elementAtOffset, element::class.java, false)?.takeIf { it.textOffset == elementOffset }
}