diff --git a/analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterGlobalModuleStateModification.kt b/analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterGlobalModuleStateModification.kt new file mode 100644 index 00000000000..02e6aa5877e --- /dev/null +++ b/analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterGlobalModuleStateModification.kt @@ -0,0 +1 @@ +// MODIFICATION_EVENT: GLOBAL_MODULE_STATE_MODIFICATION diff --git a/analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterGlobalSourceModuleStateModification.kt b/analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterGlobalSourceModuleStateModification.kt new file mode 100644 index 00000000000..82a4ade3eb9 --- /dev/null +++ b/analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterGlobalSourceModuleStateModification.kt @@ -0,0 +1 @@ +// MODIFICATION_EVENT: GLOBAL_SOURCE_MODULE_STATE_MODIFICATION diff --git a/analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterGlobalSourceOutOfBlockModification.kt b/analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterGlobalSourceOutOfBlockModification.kt new file mode 100644 index 00000000000..d6c8b8c7ecb --- /dev/null +++ b/analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterGlobalSourceOutOfBlockModification.kt @@ -0,0 +1 @@ +// MODIFICATION_EVENT: GLOBAL_SOURCE_OUT_OF_BLOCK_MODIFICATION diff --git a/analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterModuleOutOfBlockModification.kt b/analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterModuleOutOfBlockModification.kt new file mode 100644 index 00000000000..c42affd7ebd --- /dev/null +++ b/analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterModuleOutOfBlockModification.kt @@ -0,0 +1 @@ +// MODIFICATION_EVENT: MODULE_OUT_OF_BLOCK_MODIFICATION diff --git a/analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterModuleStateModification.kt b/analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterModuleStateModification.kt new file mode 100644 index 00000000000..68cda00d932 --- /dev/null +++ b/analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterModuleStateModification.kt @@ -0,0 +1 @@ +// MODIFICATION_EVENT: MODULE_STATE_MODIFICATION diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/AbstractResolveExtensionDisposalAfterModificationEventTest.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/AbstractResolveExtensionDisposalAfterModificationEventTest.kt new file mode 100644 index 00000000000..a399fc33c1e --- /dev/null +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/AbstractResolveExtensionDisposalAfterModificationEventTest.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions + +import com.intellij.mock.MockApplication +import com.intellij.mock.MockProject +import com.intellij.openapi.Disposable +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtension +import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionFile +import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider +import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionCache +import org.jetbrains.kotlin.analysis.low.level.api.fir.test.configurators.AnalysisApiFirSourceTestConfigurator +import org.jetbrains.kotlin.analysis.project.structure.KtModule +import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider +import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiBasedTest +import org.jetbrains.kotlin.analysis.test.framework.directives.ModificationEventDirectives +import org.jetbrains.kotlin.analysis.test.framework.directives.publishModificationEventByDirective +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator +import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestServiceRegistrar +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder +import org.jetbrains.kotlin.test.model.TestModule +import org.jetbrains.kotlin.test.services.TestServices +import org.jetbrains.kotlin.test.services.assertions + +/** + * A simple test which detects when resolve extension disposal after modification events/session invalidation doesn't work *at all*. + */ +abstract class AbstractResolveExtensionDisposalAfterModificationEventTest : AbstractAnalysisApiBasedTest() { + override fun doTestByMainFile( + mainFile: KtFile, + mainModule: TestModule, + testServices: TestServices, + ) { + val project = mainFile.project + val module = ProjectStructureProvider.getModule(project, mainFile, contextualModule = null) + val session = LLFirSessionCache.getInstance(project).getSession(module) + val resolveExtension = session.llResolveExtensionTool!!.extensions.single() as KtResolveExtensionWithDisposalTracker + + testServices.assertions.assertFalse(resolveExtension.isDisposed) { + "The resolve extension should not be disposed before the modification event is published." + } + + mainModule.publishModificationEventByDirective(project, module) + + testServices.assertions.assertTrue(resolveExtension.isDisposed) { + "The resolve extension should be disposed after the modification event has been published." + } + } + + override val configurator: AnalysisApiTestConfigurator + get() = ResolveExtensionDisposalTestConfigurator +} + +class KtResolveExtensionWithDisposalTracker() : KtResolveExtension() { + override fun getKtFiles(): List = emptyList() + override fun getContainedPackages(): Set = emptySet() + override fun getShadowedScope(): GlobalSearchScope = GlobalSearchScope.EMPTY_SCOPE + + var isDisposed: Boolean = false + + override fun dispose() { + isDisposed = true + } +} + +class KtResolveExtensionWithDisposalTrackerProvider() : KtResolveExtensionProvider() { + override fun provideExtensionsFor(module: KtModule): List = listOf(KtResolveExtensionWithDisposalTracker()) +} + +object ResolveExtensionDisposalTestConfigurator : AnalysisApiFirSourceTestConfigurator(analyseInDependentSession = false) { + override fun configureTest(builder: TestConfigurationBuilder, disposable: Disposable) { + super.configureTest(builder, disposable) + + builder.useDirectives(ModificationEventDirectives) + } + + override val serviceRegistrars: List + get() = buildList { + addAll(super.serviceRegistrars) + add(ResolveExtensionDisposalTestServiceRegistrar) + } +} + +object ResolveExtensionDisposalTestServiceRegistrar : AnalysisApiTestServiceRegistrar() { + override fun registerApplicationServices(application: MockApplication, testServices: TestServices) {} + override fun registerProjectExtensionPoints(project: MockProject, testServices: TestServices) {} + override fun registerProjectModelServices(project: MockProject, testServices: TestServices) {} + + override fun registerProjectServices( + project: MockProject, + testServices: TestServices, + ) { + val extensionPoint = project.extensionArea.getExtensionPoint(KtResolveExtensionProvider.EP_NAME) + extensionPoint.registerExtension(KtResolveExtensionWithDisposalTrackerProvider(), project) + } +} diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/ResolveExtensionDisposalAfterModificationEventTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/ResolveExtensionDisposalAfterModificationEventTestGenerated.java new file mode 100644 index 00000000000..9de40c42785 --- /dev/null +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/ResolveExtensionDisposalAfterModificationEventTestGenerated.java @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.analysis.api.GenerateAnalysisApiTestsKt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("analysis/low-level-api-fir/testData/resolveExtensionDisposal") +@TestDataPath("$PROJECT_ROOT") +public class ResolveExtensionDisposalAfterModificationEventTestGenerated extends AbstractResolveExtensionDisposalAfterModificationEventTest { + @Test + @TestMetadata("afterGlobalModuleStateModification.kt") + public void testAfterGlobalModuleStateModification() { + runTest("analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterGlobalModuleStateModification.kt"); + } + + @Test + @TestMetadata("afterGlobalSourceModuleStateModification.kt") + public void testAfterGlobalSourceModuleStateModification() { + runTest("analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterGlobalSourceModuleStateModification.kt"); + } + + @Test + @TestMetadata("afterGlobalSourceOutOfBlockModification.kt") + public void testAfterGlobalSourceOutOfBlockModification() { + runTest("analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterGlobalSourceOutOfBlockModification.kt"); + } + + @Test + @TestMetadata("afterModuleOutOfBlockModification.kt") + public void testAfterModuleOutOfBlockModification() { + runTest("analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterModuleOutOfBlockModification.kt"); + } + + @Test + @TestMetadata("afterModuleStateModification.kt") + public void testAfterModuleStateModification() { + runTest("analysis/low-level-api-fir/testData/resolveExtensionDisposal/afterModuleStateModification.kt"); + } + + @Test + public void testAllFilesPresentInResolveExtensionDisposal() { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testData/resolveExtensionDisposal"), Pattern.compile("^(.+)\\.kt$"), null, true); + } +} diff --git a/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/firLowLevel.kt b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/firLowLevel.kt index 6f879efc90d..d3c22b1f061 100644 --- a/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/firLowLevel.kt +++ b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/firLowLevel.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.AbstractScriptLaz import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.AbstractScriptWholeFileResolvePhaseTest import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.AbstractSourceLazyDeclarationResolveScopeBasedTest import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.AbstractSourceWholeFileResolvePhaseTest +import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions.AbstractResolveExtensionDisposalAfterModificationEventTest import org.jetbrains.kotlin.generators.TestGroup import org.jetbrains.kotlin.generators.TestGroupSuite import org.jetbrains.kotlin.generators.util.TestGeneratorUtil @@ -281,6 +282,10 @@ internal fun TestGroupSuite.generateFirLowLevelApiTests() { testClass { model("contextCollector", pattern = TestGeneratorUtil.KTS) } + + testClass { + model("resolveExtensionDisposal") + } } testGroup("analysis/low-level-api-fir/tests", "analysis/analysis-api/testData") {