[Analysis API] Cause OOB when imports change in code fragments

In K1, code fragment analysis was completely invalidated on any PSI
change. Because imports are not a part of the PSI tree of
'KtCodeFragment's, a colon tremble happened on 'addImportsFromString()'.

In K2, changes inside code fragments are always considered in-body
modifications. So, even with the colon trembling, the 'FirFile',
together with its 'FirImport's was not recreated.

^KT-65600 Fixed
This commit is contained in:
Yan Zhulanow
2024-02-15 20:13:54 +09:00
committed by Space Team
parent 45c0fa8d23
commit 1c1da6bced
2 changed files with 19 additions and 0 deletions
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi import org.jetbrains.kotlin.psi
import org.jetbrains.kotlin.psi.KotlinCodeFragmentImportModificationListener
import org.jetbrains.kotlin.psi.KtAnnotated import org.jetbrains.kotlin.psi.KtAnnotated
import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtCodeFragment import org.jetbrains.kotlin.psi.KtCodeFragment
@@ -69,6 +70,11 @@ class LLFirDeclarationModificationService(val project: Project) : Disposable {
}, },
this, this,
) )
project.messageBus.connect(this).subscribe(
KtCodeFragment.IMPORT_MODIFICATION,
KotlinCodeFragmentImportModificationListener { codeFragment -> outOfBlockModification(codeFragment) }
)
} }
private var inBlockModificationQueue: MutableSet<ChangeType.InBlock>? = null private var inBlockModificationQueue: MutableSet<ChangeType.InBlock>? = null
@@ -26,6 +26,7 @@ import com.intellij.psi.impl.source.tree.FileElement
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.IElementType
import com.intellij.testFramework.LightVirtualFile import com.intellij.testFramework.LightVirtualFile
import com.intellij.util.messages.Topic
import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
@@ -136,6 +137,10 @@ abstract class KtCodeFragment(
// Increment the modification stamp // Increment the modification stamp
clearCaches() clearCaches()
if (viewProvider.isEventSystemEnabled) {
project.messageBus.syncPublisher(IMPORT_MODIFICATION).onCodeFragmentImportsModification(this)
}
} }
} }
@@ -214,8 +219,16 @@ abstract class KtCodeFragment(
companion object { companion object {
const val IMPORT_SEPARATOR: String = "," const val IMPORT_SEPARATOR: String = ","
@Suppress("UnstableApiUsage")
val IMPORT_MODIFICATION: Topic<KotlinCodeFragmentImportModificationListener> =
Topic(KotlinCodeFragmentImportModificationListener::class.java, Topic.BroadcastDirection.TO_CHILDREN, true)
val FAKE_CONTEXT_FOR_JAVA_FILE: Key<Function0<KtElement>> = Key.create("FAKE_CONTEXT_FOR_JAVA_FILE") val FAKE_CONTEXT_FOR_JAVA_FILE: Key<Function0<KtElement>> = Key.create("FAKE_CONTEXT_FOR_JAVA_FILE")
private val LOG = Logger.getInstance(KtCodeFragment::class.java) private val LOG = Logger.getInstance(KtCodeFragment::class.java)
} }
} }
fun interface KotlinCodeFragmentImportModificationListener {
fun onCodeFragmentImportsModification(codeFragment: KtCodeFragment)
}