From ff0224d28da2846e7673a847c21641b2613cd14b Mon Sep 17 00:00:00 2001 From: Marco Pennekamp Date: Tue, 9 Jan 2024 00:52:56 +0100 Subject: [PATCH] [AA] Test project structure: Fix duplicate binary library modules in main module dependencies - `TestModuleStructureFactory.createProjectStructureByTestStructure` created `KtModule`s for each test module and then also created new `KtModule`s for binary dependencies. This split worked as long as no binary dependency was also a test module and thus part of the main modules. But if not, `addLibraryDependencies` created a duplicate `KtModule`, instead of referring to the existing binary module. - The fix registers a main binary library module in the library cache before it can become the dependency of another main module. Because main modules can only depend on main modules preceding them in the list, a separate pass over all main modules is not needed. ^KT-64647 fixed --- .../structure/TestModuleStructureFactory.kt | 61 ++++++++++++++----- ...sInheritorsCaclulatorPreAnalysisHandler.kt | 4 +- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/analysis/analysis-test-framework/tests/org/jetbrains/kotlin/analysis/test/framework/project/structure/TestModuleStructureFactory.kt b/analysis/analysis-test-framework/tests/org/jetbrains/kotlin/analysis/test/framework/project/structure/TestModuleStructureFactory.kt index 82f67de043e..e8031156a47 100644 --- a/analysis/analysis-test-framework/tests/org/jetbrains/kotlin/analysis/test/framework/project/structure/TestModuleStructureFactory.kt +++ b/analysis/analysis-test-framework/tests/org/jetbrains/kotlin/analysis/test/framework/project/structure/TestModuleStructureFactory.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtMod import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleWithFiles import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule +import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.project.structure.KtNotUnderContentRootModule import org.jetbrains.kotlin.analysis.test.framework.services.environmentManager import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys @@ -34,6 +35,10 @@ import kotlin.io.path.exists import kotlin.io.path.extension import kotlin.io.path.nameWithoutExtension +private typealias LibraryCache = MutableMap, KtBinaryModule> + +private typealias ModulesByName = Map + object TestModuleStructureFactory { fun createProjectStructureByTestStructure( moduleStructure: TestModuleStructure, @@ -44,34 +49,58 @@ object TestModuleStructureFactory { testServices.getKtModuleFactoryForTestModule(testModule).createModule(testModule, testServices, project) } - val moduleEntriesByName = moduleEntries.associateByName() + val modulesByName = moduleEntries.associateByName() - val libraryCache = mutableMapOf, KtBinaryModule>() + val libraryCache: LibraryCache = mutableMapOf() for (testModule in moduleStructure.modules) { - val moduleWithFiles = moduleEntriesByName[testModule.name] ?: moduleEntriesByName.getValue(testModule.files.single().name) - when (val ktModule = moduleWithFiles.ktModule) { - is KtNotUnderContentRootModule -> { - // Not-under-content-root modules have no external dependencies on purpose - } - is KtModuleWithModifiableDependencies -> { - addModuleDependencies(testModule, moduleEntriesByName, ktModule) - addLibraryDependencies(testModule, testServices, project, ktModule, libraryCache::getOrPut) - } - else -> error("Unexpected module type: " + ktModule.javaClass.name) - } + val ktModule = modulesByName.getByTestModule(testModule).ktModule + + ktModule.addToLibraryCacheIfNeeded(libraryCache) + ktModule.addDependencies(testModule, testServices, modulesByName, libraryCache) } return KtModuleProjectStructure(moduleEntries, libraryCache.values) } + private fun ModulesByName.getByTestModule(testModule: TestModule): KtModuleWithFiles = + this[testModule.name] ?: this.getValue(testModule.files.single().name) + + /** + * A main module may be a binary library module, which may be a dependency of subsequent main modules. We need to add such a module to + * the library cache before it is processed as a dependency. Otherwise, when another module's binary dependency is processed, + * [addLibraryDependencies] will create a *duplicate* binary library module with the same roots and name as the already existing binary + * library module. + */ + private fun KtModule.addToLibraryCacheIfNeeded(libraryCache: LibraryCache) { + if (this is KtBinaryModule) { + libraryCache.put(getBinaryRoots().toSet(), this) + } + } + + private fun KtModule.addDependencies( + testModule: TestModule, + testServices: TestServices, + modulesByName: ModulesByName, + libraryCache: LibraryCache, + ) = when (this) { + is KtNotUnderContentRootModule -> { + // Not-under-content-root modules have no external dependencies on purpose + } + is KtModuleWithModifiableDependencies -> { + addModuleDependencies(testModule, modulesByName, this) + addLibraryDependencies(testModule, testServices, project, this, libraryCache::getOrPut) + } + else -> error("Unexpected module type: " + javaClass.name) + } + private fun addModuleDependencies( testModule: TestModule, - moduleByName: Map, - ktModule: KtModuleWithModifiableDependencies + modulesByName: ModulesByName, + ktModule: KtModuleWithModifiableDependencies, ) { testModule.allDependencies.forEach { dependency -> - val dependencyKtModule = moduleByName.getValue(dependency.moduleName).ktModule + val dependencyKtModule = modulesByName.getValue(dependency.moduleName).ktModule when (dependency.relation) { DependencyRelation.RegularDependency -> ktModule.directRegularDependencies.add(dependencyKtModule) DependencyRelation.FriendDependency -> ktModule.directFriendDependencies.add(dependencyKtModule) diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/SealedClassesInheritorsCaclulatorPreAnalysisHandler.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/SealedClassesInheritorsCaclulatorPreAnalysisHandler.kt index 7fb93624a22..dca574a9bad 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/SealedClassesInheritorsCaclulatorPreAnalysisHandler.kt +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/SealedClassesInheritorsCaclulatorPreAnalysisHandler.kt @@ -42,9 +42,7 @@ class SealedClassesInheritorsCaclulatorPreAnalysisHandler( } val ktFilesByModule = moduleStructure.modules.associateWith { testModule -> - testServices.ktModuleProvider.getModuleFiles(testModule) - .filterIsInstance() - .filterNot(KtFile::isCompiled) + testServices.ktModuleProvider.getModuleFiles(testModule).filterIsInstance() } for ((testModule, ktFiles) in ktFilesByModule) {