[LL FIR] move in-block analysis declarations closer to service

^KT-61842
This commit is contained in:
Dmitrii Gridin
2023-09-15 17:59:45 +02:00
parent 0db4f464bb
commit f7fe33bd98
2 changed files with 55 additions and 52 deletions
@@ -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()
@@ -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()