diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionCache.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionCache.kt index 4c6e466ade0..7c46e2a5bd2 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionCache.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionCache.kt @@ -109,6 +109,26 @@ class LLFirSessionCache(private val project: Project) { storage.clear() } + // Removing script sessions is only needed temporarily until KTIJ-25620 has been implemented. + fun removeAllScriptSessions() { + ApplicationManager.getApplication().assertWriteAccessAllowed() + + removeAllScriptSessionsFrom(sourceCache) + removeAllScriptSessionsFrom(binaryCache) + } + + private fun removeAllScriptSessionsFrom(storage: SessionStorage) { + // `ConcurrentSoftValueHashMap` (the implementation used by `storage`) does not back its entry set but rather creates a copy, which + // is in violation of the contract of `Map.entrySet`, and thus changes to the entry set are not reflected in `storage`. Because this + // function is executed in a write action, we do not need the weak consistency guarantees made by `ConcurrentMap`'s iterator, so a + // "collect and remove" approach also works. + val scriptEntries = storage.entries.filter { (module, _) -> module is KtScriptModule || module is KtScriptDependencyModule } + for ((module, session) in scriptEntries) { + session.isValid = false + storage.remove(module) + } + } + private fun createSession(module: KtModule): LLFirSession { val sessionFactory = createPlatformAwareSessionFactory(module) return when (module) { diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionInvalidationService.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionInvalidationService.kt index 993d3eb7fa1..3d6b98b380c 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionInvalidationService.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionInvalidationService.kt @@ -8,16 +8,9 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.sessions import com.intellij.openapi.Disposable import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.project.Project -import org.jetbrains.kotlin.analysis.project.structure.KotlinModuleDependentsProvider -import org.jetbrains.kotlin.analysis.project.structure.KtModule +import org.jetbrains.kotlin.analysis.project.structure.* import org.jetbrains.kotlin.analysis.providers.analysisMessageBus -import org.jetbrains.kotlin.analysis.providers.topics.KotlinGlobalModuleStateModificationListener -import org.jetbrains.kotlin.analysis.providers.topics.KotlinGlobalOutOfBlockModificationListener -import org.jetbrains.kotlin.analysis.providers.topics.KotlinGlobalSourceModuleStateModificationListener -import org.jetbrains.kotlin.analysis.providers.topics.KotlinGlobalSourceOutOfBlockModificationListener -import org.jetbrains.kotlin.analysis.providers.topics.KotlinTopics -import org.jetbrains.kotlin.analysis.providers.topics.KotlinModuleOutOfBlockModificationListener -import org.jetbrains.kotlin.analysis.providers.topics.KotlinModuleStateModificationListener +import org.jetbrains.kotlin.analysis.providers.topics.* /** * [LLFirSessionInvalidationService] listens to [modification events][KotlinTopics] and invalidates [LLFirSession]s which depend on the @@ -81,6 +74,14 @@ class LLFirSessionInvalidationService(private val project: Project) : Disposable if (!didSessionExist) return KotlinModuleDependentsProvider.getInstance(project).getTransitiveDependents(module).forEach(sessionCache::removeSession) + + // Due to a missing IDE implementation for script dependents (see KTIJ-25620), script sessions need to be invalidated globally: + // - A script may include other scripts, so a script modification may affect any other script. + // - Script dependencies are also not linked via dependents yet, so any script dependency modification may affect any script. + // - Scripts may depend on libraries, and the IDE module dependents provider doesn't provide script dependents for libraries yet. + if (module is KtScriptModule || module is KtScriptDependencyModule || module is KtLibraryModule) { + sessionCache.removeAllScriptSessions() + } } private fun invalidateAll(includeBinaryModules: Boolean) {