diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirModuleResolveStateImpl.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirModuleResolveStateImpl.kt index dcbf76816d5..abd336d1a2d 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirModuleResolveStateImpl.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirModuleResolveStateImpl.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.FileStructureCache import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.ResolveType +import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.lazyResolveDeclaration import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeSessionProvider import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeSourcesSession diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/LowLevelFirApiFacade.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/LowLevelFirApiFacade.kt index b7b8fc9f19b..6b0bda5a57f 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/LowLevelFirApiFacade.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/LowLevelFirApiFacade.kt @@ -135,6 +135,18 @@ fun D.withFirDeclarationInWriteLock( return resolveState.withLock(this, DeclarationLockType.WRITE_LOCK, action) } +/** + * Executes [action] with given [FirDeclaration] under write lock, so resolve **is possible** inside [action] + */ +fun D.withFirDeclarationInWriteLock( + resolveState: FirModuleResolveState, + resolveType: ResolveType = ResolveType.BodyResolveWithChildren, + action: (D) -> R, +): R { + resolvedFirToType(resolveType, resolveState) + return resolveState.withLock(this, DeclarationLockType.WRITE_LOCK, action) +} + /** * Returns a list of Diagnostics compiler finds for given [KtElement] * This operation could be performance affective because it create FIleStructureElement and resolve non-local declaration into BODY phase diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/LowLevelFirApiFacadeForResolveOnAir.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/LowLevelFirApiFacadeForResolveOnAir.kt index 38e66437e9e..c9a0357e4dd 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/LowLevelFirApiFacadeForResolveOnAir.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/api/LowLevelFirApiFacadeForResolveOnAir.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.api.DeclarationCopyBuilder.wi import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FileTowerProvider import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirTowerContextProvider import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirTowerDataContextAllElementsCollector +import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.runCustomResolveUnderLock import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.FirElementsRecorder import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.KtToFirMapping import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver @@ -248,7 +249,7 @@ object LowLevelFirApiFacadeForResolveOnAir { val isInBodyReplacement = isInBodyReplacement(nonLocalDeclaration, replacement) - return FirLazyDeclarationResolver.runCustomResolveUnderLock(originalFirFile, state.rootModuleSession.cache, true) { + return state.rootModuleSession.cache.firFileLockProvider.runCustomResolveUnderLock(originalFirFile, true) { val copiedFirDeclaration = isInBodyReplacement.ifTrue { when (originalDeclaration) { is FirSimpleFunction -> diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/element/builder/FirElementBuilder.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/element/builder/FirElementBuilder.kt index 07bb44f81bb..423490d6eb0 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/element/builder/FirElementBuilder.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/element/builder/FirElementBuilder.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.FileStructureCache import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.FileStructureElement import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver +import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.declarationCanBeLazilyResolved import org.jetbrains.kotlin.idea.fir.low.level.api.util.isNonAnonymousClassOrObject import org.jetbrains.kotlin.idea.util.getElementTextInContext import org.jetbrains.kotlin.psi.* @@ -110,7 +111,7 @@ internal inline fun PsiElement.getNonLocalContainingOrThisDeclaration(predicate: if (container is KtNamedDeclaration && (container.isNonAnonymousClassOrObject() || container is KtDeclarationWithBody || container is KtProperty || container is KtTypeAlias) && container !is KtPrimaryConstructor - && FirLazyDeclarationResolver.declarationCanBeLazilyResolved(container) + && declarationCanBeLazilyResolved(container) && container !is KtEnumEntry && container !is KtFunctionLiteral && container.containingClassOrObject !is KtEnumEntry diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/builder/LockProvider.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/builder/LockProvider.kt index c9fbc3bab42..b921014c8ed 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/builder/LockProvider.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/builder/LockProvider.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.idea.fir.low.level.api.file.builder import com.google.common.collect.MapMaker +import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.PrivateForInline import org.jetbrains.kotlin.idea.fir.low.level.api.api.DeclarationLockType import org.jetbrains.kotlin.idea.fir.low.level.api.util.lockWithPCECheck @@ -69,4 +70,19 @@ internal class DeadLockGuard { } } +/** + * Runs [resolve] function (which is considered to do some resolve on [firFile]) under a lock for [firFile] + */ +internal inline fun LockProvider.runCustomResolveUnderLock( + firFile: FirFile, + checkPCE: Boolean, + body: () -> R +): R { + return if (checkPCE) { + withWriteLockPCECheck(key = firFile, lockingIntervalMs = 50L, action = body) + } else { + withWriteLock(key = firFile, action = body) + } +} + class ReadWriteDeadLockException : IllegalStateException("Acquiring write lock when read lock hold") diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/structure/FileStructureElement.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/structure/FileStructureElement.kt index 3dd257a3fd2..b763ec65781 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/structure/FileStructureElement.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/structure/FileStructureElement.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.LockProvider import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.RawFirNonLocalDeclarationBuilder +import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.declarationCanBeLazilyResolved import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirIdeProvider import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider import org.jetbrains.kotlin.idea.fir.low.level.api.transformers.FirLazyTransformerForIDE.Companion.resolvePhaseForDeclarationAndChildren @@ -231,14 +232,14 @@ internal class NonReanalyzableDeclarationStructureElement( private val recorder = object : FirElementsRecorder() { override fun visitProperty(property: FirProperty, data: MutableMap) { val psi = property.psi as? KtProperty ?: return super.visitProperty(property, data) - if (!FileElementFactory.isReanalyzableContainer(psi) || !FirLazyDeclarationResolver.declarationCanBeLazilyResolved(psi)) { + if (!FileElementFactory.isReanalyzableContainer(psi) || !declarationCanBeLazilyResolved(psi)) { super.visitProperty(property, data) } } override fun visitSimpleFunction(simpleFunction: FirSimpleFunction, data: MutableMap) { val psi = simpleFunction.psi as? KtNamedFunction ?: return super.visitSimpleFunction(simpleFunction, data) - if (!FileElementFactory.isReanalyzableContainer(psi) || !FirLazyDeclarationResolver.declarationCanBeLazilyResolved(psi)) { + if (!FileElementFactory.isReanalyzableContainer(psi) || !declarationCanBeLazilyResolved(psi)) { super.visitSimpleFunction(simpleFunction, data) } } diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/structure/FileStructureUtil.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/structure/FileStructureUtil.kt index 72096c540c3..b7f2d9868dc 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/structure/FileStructureUtil.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/structure/FileStructureUtil.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver +import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.declarationCanBeLazilyResolved import org.jetbrains.kotlin.idea.fir.low.level.api.util.replaceFirst import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject @@ -23,7 +24,7 @@ internal object FileStructureUtil { ktDeclaration !is KtClassOrObject && ktDeclaration !is KtDeclarationWithBody && ktDeclaration !is KtProperty && ktDeclaration !is KtTypeAlias -> false ktDeclaration is KtEnumEntry -> false ktDeclaration.containingClassOrObject is KtEnumEntry -> false - ktDeclaration is KtNamedDeclaration -> !FirLazyDeclarationResolver.declarationCanBeLazilyResolved(ktDeclaration) + ktDeclaration is KtNamedDeclaration -> !declarationCanBeLazilyResolved(ktDeclaration) else -> false } diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/lazy/resolve/FirLazyDeclarationResolveToType.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/lazy/resolve/FirLazyDeclarationResolveToType.kt new file mode 100644 index 00000000000..a0932ad3922 --- /dev/null +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/lazy/resolve/FirLazyDeclarationResolveToType.kt @@ -0,0 +1,135 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve + +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 + +enum class ResolveType { + NoResolve, + BodyResolveWithChildren, + CallableBodyResolve, + CallableReturnType, + AnnotationType, + AnnotationsArguments, + ClassSuperTypes, + CallableContracts, + DeclarationStatus, + ValueParametersTypes, + TypeParametersTypes, +// ResolveForMemberScope, +// ResolveForSuperMembers, +} + +internal fun FirLazyDeclarationResolver.lazyResolveDeclaration( + firDeclaration: FirDeclaration, + moduleFileCache: ModuleFileCache, + toResolveType: ResolveType, + scopeSession: ScopeSession = ScopeSession(), + checkPCE: Boolean = false, +) { + when (toResolveType) { + ResolveType.NoResolve -> return + ResolveType.CallableReturnType -> { + require(firDeclaration is FirCallableDeclaration<*>) { + "CallableReturn type cannot be applied to ${firDeclaration::class.qualifiedName}" + } + val stopAheadOfPhase = { declaration: FirDeclaration -> + declaration is FirCallableDeclaration<*> && declaration.returnTypeRef is FirResolvedTypeRef + } + lazyResolveDeclaration( + firDeclarationToResolve = firDeclaration, + moduleFileCache = moduleFileCache, + toPhase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE, + scopeSession = scopeSession, + checkPCE = checkPCE, + stopAheadOfPhase = stopAheadOfPhase, + ) + } + ResolveType.BodyResolveWithChildren, ResolveType.CallableBodyResolve -> { + require(firDeclaration is FirCallableDeclaration<*> || toResolveType != ResolveType.CallableBodyResolve) { + "CallableReturn type cannot be applied to ${firDeclaration::class.qualifiedName}" + } + lazyResolveDeclaration( + firDeclarationToResolve = firDeclaration, + moduleFileCache = moduleFileCache, + toPhase = FirResolvePhase.BODY_RESOLVE, + scopeSession = scopeSession, + checkPCE = checkPCE, + ) + } + ResolveType.AnnotationType, ResolveType.AnnotationsArguments -> { + if (firDeclaration is FirFile) { + resolveFileAnnotations( + firFile = firDeclaration, + annotations = firDeclaration.annotations, + moduleFileCache = moduleFileCache, + scopeSession = scopeSession, + checkPCE = checkPCE + ) + } else { + val toPhase = if (toResolveType == ResolveType.AnnotationType) FirResolvePhase.TYPES else FirResolvePhase.BODY_RESOLVE + lazyResolveDeclaration( + firDeclarationToResolve = firDeclaration, + moduleFileCache = moduleFileCache, + toPhase = toPhase, + scopeSession = scopeSession, + checkPCE = checkPCE, + ) + } + } + ResolveType.ClassSuperTypes -> { + require(firDeclaration is FirClassLikeDeclaration<*>) { + "CallableReturn type cannot be applied to ${firDeclaration::class.qualifiedName}" + } + lazyResolveDeclaration( + firDeclarationToResolve = firDeclaration, + moduleFileCache = moduleFileCache, + toPhase = FirResolvePhase.SUPER_TYPES, + scopeSession = scopeSession, + checkPCE = checkPCE, + ) + } + ResolveType.CallableContracts -> { + require(firDeclaration is FirCallableDeclaration<*>) { + "CallableReturn type cannot be applied to ${firDeclaration::class.qualifiedName}" + } + lazyResolveDeclaration( + firDeclarationToResolve = firDeclaration, + moduleFileCache = moduleFileCache, + toPhase = FirResolvePhase.CONTRACTS, + scopeSession = scopeSession, + checkPCE = checkPCE, + ) + } + ResolveType.DeclarationStatus -> { + require(firDeclaration !is FirFile) { + "CallableReturn type cannot be applied to ${firDeclaration::class.qualifiedName}" + } + lazyResolveDeclaration( + firDeclarationToResolve = firDeclaration, + moduleFileCache = moduleFileCache, + toPhase = FirResolvePhase.STATUS, + scopeSession = scopeSession, + checkPCE = checkPCE, + ) + } + ResolveType.ValueParametersTypes, ResolveType.TypeParametersTypes -> { + require(firDeclaration !is FirFile) { + "CallableReturn type cannot be applied to ${firDeclaration::class.qualifiedName}" + } + lazyResolveDeclaration( + firDeclarationToResolve = firDeclaration, + moduleFileCache = moduleFileCache, + toPhase = FirResolvePhase.TYPES, + scopeSession = scopeSession, + checkPCE = checkPCE, + ) + } + } +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/lazy/resolve/FirLazyDeclarationResolver.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/lazy/resolve/FirLazyDeclarationResolver.kt index 28bccaf8765..c406bf03d83 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/lazy/resolve/FirLazyDeclarationResolver.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/lazy/resolve/FirLazyDeclarationResolver.kt @@ -5,64 +5,30 @@ package org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve -import com.intellij.psi.util.parentOfType -import org.jetbrains.kotlin.fir.* +import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall +import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.resolve.ScopeSession -import org.jetbrains.kotlin.fir.resolve.providers.FirProvider -import org.jetbrains.kotlin.fir.resolve.symbolProvider import org.jetbrains.kotlin.fir.resolve.transformers.FirImportResolveTransformer -import org.jetbrains.kotlin.fir.resolve.transformers.FirProviderInterceptor import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector -import org.jetbrains.kotlin.idea.fir.low.level.api.api.* -import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.getNonLocalContainingOrThisDeclaration +import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirDeclarationDesignationWithFile +import org.jetbrains.kotlin.idea.fir.low.level.api.api.collectDesignation import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder 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.providers.firIdeProvider -import org.jetbrains.kotlin.idea.fir.low.level.api.transformers.* +import org.jetbrains.kotlin.idea.fir.low.level.api.transformers.FirFileAnnotationsResolveTransformer import org.jetbrains.kotlin.idea.fir.low.level.api.transformers.FirLazyTransformerForIDE.Companion.resolvePhaseForAllDeclarations -import org.jetbrains.kotlin.idea.fir.low.level.api.util.* +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.checkCanceled +import org.jetbrains.kotlin.idea.fir.low.level.api.util.ensurePhase +import org.jetbrains.kotlin.idea.fir.low.level.api.util.getContainingFile import org.jetbrains.kotlin.idea.util.ifFalse -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject - -enum class ResolveType { - FileAnnotations, - CallableReturnType, - ClassSuperTypes, - DeclarationStatus, - ValueParametersTypes, - TypeParametersTypes, - AnnotationType, - AnnotationParameters, - CallableBodyResolve, - ResolveForMemberScope, - ResolveForSuperMembers, - CallableContracts, - NoResolve, -} - -internal class FirLazyDeclarationResolver( - private val firFileBuilder: FirFileBuilder -) { - fun lazyResolveDeclaration( - firDeclaration: FirDeclaration, - moduleFileCache: ModuleFileCache, - toResolveType: ResolveType, - scopeSession: ScopeSession = ScopeSession(), - checkPCE: Boolean = false, - ) { - check(toResolveType == ResolveType.CallableReturnType) - lazyResolveDeclaration( - firDeclarationToResolve = firDeclaration, - moduleFileCache = moduleFileCache, - toPhase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE, - scopeSession = scopeSession, - checkPCE = checkPCE, - ) - } +import org.jetbrains.kotlin.idea.util.ifTrue +internal class FirLazyDeclarationResolver(private val firFileBuilder: FirFileBuilder) { /** * Fully resolve file annotations (synchronized) * @see resolveFileAnnotationsWithoutLock not synchronized @@ -75,7 +41,7 @@ internal class FirLazyDeclarationResolver( checkPCE: Boolean, collector: FirTowerDataContextCollector? = null, ) { - runCustomResolveUnderLock(firFile, moduleFileCache, checkPCE) { + moduleFileCache.firFileLockProvider.runCustomResolveUnderLock(firFile, checkPCE) { resolveFileAnnotationsWithoutLock( firFile = firFile, moduleFileCache = moduleFileCache, @@ -147,14 +113,16 @@ internal class FirLazyDeclarationResolver( toPhase: FirResolvePhase, scopeSession: ScopeSession = ScopeSession(), checkPCE: Boolean = false, + stopAheadOfPhase: (FirDeclaration) -> Boolean = { false }, ) { - runCustomResolveUnderLock(firFile, moduleFileCache, checkPCE) { + moduleFileCache.firFileLockProvider.runCustomResolveUnderLock(firFile, checkPCE) { lazyResolveFileDeclarationWithoutLock( firFile = firFile, moduleFileCache = moduleFileCache, toPhase = toPhase, scopeSession = scopeSession, checkPCE = checkPCE, + stopAheadOfPhase = stopAheadOfPhase, ) } } @@ -166,27 +134,54 @@ internal class FirLazyDeclarationResolver( scopeSession: ScopeSession, checkPCE: Boolean = false, collector: FirTowerDataContextCollector? = null, + stopAheadOfPhase: (FirDeclaration) -> Boolean = { false }, ) { + if (toPhase == FirResolvePhase.RAW_FIR) return if (firFile.resolvePhase == FirResolvePhase.RAW_FIR) { firFile.transform(FirImportResolveTransformer(firFile.moduleData.session), null) firFile.ensurePhase(FirResolvePhase.IMPORTS) } if (checkPCE) checkCanceled() if (toPhase == FirResolvePhase.IMPORTS) return + if (stopAheadOfPhase(firFile)) return - if (toPhase > FirResolvePhase.IMPORTS) { - resolveFileAnnotations(firFile, firFile.annotations, moduleFileCache, scopeSession, checkPCE, collector) + resolveFileAnnotationsWithoutLock(firFile, moduleFileCache, firFile.annotations, scopeSession, collector) + + if (firFile.declarations.isEmpty()) return + val designations = firFile.declarations.map { + FirDeclarationDesignationWithFile(path = emptyList(), declaration = it, firFile = firFile) } - for (declaration in firFile.declarations) { + var currentPhase = FirResolvePhase.IMPORTS + while (currentPhase < toPhase) { + currentPhase = currentPhase.next + if (currentPhase.pluginPhase) continue if (checkPCE) checkCanceled() - lazyResolveDeclaration( - firDeclarationToResolve = declaration, - moduleFileCache = moduleFileCache, - scopeSession = scopeSession, - toPhase = toPhase, - checkPCE = checkPCE - ) + if (stopAheadOfPhase(firFile)) break + + val transformersToApply = designations.mapNotNull { + val needToResolve = it.resolvePhaseForAllDeclarations(includeDeclarationPhase = false) < currentPhase + needToResolve.ifTrue { + LazyTransformerFactory.createLazyTransformer( + phase = currentPhase, + designation = it, + scopeSession = scopeSession, + declarationPhaseDowngraded = false, + moduleFileCache = moduleFileCache, + lazyDeclarationResolver = this, + towerDataContextCollector = null, + firProviderInterceptor = null, + checkPCE = checkPCE, + ) + } + } + if (transformersToApply.isEmpty()) continue + + firFileBuilder.firPhaseRunner.runPhaseWithCustomResolve(currentPhase) { + for (currentTransformer in transformersToApply) { + currentTransformer.transformDeclaration(firFileBuilder.firPhaseRunner) + } + } } } @@ -203,6 +198,7 @@ internal class FirLazyDeclarationResolver( toPhase: FirResolvePhase, checkPCE: Boolean, declarationPhaseDowngraded: Boolean = false, + stopAheadOfPhase: (FirDeclaration) -> Boolean = { false }, ) { if (toPhase == FirResolvePhase.RAW_FIR) return //TODO Should be synchronised @@ -215,12 +211,13 @@ internal class FirLazyDeclarationResolver( toPhase = toPhase, scopeSession = scopeSession, checkPCE = checkPCE, + stopAheadOfPhase = stopAheadOfPhase, ) return } val provider = firDeclarationToResolve.moduleData.session.firIdeProvider - val resolvableDeclaration = firDeclarationToResolve.getNonLocalDeclarationToResolve(provider, moduleFileCache) + val resolvableDeclaration = firDeclarationToResolve.getNonLocalDeclarationToResolve(provider, moduleFileCache, firFileBuilder) //TODO Should be synchronised if (!resolvableDeclaration.isValidForResolve()) return val containerFirFile = resolvableDeclaration.getContainingFile() @@ -231,7 +228,7 @@ internal class FirLazyDeclarationResolver( val resolvePhase = designation.resolvePhaseForAllDeclarations(includeDeclarationPhase = declarationPhaseDowngraded) if (resolvePhase >= toPhase) return - runCustomResolveUnderLock(containerFirFile, moduleFileCache, checkPCE) { + moduleFileCache.firFileLockProvider.runCustomResolveUnderLock(containerFirFile, checkPCE) { runLazyDesignatedResolveWithoutLock( designation = designation, moduleFileCache = moduleFileCache, @@ -239,6 +236,7 @@ internal class FirLazyDeclarationResolver( toPhase = toPhase, checkPCE = checkPCE, declarationPhaseDowngraded = declarationPhaseDowngraded, + stopAheadOfPhase = stopAheadOfPhase, ) } } @@ -250,6 +248,7 @@ internal class FirLazyDeclarationResolver( toPhase: FirResolvePhase, checkPCE: Boolean, declarationPhaseDowngraded: Boolean, + stopAheadOfPhase: (FirDeclaration) -> Boolean = { false }, ) { val filePhase = designation.firFile.resolvePhase if (filePhase == FirResolvePhase.RAW_FIR) { @@ -270,12 +269,15 @@ internal class FirLazyDeclarationResolver( currentPhase = currentPhase.next if (currentPhase.pluginPhase) continue if (checkPCE) checkCanceled() + if (stopAheadOfPhase(designation.declaration)) break - currentPhase.createLazyTransformer( + LazyTransformerFactory.createLazyTransformer( + phase = currentPhase, designation = designation, scopeSession = scopeSession, declarationPhaseDowngraded = declarationPhaseDowngraded, moduleFileCache = moduleFileCache, + lazyDeclarationResolver = this, towerDataContextCollector = null, firProviderInterceptor = null, checkPCE = checkPCE, @@ -306,11 +308,13 @@ internal class FirLazyDeclarationResolver( if (currentPhase.pluginPhase) continue if (checkPCE) checkCanceled() - currentPhase.createLazyTransformer( + LazyTransformerFactory.createLazyTransformer( + phase = currentPhase, designation = designation, scopeSession = scopeSession, declarationPhaseDowngraded = true, moduleFileCache = moduleFileCache, + lazyDeclarationResolver = this, towerDataContextCollector = towerDataContextCollector, firProviderInterceptor = firProviderInterceptor, checkPCE = checkPCE, @@ -318,135 +322,7 @@ internal class FirLazyDeclarationResolver( } } - private fun FirResolvePhase.createLazyTransformer( - designation: FirDeclarationDesignationWithFile, - scopeSession: ScopeSession, - declarationPhaseDowngraded: Boolean, - moduleFileCache: ModuleFileCache, - towerDataContextCollector: FirTowerDataContextCollector?, - firProviderInterceptor: FirProviderInterceptor?, - checkPCE: Boolean, - ): FirLazyTransformerForIDE = when (this) { - FirResolvePhase.SEALED_CLASS_INHERITORS -> FirLazyTransformerForIDE.DUMMY - FirResolvePhase.SUPER_TYPES -> FirDesignatedSupertypeResolverTransformerForIDE( - designation = designation, - session = designation.firFile.moduleData.session, - scopeSession = scopeSession, - declarationPhaseDowngraded = declarationPhaseDowngraded, - moduleFileCache = moduleFileCache, - firLazyDeclarationResolver = this@FirLazyDeclarationResolver, - firProviderInterceptor = firProviderInterceptor, - checkPCE = checkPCE, - ) - FirResolvePhase.TYPES -> FirDesignatedTypeResolverTransformerForIDE( - designation, - designation.firFile.moduleData.session, - scopeSession, - declarationPhaseDowngraded, - ) - FirResolvePhase.STATUS -> FirDesignatedStatusResolveTransformerForIDE( - designation, - designation.firFile.moduleData.session, - scopeSession, - declarationPhaseDowngraded, - ) - FirResolvePhase.CONTRACTS -> FirDesignatedContractsResolveTransformerForIDE( - designation, - designation.firFile.moduleData.session, - scopeSession, - declarationPhaseDowngraded, - ) - FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE -> FirDesignatedImplicitTypesTransformerForIDE( - designation, - designation.firFile.moduleData.session, - scopeSession, - declarationPhaseDowngraded, - towerDataContextCollector - ) - FirResolvePhase.BODY_RESOLVE -> FirDesignatedBodyResolveTransformerForIDE( - designation, - designation.firFile.moduleData.session, - scopeSession, - declarationPhaseDowngraded, - towerDataContextCollector, - firProviderInterceptor, - ) - else -> error("Non-lazy phase $this") - } - private fun FirDeclaration.getNonLocalDeclarationToResolve(provider: FirProvider, moduleFileCache: ModuleFileCache): FirDeclaration { - if (this is FirFile) return this - - if (this is FirPropertyAccessor || this is FirTypeParameter || this is FirValueParameter) { - val ktContainingResolvableDeclaration = when (val psi = this.psi) { - is KtPropertyAccessor -> psi.property - is KtProperty -> psi - is KtParameter, is KtTypeParameter -> psi.getNonLocalContainingOrThisDeclaration() - ?: error("Cannot find containing declaration for KtParameter") - is KtCallExpression -> { - check(this.source?.kind == FirFakeSourceElementKind.DefaultAccessor) - val delegationCall = psi.parent as KtPropertyDelegate - delegationCall.parent as KtProperty - } - null -> error("Cannot find containing declaration for KtParameter") - else -> error("Invalid source of property accessor ${psi::class}") - } - - val targetElement = - if (declarationCanBeLazilyResolved(ktContainingResolvableDeclaration)) ktContainingResolvableDeclaration - else ktContainingResolvableDeclaration.getNonLocalContainingOrThisDeclaration() - check(targetElement != null) { "Container for local declaration cannot be null" } - - return targetElement.findSourceNonLocalFirDeclaration( - firFileBuilder = firFileBuilder, - firSymbolProvider = moduleData.session.symbolProvider, - moduleFileCache = moduleFileCache - ) - } - - val ktDeclaration = (psi as? KtDeclaration) ?: run { - (source as? FirFakeSourceElement<*>).psi?.parentOfType() - } - check(ktDeclaration is KtDeclaration) { - "FirDeclaration should have a PSI of type KtDeclaration" - } - - if (source !is FirFakeSourceElement<*> && declarationCanBeLazilyResolved(ktDeclaration)) return this - val nonLocalPsi = ktDeclaration.getNonLocalContainingOrThisDeclaration() - ?: error("Container for local declaration cannot be null") - return nonLocalPsi.findSourceNonLocalFirDeclaration(firFileBuilder, provider.symbolProvider, moduleFileCache) - } - - companion object { - /** - * Runs [resolve] function (which is considered to do some resolve on [firFile]) under a lock for [firFile] - */ - internal inline fun runCustomResolveUnderLock(firFile: FirFile, cache: ModuleFileCache, checkPCE: Boolean, body: () -> R): R { - return if (checkPCE) { - cache.firFileLockProvider.withWriteLockPCECheck(firFile, LOCKING_INTERVAL_MS, body) - } else { - cache.firFileLockProvider.withWriteLock(firFile, body) - } - } - - private const val LOCKING_INTERVAL_MS = 500L - - fun declarationCanBeLazilyResolved(declaration: KtDeclaration): Boolean { - return when (declaration) { - !is KtNamedDeclaration -> false - is KtDestructuringDeclarationEntry, is KtFunctionLiteral, is KtTypeParameter -> false - is KtPrimaryConstructor -> false - is KtParameter -> declaration.hasValOrVar() && declaration.containingClassOrObject?.getClassId() != null - is KtCallableDeclaration, is KtEnumEntry -> { - when (val parent = declaration.parent) { - is KtFile -> true - is KtClassBody -> (parent.parent as? KtClassOrObject)?.getClassId() != null - else -> false - } - } - is KtClassLikeDeclaration -> declaration.getClassId() != null - else -> error("Unexpected ${declaration::class.qualifiedName}") - } - } - } } + + diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/lazy/resolve/NonLocalDeclarationUtils.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/lazy/resolve/NonLocalDeclarationUtils.kt new file mode 100644 index 00000000000..1b6d4ef3931 --- /dev/null +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/lazy/resolve/NonLocalDeclarationUtils.kt @@ -0,0 +1,85 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve + +import com.intellij.psi.util.parentOfType +import org.jetbrains.kotlin.fir.FirFakeSourceElement +import org.jetbrains.kotlin.fir.FirFakeSourceElementKind +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.psi +import org.jetbrains.kotlin.fir.resolve.providers.FirProvider +import org.jetbrains.kotlin.fir.resolve.symbolProvider +import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.getNonLocalContainingOrThisDeclaration +import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder +import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache +import org.jetbrains.kotlin.idea.fir.low.level.api.util.findSourceNonLocalFirDeclaration +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject + +internal fun FirDeclaration.getNonLocalDeclarationToResolve( + provider: FirProvider, + moduleFileCache: ModuleFileCache, + firFileBuilder: FirFileBuilder +): FirDeclaration { + if (this is FirFile) return this + + if (this is FirPropertyAccessor || this is FirTypeParameter || this is FirValueParameter) { + val ktContainingResolvableDeclaration = when (val psi = this.psi) { + is KtPropertyAccessor -> psi.property + is KtProperty -> psi + is KtParameter, is KtTypeParameter -> psi.getNonLocalContainingOrThisDeclaration() + ?: error("Cannot find containing declaration for KtParameter") + is KtCallExpression -> { + check(this.source?.kind == FirFakeSourceElementKind.DefaultAccessor) + val delegationCall = psi.parent as KtPropertyDelegate + delegationCall.parent as KtProperty + } + null -> error("Cannot find containing declaration for KtParameter") + else -> error("Invalid source of property accessor ${psi::class}") + } + + val targetElement = + if (declarationCanBeLazilyResolved(ktContainingResolvableDeclaration)) ktContainingResolvableDeclaration + else ktContainingResolvableDeclaration.getNonLocalContainingOrThisDeclaration() + check(targetElement != null) { "Container for local declaration cannot be null" } + + return targetElement.findSourceNonLocalFirDeclaration( + firFileBuilder = firFileBuilder, + firSymbolProvider = moduleData.session.symbolProvider, + moduleFileCache = moduleFileCache + ) + } + + val ktDeclaration = (psi as? KtDeclaration) ?: run { + (source as? FirFakeSourceElement<*>).psi?.parentOfType() + } + check(ktDeclaration is KtDeclaration) { + "FirDeclaration should have a PSI of type KtDeclaration" + } + + if (source !is FirFakeSourceElement<*> && declarationCanBeLazilyResolved(ktDeclaration)) return this + val nonLocalPsi = ktDeclaration.getNonLocalContainingOrThisDeclaration() + ?: error("Container for local declaration cannot be null") + return nonLocalPsi.findSourceNonLocalFirDeclaration(firFileBuilder, provider.symbolProvider, moduleFileCache) +} + +internal fun declarationCanBeLazilyResolved(declaration: KtDeclaration): Boolean { + return when (declaration) { + !is KtNamedDeclaration -> false + is KtDestructuringDeclarationEntry, is KtFunctionLiteral, is KtTypeParameter -> false + is KtPrimaryConstructor -> false + is KtParameter -> declaration.hasValOrVar() && declaration.containingClassOrObject?.getClassId() != null + is KtCallableDeclaration, is KtEnumEntry -> { + when (val parent = declaration.parent) { + is KtFile -> true + is KtClassBody -> (parent.parent as? KtClassOrObject)?.getClassId() != null + else -> false + } + } + is KtClassLikeDeclaration -> declaration.getClassId() != null + else -> error("Unexpected ${declaration::class.qualifiedName}") + } +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/transformers/FirDesignatedSupertypeResolverTransformerForIDE.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/transformers/FirDesignatedSupertypeResolverTransformerForIDE.kt index 27a3e0826b2..88bb16ceeaf 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/transformers/FirDesignatedSupertypeResolverTransformerForIDE.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/transformers/FirDesignatedSupertypeResolverTransformerForIDE.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.expressions.FirStatement +import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.transformers.* import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef @@ -18,8 +19,8 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirDeclarationDesignation import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirDeclarationDesignationWithFile import org.jetbrains.kotlin.idea.fir.low.level.api.api.collectDesignation 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.lazy.resolve.FirLazyDeclarationResolver -import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver.Companion.runCustomResolveUnderLock import org.jetbrains.kotlin.idea.fir.low.level.api.transformers.FirLazyTransformerForIDE.Companion.isResolvedForAllDeclarations import org.jetbrains.kotlin.idea.fir.low.level.api.transformers.FirLazyTransformerForIDE.Companion.updateResolvedPhaseForDeclarationAndChildren import org.jetbrains.kotlin.idea.fir.low.level.api.util.checkCanceled @@ -96,19 +97,19 @@ internal class FirDesignatedSupertypeResolverTransformerForIDE( private fun collect(designation: FirDeclarationDesignationWithFile): Collection { val visited = mutableMapOf() val toVisit = mutableListOf() - toVisit.add(designation) + toVisit.add(designation) while (toVisit.isNotEmpty()) { for (nowVisit in toVisit) { if (checkPCE) checkCanceled() val resolver = DesignatedFirSupertypeResolverVisitor(nowVisit) - runCustomResolveUnderLock(nowVisit.firFile, moduleFileCache, checkPCE) { + moduleFileCache.firFileLockProvider.runCustomResolveUnderLock(nowVisit.firFile, checkPCE) { firLazyDeclarationResolver.lazyResolveFileDeclaration( firFile = nowVisit.firFile, moduleFileCache = moduleFileCache, toPhase = FirResolvePhase.IMPORTS, scopeSession = scopeSession, - checkPCE = false, + checkPCE = true, ) nowVisit.firFile.accept(resolver, null) } @@ -122,6 +123,7 @@ internal class FirDesignatedSupertypeResolverTransformerForIDE( for (reference in value.supertypeRefs) { val classLikeDeclaration = reference.type.toSymbol(session)?.fir if (classLikeDeclaration !is FirClassLikeDeclaration<*>) continue + if (classLikeDeclaration is FirJavaClass) continue if (visited.containsKey(classLikeDeclaration)) continue val containingFile = moduleFileCache.getContainerFirFile(classLikeDeclaration) ?: continue toVisit.add(classLikeDeclaration.collectDesignation(containingFile)) @@ -144,7 +146,7 @@ internal class FirDesignatedSupertypeResolverTransformerForIDE( val filesToDesignations = visited.groupBy { it.firFile } for (designationsPerFile in filesToDesignations) { if (checkPCE) checkCanceled() - runCustomResolveUnderLock(designationsPerFile.key, moduleFileCache, checkPCE) { + moduleFileCache.firFileLockProvider.runCustomResolveUnderLock(designationsPerFile.key, checkPCE) { applyToFileSymbols(designationsPerFile.value) } } diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/transformers/LazyTransformerFactory.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/transformers/LazyTransformerFactory.kt new file mode 100644 index 00000000000..92732848515 --- /dev/null +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/transformers/LazyTransformerFactory.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.fir.low.level.api.transformers + +import org.jetbrains.kotlin.fir.declarations.FirResolvePhase +import org.jetbrains.kotlin.fir.resolve.ScopeSession +import org.jetbrains.kotlin.fir.resolve.transformers.FirProviderInterceptor +import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector +import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirDeclarationDesignationWithFile +import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache +import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver + +internal object LazyTransformerFactory { + fun createLazyTransformer( + phase: FirResolvePhase, + designation: FirDeclarationDesignationWithFile, + scopeSession: ScopeSession, + declarationPhaseDowngraded: Boolean, + moduleFileCache: ModuleFileCache, + lazyDeclarationResolver: FirLazyDeclarationResolver, + towerDataContextCollector: FirTowerDataContextCollector?, + firProviderInterceptor: FirProviderInterceptor?, + checkPCE: Boolean, + ): FirLazyTransformerForIDE = when (phase) { + FirResolvePhase.SEALED_CLASS_INHERITORS -> FirLazyTransformerForIDE.DUMMY + FirResolvePhase.SUPER_TYPES -> FirDesignatedSupertypeResolverTransformerForIDE( + designation = designation, + session = designation.firFile.moduleData.session, + scopeSession = scopeSession, + declarationPhaseDowngraded = declarationPhaseDowngraded, + moduleFileCache = moduleFileCache, + firLazyDeclarationResolver = lazyDeclarationResolver, + firProviderInterceptor = firProviderInterceptor, + checkPCE = checkPCE, + ) + FirResolvePhase.TYPES -> FirDesignatedTypeResolverTransformerForIDE( + designation, + designation.firFile.moduleData.session, + scopeSession, + declarationPhaseDowngraded, + ) + FirResolvePhase.STATUS -> FirDesignatedStatusResolveTransformerForIDE( + designation, + designation.firFile.moduleData.session, + scopeSession, + declarationPhaseDowngraded, + ) + FirResolvePhase.CONTRACTS -> FirDesignatedContractsResolveTransformerForIDE( + designation, + designation.firFile.moduleData.session, + scopeSession, + declarationPhaseDowngraded, + ) + FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE -> FirDesignatedImplicitTypesTransformerForIDE( + designation, + designation.firFile.moduleData.session, + scopeSession, + declarationPhaseDowngraded, + towerDataContextCollector + ) + FirResolvePhase.BODY_RESOLVE -> FirDesignatedBodyResolveTransformerForIDE( + designation, + designation.firFile.moduleData.session, + scopeSession, + declarationPhaseDowngraded, + towerDataContextCollector, + firProviderInterceptor, + ) + else -> error("Non-lazy phase $this") + } +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirLazyDeclarationResolveTest.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirLazyDeclarationResolveTest.kt index bf93d7c2334..a34cb528ad7 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirLazyDeclarationResolveTest.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirLazyDeclarationResolveTest.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo import org.jetbrains.kotlin.idea.caches.project.getModuleInfo import org.jetbrains.kotlin.idea.fir.low.level.api.api.withFirDeclaration import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver +import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.declarationCanBeLazilyResolved import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor import org.jetbrains.kotlin.psi.KtDeclaration @@ -32,7 +33,7 @@ abstract class AbstractFirLazyDeclarationResolveTest : KotlinLightCodeInsightFix val testDataFile = File(path) val ktFile = myFixture.configureByText(testDataFile.name, FileUtil.loadFile(testDataFile)) as KtFile val lazyDeclarations = ktFile.collectDescendantsOfType { ktDeclaration -> - FirLazyDeclarationResolver.declarationCanBeLazilyResolved(ktDeclaration) + declarationCanBeLazilyResolved(ktDeclaration) } val declarationToResolve = lazyDeclarations.firstOrNull { it.name?.lowercase() == "resolveme" } diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirLibraryModuleDeclarationResolveTest.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirLibraryModuleDeclarationResolveTest.kt index 2fe3fb911e4..1ebca687ea0 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirLibraryModuleDeclarationResolveTest.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirLibraryModuleDeclarationResolveTest.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.fir.low.level.api import com.intellij.testFramework.LightProjectDescriptor import org.jetbrains.kotlin.fir.FirRenderer +import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.idea.fir.low.level.api.api.withFirDeclaration import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase @@ -48,7 +49,7 @@ abstract class AbstractFirLibraryModuleDeclarationResolveTest : KotlinLightCodeI // We intentionally use ktFile here as a context element, because resolving // from compiled PSI-elements (e.g. caretResolutionTarget) is not yet supported resolveWithClearCaches(ktFile) { resolveState -> - val renderedDeclaration = caretResolutionTarget.withFirDeclaration(resolveState) { firDeclaration -> + val renderedDeclaration = caretResolutionTarget.withFirDeclaration(resolveState, FirResolvePhase.RAW_FIR) { firDeclaration -> firDeclaration.render(FirRenderer.RenderMode.WithResolvePhases) } diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractPartialRawFirBuilderTestCase.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractPartialRawFirBuilderTestCase.kt index 225d03ea297..ffbf12b0800 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractPartialRawFirBuilderTestCase.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractPartialRawFirBuilderTestCase.kt @@ -17,10 +17,7 @@ import org.jetbrains.kotlin.fir.scopes.FirScopeProvider import org.jetbrains.kotlin.fir.scopes.FirTypeScope import org.jetbrains.kotlin.fir.session.FirSessionFactory import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid -import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirDeclarationDesignation -import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirDeclarationDesignation import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.RawFirNonLocalDeclarationBuilder -import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider import org.jetbrains.kotlin.parsing.KotlinParserDefinition import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirTypeAndAnnotations.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirTypeAndAnnotations.kt index 5858f9e8a76..db0031f818d 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirTypeAndAnnotations.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/KtFirTypeAndAnnotations.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.idea.frontend.api.fir.utils.weakRef import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtAnnotationCall import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtTypeAndAnnotations import org.jetbrains.kotlin.idea.frontend.api.types.KtType +import org.jetbrains.kotlin.idea.util.ifTrue internal class KtFirTypeAndAnnotations( private val containingDeclaration: FirRefWithValidityCheck, @@ -64,10 +65,10 @@ internal fun FirRefWithValidityCheck>.superTypesAndAnnotationsList(b internal fun FirRefWithValidityCheck.superTypesAndAnnotationsListForRegularClass(builder: KtSymbolByFirBuilder): List { return withFir { fir -> - if (fir.resolvePhase >= FirResolvePhase.SUPER_TYPES) { + (fir.resolvePhase >= FirResolvePhase.SUPER_TYPES).ifTrue { fir.superTypeRefs.mapToTypeAndAnnotations(this, builder) - } else null - } ?: withFirWithPossibleResolveInside { fir -> + } + } ?: withFirWithPossibleResolveInside(ResolveType.NoResolve) { fir -> fir.resolveSupertypesInTheAir(builder.rootSession).mapToTypeAndAnnotations(this, builder) } } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/annotations/KtFirAnnotationCall.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/annotations/KtFirAnnotationCall.kt index 0f40626e5da..b0d7923593f 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/annotations/KtFirAnnotationCall.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/annotations/KtFirAnnotationCall.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.idea.fir.findPsi +import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.ResolveType import org.jetbrains.kotlin.idea.frontend.api.fir.utils.* import org.jetbrains.kotlin.idea.frontend.api.fir.utils.FirRefWithValidityCheck import org.jetbrains.kotlin.idea.frontend.api.fir.utils.convertConstantExpression @@ -35,14 +36,14 @@ internal class KtFirAnnotationCall( } override val classId: ClassId? by cached { - containingDeclaration.withFirWithPossibleResolveInside(AnnotationPhases.PHASE_FOR_ANNOTATION_CLASS_ID) { fir -> + containingDeclaration.withFirWithPossibleResolveInside(ResolveType.AnnotationType) { fir -> annotationCallRef.getClassId(fir.moduleData.session) } } override val useSiteTarget: AnnotationUseSiteTarget? get() = annotationCallRef.useSiteTarget - override val arguments: List by containingDeclaration.withFirAndCache(FirResolvePhase.TYPES) { fir -> + override val arguments: List by containingDeclaration.withFirAndCache(ResolveType.AnnotationsArguments) { fir -> mapAnnotationParameters(annotationCallRef, fir.moduleData.session).map { (name, expression) -> KtNamedConstantValue(name, expression.convertConstantExpression()) } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/annotations/firAnnotationUtils.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/annotations/firAnnotationUtils.kt index fb06befecc8..72e819d83a8 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/annotations/firAnnotationUtils.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/symbols/annotations/firAnnotationUtils.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.expressions.coneClassLikeType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolved import org.jetbrains.kotlin.fir.types.classId +import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.ResolveType import org.jetbrains.kotlin.idea.frontend.api.fir.utils.FirRefWithValidityCheck import org.jetbrains.kotlin.name.ClassId @@ -29,16 +30,11 @@ internal fun FirRefWithValidityCheck.toAnnotationsList( } internal fun FirRefWithValidityCheck.containsAnnotation(classId: ClassId): Boolean = - withFir(AnnotationPhases.PHASE_FOR_ANNOTATION_CLASS_ID) { fir -> + withFir(ResolveType.AnnotationType) { fir -> fir.annotations.any { it.getClassId(fir.moduleData.session) == classId } } internal fun FirRefWithValidityCheck.getAnnotationClassIds(): Collection = - withFir(AnnotationPhases.PHASE_FOR_ANNOTATION_CLASS_ID) { fir -> + withFir(ResolveType.AnnotationType) { fir -> fir.annotations.mapNotNull { it.getClassId(fir.moduleData.session) } - } - - -internal object AnnotationPhases { - val PHASE_FOR_ANNOTATION_CLASS_ID = FirResolvePhase.TYPES -} + } \ No newline at end of file diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/utils/FirRefWithValidityCheck.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/utils/FirRefWithValidityCheck.kt index 56dd3426a96..c2ddea967bf 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/utils/FirRefWithValidityCheck.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/utils/FirRefWithValidityCheck.kt @@ -80,6 +80,31 @@ internal class FirRefWithValidityCheck(fir: D, resolveSt } } + /** + * Runs [action] with fir element with write action hold + * Consider using this then [action] may call some resolve + */ + inline fun withFirWithPossibleResolveInside( + resolveType: ResolveType = ResolveType.NoResolve, + crossinline action: (fir: D) -> R + ): R { + token.assertIsValidAndAccessible() + val fir = firWeakRef.get() + ?: throw EntityWasGarbageCollectedException("FirElement") + val resolveState = resolveStateWeakRef.get() + ?: throw EntityWasGarbageCollectedException("FirModuleResolveState") + return when (resolveType) { + ResolveType.BodyResolveWithChildren -> { + /* + The ResolveType type is the maximum possible phase we can resolve our declaration to + So there is not need to run whole `action` under write lock + */ + action(fir.withFirDeclarationInWriteLock(resolveState, resolveType) { it }) + } + else -> fir.withFirDeclarationInWriteLock(resolveState, resolveType) { action(it) } + } + } + val resolveState get() = resolveStateWeakRef.get() ?: throw EntityWasGarbageCollectedException("FirModuleResolveState") @@ -95,7 +120,7 @@ internal class FirRefWithValidityCheck(fir: D, resolveSt val resolveState = resolveStateWeakRef.get() ?: throw EntityWasGarbageCollectedException("FirModuleResolveState") return when (type) { - ResolveType.CallableBodyResolve -> { + ResolveType.BodyResolveWithChildren -> { /* The CallableBodyResolve type is the maximum possible phase we can resolve our declaration to So there is not need to run whole `action` under read lock