[LL FIR] reanalyse declarations on the air for incremental analysis

We cannot do it inplace now as we do not have a lock to do
it under after analysis started to use declaration-level lock

This part is mostly covered by the plugin tests
(which failed after the previous commit)

^KT-56543
This commit is contained in:
Dmitrii Gridin
2023-04-04 17:56:47 +02:00
committed by Space Team
parent 72def186a3
commit 332cdc514b
4 changed files with 233 additions and 95 deletions
@@ -8,23 +8,22 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirModuleResolveComponents
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.collectDesignation
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.ClassDiagnosticRetriever
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.FileDiagnosticRetriever
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.FileStructureElementDiagnostics
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.SingleNonLocalDeclarationDiagnosticRetriever
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.RawFirNonLocalDeclarationBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withInvalidationOnException
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.FirBackingFieldBuilder
import org.jetbrains.kotlin.fir.declarations.builder.FirFunctionBuilder
import org.jetbrains.kotlin.fir.declarations.builder.FirPropertyBuilder
import org.jetbrains.kotlin.fir.declarations.impl.FirPrimaryConstructor
import org.jetbrains.kotlin.fir.scopes.kotlinScopeProvider
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.psi.*
import java.util.concurrent.ConcurrentHashMap
@@ -108,42 +107,33 @@ internal class ReanalyzableFunctionStructureElement(
override fun reanalyze(newKtDeclaration: KtNamedFunction): ReanalyzableFunctionStructureElement {
val originalFunction = firSymbol.fir as FirSimpleFunction
val designation = originalFunction.collectDesignation()
val originalDesignation = originalFunction.collectDesignation()
val temporaryFunction = RawFirNonLocalDeclarationBuilder.buildWithFunctionSymbolRebind(
val newFunction = RawFirNonLocalDeclarationBuilder.buildNewSimpleFunction(
session = originalFunction.moduleData.session,
scopeProvider = originalFunction.moduleData.session.kotlinScopeProvider,
designation = designation,
rootNonLocalDeclaration = newKtDeclaration,
) as FirSimpleFunction
designation = originalDesignation,
newFunction = newKtDeclaration,
additionalFunctionInit = {
copyUnmodifiableFieldsForFunction(originalFunction)
},
)
return moduleComponents.globalResolveComponents.lockProvider.withGlobalLock(firFile) {
val upgradedPhase = minOf(originalFunction.resolvePhase, FirResolvePhase.DECLARATIONS)
newFunction.apply {
copyAllExceptBodyForFunction(originalFunction)
withInvalidationOnException(moduleComponents.session) {
with(originalFunction) {
replaceBody(temporaryFunction.body)
replaceContractDescription(temporaryFunction.contractDescription)
@OptIn(ResolveStateAccess::class)
resolveState = upgradedPhase.asResolveState()
}
designation.toSequence(includeTarget = true).forEach {
@OptIn(ResolveStateAccess::class)
it.resolveState = minOf(it.resolvePhase, upgradedPhase).asResolveState()
}
originalFunction.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE)
ReanalyzableFunctionStructureElement(
firFile,
newKtDeclaration,
originalFunction.symbol,
newKtDeclaration.modificationStamp,
moduleComponents,
)
}
@OptIn(ResolveStateAccess::class)
resolveState = FirResolvePhase.STATUS.asResolveState()
}
newFunction.bodyResolveOnAir(originalDesignation, firFile, moduleComponents)
return ReanalyzableFunctionStructureElement(
firFile,
newKtDeclaration,
newFunction.symbol,
newKtDeclaration.modificationStamp,
moduleComponents,
)
}
}
@@ -158,43 +148,67 @@ internal class ReanalyzablePropertyStructureElement(
override fun reanalyze(newKtDeclaration: KtProperty): ReanalyzablePropertyStructureElement {
val originalProperty = firSymbol.fir
val designation = originalProperty.collectDesignation()
val originalDesignation = originalProperty.collectDesignation()
val temporaryProperty = RawFirNonLocalDeclarationBuilder.buildWithFunctionSymbolRebind(
val newProperty = RawFirNonLocalDeclarationBuilder.buildNewProperty(
session = originalProperty.moduleData.session,
scopeProvider = originalProperty.moduleData.session.kotlinScopeProvider,
designation = designation,
rootNonLocalDeclaration = newKtDeclaration,
) as FirProperty
return moduleComponents.globalResolveComponents.lockProvider.withGlobalLock(firFile) {
val getterPhase = originalProperty.getter?.resolvePhase ?: originalProperty.resolvePhase
val setterPhase = originalProperty.setter?.resolvePhase ?: originalProperty.resolvePhase
val upgradedPhase = minOf(originalProperty.resolvePhase, getterPhase, setterPhase, FirResolvePhase.DECLARATIONS)
withInvalidationOnException(moduleComponents.session) {
@OptIn(ResolveStateAccess::class)
with(originalProperty) {
getter?.replaceBody(temporaryProperty.getter?.body)
setter?.replaceBody(temporaryProperty.setter?.body)
replaceInitializer(temporaryProperty.initializer)
getter?.resolveState = upgradedPhase.asResolveState()
setter?.resolveState = upgradedPhase.asResolveState()
resolveState = upgradedPhase.asResolveState()
replaceBodyResolveState(FirPropertyBodyResolveState.NOTHING_RESOLVED)
}
originalProperty.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE)
ReanalyzablePropertyStructureElement(
firFile,
newKtDeclaration,
originalProperty.symbol,
newKtDeclaration.modificationStamp,
moduleComponents,
designation = originalDesignation,
newProperty = newKtDeclaration,
additionalPropertyInit = {
copyUnmodifiableFieldsForProperty(originalProperty)
},
additionalAccessorInit = {
copyUnmodifiableFieldsForFunction(
if (isGetter) {
originalProperty.getter!!
} else {
originalProperty.setter!!
}
)
},
additionalBackingFieldInit = {
copyUnmodifiableFieldsForBackingField(originalProperty.backingField!!)
},
)
newProperty.apply {
copyAllExceptBodyFromCallable(originalProperty)
replaceBodyResolveState(FirPropertyBodyResolveState.NOTHING_RESOLVED)
@OptIn(ResolveStateAccess::class)
resolveState = FirResolvePhase.STATUS.asResolveState()
getter?.let { getter ->
getter.copyAllExceptBodyForFunction(originalProperty.getter!!)
@OptIn(ResolveStateAccess::class)
getter.resolveState = FirResolvePhase.STATUS.asResolveState()
}
setter?.let { setter ->
setter.copyAllExceptBodyForFunction(originalProperty.setter!!)
@OptIn(ResolveStateAccess::class)
setter.resolveState = FirResolvePhase.STATUS.asResolveState()
}
backingField?.let { backingField ->
backingField.copyAllExceptBodyFromCallable(originalProperty.backingField!!)
@OptIn(ResolveStateAccess::class)
backingField.resolveState = FirResolvePhase.STATUS.asResolveState()
}
}
newProperty.bodyResolveOnAir(originalDesignation, firFile, moduleComponents)
return ReanalyzablePropertyStructureElement(
firFile,
newKtDeclaration,
newProperty.symbol,
newKtDeclaration.modificationStamp,
moduleComponents,
)
}
}
@@ -297,3 +311,55 @@ internal class RootStructureElement(
}
}
}
private fun <C : FirCallableDeclaration> C.bodyResolveOnAir(
originalDesignation: FirDesignation,
firFile: FirFile,
moduleComponents: LLFirModuleResolveComponents
) {
val designationToResolveOnAir = FirDesignationWithFile(originalDesignation.path, this, firFile)
moduleComponents.firModuleLazyDeclarationResolver.runLazyDesignatedOnAirResolveToBodyWithoutLock(
designationToResolveOnAir,
onAirCreatedDeclaration = false,
towerDataContextCollector = null
)
}
private fun FirPropertyBuilder.copyUnmodifiableFieldsForProperty(prototype: FirProperty) {
attributes = prototype.attributes
dispatchReceiverType = prototype.dispatchReceiverType
}
private fun FirFunctionBuilder.copyUnmodifiableFieldsForFunction(prototype: FirFunction) {
attributes = prototype.attributes
dispatchReceiverType = prototype.dispatchReceiverType
}
private fun FirBackingFieldBuilder.copyUnmodifiableFieldsForBackingField(prototype: FirBackingField) {
attributes = prototype.attributes
dispatchReceiverType = prototype.dispatchReceiverType
}
private fun <F : FirFunction> F.copyAllExceptBodyForFunction(prototype: F) {
this.copyAllExceptBodyFromCallable(prototype)
this.replaceValueParameters(prototype.valueParameters)
if (this is FirContractDescriptionOwner) {
this.replaceContractDescription((prototype as FirContractDescriptionOwner).contractDescription)
}
}
private fun <C : FirCallableDeclaration> C.copyAllExceptBodyFromCallable(prototype: C) {
this.replaceAnnotations(prototype.annotations)
this.replaceReturnTypeRef(prototype.returnTypeRef)
this.replaceReceiverParameter(prototype.receiverParameter)
this.replaceStatus(prototype.status)
this.replaceDeprecationsProvider(prototype.deprecationsProvider)
this.replaceContextReceivers(prototype.contextReceivers)
// TODO add replaceTypeParameter to FirCallableDeclaration instead of this unsafe case
(this.typeParameters as MutableList<FirTypeParameterRef>).apply {
clear()
addAll(prototype.typeParameters)
}
}
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignationWithFil
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirClassWithAllMembersResolveTarget
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.forEachPathElementAndTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.LLFirLazyResolverRunner
@@ -55,6 +54,7 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
targets = LLFirResolveMultiDesignationCollector.getDesignationsToResolve(target),
scopeSession = scopeSession,
toPhase = toPhase,
towerDataContextCollector = null,
)
} catch (e: Exception) {
handleExceptionFromResolve(e, target, fromPhase, toPhase)
@@ -84,6 +84,7 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
targets = LLFirResolveMultiDesignationCollector.getDesignationsToResolveWithCallableMembers(target),
scopeSession = scopeSession,
toPhase = toPhase,
towerDataContextCollector = null,
)
} catch (e: Exception) {
handleExceptionFromResolve(e, target, fromPhase, toPhase)
@@ -105,45 +106,41 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
toPhase: FirResolvePhase
) {
try {
lazyResolveTargets(listOf(target), moduleComponents.scopeSessionProvider.getScopeSession(), toPhase)
lazyResolveTargets(
targets = listOf(target),
moduleComponents.scopeSessionProvider.getScopeSession(),
toPhase,
towerDataContextCollector = null,
)
} catch (e: Exception) {
handleExceptionFromResolve(e, target, toPhase)
}
}
internal fun runLazyDesignatedOnAirResolveToBodyWithoutLock(
fun runLazyDesignatedOnAirResolveToBodyWithoutLock(
designation: FirDesignationWithFile,
onAirCreatedDeclaration: Boolean,
towerDataContextCollector: FirTowerDataContextCollector?,
) {
resolveFileToImportsWithoutLock(designation.firFile)
val resolveTarget = when (designation.target) {
val target = when (designation.target) {
is FirRegularClass -> LLFirClassWithAllMembersResolveTarget(designation.firFile, designation.path, designation.target)
else -> LLFirSingleResolveTarget(designation.firFile, designation.path, designation.target)
}
fun runTransformation() {
val scopeSession = ScopeSession()
var currentPhase = maxOf(designation.target.resolvePhase, FirResolvePhase.IMPORTS)
while (currentPhase < FirResolvePhase.BODY_RESOLVE) {
currentPhase = currentPhase.next
checkCanceled()
LLFirLazyResolverRunner.runLazyResolverByPhase(
phase = currentPhase,
target = resolveTarget,
scopeSession = scopeSession,
lockProvider = moduleComponents.globalResolveComponents.lockProvider,
towerDataContextCollector = towerDataContextCollector,
)
}
lazyResolveTargets(listOf(target), scopeSession, FirResolvePhase.BODY_RESOLVE, towerDataContextCollector)
}
if (onAirCreatedDeclaration) {
withOnAirDesignation(designation, ::runTransformation)
} else {
runTransformation()
try {
if (onAirCreatedDeclaration) {
withOnAirDesignation(designation, ::runTransformation)
} else {
runTransformation()
}
} catch (e: Exception) {
handleExceptionFromResolve(e, target, FirResolvePhase.BODY_RESOLVE)
}
}
@@ -166,6 +163,7 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
targets: List<LLFirResolveTarget>,
scopeSession: ScopeSession,
toPhase: FirResolvePhase,
towerDataContextCollector: FirTowerDataContextCollector?,
) {
if (targets.isEmpty()) return
var currentPhase = getMinResolvePhase(targets).coerceAtLeast(FirResolvePhase.IMPORTS)
@@ -181,7 +179,7 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
target = target,
scopeSession = scopeSession,
lockProvider = moduleComponents.globalResolveComponents.lockProvider,
towerDataContextCollector = null
towerDataContextCollector = towerDataContextCollector,
)
}
}
@@ -191,7 +189,7 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
var min = FirResolvePhase.BODY_RESOLVE
for (designation in designations) {
if (min == FirResolvePhase.RAW_FIR) break
designation.forEachPathElementAndTarget { target ->
designation.forEachTarget { target ->
min = minOf(min, target.resolvePhase)
}
}
@@ -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.
*/
@@ -14,6 +14,10 @@ import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.builder.BodyBuildingMode
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.FirBackingFieldBuilder
import org.jetbrains.kotlin.fir.declarations.builder.FirFunctionBuilder
import org.jetbrains.kotlin.fir.declarations.builder.FirPropertyAccessorBuilder
import org.jetbrains.kotlin.fir.declarations.builder.FirPropertyBuilder
import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.references.FirSuperReference
@@ -24,9 +28,7 @@ import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.psi
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull
internal class RawFirNonLocalDeclarationBuilder private constructor(
@@ -35,9 +37,71 @@ internal class RawFirNonLocalDeclarationBuilder private constructor(
private val originalDeclaration: FirDeclaration,
private val declarationToBuild: KtDeclaration,
private val functionsToRebind: Set<FirFunction>? = null,
private val replacementApplier: RawFirReplacement.Applier? = null
private val replacementApplier: RawFirReplacement.Applier? = null,
private val additionalFunctionInit: FirFunctionBuilder.() -> Unit = {},
private val additionalPropertyInit: FirPropertyBuilder.() -> Unit = {},
private val additionalAccessorInit: FirPropertyAccessorBuilder.() -> Unit = {},
private val additionalBackingFieldInit: FirBackingFieldBuilder.() -> Unit = {},
) : RawFirBuilder(session, baseScopeProvider, bodyBuildingMode = BodyBuildingMode.NORMAL) {
override fun FirFunctionBuilder.additionalFunctionInit() {
additionalFunctionInit.invoke(this)
}
override fun FirPropertyBuilder.additionalPropertyInit() {
additionalPropertyInit.invoke(this)
}
override fun FirPropertyAccessorBuilder.additionalPropertyAccessorInit() {
additionalAccessorInit.invoke(this)
}
override fun FirBackingFieldBuilder.additionalBackingFieldInit() {
additionalBackingFieldInit.invoke(this)
}
companion object {
fun buildNewSimpleFunction(
session: FirSession,
scopeProvider: FirScopeProvider,
designation: FirDesignation,
newFunction: KtNamedFunction,
additionalFunctionInit: FirFunctionBuilder.() -> Unit,
): FirSimpleFunction {
val builder = RawFirNonLocalDeclarationBuilder(
session = session,
baseScopeProvider = scopeProvider,
originalDeclaration = designation.target as FirDeclaration,
declarationToBuild = newFunction,
additionalFunctionInit = additionalFunctionInit,
)
builder.context.packageFqName = newFunction.containingKtFile.packageFqName
return builder.moveNext(designation.path.iterator(), containingClass = null) as FirSimpleFunction
}
fun buildNewProperty(
session: FirSession,
scopeProvider: FirScopeProvider,
designation: FirDesignation,
newProperty: KtProperty,
additionalPropertyInit: FirPropertyBuilder.() -> Unit,
additionalAccessorInit: FirPropertyAccessorBuilder.() -> Unit,
additionalBackingFieldInit: FirBackingFieldBuilder.() -> Unit,
): FirProperty {
val builder = RawFirNonLocalDeclarationBuilder(
session = session,
baseScopeProvider = scopeProvider,
originalDeclaration = designation.target as FirDeclaration,
declarationToBuild = newProperty,
additionalPropertyInit = additionalPropertyInit,
additionalAccessorInit = additionalAccessorInit,
additionalBackingFieldInit = additionalBackingFieldInit,
)
builder.context.packageFqName = newProperty.containingKtFile.packageFqName
return builder.moveNext(designation.path.iterator(), containingClass = null) as FirProperty
}
fun buildWithReplacement(
session: FirSession,
scopeProvider: FirScopeProvider,
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2022 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.
*/
@@ -57,6 +57,10 @@ open class RawFirBuilder(
bodyBuildingMode: BodyBuildingMode = BodyBuildingMode.NORMAL
) : BaseFirBuilder<PsiElement>(session) {
protected open fun bindFunctionTarget(target: FirFunctionTarget, function: FirFunction) = target.bind(function)
protected open fun FirFunctionBuilder.additionalFunctionInit() {}
protected open fun FirPropertyBuilder.additionalPropertyInit() {}
protected open fun FirPropertyAccessorBuilder.additionalPropertyAccessorInit() {}
protected open fun FirBackingFieldBuilder.additionalBackingFieldInit() {}
var mode: BodyBuildingMode = bodyBuildingMode
private set
@@ -479,6 +483,8 @@ open class RawFirBuilder(
this.contractDescription = it
}
this.propertySymbol = propertySymbol
additionalPropertyAccessorInit()
}.also {
it.initContainingClassAttr()
bindFunctionTarget(accessorTarget, it)
@@ -558,6 +564,8 @@ open class RawFirBuilder(
this.initializer = backingFieldInitializer
this.isVar = property.isVar
this.isVal = !property.isVar
additionalBackingFieldInit()
}
} else {
FirDefaultPropertyBackingField(
@@ -1497,6 +1505,7 @@ open class RawFirBuilder(
}
}
context.firFunctionTargets.removeLast()
additionalFunctionInit()
}.build().also {
bindFunctionTarget(target, it)
if (it is FirSimpleFunction) {
@@ -1847,6 +1856,7 @@ open class RawFirBuilder(
}
contextReceivers.addAll(convertContextReceivers(this@toFirProperty.contextReceivers))
additionalPropertyInit()
}.also {
if (!isLocal) {
fillDanglingConstraintsTo(it)