[LL FIR] rewrite on-air resolve

Now we will resolve declarations only ones and to the
minimal required phase to get a fully resolved element
depends on an insert position, because not all elements
require body resolution

A side change: `getOnAirGetTowerContextProvider` now
won't do rebind to the original declaration. In the future,
this part will be rewritten to avoid on-air resolution,
so it is not a problem

^KT-59685 Fixed
This commit is contained in:
Dmitrii Gridin
2023-07-06 11:02:35 +02:00
committed by Space Team
parent 61acd2b6f5
commit 6867dffff1
17 changed files with 345 additions and 263 deletions
@@ -1,118 +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.analysis.low.level.api.fir
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.*
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
internal object DeclarationCopyBuilder {
fun FirSimpleFunction.withBodyFrom(
functionWithBody: FirSimpleFunction,
): FirSimpleFunction = buildSimpleFunctionCopy(this) {
body = functionWithBody.body
symbol = functionWithBody.symbol
initDeclaration(this@withBodyFrom, functionWithBody)
}.apply { reassignAllReturnTargets(functionWithBody) }
fun FirRegularClass.withBodyFrom(
classWithBody: FirRegularClass,
): FirRegularClass = buildRegularClassCopy(this) {
declarations.clear()
declarations.addAll(classWithBody.declarations)
symbol = classWithBody.symbol
initDeclaration(this@withBodyFrom, classWithBody)
}
fun FirScript.withBodyFrom(
scriptWithBody: FirScript
): FirScript = buildScriptCopy(this) {
statements.clear()
statements.addAll(scriptWithBody.statements)
symbol = scriptWithBody.symbol
initDeclaration(this@withBodyFrom, scriptWithBody)
}
fun FirProperty.withBodyFrom(propertyWithBody: FirProperty): FirProperty {
val newSetter = getAccessorToUse(this, propertyWithBody) { it.setter }
val newGetter = getAccessorToUse(this, propertyWithBody) { it.getter }
val propertyResolvePhase = minOf(
this@withBodyFrom.resolvePhase,
FirResolvePhase.DECLARATIONS,
newGetter?.resolvePhase ?: FirResolvePhase.BODY_RESOLVE,
newSetter?.resolvePhase ?: FirResolvePhase.BODY_RESOLVE,
propertyWithBody.getter?.resolvePhase ?: FirResolvePhase.BODY_RESOLVE,
propertyWithBody.setter?.resolvePhase ?: FirResolvePhase.BODY_RESOLVE,
)
return buildPropertyCopy(this@withBodyFrom) {
symbol = propertyWithBody.symbol
initializer = propertyWithBody.initializer
getter = newGetter
setter = newSetter
if (propertyResolvePhase < FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) {
bodyResolveState = FirPropertyBodyResolveState.NOTHING_RESOLVED
}
initDeclaration(this@withBodyFrom, propertyWithBody)
resolvePhase = propertyResolvePhase
}
}
private inline fun getAccessorToUse(
originalProperty: FirProperty,
replacementProperty: FirProperty,
getAccessor: (FirProperty) -> FirPropertyAccessor?
): FirPropertyAccessor? {
val originalAccessor = getAccessor(originalProperty)
val replacementAccessor = getAccessor(replacementProperty)
// accessor has a header type, and we want it type to be resolved
return if (originalAccessor != null && replacementAccessor != null) {
buildPropertyAccessorCopy(originalAccessor) {
body = replacementAccessor.body
symbol = replacementAccessor.symbol
initDeclaration(originalAccessor, replacementAccessor)
}.apply { reassignAllReturnTargets(replacementAccessor) }
} else {
replacementAccessor
}
}
private fun FirDeclarationBuilder.initDeclaration(
originalDeclaration: FirDeclaration,
builtDeclaration: FirDeclaration,
) {
resolvePhase = minOf(originalDeclaration.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtDeclaration.source
moduleData = originalDeclaration.moduleData
}
private fun FirScriptBuilder.initDeclaration(
originalDeclaration: FirDeclaration,
builtDeclaration: FirDeclaration,
) {
resolvePhase = minOf(originalDeclaration.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtDeclaration.source
moduleData = originalDeclaration.moduleData
}
private fun FirFunction.reassignAllReturnTargets(from: FirFunction) {
this.accept(object : FirVisitorVoid() {
override fun visitElement(element: FirElement) {
if (element is FirReturnExpression && element.target.labeledElement == from) {
element.target.bind(this@reassignAllReturnTargets)
}
element.acceptChildren(this)
}
})
}
}
@@ -7,12 +7,11 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.analysis.low.level.api.fir.DeclarationCopyBuilder.withBodyFrom
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirResolveSessionDepended
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirWholeFileResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FileTowerProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FirTowerContextProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FirTowerDataContextAllElementsCollector
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.canBePartOfParentDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.getNonLocalContainingOrThisDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.FirElementsRecorder
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.KtToFirMapping
@@ -23,11 +22,11 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirResolvableM
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirResolvableSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.state.LLFirResolvableResolveSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.FirElementFinder
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.errorWithFirSpecificEntries
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.originalDeclaration
import org.jetbrains.kotlin.analysis.utils.errors.buildErrorWithAttachment
import org.jetbrains.kotlin.analysis.utils.errors.withPsiEntry
import org.jetbrains.kotlin.analysis.utils.printer.getElementTextInContext
import org.jetbrains.kotlin.analysis.utils.printer.parentOfType
import org.jetbrains.kotlin.analysis.utils.printer.parentsOfType
import org.jetbrains.kotlin.fir.FirElement
@@ -41,27 +40,19 @@ import org.jetbrains.kotlin.fir.scopes.createImportingScopes
import org.jetbrains.kotlin.fir.scopes.kotlinScopeProvider
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
object LowLevelFirApiFacadeForResolveOnAir {
private fun PsiElement.onAirGetNonLocalContainingOrThisDeclarationFor(): KtDeclaration? {
private fun KtDeclaration.isApplicableForOnAirResolve(): Boolean = when (this) {
is KtDestructuringDeclaration -> false
else -> true
}
private fun PsiElement.onAirGetNonLocalContainingOrThisDeclaration(): KtDeclaration? {
return getNonLocalContainingOrThisDeclaration { declaration ->
when (declaration) {
is KtNamedFunction,
is KtProperty,
is KtTypeAlias,
is KtScript -> {
true
}
is KtClassOrObject -> {
declaration !is KtEnumEntry
}
else -> {
false
}
}
declaration.isApplicableForOnAirResolve() && !declaration.canBePartOfParentDeclaration
}
}
@@ -83,8 +74,9 @@ object LowLevelFirApiFacadeForResolveOnAir {
val declaration = runBodyResolveOnAir(
firResolveSession = firResolveSession,
replacement = RawFirReplacement(place, elementToResolve),
onAirCreatedDeclaration = true
originalPlace = place,
replacementElement = elementToResolve,
collector = null,
)
val expressionLocator = object : FirVisitorVoid() {
@@ -114,9 +106,12 @@ object LowLevelFirApiFacadeForResolveOnAir {
val moduleComponents = session.moduleComponents
val onAirFirFile = RawFirNonLocalDeclarationBuilder.buildNewFile(session, session.kotlinScopeProvider, ktFile)
val target = LLFirWholeFileResolveTarget(onAirFirFile)
val collector = FirTowerDataContextAllElementsCollector()
moduleComponents.firModuleLazyDeclarationResolver.lazyResolveTarget(target, FirResolvePhase.BODY_RESOLVE, collector)
moduleComponents.firModuleLazyDeclarationResolver.runLazyDesignatedOnAirResolve(
FirDesignationWithFile(emptyList(), onAirFirFile, onAirFirFile),
collector,
)
return collector
}
@@ -137,8 +132,8 @@ object LowLevelFirApiFacadeForResolveOnAir {
runBodyResolveOnAir(
firResolveSession = firResolveSession,
collector = it,
onAirCreatedDeclaration = false,
replacement = RawFirReplacement(validPlace, validPlace),
originalPlace = validPlace,
replacementElement = validPlace,
)
}
}
@@ -164,39 +159,48 @@ object LowLevelFirApiFacadeForResolveOnAir {
return FirTowerDataContext().addNonLocalTowerDataElements(fileScopeElements)
}
/**
* Makes a copy of the original fir declaration and resolves it to the minimum required phase
* depends on [elementToAnalyze] position inside a non-local containing declaration.
*
* If [elementToAnalyze] is non-local declaration then resolves it to [FirResolvePhase.BODY_RESOLVE].
*
* Resulted [LLFirResolveSession] have [FirTowerDataContext] for elements from the copy.
*/
fun getFirResolveSessionForDependentCopy(
originalFirResolveSession: LLFirResolveSession,
originalKtFile: KtFile,
elementToAnalyze: KtElement
elementToAnalyze: KtElement,
): LLFirResolveSession {
require(originalFirResolveSession is LLFirResolvableResolveSession)
require(elementToAnalyze !is KtFile) { "KtFile for dependency element not supported" }
val dependencyNonLocalDeclaration = elementToAnalyze.onAirGetNonLocalContainingOrThisDeclarationFor() as? KtNamedDeclaration
val minimalCopiedDeclaration = elementToAnalyze.getNonLocalContainingOrThisDeclaration {
it.isApplicableForOnAirResolve()
}
if (dependencyNonLocalDeclaration == null) {
val copiedNonLocalDeclaration = minimalCopiedDeclaration?.onAirGetNonLocalContainingOrThisDeclaration()
if (copiedNonLocalDeclaration == null) {
val towerDataContext = onAirGetTowerContextForFile(originalFirResolveSession, originalKtFile)
val fileTowerProvider = FileTowerProvider(elementToAnalyze.containingKtFile, towerDataContext)
return LLFirResolveSessionDepended(originalFirResolveSession, fileTowerProvider, ktToFirMapping = null)
}
val sameDeclarationInOriginalFile = PsiTreeUtil.findSameElementInCopy(dependencyNonLocalDeclaration, originalKtFile)
val originalNonLocalDeclaration = PsiTreeUtil.findSameElementInCopy(copiedNonLocalDeclaration, originalKtFile)
?: buildErrorWithAttachment("Cannot find original function matching") {
withPsiEntry("matchingPsi", dependencyNonLocalDeclaration, originalFirResolveSession::getModule)
withPsiEntry("matchingPsi", elementToAnalyze, originalFirResolveSession::getModule)
withPsiEntry("originalFile", originalKtFile, originalFirResolveSession::getModule)
}
recordOriginalDeclaration(
targetDeclaration = dependencyNonLocalDeclaration,
originalDeclaration = sameDeclarationInOriginalFile
)
recordOriginalDeclaration(targetDeclaration = copiedNonLocalDeclaration, originalDeclaration = originalNonLocalDeclaration)
val collector = FirTowerDataContextAllElementsCollector()
val copiedFirDeclaration = runBodyResolveOnAir(
originalFirResolveSession,
replacement = RawFirReplacement(sameDeclarationInOriginalFile, dependencyNonLocalDeclaration),
onAirCreatedDeclaration = true,
originalPlace = originalNonLocalDeclaration,
replacementElement = copiedNonLocalDeclaration,
collector = collector,
forcedResolvePhase = requiredResolvePhase(minimalCopiedDeclaration, elementToAnalyze),
)
val mapping = KtToFirMapping(copiedFirDeclaration, FirElementsRecorder())
@@ -229,10 +233,9 @@ object LowLevelFirApiFacadeForResolveOnAir {
}
val declarationResolver = llFirResolvableSession.moduleComponents.firModuleLazyDeclarationResolver
declarationResolver.runLazyDesignatedOnAirResolveToBodyWithoutLock(
declarationResolver.runLazyDesignatedOnAirResolve(
FirDesignationWithFile(path = emptyList(), target = fileAnnotationsContainer, firFile),
onAirCreatedDeclaration = true,
collector
collector,
)
collector?.addFileContext(firFile, firFile.createTowerDataContext(firResolveSession.getScopeSessionFor(llFirResolvableSession)))
@@ -241,70 +244,117 @@ object LowLevelFirApiFacadeForResolveOnAir {
return fileAnnotationsContainer.annotations.single()
}
/**
* Creates a new fir declaration from closer non-local declaration based on [originalPlace] position.
* The resulted [FirResolvePhase] depends on [forcedResolvePhase] or the position of [originalPlace] inside the non-local declaration.
*
* Note: the new declaration and its content won't be rebinded to the original.
*
* @see requiredResolvePhase
*/
private fun runBodyResolveOnAir(
firResolveSession: LLFirResolvableResolveSession,
replacement: RawFirReplacement,
onAirCreatedDeclaration: Boolean,
collector: FirResolveContextCollector? = null,
originalPlace: KtElement,
replacementElement: KtElement,
collector: FirResolveContextCollector?,
forcedResolvePhase: FirResolvePhase? = null,
): FirElement {
val nonLocalDeclaration = replacement.from.onAirGetNonLocalContainingOrThisDeclarationFor()
val originalFirFile = firResolveSession.getOrBuildFirFile(replacement.from.containingKtFile)
val minimalOriginalDeclarationToReplace = originalPlace.getNonLocalContainingOrThisDeclaration {
it.isApplicableForOnAirResolve()
}
if (nonLocalDeclaration == null) {
val originalFirFile = firResolveSession.getOrBuildFirFile(originalPlace.containingKtFile)
val originalDeclaration = minimalOriginalDeclarationToReplace?.onAirGetNonLocalContainingOrThisDeclaration()
if (originalDeclaration == null) {
//It is possible that it is file annotation is going to resolve
val annotationCall = replacement.from.parentOfType<KtAnnotationEntry>(withSelf = true)
val annotationCall = originalPlace.parentOfType<KtAnnotationEntry>(withSelf = true)
if (annotationCall != null) {
return tryResolveAsFileAnnotation(
annotationEntry = annotationCall,
replacement = replacement,
replacement = RawFirReplacement(from = originalPlace, to = replacementElement),
firFile = originalFirFile,
collector = collector,
firResolveSession = firResolveSession,
)
} else {
error("Cannot find enclosing declaration for ${replacement.from.getElementTextInContext()}")
errorWithFirSpecificEntries(
"Cannot find enclosing declaration for ${originalPlace::class.simpleName}",
psi = originalPlace,
)
}
}
val originalDeclaration = nonLocalDeclaration.getOrBuildFirOfType<FirDeclaration>(firResolveSession)
val originalDesignation = originalDeclaration.collectDesignation()
val newDeclarationWithReplacement = RawFirNonLocalDeclarationBuilder.buildWithReplacement(
session = originalDeclaration.moduleData.session,
scopeProvider = originalDeclaration.moduleData.session.kotlinScopeProvider,
designation = originalDesignation,
rootNonLocalDeclaration = nonLocalDeclaration,
val originalDesignationPath = FirElementFinder.collectDesignationPath(originalFirFile, originalDeclaration)
?: errorWithFirSpecificEntries(message = "Impossible to collect designation", fir = originalFirFile, psi = originalDeclaration)
val originalFirDeclaration = originalDesignationPath.target
val session = originalFirDeclaration.llFirResolvableSession ?: error("Expected resolvable session")
/**
* Special case for [getOnAirGetTowerContextProvider]. Can be dropped after KT-59498
*/
val replacement = if (originalPlace === replacementElement) {
RawFirReplacement(from = originalDeclaration, to = originalDeclaration)
} else {
RawFirReplacement(from = originalPlace, to = replacementElement)
}
val newFirDeclaration = RawFirNonLocalDeclarationBuilder.buildWithReplacement(
session = session,
scopeProvider = session.kotlinScopeProvider,
designation = FirDesignation(originalDesignationPath.path, originalFirDeclaration),
rootNonLocalDeclaration = originalDeclaration,
replacement = replacement,
)
val isInBodyReplacement = isInBodyReplacement(nonLocalDeclaration, replacement)
val copiedFirDeclaration = if (isInBodyReplacement) {
when (originalDeclaration) {
is FirSimpleFunction -> originalDeclaration.withBodyFrom(newDeclarationWithReplacement as FirSimpleFunction)
is FirProperty -> originalDeclaration.withBodyFrom(newDeclarationWithReplacement as FirProperty)
is FirRegularClass -> originalDeclaration.withBodyFrom(newDeclarationWithReplacement as FirRegularClass)
is FirScript -> originalDeclaration.withBodyFrom(newDeclarationWithReplacement as FirScript)
is FirTypeAlias -> newDeclarationWithReplacement
else -> error("Not supported type ${originalDeclaration::class.simpleName}")
}
} else {
newDeclarationWithReplacement
session.moduleComponents.firModuleLazyDeclarationResolver.runLazyDesignatedOnAirResolve(
FirDesignationWithFile(originalDesignationPath.path, newFirDeclaration, originalFirFile),
collector,
forcedResolvePhase ?: requiredResolvePhase(minimalOriginalDeclarationToReplace, originalPlace),
)
return newFirDeclaration
}
private fun requiredResolvePhase(
container: KtDeclaration,
elementToReplace: PsiElement,
): FirResolvePhase = when {
bodyResolveRequired(container, elementToReplace) -> FirResolvePhase.BODY_RESOLVE
annotationMappingRequired(container, elementToReplace) -> FirResolvePhase.ANNOTATIONS_ARGUMENTS_MAPPING
/**
* Currently it is a minimal phase there we can collect [FirResolveContextCollector]
*/
else -> FirResolvePhase.ARGUMENTS_OF_ANNOTATIONS
}
private fun annotationMappingRequired(
container: KtDeclaration,
elementToReplace: PsiElement,
): Boolean = elementToReplace.parentsWithSelf.takeWhile { it != container }.any {
it is KtAnnotationEntry
} || elementToReplace.anyDescendantOfType<KtAnnotationEntry>(
canGoInside = {
// We shouldn't go inside a class body, because class resolution won't process such elements
// In case of container is class, we are interested only in elements outside its body
it !is KtClassBody
},
)
private fun bodyResolveRequired(container: KtDeclaration, elementToReplace: PsiElement): Boolean = when {
container == elementToReplace -> true
container is KtDeclarationWithBody -> container.bodyExpression?.isAncestor(elementToReplace)
container is KtProperty -> container.delegateExpressionOrInitializer?.isAncestor(elementToReplace)
container is KtParameter -> container.defaultValue?.isAncestor(elementToReplace)
container is KtEnumEntry -> {
container.initializerList?.isAncestor(elementToReplace) == true || container.body?.isAncestor(elementToReplace) == true
}
val onAirDesignation = FirDesignationWithFile(
path = originalDesignation.path,
target = copiedFirDeclaration,
firFile = originalFirFile
)
val resolvableSession = onAirDesignation.target.llFirResolvableSession ?: error("Expected resolvable session")
resolvableSession.moduleComponents.firModuleLazyDeclarationResolver.runLazyDesignatedOnAirResolveToBodyWithoutLock(
designation = onAirDesignation,
onAirCreatedDeclaration = onAirCreatedDeclaration,
towerDataContextCollector = collector,
)
return copiedFirDeclaration
}
container is KtScript || container is KtAnonymousInitializer -> true
else -> false
} == true
private fun isInBodyReplacement(ktDeclaration: KtDeclaration, replacement: RawFirReplacement): Boolean {
fun check(container: KtElement?): Boolean {
@@ -0,0 +1,41 @@
/*
* 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.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets
import org.jetbrains.kotlin.fir.FirElementWithResolveState
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
/**
* [LLFirResolveTarget] representing all declarations in a class (including nested classes).
* All of them are going to be resolved.
*/
class LLFirWholeClassResolveTarget(
override val firFile: FirFile,
override val path: List<FirRegularClass>,
val target: FirRegularClass,
) : LLFirResolveTarget() {
override fun forEachTarget(action: (FirElementWithResolveState) -> Unit) {
action(target)
fun goInside(target: FirElementWithResolveState) {
action(target)
if (target is FirRegularClass) {
target.declarations.forEach(::goInside)
}
}
forEachDeclaration(::goInside)
}
inline fun forEachDeclaration(action: (FirElementWithResolveState) -> Unit) {
for (member in target.declarations) {
action(member)
}
}
override fun toStringForTarget(): String = "*"
}
@@ -7,9 +7,10 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirModuleResolveComponents
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignationWithFile
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.LLFirWholeClassResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirWholeFileResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.InvalidSessionException
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession
@@ -106,6 +107,9 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
towerDataContextCollector: FirResolveContextCollector?,
) {
try {
resolveFileToImportsWithLock(target.firFile)
if (toPhase == FirResolvePhase.IMPORTS) return
lazyResolveTargets(
targets = listOf(target),
moduleComponents.scopeSessionProvider.getScopeSession(),
@@ -117,31 +121,35 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
}
}
fun runLazyDesignatedOnAirResolveToBodyWithoutLock(
/**
* Resolve on-air created declaration in a context of real [FirDesignationWithFile.firFile] and [FirDesignationWithFile.path].
* If target declaration is [FirFile] then the entire file will be resolved.
* The same for [FirRegularClass] if [resolvePhase] is [FirResolvePhase.BODY_RESOLVE].
*/
fun runLazyDesignatedOnAirResolve(
designation: FirDesignationWithFile,
onAirCreatedDeclaration: Boolean,
towerDataContextCollector: FirResolveContextCollector?,
resolvePhase: FirResolvePhase = FirResolvePhase.BODY_RESOLVE,
) {
resolveFileToImportsWithLock(designation.firFile)
val target = when (designation.target) {
is FirRegularClass -> LLFirClassWithAllMembersResolveTarget(designation.firFile, designation.path, designation.target)
val target = when {
designation.target is FirFile -> LLFirWholeFileResolveTarget(designation.firFile)
resolvePhase == FirResolvePhase.BODY_RESOLVE && designation.target is FirRegularClass -> {
LLFirWholeClassResolveTarget(designation.firFile, designation.path, designation.target)
}
else -> LLFirSingleResolveTarget(designation.firFile, designation.path, designation.target)
}
fun runTransformation() {
val scopeSession = ScopeSession()
lazyResolveTargets(listOf(target), scopeSession, FirResolvePhase.BODY_RESOLVE, towerDataContextCollector)
}
try {
if (onAirCreatedDeclaration) {
withOnAirDesignation(designation, ::runTransformation)
} else {
runTransformation()
withOnAirDesignation(designation) {
// New session to avoid garbage in the original session
val scopeSession = ScopeSession()
lazyResolveTargets(listOf(target), scopeSession, resolvePhase, towerDataContextCollector)
}
} catch (e: Exception) {
handleExceptionFromResolve(e, target, FirResolvePhase.BODY_RESOLVE)
handleExceptionFromResolve(e, target, resolvePhase)
}
}
@@ -200,7 +208,7 @@ private fun handleExceptionFromResolve(
exception: Exception,
firDeclarationToResolve: FirElementWithResolveState,
fromPhase: FirResolvePhase,
toPhase: FirResolvePhase
toPhase: FirResolvePhase,
): Nothing {
if (exception is InvalidSessionException) {
throw exception
@@ -235,7 +243,7 @@ private fun handleExceptionFromResolve(
private fun handleExceptionFromResolve(
exception: Exception,
designation: LLFirResolveTarget,
toPhase: FirResolvePhase
toPhase: FirResolvePhase,
): Nothing {
if (exception is InvalidSessionException) {
throw exception
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignation
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.errorWithFirSpecificEntries
import org.jetbrains.kotlin.analysis.utils.errors.buildErrorWithAttachment
import org.jetbrains.kotlin.analysis.utils.errors.requireIsInstance
import org.jetbrains.kotlin.analysis.utils.errors.withPsiEntry
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.builder.BodyBuildingMode
@@ -23,6 +24,7 @@ import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.name.NameUtils
@@ -111,6 +113,12 @@ internal class RawFirNonLocalDeclarationBuilder private constructor(
}
private inner class VisitorWithReplacement(private val containingClass: FirRegularClass?) : Visitor() {
fun convertDestructuringDeclaration(element: KtDestructuringDeclaration): FirVariable {
val replacementDeclaration = replacementApplier?.tryReplace(element) ?: element
requireIsInstance<KtDestructuringDeclaration>(replacementDeclaration)
return buildErrorTopLevelDestructuringDeclaration(replacementDeclaration.toFirSourceElement())
}
override fun convertElement(element: KtElement, original: FirElement?): FirElement? =
super.convertElement(replacementApplier?.tryReplace(element) ?: element, original)
@@ -128,6 +136,34 @@ internal class RawFirNonLocalDeclarationBuilder private constructor(
)
}
override fun convertPropertyAccessor(
accessor: KtPropertyAccessor?,
property: KtProperty,
propertyTypeRef: FirTypeRef,
propertySymbol: FirPropertySymbol,
isGetter: Boolean,
accessorAnnotationsFromProperty: List<FirAnnotation>,
parameterAnnotationsFromProperty: List<FirAnnotation>,
): FirPropertyAccessor? {
val replacementAccessor = if (accessor != null) {
val replacementAccessor = replacementApplier?.tryReplace(accessor) ?: accessor
check(replacementAccessor is KtPropertyAccessor)
replacementAccessor
} else {
accessor
}
return super.convertPropertyAccessor(
replacementAccessor,
property,
propertyTypeRef,
propertySymbol,
isGetter,
accessorAnnotationsFromProperty,
parameterAnnotationsFromProperty
)
}
override fun convertValueParameter(
valueParameter: KtParameter,
functionSymbol: FirFunctionSymbol<*>,
@@ -273,6 +309,7 @@ internal class RawFirNonLocalDeclarationBuilder private constructor(
else -> visitor.convertElement(declarationToBuild, originalDeclaration)
}
}
is KtDestructuringDeclaration -> visitor.convertDestructuringDeclaration(declarationToBuild)
else -> visitor.convertElement(declarationToBuild, originalDeclaration)
} as FirDeclaration
}
@@ -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.
*/
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.psi.*
internal data class RawFirReplacement(val from: KtElement, val to: KtElement) {
companion object {
fun isApplicableForReplacement(element: KtElement) = when (element) {
is KtFile, is KtScript, is KtClassInitializer, is KtClassOrObject, is KtObjectLiteralExpression, is KtTypeAlias,
is KtFile, is KtScript, is KtClassInitializer, is KtPropertyAccessor, is KtConstructor<*>, is KtClassOrObject, is KtObjectLiteralExpression, is KtTypeAlias,
is KtNamedFunction, is KtLambdaExpression, is KtAnonymousInitializer, is KtProperty, is KtTypeReference,
is KtAnnotationEntry, is KtTypeParameter, is KtTypeProjection, is KtParameter, is KtBlockExpression,
is KtSimpleNameExpression, is KtConstantExpression, is KtStringTemplateExpression, is KtReturnExpression,
@@ -19,7 +19,8 @@ internal data class RawFirReplacement(val from: KtElement, val to: KtElement) {
is KtIsExpression, is KtUnaryExpression, is KtCallExpression, is KtArrayAccessExpression, is KtQualifiedExpression,
is KtThisExpression, is KtSuperExpression, is KtParenthesizedExpression, is KtLabeledExpression, is KtAnnotatedExpression,
is KtThrowExpression, is KtDestructuringDeclaration, is KtClassLiteralExpression, is KtCallableReferenceExpression,
is KtCollectionLiteralExpression -> true
is KtCollectionLiteralExpression,
-> true
else -> false
}
}
@@ -29,12 +29,12 @@ internal abstract class LLFirAbstractBodyTargetResolver(
isJumpingPhase: Boolean = false
) : LLFirTargetResolver(resolveTarget, lockProvider, resolvePhase, isJumpingPhase) {
protected fun createReturnTypeCalculator(
towerDataContextCollector: FirResolveContextCollector?,
firResolveContextCollector: FirResolveContextCollector?,
): ReturnTypeCalculator = LLFirReturnTypeCalculatorWithJump(
scopeSession,
implicitBodyResolveComputationSession,
lockProvider,
towerDataContextCollector,
firResolveContextCollector,
)
abstract val transformer: FirAbstractBodyResolveTransformerDispatcher
@@ -47,6 +47,10 @@ internal abstract class LLFirAbstractBodyTargetResolver(
override fun withFile(firFile: FirFile, action: () -> Unit) {
transformer.context.withFile(firFile, transformer.components) {
transformer.firResolveContextCollector?.let { collector ->
collector.addFileContext(firFile, transformer.context.towerDataContext)
}
action()
}
}
@@ -55,6 +59,10 @@ internal abstract class LLFirAbstractBodyTargetResolver(
override fun withRegularClassImpl(firClass: FirRegularClass, action: () -> Unit) {
transformer.declarationsTransformer.context.withContainingClass(firClass) {
transformer.declarationsTransformer.withRegularClass(firClass) {
transformer.firResolveContextCollector?.let { collector ->
collector.addDeclarationContext(firClass, transformer.context)
}
action()
firClass
}
@@ -32,7 +32,7 @@ internal object LLFirAnnotationArgumentMappingLazyResolver : LLFirLazyResolver(F
scopeSession: ScopeSession,
towerDataContextCollector: FirResolveContextCollector?,
) {
val resolver = LLFirAnnotationArgumentsMappingTargetResolver(target, lockProvider, session, scopeSession)
val resolver = LLFirAnnotationArgumentsMappingTargetResolver(target, lockProvider, session, scopeSession, towerDataContextCollector)
resolver.resolveDesignation()
}
@@ -57,6 +57,7 @@ private class LLFirAnnotationArgumentsMappingTargetResolver(
lockProvider: LLFirLockProvider,
session: FirSession,
scopeSession: ScopeSession,
firResolveContextCollector: FirResolveContextCollector?,
) : LLFirAbstractBodyTargetResolver(
resolveTarget,
lockProvider,
@@ -67,7 +68,8 @@ private class LLFirAnnotationArgumentsMappingTargetResolver(
session,
scopeSession,
resolverPhase,
returnTypeCalculator = createReturnTypeCalculator(towerDataContextCollector = null)
returnTypeCalculator = createReturnTypeCalculator(firResolveContextCollector = firResolveContextCollector),
firResolveContextCollector = firResolveContextCollector,
)
override fun doLazyResolveUnderLock(target: FirElementWithResolveState) {
@@ -17,9 +17,11 @@ import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirElementWithResolveState
import org.jetbrains.kotlin.fir.FirFileAnnotationsContainer
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveContext
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirResolveContextCollector
import org.jetbrains.kotlin.fir.resolve.transformers.plugin.FirAnnotationArgumentsResolveTransformer
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
@@ -33,7 +35,7 @@ internal object LLFirAnnotationArgumentsLazyResolver : LLFirLazyResolver(FirReso
scopeSession: ScopeSession,
towerDataContextCollector: FirResolveContextCollector?,
) {
val resolver = LLFirAnnotationArgumentsTargetResolver(target, lockProvider, session, scopeSession)
val resolver = LLFirAnnotationArgumentsTargetResolver(target, lockProvider, session, scopeSession, towerDataContextCollector)
resolver.resolveDesignation()
}
@@ -68,6 +70,7 @@ private class LLFirAnnotationArgumentsTargetResolver(
lockProvider: LLFirLockProvider,
session: FirSession,
scopeSession: ScopeSession,
firResolveContextCollector: FirResolveContextCollector?,
) : LLFirAbstractBodyTargetResolver(
target,
lockProvider,
@@ -78,13 +81,55 @@ private class LLFirAnnotationArgumentsTargetResolver(
session,
scopeSession,
resolverPhase,
returnTypeCalculator = createReturnTypeCalculator(towerDataContextCollector = null)
returnTypeCalculator = createReturnTypeCalculator(firResolveContextCollector = firResolveContextCollector),
firResolveContextCollector = firResolveContextCollector,
)
override fun doLazyResolveUnderLock(target: FirElementWithResolveState) {
collectTowerDataContext(target)
FirLazyBodiesCalculator.calculateAnnotations(target)
transformAnnotations(target)
}
private fun collectTowerDataContext(target: FirElementWithResolveState) {
val contextCollector = transformer.firResolveContextCollector
if (contextCollector == null || target !is FirDeclaration) return
val bodyResolveContext = transformer.context
withTypeParametersIfMemberDeclaration(bodyResolveContext, target) {
when (target) {
is FirRegularClass -> {
contextCollector.addClassHeaderContext(target, bodyResolveContext.towerDataContext)
}
is FirFunction -> bodyResolveContext.forFunctionBody(target, transformer.components) {
contextCollector.addDeclarationContext(target, bodyResolveContext)
}
else -> contextCollector.addDeclarationContext(target, bodyResolveContext)
}
}
if (target is FirRegularClass) {
withRegularClass(target) {
contextCollector.addDeclarationContext(target, bodyResolveContext)
}
}
}
private inline fun withTypeParametersIfMemberDeclaration(
context: BodyResolveContext,
target: FirElementWithResolveState,
action: () -> Unit,
) {
if (target is FirMemberDeclaration) {
@OptIn(PrivateForInline::class)
context.withTypeParametersOf(target, action)
} else {
action()
}
}
}
internal fun LLFirAbstractBodyTargetResolver.transformAnnotations(target: FirElementWithResolveState) {
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.analysis.utils.errors.checkWithAttachmentBuilder
import org.jetbrains.kotlin.fir.FirElementWithResolveState
import org.jetbrains.kotlin.fir.FirFileAnnotationsContainer
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.getExplicitBackingField
import org.jetbrains.kotlin.fir.expressions.*
@@ -75,7 +74,7 @@ private class LLFirBodyTargetResolver(
lockProvider: LLFirLockProvider,
session: FirSession,
scopeSession: ScopeSession,
towerDataContextCollector: FirResolveContextCollector?,
firResolveContextCollector: FirResolveContextCollector?,
) : LLFirAbstractBodyTargetResolver(
target,
lockProvider,
@@ -87,8 +86,8 @@ private class LLFirBodyTargetResolver(
phase = resolverPhase,
implicitTypeOnly = false,
scopeSession = scopeSession,
returnTypeCalculator = createReturnTypeCalculator(towerDataContextCollector = towerDataContextCollector),
firResolveContextCollector = towerDataContextCollector,
returnTypeCalculator = createReturnTypeCalculator(firResolveContextCollector = firResolveContextCollector),
firResolveContextCollector = firResolveContextCollector,
) {
override val preserveCFGForClasses: Boolean get() = false
}
@@ -130,12 +129,7 @@ private class LLFirBodyTargetResolver(
}
private fun resolveMembersForControlFlowGraph(target: FirRegularClass) {
withTypeArguments(target) {
transformer.firResolveContextCollector?.addClassHeaderContext(target, transformer.context.towerDataContext)
}
withRegularClass(target) {
transformer.firResolveContextCollector?.addDeclarationContext(target, transformer.context)
for (member in target.declarations) {
if (member is FirControlFlowGraphOwner && member.isUsedInControlFlowGraphBuilderForClass) {
member.lazyResolveToPhase(resolverPhase.previous)
@@ -146,18 +140,6 @@ private class LLFirBodyTargetResolver(
}
override fun doLazyResolveUnderLock(target: FirElementWithResolveState) {
val contextCollector = transformer.firResolveContextCollector
if (contextCollector != null && target is FirDeclaration) {
val bodyResolveContext = transformer.context
if (target is FirFunction) {
bodyResolveContext.forFunctionBody(target, transformer.components) {
contextCollector.addDeclarationContext(target, bodyResolveContext)
}
} else {
contextCollector.addDeclarationContext(target, bodyResolveContext)
}
}
when (target) {
is FirRegularClass -> error("Should have been resolved in ${::doResolveWithoutLock.name}")
is FirConstructor -> resolve(target, BodyStateKeepers.CONSTRUCTOR)
@@ -175,13 +157,6 @@ private class LLFirBodyTargetResolver(
else -> throwUnexpectedFirElementError(target)
}
}
private inline fun withTypeArguments(regularClass: FirRegularClass, action: () -> Unit) {
@OptIn(PrivateForInline::class)
transformer.declarationsTransformer.context.withTypeParametersOf(regularClass) {
action()
}
}
}
internal object BodyStateKeepers {
@@ -56,7 +56,7 @@ internal class LLFirImplicitBodyTargetResolver(
lockProvider: LLFirLockProvider,
session: FirSession,
scopeSession: ScopeSession,
towerDataContextCollector: FirResolveContextCollector?,
firResolveContextCollector: FirResolveContextCollector?,
implicitBodyResolveComputationSession: ImplicitBodyResolveComputationSession? = null,
) : LLFirAbstractBodyTargetResolver(
target,
@@ -72,8 +72,8 @@ internal class LLFirImplicitBodyTargetResolver(
phase = resolverPhase,
implicitTypeOnly = true,
scopeSession = scopeSession,
firResolveContextCollector = towerDataContextCollector,
returnTypeCalculator = createReturnTypeCalculator(towerDataContextCollector = towerDataContextCollector),
firResolveContextCollector = firResolveContextCollector,
returnTypeCalculator = createReturnTypeCalculator(firResolveContextCollector = firResolveContextCollector),
) {
override val preserveCFGForClasses: Boolean get() = false
}
@@ -38,7 +38,7 @@ internal class LLFirReturnTypeCalculatorWithJump(
lockProvider = lockProvider,
session = declaration.moduleData.session,
scopeSession = scopeSession,
towerDataContextCollector = towerDataContextCollector,
firResolveContextCollector = towerDataContextCollector,
implicitBodyResolveComputationSession = implicitBodyResolveComputationSession,
)
@@ -79,6 +79,12 @@ internal abstract class LLFirTargetResolver(
is LLFirWholeFileResolveTarget -> {
resolveTarget.forEachTopLevelDeclaration(::resolveTargetWithNestedDeclarations)
}
is LLFirWholeClassResolveTarget -> {
performResolve(resolveTarget.target)
withRegularClass(resolveTarget.target) {
resolveTarget.forEachDeclaration(::resolveTargetWithNestedDeclarations)
}
}
}
}
}
@@ -48,14 +48,14 @@ internal object FirElementFinder {
nonLocalDeclaration = nonLocalDeclaration,
)?.pathWithTarget
private class FirDeclarationDesignation(
class FirDeclarationDesignation(
val path: List<FirRegularClass>,
val target: FirDeclaration,
) {
val pathWithTarget: List<FirDeclaration> get() = path + target
}
private fun collectDesignationPath(
fun collectDesignationPath(
firFile: FirFile,
nonLocalDeclaration: KtDeclaration,
): FirDeclarationDesignation? = collectDesignationPath(
@@ -228,6 +228,23 @@ open class PsiRawFirBuilder(
context
)
open fun convertPropertyAccessor(
accessor: KtPropertyAccessor?,
property: KtProperty,
propertyTypeRef: FirTypeRef,
propertySymbol: FirPropertySymbol,
isGetter: Boolean,
accessorAnnotationsFromProperty: List<FirAnnotation>,
parameterAnnotationsFromProperty: List<FirAnnotation>,
): FirPropertyAccessor? = accessor.toFirPropertyAccessor(
property,
propertyTypeRef,
propertySymbol,
isGetter,
accessorAnnotationsFromProperty,
parameterAnnotationsFromProperty,
)
open fun convertValueParameter(
valueParameter: KtParameter,
functionSymbol: FirFunctionSymbol<*>,
@@ -1914,7 +1931,8 @@ open class PsiRawFirBuilder(
propertyAnnotations.filter { it.useSiteTarget == FIELD || it.useSiteTarget == PROPERTY_DELEGATE_FIELD },
)
getter = this@toFirProperty.getter.toFirPropertyAccessor(
getter = convertPropertyAccessor(
this@toFirProperty.getter,
this@toFirProperty,
propertyType,
propertySymbol = symbol,
@@ -1922,7 +1940,9 @@ open class PsiRawFirBuilder(
accessorAnnotationsFromProperty = propertyAnnotations.filterUseSiteTarget(PROPERTY_GETTER),
parameterAnnotationsFromProperty = emptyList()
)
setter = this@toFirProperty.setter.toFirPropertyAccessor(
setter = convertPropertyAccessor(
this@toFirProperty.setter,
this@toFirProperty,
propertyType,
propertySymbol = symbol,
@@ -19,6 +19,8 @@ import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveCon
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirExpressionsResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirResolveContextCollector
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.visitors.transformSingle
open class FirAnnotationArgumentsMappingTransformer(
@@ -27,6 +29,7 @@ open class FirAnnotationArgumentsMappingTransformer(
resolvePhase: FirResolvePhase,
outerBodyResolveContext: BodyResolveContext? = null,
returnTypeCalculator: ReturnTypeCalculator = ReturnTypeCalculatorForFullBodyResolve,
firResolveContextCollector: FirResolveContextCollector? = null,
) : FirAbstractBodyResolveTransformerDispatcher(
session,
resolvePhase,
@@ -34,6 +37,7 @@ open class FirAnnotationArgumentsMappingTransformer(
scopeSession,
outerBodyResolveContext = outerBodyResolveContext,
returnTypeCalculator = returnTypeCalculator,
firResolveContextCollector = firResolveContextCollector,
) {
final override val expressionsTransformer: FirExpressionsResolveTransformer =
FirExpressionTransformerForAnnotationArgumentsMapping(this)
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclaration
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirExpressionsResolveTransformer
import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeAmbiguouslyResolvedAnnotationArgument
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirResolveContextCollector
import org.jetbrains.kotlin.fir.symbols.impl.FirEnumEntrySymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
@@ -38,6 +39,7 @@ open class FirAnnotationArgumentsResolveTransformer(
resolvePhase: FirResolvePhase,
outerBodyResolveContext: BodyResolveContext? = null,
returnTypeCalculator: ReturnTypeCalculator = ReturnTypeCalculatorForFullBodyResolve,
firResolveContextCollector: FirResolveContextCollector? = null,
) : FirAbstractBodyResolveTransformerDispatcher(
session,
resolvePhase,
@@ -45,6 +47,7 @@ open class FirAnnotationArgumentsResolveTransformer(
scopeSession,
outerBodyResolveContext = outerBodyResolveContext,
returnTypeCalculator = returnTypeCalculator,
firResolveContextCollector = firResolveContextCollector,
) {
final override val expressionsTransformer: FirExpressionsResolveTransformer =
FirExpressionsResolveTransformerForSpecificAnnotations(this)