[LL FIR] introduce lazyResolveRecursively API

FirFile after KT-56683 has its own phases and resolution logic,
so we should have a separate API for lazy resolution for FirFile and for
the entire file

^KT-61296 Fixed
This commit is contained in:
Dmitrii Gridin
2023-09-11 20:13:40 +02:00
committed by Space Team
parent b77ae5d275
commit 31b36ee766
16 changed files with 120 additions and 55 deletions
@@ -18,8 +18,6 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.DiagnosticCheckerFilt
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDiagnosticsForFile import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDiagnosticsForFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirWholeElementResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.resolve
import org.jetbrains.kotlin.analysis.low.level.api.fir.compile.CodeFragmentCapturedId import org.jetbrains.kotlin.analysis.low.level.api.fir.compile.CodeFragmentCapturedId
import org.jetbrains.kotlin.analysis.low.level.api.fir.compile.CodeFragmentCapturedValueAnalyzer import org.jetbrains.kotlin.analysis.low.level.api.fir.compile.CodeFragmentCapturedValueAnalyzer
import org.jetbrains.kotlin.analysis.low.level.api.fir.compile.CompilationPeerCollector import org.jetbrains.kotlin.analysis.low.level.api.fir.compile.CompilationPeerCollector
@@ -51,6 +49,7 @@ import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.references.FirThisReference import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.references.toResolvedSymbol import org.jetbrains.kotlin.fir.references.toResolvedSymbol
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.PsiIrFileEntry import org.jetbrains.kotlin.ir.PsiIrFileEntry
@@ -291,7 +290,7 @@ internal class KtFirCompilerFacility(
private fun getFullyResolvedFirFile(file: KtFile): FirFile { private fun getFullyResolvedFirFile(file: KtFile): FirFile {
val firFile = file.getOrBuildFirFile(firResolveSession) val firFile = file.getOrBuildFirFile(firResolveSession)
LLFirWholeElementResolveTarget(firFile).resolve(FirResolvePhase.BODY_RESOLVE) firFile.lazyResolveToPhaseRecursively(FirResolvePhase.BODY_RESOLVE)
return firFile return firFile
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. * 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -18,7 +18,6 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile
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.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.fir.psi
@@ -30,9 +29,9 @@ import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedReferenceError import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedReferenceError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedSymbolError import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedSymbolError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedTypeQualifierError import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedTypeQualifierError
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.classId import org.jetbrains.kotlin.fir.types.classId
@@ -59,7 +58,7 @@ internal class KtFirImportOptimizer(
val existingImports = file.importDirectives val existingImports = file.importDirectives
if (existingImports.isEmpty()) return KtImportOptimizerResult() if (existingImports.isEmpty()) return KtImportOptimizerResult()
val firFile = file.getOrBuildFirFile(firResolveSession).apply { lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE) } val firFile = file.getOrBuildFirFile(firResolveSession).apply { lazyResolveToPhaseRecursively(FirResolvePhase.BODY_RESOLVE) }
val explicitlyImportedFqNames = existingImports val explicitlyImportedFqNames = existingImports
.asSequence() .asSequence()
@@ -41,4 +41,16 @@ internal class LLFirLazyDeclarationResolver : FirLazyDeclarationResolver() {
toPhase = toPhase, toPhase = toPhase,
) )
} }
override fun lazyResolveToPhaseRecursively(symbol: FirBasedSymbol<*>, toPhase: FirResolvePhase) {
val fir = symbol.fir
val session = fir.moduleData.session
if (session !is LLFirResolvableModuleSession) return
val moduleComponents = session.moduleComponents
moduleComponents.firModuleLazyDeclarationResolver.lazyResolveRecursively(
target = fir,
scopeSession = moduleComponents.scopeSessionProvider.getScopeSession(),
toPhase = toPhase,
)
}
} }
@@ -16,6 +16,8 @@ class LLFirSingleResolveTarget(
containerClasses: List<FirRegularClass>, containerClasses: List<FirRegularClass>,
target: FirElementWithResolveState, target: FirElementWithResolveState,
) : LLFirResolveTarget(firFile, containerClasses, target) { ) : LLFirResolveTarget(firFile, containerClasses, target) {
constructor(firFile: FirFile) : this(firFile, emptyList(), firFile)
override fun visitTargetElement( override fun visitTargetElement(
element: FirElementWithResolveState, element: FirElementWithResolveState,
visitor: LLFirResolveTargetVisitor, visitor: LLFirResolveTargetVisitor,
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.resolve.providers.firProvider import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
@@ -85,7 +86,7 @@ internal class FirElementBuilder(
private fun getOrBuildFirForKtFile(ktFile: KtFile): FirFile { private fun getOrBuildFirForKtFile(ktFile: KtFile): FirFile {
val firFile = moduleComponents.firFileBuilder.buildRawFirFileWithCaching(ktFile) val firFile = moduleComponents.firFileBuilder.buildRawFirFileWithCaching(ktFile)
firFile.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE) firFile.lazyResolveToPhaseRecursively(FirResolvePhase.BODY_RESOLVE)
return firFile return firFile
} }
@@ -31,7 +31,7 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
* *
* Might resolve additional required declarations. * Might resolve additional required declarations.
* *
* Resolution is performed under the lock specific to each declaration which is going to be resolved. * Resolution is performed under the lock specific to each declaration that is going to be resolved.
* *
* Suitable for body resolve or/and on-air resolve. * Suitable for body resolve or/and on-air resolve.
*/ */
@@ -40,28 +40,8 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
scopeSession: ScopeSession, scopeSession: ScopeSession,
toPhase: FirResolvePhase, toPhase: FirResolvePhase,
) { ) {
val fromPhase = target.resolvePhase if (target.resolvePhase >= toPhase) return
lazyResolve(target, scopeSession, toPhase, LLFirResolveMultiDesignationCollector::getDesignationsToResolve)
/**
* Currently [lazyResolve] on file means [LLFirWholeElementResolveTarget], but also [FirFile] itself
* has [resolvePhase] which does not match with the entire file resolution state.
* This additional [FirFile] condition can be dropped after KT-61296
*/
if (target !is FirFile && fromPhase >= toPhase) return
try {
resolveContainingFileToImports(target)
if (toPhase == FirResolvePhase.IMPORTS) return
lazyResolveTargets(
targets = LLFirResolveMultiDesignationCollector.getDesignationsToResolve(target),
scopeSession = scopeSession,
toPhase = toPhase,
towerDataContextCollector = null,
)
} catch (e: Exception) {
handleExceptionFromResolve(e, target, fromPhase, toPhase)
}
} }
/** /**
@@ -69,7 +49,7 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
* *
* Might resolve additional required declarations. * Might resolve additional required declarations.
* *
* Resolution is performed under the lock specific to each declaration which is going to be resolved. * Resolution is performed under the lock specific to each declaration that is going to be resolved.
* *
* Suitable for body resolve or/and on-air resolve. * Suitable for body resolve or/and on-air resolve.
*/ */
@@ -77,6 +57,32 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
target: FirRegularClass, target: FirRegularClass,
scopeSession: ScopeSession, scopeSession: ScopeSession,
toPhase: FirResolvePhase, toPhase: FirResolvePhase,
) {
lazyResolve(target, scopeSession, toPhase, LLFirResolveMultiDesignationCollector::getDesignationsToResolveWithCallableMembers)
}
/**
* Lazily resolves the [target] with nested declarations to a given [toPhase] recursively.
*
* Might resolve additional required declarations.
*
* Resolution is performed under the lock specific to each declaration that is going to be resolved.
*
* Suitable for body resolve or/and on-air resolve.
*/
fun lazyResolveRecursively(
target: FirElementWithResolveState,
scopeSession: ScopeSession,
toPhase: FirResolvePhase,
) {
lazyResolve(target, scopeSession, toPhase, LLFirResolveMultiDesignationCollector::getDesignationsToResolveRecursively)
}
private inline fun <T : FirElementWithResolveState> lazyResolve(
target: T,
scopeSession: ScopeSession,
toPhase: FirResolvePhase,
resolveTargets: (T) -> List<LLFirResolveTarget>,
) { ) {
val fromPhase = target.resolvePhase val fromPhase = target.resolvePhase
try { try {
@@ -84,7 +90,7 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
if (toPhase == FirResolvePhase.IMPORTS) return if (toPhase == FirResolvePhase.IMPORTS) return
lazyResolveTargets( lazyResolveTargets(
targets = LLFirResolveMultiDesignationCollector.getDesignationsToResolveWithCallableMembers(target), targets = resolveTargets(target),
scopeSession = scopeSession, scopeSession = scopeSession,
toPhase = toPhase, toPhase = toPhase,
towerDataContextCollector = null, towerDataContextCollector = null,
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDesignationWithFile import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDesignationWithFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirClassWithAllCallablesResolveTarget import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirClassWithAllCallablesResolveTarget
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.LLFirSingleResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirWholeElementResolveTarget import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirWholeElementResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.asResolveTarget 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
@@ -19,12 +20,10 @@ import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticPropertyAccessor import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticPropertyAccessor
internal object LLFirResolveMultiDesignationCollector { internal object LLFirResolveMultiDesignationCollector {
fun getDesignationsToResolve(target: FirElementWithResolveState): List<LLFirResolveTarget> { fun getDesignationsToResolve(target: FirElementWithResolveState): List<LLFirResolveTarget> = when (target) {
return when (target) { is FirFile -> listOf(LLFirSingleResolveTarget(target))
is FirFile -> listOf(LLFirWholeElementResolveTarget(target)) else -> getMainDesignationToResolve(target)?.withAnnotationContainer()
else -> getMainDesignationToResolve(target)?.withAnnotationContainer() } ?: emptyList()
} ?: emptyList()
}
fun getDesignationsToResolveWithCallableMembers(target: FirRegularClass): List<LLFirResolveTarget> { fun getDesignationsToResolveWithCallableMembers(target: FirRegularClass): List<LLFirResolveTarget> {
val designation = target.tryCollectDesignationWithFile() ?: return emptyList() val designation = target.tryCollectDesignationWithFile() ?: return emptyList()
@@ -32,6 +31,14 @@ internal object LLFirResolveMultiDesignationCollector {
return resolveTarget.withAnnotationContainer() return resolveTarget.withAnnotationContainer()
} }
fun getDesignationsToResolveRecursively(target: FirElementWithResolveState): List<LLFirResolveTarget> {
if (target is FirFile) return listOf(LLFirWholeElementResolveTarget(target))
val designation = target.tryCollectDesignationWithFile() ?: return emptyList()
val resolveTarget = LLFirWholeElementResolveTarget(designation.firFile, designation.path, target)
return resolveTarget.withAnnotationContainer()
}
private fun LLFirResolveTarget.withAnnotationContainer(): List<LLFirResolveTarget> { private fun LLFirResolveTarget.withAnnotationContainer(): List<LLFirResolveTarget> {
val annotationsContainer = firFile.annotationsContainer val annotationsContainer = firFile.annotationsContainer
if (annotationsContainer?.shouldBeResolved() != true) return listOf(this) if (annotationsContainer?.shouldBeResolved() != true) return listOf(this)
@@ -1,5 +1,5 @@
RAW_FIR: RAW_FIR:
FILE: [ResolvedTo(IMPORTS)] fileElements.kt FILE: [ResolvedTo(RAW_FIR)] fileElements.kt
@FILE:Deprecated[Unresolved](LAZY_EXPRESSION) @FILE:Deprecated[Unresolved](LAZY_EXPRESSION)
@FILE:Anno[Unresolved](LAZY_EXPRESSION) @FILE:Anno[Unresolved](LAZY_EXPRESSION)
[ResolvedTo(RAW_FIR)] annotations container [ResolvedTo(RAW_FIR)] annotations container
@@ -51,11 +51,7 @@ abstract class AbstractFirLazyDeclarationResolveTest : AbstractFirLazyDeclaratio
val session = firResolveSession.useSiteFirSession as LLFirResolvableModuleSession val session = firResolveSession.useSiteFirSession as LLFirResolvableModuleSession
val file = session.moduleComponents.firFileBuilder.buildRawFirFileWithCaching(ktFile) val file = session.moduleComponents.firFileBuilder.buildRawFirFileWithCaching(ktFile)
file to fun(phase: FirResolvePhase) { file to fun(phase: FirResolvePhase) {
session.moduleComponents.firModuleLazyDeclarationResolver.lazyResolveTarget( file.lazyResolveToPhase(phase)
LLFirSingleResolveTarget(file, emptyList(), file),
phase,
towerDataContextCollector = null,
)
} }
} }
else -> { else -> {
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirScriptSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirScriptSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
@@ -83,7 +83,7 @@ abstract class AbstractFirLazyDeclarationResolveTestCase : AbstractLowLevelApiSi
resolveWithClearCaches(ktFile) { llSession -> resolveWithClearCaches(ktFile) { llSession ->
checkSession(llSession) checkSession(llSession)
val firFile = llSession.getOrBuildFirFile(ktFile) val firFile = llSession.getOrBuildFirFile(ktFile)
firFile.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE) firFile.lazyResolveToPhaseRecursively(FirResolvePhase.BODY_RESOLVE)
if (resultBuilder.isNotEmpty()) { if (resultBuilder.isNotEmpty()) {
resultBuilder.appendLine() resultBuilder.appendLine()
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. * 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.transformers.FirSealedClassInheritorsProcessor import org.jetbrains.kotlin.fir.resolve.transformers.FirSealedClassInheritorsProcessor
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.directives.model.DirectiveApplicability import org.jetbrains.kotlin.test.directives.model.DirectiveApplicability
@@ -64,7 +64,7 @@ class SealedClassesInheritorsCaclulatorPreAnalysisHandler(
firFiles: List<FirFile>, firFiles: List<FirFile>,
tmpFirResolveSession: LLFirResolveSession tmpFirResolveSession: LLFirResolveSession
): Map<ClassId, List<ClassId>> { ): Map<ClassId, List<ClassId>> {
firFiles.forEach { it.lazyResolveToPhase(FirResolvePhase.TYPES) } firFiles.forEach { it.lazyResolveToPhaseRecursively(FirResolvePhase.TYPES) }
val inheritorsCollector = FirSealedClassInheritorsProcessor.InheritorsCollector(tmpFirResolveSession.useSiteFirSession) val inheritorsCollector = FirSealedClassInheritorsProcessor.InheritorsCollector(tmpFirResolveSession.useSiteFirSession)
val sealedClassInheritorsMap = mutableMapOf<FirRegularClass, MutableList<ClassId>>() val sealedClassInheritorsMap = mutableMapOf<FirRegularClass, MutableList<ClassId>>()
firFiles.forEach { it.accept(inheritorsCollector, sealedClassInheritorsMap) } firFiles.forEach { it.accept(inheritorsCollector, sealedClassInheritorsMap) }
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerPro
import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
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.psi.psiUtil.parentsWithSelf
@@ -70,7 +70,7 @@ internal fun testInBlockModification(
val declaration = elementToModify.getNonLocalContainingOrThisDeclaration() ?: file val declaration = elementToModify.getNonLocalContainingOrThisDeclaration() ?: file
val firDeclarationBefore = declaration.getOrBuildFirOfType<FirDeclaration>(firSession) val firDeclarationBefore = declaration.getOrBuildFirOfType<FirDeclaration>(firSession)
val declarationToRender = if (dumpFirFile) { val declarationToRender = if (dumpFirFile) {
file.getOrBuildFirFile(firSession).also { it.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE) } file.getOrBuildFirFile(firSession).also { it.lazyResolveToPhaseRecursively(FirResolvePhase.BODY_RESOLVE) }
} else { } else {
firDeclarationBefore firDeclarationBefore
} }
@@ -100,7 +100,7 @@ internal fun testInBlockModification(
val textAfter = if (dumpFirFile) { val textAfter = if (dumpFirFile) {
// we should resolve the entire file instead of the declaration to be sure that this declaration will be // we should resolve the entire file instead of the declaration to be sure that this declaration will be
// resolved by file resolution as well // resolved by file resolution as well
declarationToRender.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE) declarationToRender.lazyResolveToPhaseRecursively(FirResolvePhase.BODY_RESOLVE)
declarationToRender.render() declarationToRender.render()
} else { } else {
declaration.getOrBuildFirOfType<FirDeclaration>(firSession) declaration.getOrBuildFirOfType<FirDeclaration>(firSession)
@@ -16,4 +16,5 @@ object FirDummyCompilerLazyDeclarationResolver : FirLazyDeclarationResolver() {
override fun lazyResolveToPhase(symbol: FirBasedSymbol<*>, toPhase: FirResolvePhase) {} override fun lazyResolveToPhase(symbol: FirBasedSymbol<*>, toPhase: FirResolvePhase) {}
override fun lazyResolveToPhaseWithCallableMembers(symbol: FirClassSymbol<*>, toPhase: FirResolvePhase) {} override fun lazyResolveToPhaseWithCallableMembers(symbol: FirClassSymbol<*>, toPhase: FirResolvePhase) {}
override fun lazyResolveToPhaseRecursively(symbol: FirBasedSymbol<*>, toPhase: FirResolvePhase) {}
} }
@@ -40,8 +40,20 @@ abstract class FirLazyDeclarationResolver : FirSessionComponent {
} }
} }
/**
* @see org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
*/
abstract fun lazyResolveToPhase(symbol: FirBasedSymbol<*>, toPhase: FirResolvePhase) abstract fun lazyResolveToPhase(symbol: FirBasedSymbol<*>, toPhase: FirResolvePhase)
/**
* @see org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseWithCallableMembers
*/
abstract fun lazyResolveToPhaseWithCallableMembers(symbol: FirClassSymbol<*>, toPhase: FirResolvePhase) abstract fun lazyResolveToPhaseWithCallableMembers(symbol: FirClassSymbol<*>, toPhase: FirResolvePhase)
/**
* @see org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
*/
abstract fun lazyResolveToPhaseRecursively(symbol: FirBasedSymbol<*>, toPhase: FirResolvePhase)
} }
class FirLazyResolveContractViolationException( class FirLazyResolveContractViolationException(
@@ -104,7 +116,6 @@ fun FirDeclaration.lazyResolveToPhase(toPhase: FirResolvePhase) {
* *
* @see lazyResolveToPhase * @see lazyResolveToPhase
*/ */
fun FirClassSymbol<*>.lazyResolveToPhaseWithCallableMembers(toPhase: FirResolvePhase) { fun FirClassSymbol<*>.lazyResolveToPhaseWithCallableMembers(toPhase: FirResolvePhase) {
fir.lazyDeclarationResolver.lazyResolveToPhaseWithCallableMembers(this, toPhase) fir.lazyDeclarationResolver.lazyResolveToPhaseWithCallableMembers(this, toPhase)
} }
@@ -117,3 +128,31 @@ fun FirClassSymbol<*>.lazyResolveToPhaseWithCallableMembers(toPhase: FirResolveP
fun FirClass.lazyResolveToPhaseWithCallableMembers(toPhase: FirResolvePhase) { fun FirClass.lazyResolveToPhaseWithCallableMembers(toPhase: FirResolvePhase) {
symbol.lazyResolveToPhaseWithCallableMembers(toPhase) symbol.lazyResolveToPhaseWithCallableMembers(toPhase)
} }
/**
* Lazy resolve [FirBasedSymbol] and all nested declarations to [FirResolvePhase].
*
* In the case of lazy resolution (inside Analysis API), it checks that the declaration phase `>= toPhase`.
* If not, it resolves the declaration for the requested phase.
*
* If the [lazyResolveToPhase] is called inside a fir transformer,
* it should always request the phase which is strictly lower than the current transformer phase,
* otherwise a deadlock/StackOverflow is possible.
*
* For the compiler mode, it does nothing, as the compiler is non-lazy.
*
* @receiver [FirBasedSymbol] which should be resolved
* @param toPhase the minimum phase, the declaration and all nested declarations should be resolved to after an execution of the [lazyResolveToPhase]
*/
fun FirBasedSymbol<*>.lazyResolveToPhaseRecursively(toPhase: FirResolvePhase) {
fir.lazyDeclarationResolver.lazyResolveToPhaseRecursively(this, toPhase)
}
/**
* Lazy resolve [FirDeclaration] and all nested declarations to [FirResolvePhase].
*
* @see lazyResolveToPhaseRecursively
*/
fun FirDeclaration.lazyResolveToPhaseRecursively(toPhase: FirResolvePhase) {
symbol.lazyResolveToPhaseRecursively(toPhase)
}
@@ -1,5 +1,4 @@
// IGNORE_REVERSED_RESOLVE // IGNORE_REVERSED_RESOLVE
// IGNORE_CONTRACT_VIOLATIONS
// FIR_IDENTICAL // FIR_IDENTICAL
@Retention(AnnotationRetention.SOURCE) @Retention(AnnotationRetention.SOURCE)
@Repeatable @Repeatable
@@ -28,6 +28,10 @@ class FirCompilerLazyDeclarationResolverWithPhaseChecking : FirLazyDeclarationRe
checkIfCanLazyResolveToPhase(toPhase) checkIfCanLazyResolveToPhase(toPhase)
} }
override fun lazyResolveToPhaseRecursively(symbol: FirBasedSymbol<*>, toPhase: FirResolvePhase) {
checkIfCanLazyResolveToPhase(toPhase)
}
override fun startResolvingPhase(phase: FirResolvePhase) { override fun startResolvingPhase(phase: FirResolvePhase) {
check(currentTransformerPhase == null) check(currentTransformerPhase == null)
currentTransformerPhase = phase currentTransformerPhase = phase