[LL FIR] do not reanalyze non-top level properties with initializer

Treat changes inside non-top level property initializer as OOB
It is OOB because CFG for a class depends on a property initializer,
so we must rebuild CFG for the class

Also, this commit makes the API more understandable

^KT-59687
This commit is contained in:
Dmitrii Gridin
2023-06-27 18:37:59 +02:00
parent 8137bee09c
commit 99e2a14b9c
5 changed files with 34 additions and 33 deletions
@@ -36,7 +36,6 @@ import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
@@ -307,15 +306,3 @@ internal fun PsiElement.getNonLocalContainingOrThisDeclaration(predicate: (KtDec
return candidate
}
@Suppress("unused") // Used in the IDE plugin
fun PsiElement.getNonLocalContainingInBodyDeclarationWith(): KtDeclaration? =
getNonLocalContainingOrThisDeclaration { declaration ->
when (declaration) {
is KtNamedFunction -> declaration.bodyExpression?.isAncestor(this) == true
is KtProperty -> declaration.initializer?.isAncestor(this) == true ||
declaration.getter?.bodyExpression?.isAncestor(this) == true ||
declaration.setter?.bodyExpression?.isAncestor(this) == true
else -> false
}
}
@@ -5,18 +5,19 @@
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.LLFirInternals
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
internal object FileElementFactory {
/**
* should be consistent with [isReanalyzableContainer]
*/
fun createFileStructureElement(
firDeclaration: FirDeclaration,
ktDeclaration: KtDeclaration,
@@ -103,22 +104,35 @@ internal object FileElementFactory {
}
/**
* should be consistent with [createFileStructureElement]
* @return The declaration in which a change of the passed receiver parameter can be treated as in-block modification
*/
//TODO make internal
fun isReanalyzableContainer(
ktDeclaration: KtDeclaration,
): Boolean = when (ktDeclaration) {
is KtNamedFunction -> ktDeclaration.isReanalyzableContainer()
is KtProperty -> ktDeclaration.isReanalyzableContainer()
else -> false
@LLFirInternals
@Suppress("unused") // Used in the IDE plugin
fun PsiElement.getNonLocalReanalyzableContainingDeclaration(): KtDeclaration? {
return when (val declaration = getNonLocalContainingOrThisDeclaration()) {
is KtNamedFunction -> declaration.takeIf { function ->
function.isReanalyzableContainer() && function.bodyExpression?.isAncestor(this) == true
}
is KtPropertyAccessor -> declaration.takeIf { accessor ->
accessor.isReanalyzableContainer() && accessor.bodyExpression?.isAncestor(this) == true
}
is KtProperty -> declaration.takeIf { property ->
property.isReanalyzableContainer() && property.delegateExpressionOrInitializer?.isAncestor(this) == true
}
else -> null
}
}
private fun KtNamedFunction.isReanalyzableContainer() =
name != null && hasExplicitTypeOrUnit
private fun KtNamedFunction.isReanalyzableContainer(): Boolean = name != null && (hasBlockBody() || typeReference != null)
private fun KtProperty.isReanalyzableContainer() =
name != null && typeReference != null
private fun KtPropertyAccessor.isReanalyzableContainer(): Boolean {
val property = property
return property.name != null && (hasBlockBody() || property.typeReference != null)
}
private fun KtProperty.isReanalyzableContainer(): Boolean =
name != null && typeReference != null && (isTopLevel || !hasDelegateExpressionOrInitializer())
private val KtNamedFunction.hasExplicitTypeOrUnit
get() = hasBlockBody() || typeReference != null
@@ -7,5 +7,5 @@ class X {/* NonReanalyzableClassDeclarationStructureElement */
val y = 42/* NonReanalyzableNonClassDeclarationStructureElement */
var z: Int = 15/* ReanalyzablePropertyStructureElement */
var z: Int = 15/* NonReanalyzableNonClassDeclarationStructureElement */
}
@@ -5,7 +5,7 @@ var x: Int = 10/* ReanalyzablePropertyStructureElement */
}
class X {/* NonReanalyzableClassDeclarationStructureElement */
var y: Int = 10/* ReanalyzablePropertyStructureElement */
var y: Int = 10/* NonReanalyzableNonClassDeclarationStructureElement */
get() = field
set(value) {
field = value
@@ -1,7 +1,7 @@
open class A
(init: A.() -> Unit)/* NonReanalyzableNonClassDeclarationStructureElement */
{/* NonReanalyzableClassDeclarationStructureElement */
val prop: String = ""/* ReanalyzablePropertyStructureElement */
val prop: String = ""/* NonReanalyzableNonClassDeclarationStructureElement */
}
class B()/* NonReanalyzableNonClassDeclarationStructureElement */ : A()/* NonReanalyzableClassDeclarationStructureElement */