[FIR IDE] Fix invalid resolve for local declarations that could be fully replaced with new instance

+small optimisations for IMPORTS phase
This commit is contained in:
Igor Yakovlev
2021-08-13 14:35:03 +02:00
parent ed0bfabc51
commit 6360366ecc
8 changed files with 158 additions and 96 deletions
@@ -74,8 +74,8 @@ inline fun <R> KtDeclaration.withFirDeclaration(
action: (FirDeclaration) -> R
): R {
val firDeclaration = resolveState.findSourceFirDeclaration(this)
firDeclaration.resolvedFirToType(resolveType, resolveState)
return action(firDeclaration)
val resolvedDeclaration = firDeclaration.resolvedFirToType(resolveType, resolveState)
return action(resolvedDeclaration)
}
/**
@@ -124,8 +124,8 @@ fun <D : FirDeclaration, R> D.withFirDeclaration(
phase: FirResolvePhase = FirResolvePhase.RAW_FIR,
action: (D) -> R,
): R {
resolvedFirToPhase(phase, resolveState)
return action(this)
val resolvedDeclaration = resolvedFirToPhase(phase, resolveState)
return action(resolvedDeclaration)
}
/**
@@ -137,8 +137,8 @@ fun <D : FirDeclaration, R> D.withFirDeclaration(
resolveState: FirModuleResolveState,
action: (D) -> R,
): R {
resolvedFirToType(type, resolveState)
return action(this)
val resolvedDeclaration = resolvedFirToType(type, resolveState)
return action(resolvedDeclaration)
}
/**
@@ -116,17 +116,19 @@ internal class FileStructure private constructor(
moduleFileCache,
firFile
)
firLazyDeclarationResolver.lazyResolveDeclaration(
val resolvedDeclaration = firLazyDeclarationResolver.lazyResolveDeclaration(
firDeclarationToResolve = firDeclaration,
moduleFileCache = moduleFileCache,
scopeSession = ScopeSession(),
toPhase = FirResolvePhase.BODY_RESOLVE,
checkPCE = true,
)
return FileElementFactory.createFileStructureElement(firDeclaration, declaration, firFile, moduleFileCache.firFileLockProvider)
// return moduleFileCache.firFileLockProvider.withReadLock(firFile) {
// FileElementFactory.createFileStructureElement(firDeclaration, declaration, firFile, moduleFileCache.firFileLockProvider)
// }
return FileElementFactory.createFileStructureElement(
firDeclaration = resolvedDeclaration,
ktDeclaration = declaration,
firFile = firFile,
firFileLockProvider = moduleFileCache.firFileLockProvider
)
}
private fun createStructureElement(container: KtAnnotated): FileStructureElement = when (container) {
@@ -140,13 +140,16 @@ internal class ReanalyzableFunctionStructureElement(
it.replaceResolvePhase(minOf(it.resolvePhase, upgradedPhase))
}
firLazyDeclarationResolver.lazyResolveDeclaration(
val resolvedDeclaration = firLazyDeclarationResolver.lazyResolveDeclaration(
firDeclarationToResolve = originalFunction,
moduleFileCache = cache,
scopeSession = ScopeSession(),
toPhase = FirResolvePhase.BODY_RESOLVE,
checkPCE = true,
)
check(resolvedDeclaration === originalFunction) {
"Reanalysed declaration not expected to be updated"
}
ReanalyzableFunctionStructureElement(
firFile,
@@ -200,13 +203,16 @@ internal class ReanalyzablePropertyStructureElement(
replaceInitializerAndAccessorsAreResolved(false)
}
firLazyDeclarationResolver.lazyResolveDeclaration(
val resolvedDeclaration = firLazyDeclarationResolver.lazyResolveDeclaration(
firDeclarationToResolve = originalProperty,
moduleFileCache = cache,
scopeSession = ScopeSession(),
toPhase = FirResolvePhase.BODY_RESOLVE,
checkPCE = true,
)
check(resolvedDeclaration === originalProperty) {
"Reanalysed declaration not expected to be updated"
}
ReanalyzablePropertyStructureElement(
firFile,
@@ -9,8 +9,6 @@ import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.runCustomResolveUnderLock
import org.jetbrains.kotlin.idea.fir.low.level.api.util.getContainingFile
enum class ResolveType {
NoResolve,
@@ -28,40 +26,43 @@ enum class ResolveType {
// ResolveForSuperMembers,
}
internal fun FirLazyDeclarationResolver.lazyResolveDeclaration(
firDeclaration: FirDeclaration,
internal fun <D : FirDeclaration> FirLazyDeclarationResolver.lazyResolveDeclaration(
firDeclaration: D,
moduleFileCache: ModuleFileCache,
toResolveType: ResolveType,
scopeSession: ScopeSession,
checkPCE: Boolean = false,
) {
when (toResolveType) {
ResolveType.NoResolve -> return
): D {
return when (toResolveType) {
ResolveType.NoResolve -> return firDeclaration
ResolveType.CallableReturnType -> {
require(firDeclaration is FirCallableDeclaration) {
"CallableReturnType type cannot be applied to ${firDeclaration::class.qualifiedName}"
}
if (firDeclaration.resolvePhase < FirResolvePhase.TYPES) {
if (firDeclaration.returnTypeRef is FirResolvedTypeRef) return
lazyResolveDeclaration(
firDeclarationToResolve = firDeclaration,
var currentDeclaration = firDeclaration as FirCallableDeclaration
if (currentDeclaration.resolvePhase < FirResolvePhase.TYPES) {
if (currentDeclaration.returnTypeRef !is FirResolvedTypeRef) {
currentDeclaration = lazyResolveDeclaration(
firDeclarationToResolve = currentDeclaration,
moduleFileCache = moduleFileCache,
toPhase = FirResolvePhase.TYPES,
scopeSession = scopeSession,
checkPCE = checkPCE,
)
}
}
if (currentDeclaration.returnTypeRef !is FirResolvedTypeRef) {
currentDeclaration = lazyResolveDeclaration(
firDeclarationToResolve = currentDeclaration,
moduleFileCache = moduleFileCache,
toPhase = FirResolvePhase.TYPES,
toPhase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE,
scopeSession = scopeSession,
checkPCE = checkPCE,
)
}
if (firDeclaration.returnTypeRef is FirResolvedTypeRef) return
lazyResolveDeclaration(
firDeclarationToResolve = firDeclaration,
moduleFileCache = moduleFileCache,
toPhase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE,
scopeSession = scopeSession,
checkPCE = checkPCE,
)
check(firDeclaration.returnTypeRef is FirResolvedTypeRef)
check(currentDeclaration.returnTypeRef is FirResolvedTypeRef)
currentDeclaration as D
}
ResolveType.BodyResolveWithChildren, ResolveType.CallableBodyResolve -> {
require(firDeclaration is FirCallableDeclaration || toResolveType != ResolveType.CallableBodyResolve) {
@@ -84,6 +85,7 @@ internal fun FirLazyDeclarationResolver.lazyResolveDeclaration(
scopeSession = scopeSession,
checkPCE = checkPCE
)
firDeclaration
} else {
val toPhase =
if (toResolveType == ResolveType.AnnotationType) FirResolvePhase.TYPES else FirResolvePhase.ARGUMENTS_OF_ANNOTATIONS
@@ -11,7 +11,9 @@ import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticPropertyAccessor
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirAnnotationResolveStatus
import org.jetbrains.kotlin.fir.realPsi
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.firProvider
import org.jetbrains.kotlin.fir.resolve.transformers.FirImportResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirDeclarationDesignationWithFile
@@ -25,6 +27,7 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider
import org.jetbrains.kotlin.idea.fir.low.level.api.transformers.FirFileAnnotationsResolveTransformer
import org.jetbrains.kotlin.idea.fir.low.level.api.transformers.FirProviderInterceptorForIDE
import org.jetbrains.kotlin.idea.fir.low.level.api.transformers.LazyTransformerFactory
import org.jetbrains.kotlin.idea.fir.low.level.api.util.FirElementFinder
import org.jetbrains.kotlin.idea.fir.low.level.api.util.checkCanceled
import org.jetbrains.kotlin.idea.fir.low.level.api.util.findSourceNonLocalFirDeclaration
@@ -45,7 +48,6 @@ internal class FirLazyDeclarationResolver(private val firFileBuilder: FirFileBui
moduleFileCache.firFileLockProvider.runCustomResolveUnderLock(firFile, checkPCE) {
resolveFileAnnotationsWithoutLock(
firFile = firFile,
moduleFileCache = moduleFileCache,
annotations = annotations,
scopeSession = scopeSession,
collector = collector
@@ -59,20 +61,12 @@ internal class FirLazyDeclarationResolver(private val firFileBuilder: FirFileBui
*/
private fun resolveFileAnnotationsWithoutLock(
firFile: FirFile,
moduleFileCache: ModuleFileCache,
annotations: List<FirAnnotationCall>,
scopeSession: ScopeSession,
collector: FirTowerDataContextCollector? = null,
) {
if (firFile.resolvePhase < FirResolvePhase.IMPORTS) {
lazyResolveFileDeclarationWithoutLock(
firFile = firFile,
moduleFileCache = moduleFileCache,
toPhase = FirResolvePhase.IMPORTS,
scopeSession = scopeSession,
checkPCE = false,
collector = collector
)
resolveFileToImportsWithoutLock(firFile, false)
}
if (!annotations.all { it.resolveStatus == FirAnnotationResolveStatus.Resolved }) {
@@ -84,7 +78,6 @@ internal class FirLazyDeclarationResolver(private val firFileBuilder: FirFileBui
firTowerDataContextCollector = collector,
).transformDeclaration(firFileBuilder.firPhaseRunner)
}
}
private fun FirDeclaration.isValidForResolve(): Boolean = when (origin) {
@@ -124,6 +117,8 @@ internal class FirLazyDeclarationResolver(private val firFileBuilder: FirFileBui
checkPCE: Boolean = false,
) {
if (toPhase == FirResolvePhase.RAW_FIR) return
resolveFileToImports(firFile, moduleFileCache, checkPCE)
if (toPhase == FirResolvePhase.IMPORTS) return
if (firFile.resolvePhase >= toPhase) return
moduleFileCache.firFileLockProvider.runCustomResolveUnderLock(firFile, checkPCE) {
lazyResolveFileDeclarationWithoutLock(
@@ -136,6 +131,20 @@ internal class FirLazyDeclarationResolver(private val firFileBuilder: FirFileBui
}
}
private fun resolveFileToImports(firFile: FirFile, moduleFileCache: ModuleFileCache, checkPCE: Boolean) {
if (firFile.resolvePhase >= FirResolvePhase.IMPORTS) return
moduleFileCache.firFileLockProvider.runCustomResolveUnderLock(firFile, checkPCE) {
resolveFileToImportsWithoutLock(firFile, checkPCE)
}
}
private fun resolveFileToImportsWithoutLock(firFile: FirFile, checkPCE: Boolean) {
if (firFile.resolvePhase >= FirResolvePhase.IMPORTS) return
if (checkPCE) checkCanceled()
firFile.transform<FirElement, Any?>(FirImportResolveTransformer(firFile.moduleData.session), null)
firFile.replaceResolvePhase(FirResolvePhase.IMPORTS)
}
private fun lazyResolveFileDeclarationWithoutLock(
firFile: FirFile,
moduleFileCache: ModuleFileCache,
@@ -145,16 +154,11 @@ internal class FirLazyDeclarationResolver(private val firFileBuilder: FirFileBui
collector: FirTowerDataContextCollector? = null,
) {
if (toPhase == FirResolvePhase.RAW_FIR) return
val resolvePhase = firFile.resolvePhase
if (resolvePhase >= toPhase) return
if (resolvePhase == FirResolvePhase.RAW_FIR) {
firFile.transform<FirElement, Any?>(FirImportResolveTransformer(firFile.moduleData.session), null)
firFile.replaceResolvePhase(FirResolvePhase.IMPORTS)
}
if (checkPCE) checkCanceled()
resolveFileToImportsWithoutLock(firFile, checkPCE)
if (toPhase == FirResolvePhase.IMPORTS) return
if (checkPCE) checkCanceled()
resolveFileAnnotationsWithoutLock(firFile, moduleFileCache, firFile.annotations, scopeSession, collector)
resolveFileAnnotationsWithoutLock(firFile, firFile.annotations, scopeSession, collector)
val validForResolveDeclarations = firFile.declarations
.filter { it.isValidForResolve() && it.resolvePhase < toPhase }
@@ -196,23 +200,44 @@ internal class FirLazyDeclarationResolver(private val firFileBuilder: FirFileBui
}
}
private fun fastTrackForImportsPhase(
firDeclarationToResolve: FirDeclaration,
moduleFileCache: ModuleFileCache,
checkPCE: Boolean,
): Boolean {
val provider = firDeclarationToResolve.moduleData.session.firProvider
val firFile = when (firDeclarationToResolve) {
is FirFile -> firDeclarationToResolve
is FirCallableDeclaration -> provider.getFirCallableContainerFile(firDeclarationToResolve.symbol)
is FirClassLikeDeclaration -> provider.getFirClassifierContainerFile(firDeclarationToResolve.symbol)
else -> null
} ?: return false
resolveFileToImports(firFile, moduleFileCache, checkPCE)
return true
}
/**
* Run designated resolve only designation with fully resolved path (synchronized).
* Suitable for body resolve or/and on-air resolve.
* @see lazyResolveDeclaration for ordinary resolve
* @param firDeclarationToResolve target non-local declaration
*/
fun lazyResolveDeclaration(
firDeclarationToResolve: FirDeclaration,
fun <D : FirDeclaration> lazyResolveDeclaration(
firDeclarationToResolve: D,
moduleFileCache: ModuleFileCache,
scopeSession: ScopeSession,
toPhase: FirResolvePhase,
checkPCE: Boolean,
skipLocalDeclaration: Boolean = false,
) {
if (toPhase == FirResolvePhase.RAW_FIR) return
if (!firDeclarationToResolve.isValidForResolve()) return
if (firDeclarationToResolve.resolvePhase >= toPhase) return
): D {
if (toPhase == FirResolvePhase.RAW_FIR) return firDeclarationToResolve
if (toPhase == FirResolvePhase.IMPORTS) {
if (fastTrackForImportsPhase(firDeclarationToResolve, moduleFileCache, checkPCE)) {
return firDeclarationToResolve
}
}
if (!firDeclarationToResolve.isValidForResolve()) return firDeclarationToResolve
if (firDeclarationToResolve.resolvePhase >= toPhase) return firDeclarationToResolve
if (firDeclarationToResolve is FirFile) {
lazyResolveFileDeclaration(
@@ -222,23 +247,25 @@ internal class FirLazyDeclarationResolver(private val firFileBuilder: FirFileBui
scopeSession = scopeSession,
checkPCE = checkPCE,
)
return
return firDeclarationToResolve
}
val requestedDeclarationDesignation = firDeclarationToResolve.tryCollectDesignationWithFile()
val designation: FirDeclarationDesignationWithFile
val neededPhase: FirResolvePhase
val isLocalDeclarationResolveRequested: Boolean
if (requestedDeclarationDesignation != null) {
designation = requestedDeclarationDesignation
neededPhase = toPhase
isLocalDeclarationResolveRequested = false
} else {
val possiblyLocalDeclaration = firDeclarationToResolve.getKtDeclarationForFirElement()
val nonLocalDeclaration = possiblyLocalDeclaration.getNonLocalContainingOrThisDeclaration()
?: error("Container for local declaration cannot be null")
val isLocalDeclarationResolveRequested = possiblyLocalDeclaration != nonLocalDeclaration
if (isLocalDeclarationResolveRequested && skipLocalDeclaration) return
isLocalDeclarationResolveRequested = possiblyLocalDeclaration != nonLocalDeclaration
if (isLocalDeclarationResolveRequested && skipLocalDeclaration) return firDeclarationToResolve
val nonLocalFirDeclaration = nonLocalDeclaration.findSourceNonLocalFirDeclaration(
firFileBuilder,
@@ -248,14 +275,19 @@ internal class FirLazyDeclarationResolver(private val firFileBuilder: FirFileBui
neededPhase = if (isLocalDeclarationResolveRequested) FirResolvePhase.BODY_RESOLVE else toPhase
if (nonLocalFirDeclaration.resolvePhase >= neededPhase) return
if (!nonLocalFirDeclaration.isValidForResolve()) return
if (nonLocalFirDeclaration.resolvePhase >= neededPhase) return firDeclarationToResolve
if (!nonLocalFirDeclaration.isValidForResolve()) return firDeclarationToResolve
designation = nonLocalFirDeclaration.collectDesignationWithFile()
}
if (firDeclarationToResolve.resolvePhase >= toPhase) return
if (designation.declaration.resolvePhase >= neededPhase) return firDeclarationToResolve
if (neededPhase == FirResolvePhase.IMPORTS) {
resolveFileToImports(designation.firFile, moduleFileCache, checkPCE)
return firDeclarationToResolve
}
moduleFileCache.firFileLockProvider.runCustomResolveUnderLock(designation.firFile, checkPCE) {
runLazyDesignatedResolveWithoutLock(
designation = designation,
@@ -265,6 +297,31 @@ internal class FirLazyDeclarationResolver(private val firFileBuilder: FirFileBui
checkPCE = checkPCE,
)
}
if (!isLocalDeclarationResolveRequested) return firDeclarationToResolve
return remapDeclarationInContainerIfNeeded(firDeclarationToResolve, designation.declaration, toPhase)
}
private fun <D : FirDeclaration> remapDeclarationInContainerIfNeeded(
declarationToRemap: D,
firContainer: FirDeclaration,
firResolvePhase: FirResolvePhase
): D {
if (declarationToRemap.resolvePhase >= firResolvePhase) return declarationToRemap
val realPsi = declarationToRemap.realPsi
check(realPsi != null) {
"Cannot remap element without PSI"
}
val firDeclaration = FirElementFinder.findElementIn<FirDeclaration>(firContainer) {
it.realPsi == realPsi
} as? D
check(firDeclaration != null) {
"Containing declaration was resolved but local didn't found in it"
}
check(firDeclaration.resolvePhase >= firResolvePhase) {
"Found local declaration wasn't completely resolved"
}
return firDeclaration
}
private fun runLazyDesignatedResolveWithoutLock(
@@ -275,19 +332,12 @@ internal class FirLazyDeclarationResolver(private val firFileBuilder: FirFileBui
checkPCE: Boolean,
) {
if (toPhase == FirResolvePhase.RAW_FIR) return
resolveFileToImportsWithoutLock(designation.firFile, checkPCE)
if (toPhase == FirResolvePhase.IMPORTS) return
val declarationResolvePhase = designation.declaration.resolvePhase
if (declarationResolvePhase >= toPhase) return
if (designation.firFile.resolvePhase == FirResolvePhase.RAW_FIR) {
lazyResolveFileDeclarationWithoutLock(
firFile = designation.firFile,
moduleFileCache = moduleFileCache,
toPhase = FirResolvePhase.IMPORTS,
scopeSession = scopeSession,
checkPCE = checkPCE
)
}
if (toPhase == FirResolvePhase.IMPORTS) return
var currentPhase = maxOf(declarationResolvePhase, FirResolvePhase.IMPORTS)
while (currentPhase < toPhase) {
@@ -315,9 +365,11 @@ internal class FirLazyDeclarationResolver(private val firFileBuilder: FirFileBui
onAirCreatedDeclaration: Boolean,
towerDataContextCollector: FirTowerDataContextCollector?,
) {
val scopeSession = ScopeSession()
resolveFileToImportsWithoutLock(designation.firFile, checkPCE)
var currentPhase = maxOf(designation.declaration.resolvePhase, FirResolvePhase.IMPORTS)
val scopeSession = ScopeSession()
val firProviderInterceptor = if (onAirCreatedDeclaration) {
FirProviderInterceptorForIDE.createForFirElement(
session = designation.firFile.moduleData.session,
@@ -22,19 +22,19 @@ FILE: localDeclaration.kt
IMPORTS:
FILE: localDeclaration.kt
public final [STATUS] class A : R|kotlin/Any| {
public [STATUS] [ContainingClassKey=A] constructor(): R|A| {
public? final? [RAW_FIR] class A : R|kotlin/Any| {
public? [RAW_FIR] [ContainingClassKey=A] constructor(): R|A| {
super<R|kotlin/Any|>()
}
public final [BODY_RESOLVE] fun x(): R|kotlin/Unit| {
local final [BODY_RESOLVE] [ContainingClassKey=A] class resolveMe : R|kotlin/Any| {
public [BODY_RESOLVE] [ContainingClassKey=resolveMe] constructor(): R|A.resolveMe| {
public? final? [RAW_FIR] fun x(): R|kotlin/Unit| {
local final? [RAW_FIR] [ContainingClassKey=A] class resolveMe : R|kotlin/Any| {
public? [RAW_FIR] [ContainingClassKey=resolveMe] constructor(): R|A.resolveMe| {
super<R|kotlin/Any|>()
}
public final [BODY_RESOLVE] val e: R|kotlin/Int| = Int(2)
[BODY_RESOLVE] [ContainingClassKey=resolveMe] public get(): R|kotlin/Int|
public? final? [RAW_FIR] val e: <implicit> = IntegerLiteral(2)
[TYPES] [ContainingClassKey=resolveMe] public? get(): <implicit>
}
@@ -15,13 +15,13 @@ FILE: localFunction.kt
IMPORTS:
FILE: localFunction.kt
public final [STATUS] class A : R|kotlin/Any| {
public [STATUS] [ContainingClassKey=A] constructor(): R|A| {
public? final? [RAW_FIR] class A : R|kotlin/Any| {
public? [RAW_FIR] [ContainingClassKey=A] constructor(): R|A| {
super<R|kotlin/Any|>()
}
public final [BODY_RESOLVE] fun x(): R|kotlin/Unit| {
local final [BODY_RESOLVE] fun resolveMe(): R|kotlin/Unit| {
public? final? [RAW_FIR] fun x(): R|kotlin/Unit| {
local final? [RAW_FIR] fun resolveMe(): R|kotlin/Unit| {
}
}
@@ -18,16 +18,16 @@ FILE: parameterOfLocalSetter.kt
IMPORTS:
FILE: parameterOfLocalSetter.kt
public final [BODY_RESOLVE] fun ddd(): R|kotlin/Unit| {
local final [BODY_RESOLVE] class XX : R|kotlin/Any| {
public [BODY_RESOLVE] [ContainingClassKey=XX] constructor(): R|XX| {
public? final? [RAW_FIR] fun ddd(): R|kotlin/Unit| {
local final? [RAW_FIR] class XX : R|kotlin/Any| {
public? [RAW_FIR] [ContainingClassKey=XX] constructor(): R|XX| {
super<R|kotlin/Any|>()
}
public final [BODY_RESOLVE] var x: R|kotlin/Int| = Int(2)
[BODY_RESOLVE] [ContainingClassKey=XX] public get(): R|kotlin/Int|
[BODY_RESOLVE] [ContainingClassKey=XX] public set([BODY_RESOLVE] resolveMe: R|kotlin/Int|): R|kotlin/Unit| {
^ Q|kotlin/Unit|
public? final? [RAW_FIR] var x: Int = IntegerLiteral(2)
[TYPES] [ContainingClassKey=XX] public? get(): Int
[RAW_FIR] [ContainingClassKey=XX] public? set([RAW_FIR] resolveMe: Int): R|kotlin/Unit| {
^ Unit#
}
}