diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FileElementFactory.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FileElementFactory.kt index d57ee8fda0d..552d5d3b12a 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FileElementFactory.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FileElementFactory.kt @@ -5,17 +5,12 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure -import com.intellij.psi.PsiElement import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirModuleResolveComponents import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDesignationWithFile import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirClassWithSpecificMembersResolveTarget -import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.getNonLocalContainingOrThisDeclaration import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.impl.FirPrimaryConstructor -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.isAncestor -import org.jetbrains.kotlin.psi.psiUtil.isContractDescriptionCallPsiCheck internal object FileElementFactory { fun createFileStructureElement( @@ -74,50 +69,3 @@ internal object FileElementFactory { ) } } - -/** - * Covered by org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.AbstractInBlockModificationTest - * on the compiler side and by - * org.jetbrains.kotlin.idea.fir.analysis.providers.trackers.AbstractProjectWideOutOfBlockKotlinModificationTrackerTest - * on the plugin part - * - * @return The declaration in which a change of the passed receiver parameter can be treated as in-block modification - */ -internal fun PsiElement.getNonLocalReanalyzableContainingDeclaration(): KtDeclaration? { - return when (val declaration = getNonLocalContainingOrThisDeclaration()) { - is KtNamedFunction -> declaration.takeIf { function -> - function.isReanalyzableContainer() && isElementInsideBody(declaration = function, child = this) - } - - is KtPropertyAccessor -> declaration.takeIf { accessor -> - accessor.isReanalyzableContainer() && isElementInsideBody(declaration = accessor, child = this) - } - - is KtProperty -> declaration.takeIf { property -> - property.isReanalyzableContainer() && property.delegateExpressionOrInitializer?.isAncestor(this) == true - } - - else -> null - } -} - -private fun isElementInsideBody(declaration: KtDeclarationWithBody, child: PsiElement): Boolean { - val body = declaration.bodyExpression ?: return false - if (!body.isAncestor(child)) return false - return !isInsideContract(body = body, child = child) -} - -private fun isInsideContract(body: KtExpression, child: PsiElement): Boolean { - if (body !is KtBlockExpression) return false - - val firstStatement = body.firstStatement ?: return false - if (!firstStatement.isContractDescriptionCallPsiCheck()) return false - return firstStatement.isAncestor(child) -} - -private fun KtNamedFunction.isReanalyzableContainer(): Boolean = hasBlockBody() || typeReference != null - -private fun KtPropertyAccessor.isReanalyzableContainer(): Boolean = isSetter || hasBlockBody() || property.typeReference != null - -private fun KtProperty.isReanalyzableContainer(): Boolean = typeReference != null && !hasDelegateExpressionOrInitializer() - diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/LLFirDeclarationModificationService.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/LLFirDeclarationModificationService.kt index 9154d82c550..c1be990cf6a 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/LLFirDeclarationModificationService.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/LLFirDeclarationModificationService.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirInternals import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getFirResolveSession import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile import org.jetbrains.kotlin.analysis.low.level.api.fir.api.resolveToFirSymbol +import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.getNonLocalContainingOrThisDeclaration import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.LLFirDeclarationModificationService.ModificationType import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirResolvableModuleSession import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirResolvableSession @@ -27,9 +28,17 @@ import org.jetbrains.kotlin.analysis.providers.analysisMessageBus import org.jetbrains.kotlin.analysis.providers.topics.KotlinTopics.MODULE_OUT_OF_BLOCK_MODIFICATION import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.psi.KtAnnotated +import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtCodeFragment import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtDeclarationWithBody import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtPropertyAccessor +import org.jetbrains.kotlin.psi.psiUtil.isAncestor +import org.jetbrains.kotlin.psi.psiUtil.isContractDescriptionCallPsiCheck /** * This service is responsible for processing incoming [PsiElement] changes to reflect them on FIR tree. @@ -221,3 +230,49 @@ private sealed class ChangeType { override fun hashCode(): Int = blockOwner.hashCode() } } + +/** + * Covered by org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.AbstractInBlockModificationTest + * on the compiler side and by + * org.jetbrains.kotlin.idea.fir.analysis.providers.trackers.AbstractProjectWideOutOfBlockKotlinModificationTrackerTest + * on the plugin part + * + * @return The declaration in which a change of the passed receiver parameter can be treated as in-block modification + */ +internal fun PsiElement.getNonLocalReanalyzableContainingDeclaration(): KtDeclaration? { + return when (val declaration = getNonLocalContainingOrThisDeclaration()) { + is KtNamedFunction -> declaration.takeIf { function -> + function.isReanalyzableContainer() && isElementInsideBody(declaration = function, child = this) + } + + is KtPropertyAccessor -> declaration.takeIf { accessor -> + accessor.isReanalyzableContainer() && isElementInsideBody(declaration = accessor, child = this) + } + + is KtProperty -> declaration.takeIf { property -> + property.isReanalyzableContainer() && property.delegateExpressionOrInitializer?.isAncestor(this) == true + } + + else -> null + } +} + +private fun isElementInsideBody(declaration: KtDeclarationWithBody, child: PsiElement): Boolean { + val body = declaration.bodyExpression ?: return false + if (!body.isAncestor(child)) return false + return !isInsideContract(body = body, child = child) +} + +private fun isInsideContract(body: KtExpression, child: PsiElement): Boolean { + if (body !is KtBlockExpression) return false + + val firstStatement = body.firstStatement ?: return false + if (!firstStatement.isContractDescriptionCallPsiCheck()) return false + return firstStatement.isAncestor(child) +} + +private fun KtNamedFunction.isReanalyzableContainer(): Boolean = hasBlockBody() || typeReference != null + +private fun KtPropertyAccessor.isReanalyzableContainer(): Boolean = isSetter || hasBlockBody() || property.typeReference != null + +private fun KtProperty.isReanalyzableContainer(): Boolean = typeReference != null && !hasDelegateExpressionOrInitializer()