From 3598ba1d4532e5313c1b939e24a24f0981238dfd Mon Sep 17 00:00:00 2001 From: "sebastian.sellmair" Date: Wed, 15 Dec 2021 12:05:19 +0100 Subject: [PATCH] [MPP][Test] Implement Diagnostics with mpp composite analysis tests Using the 'MULTIPLATFORM_COMPOSITE_ANALYSIS_MODE' test directive will add support for testing "cli metadata compilation" and "ide analysis" _like_ configurations. In this configuration, dependsOn modules will not be analyzed together with dependents in the same ModuleDescriptor, but will be passed as separate 'expectedBy' dependency descriptor. Used to cover ^KT-50120 inside kotlin.git --- .../compiler/TopDownAnalyzerFacadeForJVM.kt | 5 +- .../kotlin/resolve/ModuleStructureOracle.kt | 6 +- .../kotlin/resolve/TargetEnvironment.kt | 1 - .../MultiplatformDiagnosticsDirectives.kt | 19 ++ .../frontend/classic/ClassicFrontendFacade.kt | 186 +++++++++--------- .../MultiplatformAnalysisConfiguration.kt | 166 ++++++++++++++++ ...sWithMultiplatformCompositeAnalysisTest.kt | 20 ++ .../JsEnvironmentConfigurator.kt | 15 +- .../FakeTopDownAnalyzerFacadeForNative.kt | 11 +- .../generators/GenerateJUnit5CompilerTests.kt | 7 + 10 files changed, 329 insertions(+), 107 deletions(-) create mode 100644 compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/MultiplatformDiagnosticsDirectives.kt create mode 100644 compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/classic/MultiplatformAnalysisConfiguration.kt create mode 100644 compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractDiagnosticsWithMultiplatformCompositeAnalysisTest.kt diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt index 1d48ab8ffd1..255f566c198 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt @@ -95,10 +95,11 @@ object TopDownAnalyzerFacadeForJVM { sourceModuleSearchScope: GlobalSearchScope = newModuleSearchScope(project, files), klibList: List = emptyList(), explicitModuleDependencyList: List = emptyList(), - explicitModuleFriendsList: List = emptyList() + explicitModuleFriendsList: List = emptyList(), + explicitCompilerEnvironment: TargetEnvironment = CompilerEnvironment ): AnalysisResult { val container = createContainer( - project, files, trace, configuration, packagePartProvider, declarationProviderFactory, CompilerEnvironment, + project, files, trace, configuration, packagePartProvider, declarationProviderFactory, explicitCompilerEnvironment, sourceModuleSearchScope, klibList, explicitModuleDependencyList = explicitModuleDependencyList, explicitModuleFriendsList = explicitModuleFriendsList ) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModuleStructureOracle.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModuleStructureOracle.kt index fe914a7272b..c9ee3b8dc83 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModuleStructureOracle.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModuleStructureOracle.kt @@ -5,10 +5,12 @@ package org.jetbrains.kotlin.resolve +import org.jetbrains.kotlin.container.DefaultImplementation import org.jetbrains.kotlin.descriptors.ModuleDescriptor +@DefaultImplementation(ModuleStructureOracle.SingleModule::class) interface ModuleStructureOracle { - // May be faster than `findAllImplementingModules(module).isNotEmpty()` + // May be faster than `findAllReversedDependsOnPaths(module).isNotEmpty()` fun hasImplementingModules(module: ModuleDescriptor): Boolean fun findAllReversedDependsOnPaths(module: ModuleDescriptor): List @@ -31,4 +33,4 @@ interface ModuleStructureOracle { class ModulePath(val nodes: List) { constructor(vararg nodes: ModuleDescriptor) : this(nodes.toList()) -} \ No newline at end of file +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetEnvironment.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetEnvironment.kt index 99a0ce9aea4..f7bdc21fdc9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetEnvironment.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetEnvironment.kt @@ -33,7 +33,6 @@ abstract class TargetEnvironment(private val name: String) { container.useInstance(BodyResolveCache.ThrowException) container.useImpl() container.useImpl() - container.useInstance(ModuleStructureOracle.SingleModule) container.useImpl() } } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/MultiplatformDiagnosticsDirectives.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/MultiplatformDiagnosticsDirectives.kt new file mode 100644 index 00000000000..077dfec8e7d --- /dev/null +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/MultiplatformDiagnosticsDirectives.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2010-2021 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.test.directives + +import org.jetbrains.kotlin.test.directives.model.DirectiveApplicability +import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer + +object MultiplatformDiagnosticsDirectives : SimpleDirectivesContainer() { + val MULTIPLATFORM_COMPOSITE_ANALYSIS_MODE by directive( + description = """ + Will setup the classical frontend similar to 'CLI metadata compilation' or 'IDE analysis' where dependsOn sources + are located in separate module descriptors. + """.trimIndent(), + applicability = DirectiveApplicability.Global + ) +} diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/classic/ClassicFrontendFacade.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/classic/ClassicFrontendFacade.kt index 362291c88c2..45698c33633 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/classic/ClassicFrontendFacade.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/classic/ClassicFrontendFacade.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.context.ModuleContext import org.jetbrains.kotlin.context.ProjectContext import org.jetbrains.kotlin.context.withModule import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider +import org.jetbrains.kotlin.descriptors.impl.ModuleDependenciesImpl import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.frontend.java.di.createContainerForLazyResolveWithJava import org.jetbrains.kotlin.frontend.java.di.initJvmBuiltInsForTopDownAnalysis @@ -39,8 +40,6 @@ import org.jetbrains.kotlin.ir.backend.js.toResolverLogger import org.jetbrains.kotlin.ir.util.IrMessageLogger import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS import org.jetbrains.kotlin.js.config.JSConfigurationKeys -import org.jetbrains.kotlin.konan.properties.propertyList -import org.jetbrains.kotlin.library.KLIB_PROPERTY_DEPENDS import org.jetbrains.kotlin.library.unresolvedDependencies import org.jetbrains.kotlin.load.java.lazy.SingleModuleClassResolver import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager @@ -52,75 +51,45 @@ import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlin.platform.konan.isNative import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.resolve.CompilerEnvironment import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer +import org.jetbrains.kotlin.resolve.TargetEnvironment import org.jetbrains.kotlin.resolve.TopDownAnalysisMode import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory -import org.jetbrains.kotlin.resolve.multiplatform.isCommonSource import org.jetbrains.kotlin.serialization.deserialization.MetadataPartProvider import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.StorageManager -import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives -import org.jetbrains.kotlin.test.model.* +import org.jetbrains.kotlin.test.directives.MultiplatformDiagnosticsDirectives.MULTIPLATFORM_COMPOSITE_ANALYSIS_MODE +import org.jetbrains.kotlin.test.model.DependencyRelation +import org.jetbrains.kotlin.test.model.FrontendFacade +import org.jetbrains.kotlin.test.model.FrontendKinds +import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.services.* import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator import org.jetbrains.kotlin.test.util.KtTestUtil +import org.jetbrains.kotlin.types.typeUtil.closure import org.jetbrains.kotlin.utils.addToStdlib.runIf import java.io.File class ClassicFrontendFacade( - testServices: TestServices + testServices: TestServices, ) : FrontendFacade(testServices, FrontendKinds.ClassicFrontend) { override val additionalServices: List get() = listOf(service(::ModuleDescriptorProvider)) override fun analyze(module: TestModule): ClassicFrontendOutputArtifact { - val dependencyProvider = testServices.dependencyProvider val moduleDescriptorProvider = testServices.moduleDescriptorProvider val compilerConfigurationProvider = testServices.compilerConfigurationProvider val packagePartProviderFactory = compilerConfigurationProvider.getPackagePartProviderFactory(module) val project = compilerConfigurationProvider.getProject(module) val configuration = compilerConfigurationProvider.getCompilerConfiguration(module) + val multiplatformConfiguration = MultiplatformAnalysisConfiguration(testServices) - val ktFilesMap = testServices.sourceFileProvider.getKtFilesForSourceFiles(module.files, project).toMutableMap() - val languageVersionSettings = module.languageVersionSettings - - val sourceDependencies = module.allDependencies.filter { it.kind == DependencyKind.Source } - val dependentDescriptors = sourceDependencies.mapNotNull { - val testModule = dependencyProvider.getTestModule(it.moduleName) - if (testModule.targetPlatform.isCommon()) return@mapNotNull null - moduleDescriptorProvider.getModuleDescriptor(testModule) - } - - val friendDescriptors = module.friendDependencies.filter { it.kind == DependencyKind.Source }.map { - moduleDescriptorProvider.getModuleDescriptor(dependencyProvider.getTestModule(it.moduleName)) - } - - var hasCommonModules = false - - fun addDependsOnSources(dependencies: List) { - if (dependencies.isEmpty()) return - hasCommonModules = true - for (dependency in dependencies) { - val dependencyModule = dependencyProvider.getTestModule(dependency.moduleName) - val artifact = dependencyProvider.getArtifact(dependencyModule, FrontendKinds.ClassicFrontend) - /* - * We need create KtFiles again with new project because otherwise we can access to some caches using - * old project as key which may leads to missing services in core environment - */ - val ktFiles = testServices.sourceFileProvider.getKtFilesForSourceFiles(artifact.allKtFiles.keys, project) - ktFiles.values.forEach { ktFile -> ktFile.isCommonSource = true } - ktFilesMap.putAll(ktFiles) - addDependsOnSources(dependencyModule.dependsOnDependencies) - } - } - - addDependsOnSources(module.dependsOnDependencies) - + val ktFilesMap = multiplatformConfiguration.getKtFilesForForSourceFiles(project, module) val ktFiles = ktFilesMap.values.toList() + setupJavacIfNeeded(module, ktFiles, configuration) val analysisResult = performResolve( module, @@ -128,16 +97,17 @@ class ClassicFrontendFacade( configuration, packagePartProviderFactory, ktFiles, - dependentDescriptors, - friendDescriptors, - hasCommonModules + compilerEnvironment = multiplatformConfiguration.getCompilerEnvironment(module), + dependencyDescriptors = multiplatformConfiguration.getDependencyDescriptors(module), + friendsDescriptors = multiplatformConfiguration.getFriendDescriptors(module), + dependsOnDescriptors = multiplatformConfiguration.getDependsOnDescriptors(module) ) moduleDescriptorProvider.replaceModuleDescriptorForModule(module, analysisResult.moduleDescriptor) return ClassicFrontendOutputArtifact( ktFilesMap, analysisResult, project, - languageVersionSettings + module.languageVersionSettings ) } @@ -160,28 +130,29 @@ class ClassicFrontendFacade( configuration: CompilerConfiguration, packagePartProviderFactory: (GlobalSearchScope) -> JvmPackagePartProvider, files: List, - dependentDescriptors: List, + compilerEnvironment: TargetEnvironment, + dependencyDescriptors: List, friendsDescriptors: List, - hasCommonModules: Boolean + dependsOnDescriptors: List ): AnalysisResult { val targetPlatform = module.targetPlatform return when { targetPlatform.isJvm() -> performJvmModuleResolve( - module, - project, - configuration, - packagePartProviderFactory, - files, - dependentDescriptors, - friendsDescriptors, - hasCommonModules + module, project, configuration, packagePartProviderFactory, + files, compilerEnvironment, dependencyDescriptors, friendsDescriptors, dependencyDescriptors ) targetPlatform.isJs() -> when { - module.targetBackend?.isIR != true -> performJsModuleResolve(project, configuration, files, dependentDescriptors) - else -> performJsIrModuleResolve(module, project, configuration, files, dependentDescriptors, friendsDescriptors) + module.targetBackend?.isIR != true -> performJsModuleResolve( + project, configuration, compilerEnvironment, files, dependencyDescriptors + ) + else -> performJsIrModuleResolve( + module, project, configuration, compilerEnvironment, files, dependencyDescriptors, friendsDescriptors + ) } - targetPlatform.isNative() -> performNativeModuleResolve(module, project, files) - targetPlatform.isCommon() -> performCommonModuleResolve(module, files) + targetPlatform.isNative() -> performNativeModuleResolve( + module, project, compilerEnvironment, files, dependencyDescriptors, friendsDescriptors, dependsOnDescriptors + ) + targetPlatform.isCommon() -> performCommonModuleResolve(module, compilerEnvironment, files) else -> error("Should not be here") } } @@ -193,45 +164,48 @@ class ClassicFrontendFacade( configuration: CompilerConfiguration, packagePartProviderFactory: (GlobalSearchScope) -> JvmPackagePartProvider, files: List, - dependentDescriptors: List, + compilerEnviornment: TargetEnvironment, + dependencyDescriptors: List, friendsDescriptors: List, - hasCommonModules: Boolean + dependsOnDescriptors: List ): AnalysisResult { val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(project) configuration.getList(JVMConfigurationKeys.FRIEND_PATHS).forEach { moduleVisibilityManager.addFriendPath(it) } val moduleTrace = NoScopeRecordCliBindingTrace() - if (!hasCommonModules) { + if (module.dependsOnDependencies.isEmpty()) { return TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration( project, files, moduleTrace, configuration.copy(), packagePartProviderFactory, - explicitModuleDependencyList = dependentDescriptors, - explicitModuleFriendsList = friendsDescriptors + explicitModuleDependencyList = dependencyDescriptors, + explicitModuleFriendsList = friendsDescriptors, + explicitCompilerEnvironment = compilerEnviornment ) } val moduleContentScope = GlobalSearchScope.allScope(project) val moduleClassResolver = SingleModuleClassResolver() - val moduleContext = createModuleContext(module, project, dependentDescriptors, friendsDescriptors) { + val moduleContext = createModuleContext(module, project, dependencyDescriptors, friendsDescriptors, dependsOnDescriptors) { JvmBuiltIns(it, JvmBuiltIns.Kind.FROM_CLASS_LOADER) } val moduleDescriptor = moduleContext.module as ModuleDescriptorImpl val jvmTarget = configuration[JVM_TARGET] ?: JvmTarget.DEFAULT val container = createContainerForLazyResolveWithJava( - JvmPlatforms.jvmPlatformByTargetVersion(jvmTarget), // TODO(dsavvinov): do not pass JvmTarget around - moduleContext, - moduleTrace, - FileBasedDeclarationProviderFactory(moduleContext.storageManager, files), - moduleContentScope, - moduleClassResolver, - CompilerEnvironment, LookupTracker.DO_NOTHING, - ExpectActualTracker.DoNothing, - InlineConstTracker.DoNothing, - packagePartProviderFactory(moduleContentScope), - module.languageVersionSettings, + jvmPlatform = JvmPlatforms.jvmPlatformByTargetVersion(jvmTarget), // TODO(dsavvinov): do not pass JvmTarget around + moduleContext = moduleContext, + bindingTrace = moduleTrace, + declarationProviderFactory = FileBasedDeclarationProviderFactory(moduleContext.storageManager, files), + moduleContentScope = moduleContentScope, + moduleClassResolver = moduleClassResolver, + targetEnvironment = compilerEnviornment, + lookupTracker = LookupTracker.DO_NOTHING, + expectActualTracker = ExpectActualTracker.DoNothing, + inlineConstTracker = InlineConstTracker.DoNothing, + packagePartProvider = packagePartProviderFactory(moduleContentScope), + languageVersionSettings = module.languageVersionSettings, useBuiltInsProvider = true ) @@ -257,17 +231,18 @@ class ClassicFrontendFacade( private fun performJsModuleResolve( project: Project, configuration: CompilerConfiguration, + compilerEnvironment: TargetEnvironment, files: List, - dependentDescriptors: List + dependencyDescriptors: List ): AnalysisResult { - // `dependentDescriptors` - modules with source dependency kind + // `dependencyDescriptors` - modules with source dependency kind // 'jsConfig.moduleDescriptors' - modules with binary dependency kind - val jsConfig = JsEnvironmentConfigurator.createJsConfig(project, configuration) + val jsConfig = JsEnvironmentConfigurator.createJsConfig(project, configuration, compilerEnvironment) return TopDownAnalyzerFacadeForJS.analyzeFiles( files, project = jsConfig.project, configuration = jsConfig.configuration, - moduleDescriptors = dependentDescriptors + jsConfig.moduleDescriptors, + moduleDescriptors = dependencyDescriptors + jsConfig.moduleDescriptors, friendModuleDescriptors = jsConfig.friendModuleDescriptors, targetEnvironment = jsConfig.targetEnvironment, ) @@ -310,15 +285,16 @@ class ClassicFrontendFacade( module: TestModule, project: Project, configuration: CompilerConfiguration, + compilerEnvironment: TargetEnvironment, files: List, - dependentDescriptors: List, + dependencyDescriptors: List, friendsDescriptors: List, ): AnalysisResult { val runtimeKlibsNames = JsEnvironmentConfigurator.getRuntimePathsForModule(module, testServices) val runtimeKlibs = loadKlib(runtimeKlibsNames, configuration) val transitiveLibraries = JsEnvironmentConfigurator.getDependencies(module, testServices, DependencyRelation.RegularDependency) val friendLibraries = JsEnvironmentConfigurator.getDependencies(module, testServices, DependencyRelation.FriendDependency) - val allDependencies = runtimeKlibs + dependentDescriptors + transitiveLibraries + val allDependencies = runtimeKlibs + dependencyDescriptors + transitiveLibraries val analyzer = AnalyzerWithCompilerReport(configuration) val builtInModuleDescriptor = allDependencies.firstNotNullOfOrNull { it.builtIns }?.builtInsModule @@ -329,7 +305,7 @@ class ClassicFrontendFacade( configuration, allDependencies, friendsDescriptors + friendLibraries, - CompilerEnvironment, + compilerEnvironment, thisIsBuiltInsModule = builtInModuleDescriptor == null, customBuiltInsModule = builtInModuleDescriptor ) @@ -341,31 +317,48 @@ class ClassicFrontendFacade( private fun performNativeModuleResolve( module: TestModule, project: Project, + compilerEnvironment: TargetEnvironment, files: List, + dependencyDescriptors: List, + friendsDescriptors: List, + dependsOnDescriptors: List, ): AnalysisResult { val moduleTrace = NoScopeRecordCliBindingTrace() - val moduleContext = createModuleContext(module, project, dependentDescriptors = emptyList(), friendsDescriptors = emptyList()) { + val moduleContext = createModuleContext( + module, project, + dependencyDescriptors = dependencyDescriptors, + friendsDescriptors = friendsDescriptors, + dependsOnDescriptors = dependsOnDescriptors + ) { DefaultBuiltIns() } return FakeTopDownAnalyzerFacadeForNative.analyzeFilesWithGivenTrace( files, moduleTrace, moduleContext, - module.languageVersionSettings + module.languageVersionSettings, + compilerEnvironment ) } private fun performCommonModuleResolve( module: TestModule, + compilerEnvironment: TargetEnvironment, files: List, ): AnalysisResult { + // See 'TODO' for adding dependency manager + require(module.dependsOnDependencies.isEmpty() || MULTIPLATFORM_COMPOSITE_ANALYSIS_MODE !in module.directives) { + "Analyzing common modules with 'dependsOn' edges in ${MULTIPLATFORM_COMPOSITE_ANALYSIS_MODE.name} is not supported yet.\n" + + "Module: ${module.name}\n" + + "dependsOn: ${module.dependsOnDependencies.map { it.moduleName }}" + } return CommonResolverForModuleFactory.analyzeFiles( files, Name.special("<${module.name}>"), dependOnBuiltIns = true, module.languageVersionSettings, module.targetPlatform, - CompilerEnvironment, + compilerEnvironment, // TODO: add dependency manager ) { // TODO @@ -377,8 +370,9 @@ class ClassicFrontendFacade( private fun createModuleContext( module: TestModule, project: Project, - dependentDescriptors: List, + dependencyDescriptors: List, friendsDescriptors: List, + dependsOnDescriptors: List, builtInsFactory: (StorageManager) -> KotlinBuiltIns, ): ModuleContext { val projectContext = ProjectContext(project, "test project context") @@ -389,10 +383,18 @@ class ClassicFrontendFacade( val dependencies = buildList { add(moduleDescriptor) add(moduleDescriptor.builtIns.builtInsModule) - addAll(dependentDescriptors) + addAll(dependencyDescriptors) } - moduleDescriptor.setDependencies(dependencies, friendsDescriptors.toSet()) - + moduleDescriptor.setDependencies( + ModuleDependenciesImpl( + allDependencies = dependencies, + modulesWhoseInternalsAreVisible = friendsDescriptors.toSet(), + directExpectedByDependencies = dependsOnDescriptors, + allExpectedByDependencies = dependsOnDescriptors.closure { dependsOnModuleDescriptor -> + dependsOnModuleDescriptor.expectedByModules.map { it as ModuleDescriptorImpl } + }.toSet() + ), + ) return projectContext.withModule(moduleDescriptor) } } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/classic/MultiplatformAnalysisConfiguration.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/classic/MultiplatformAnalysisConfiguration.kt new file mode 100644 index 00000000000..1b65304ca1b --- /dev/null +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/classic/MultiplatformAnalysisConfiguration.kt @@ -0,0 +1,166 @@ +/* + * Copyright 2010-2021 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. + */ + +@file:Suppress("TestFunctionName") + +package org.jetbrains.kotlin.test.frontend.classic + +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.container.StorageComponentContainer +import org.jetbrains.kotlin.container.useInstance +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.CompilerEnvironment +import org.jetbrains.kotlin.resolve.ModulePath +import org.jetbrains.kotlin.resolve.ModuleStructureOracle +import org.jetbrains.kotlin.resolve.TargetEnvironment +import org.jetbrains.kotlin.resolve.multiplatform.isCommonSource +import org.jetbrains.kotlin.test.directives.MultiplatformDiagnosticsDirectives.MULTIPLATFORM_COMPOSITE_ANALYSIS_MODE +import org.jetbrains.kotlin.test.model.* +import org.jetbrains.kotlin.test.services.* + +internal fun MultiplatformAnalysisConfiguration(testServices: TestServices): MultiplatformAnalysisConfiguration { + return if (testServices.moduleStructure.allDirectives.contains(MULTIPLATFORM_COMPOSITE_ANALYSIS_MODE)) + MultiplatformCompositeAnalysisConfiguration( + testServices.dependencyProvider, + testServices.sourceFileProvider, + testServices.moduleDescriptorProvider, + ) + else MultiplatformSeparateAnalysisConfiguration( + testServices.dependencyProvider, + testServices.sourceFileProvider, + testServices.moduleDescriptorProvider + ) +} + +internal interface MultiplatformAnalysisConfiguration { + fun getCompilerEnvironment(module: TestModule): TargetEnvironment + fun getKtFilesForForSourceFiles(project: Project, module: TestModule): Map + fun getDependencyDescriptors(module: TestModule): List + fun getFriendDescriptors(module: TestModule): List + fun getDependsOnDescriptors(module: TestModule): List +} + +/** + * Traditional 'compiler cli' configuration... + */ +internal class MultiplatformSeparateAnalysisConfiguration( + private val dependencyProvider: DependencyProvider, + private val sourceFileProvider: SourceFileProvider, + private val moduleDescriptorProvider: ModuleDescriptorProvider +) : MultiplatformAnalysisConfiguration { + + override fun getCompilerEnvironment(module: TestModule): TargetEnvironment { + return CompilerEnvironment + } + + override fun getDependencyDescriptors(module: TestModule): List { + return getDescriptors( + module.allDependencies - module.dependsOnDependencies.toSet(), + dependencyProvider, moduleDescriptorProvider + ) + } + + override fun getDependsOnDescriptors(module: TestModule): List { + return emptyList() + } + + override fun getFriendDescriptors(module: TestModule): List { + return getDescriptors( + module.friendDependencies, dependencyProvider, moduleDescriptorProvider + ) + } + + override fun getKtFilesForForSourceFiles(project: Project, module: TestModule): Map { + val ktFilesMap = sourceFileProvider.getKtFilesForSourceFiles(module.files, project).toMutableMap() + fun addDependsOnSources(dependencies: List) { + if (dependencies.isEmpty()) return + for (dependency in dependencies) { + val dependencyModule = dependencyProvider.getTestModule(dependency.moduleName) + val artifact = dependencyProvider.getArtifact(dependencyModule, FrontendKinds.ClassicFrontend) + /* + * We need create KtFiles again with new project because otherwise we can access to some caches using + * old project as key which may leads to missing services in core environment + */ + val ktFiles = sourceFileProvider.getKtFilesForSourceFiles(artifact.allKtFiles.keys, project) + ktFiles.values.forEach { ktFile -> ktFile.isCommonSource = true } + ktFilesMap.putAll(ktFiles) + addDependsOnSources(dependencyModule.dependsOnDependencies) + } + } + addDependsOnSources(module.dependsOnDependencies) + return ktFilesMap + } +} + +/** + * Metadata / IDE like configuration... + */ +internal class MultiplatformCompositeAnalysisConfiguration( + private val dependencyProvider: DependencyProvider, + private val sourceFileProvider: SourceFileProvider, + private val moduleDescriptorProvider: ModuleDescriptorProvider, +) : MultiplatformAnalysisConfiguration { + + override fun getCompilerEnvironment(module: TestModule): TargetEnvironment { + return CompositeAnalysisTargetEnvironment + } + + override fun getKtFilesForForSourceFiles(project: Project, module: TestModule): Map { + return sourceFileProvider.getKtFilesForSourceFiles(module.files, project) + } + + override fun getDependencyDescriptors(module: TestModule): List { + return getDescriptors(module.allDependencies, dependencyProvider, moduleDescriptorProvider) + } + + override fun getDependsOnDescriptors(module: TestModule): List { + return getDescriptors(module.dependsOnDependencies, dependencyProvider, moduleDescriptorProvider) + } + + override fun getFriendDescriptors(module: TestModule): List { + return getDescriptors(module.friendDependencies, dependencyProvider, moduleDescriptorProvider) + } +} + +private object CompositeAnalysisTargetEnvironment : TargetEnvironment("Test: Multiplatform with Composite Analysis") { + override fun configure(container: StorageComponentContainer) { + CompilerEnvironment.configure(container) + container.useInstance(CompositeAnalysisModuleStructureOracle) + } +} + +private object CompositeAnalysisModuleStructureOracle : ModuleStructureOracle { + + override fun hasImplementingModules(module: ModuleDescriptor): Boolean { + return findAllReversedDependsOnPaths(module).isNotEmpty() + } + + override fun findAllReversedDependsOnPaths(module: ModuleDescriptor): List { + return emptyList() // <- Not supported, yet + } + + override fun findAllDependsOnPaths(module: ModuleDescriptor): List { + return module.expectedByModules.flatMap { expectedByModule -> + if (expectedByModule.expectedByModules.isEmpty()) listOf(ModulePath(listOf(expectedByModule))) + else findAllDependsOnPaths(expectedByModule).map { path -> + ModulePath(listOf(expectedByModule) + path.nodes) + } + } + } +} + +/* Utils */ + +private fun getDescriptors( + dependencies: Iterable, + dependencyProvider: DependencyProvider, + moduleDescriptorProvider: ModuleDescriptorProvider +): List { + return dependencies.filter { it.kind == DependencyKind.Source } + .map { dependencyDescription -> dependencyProvider.getTestModule(dependencyDescription.moduleName) } + .map { dependencyModule -> moduleDescriptorProvider.getModuleDescriptor(dependencyModule) } +} diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractDiagnosticsWithMultiplatformCompositeAnalysisTest.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractDiagnosticsWithMultiplatformCompositeAnalysisTest.kt new file mode 100644 index 00000000000..8b21b053b12 --- /dev/null +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractDiagnosticsWithMultiplatformCompositeAnalysisTest.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2021 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.test.runners + +import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder +import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives +import org.jetbrains.kotlin.test.directives.MultiplatformDiagnosticsDirectives + +abstract class AbstractDiagnosticsWithMultiplatformCompositeAnalysisTest : AbstractDiagnosticTest() { + override fun configure(builder: TestConfigurationBuilder) { + super.configure(builder) + builder.defaultDirectives { + +MultiplatformDiagnosticsDirectives.MULTIPLATFORM_COMPOSITE_ANALYSIS_MODE + LanguageSettingsDirectives.LANGUAGE with "+MultiPlatformProjects" + } + } +} diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/configuration/JsEnvironmentConfigurator.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/configuration/JsEnvironmentConfigurator.kt index 2f9bfe70489..a7a79b5373d 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/configuration/JsEnvironmentConfigurator.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/configuration/JsEnvironmentConfigurator.kt @@ -8,16 +8,17 @@ package org.jetbrains.kotlin.test.services.configuration import com.intellij.openapi.project.Project import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.messages.MessageCollector -import org.jetbrains.kotlin.config.CommonConfigurationKeys -import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.config.AnalysisFlag import org.jetbrains.kotlin.config.AnalysisFlags.allowFullyQualifiedNameInKClass +import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.LanguageVersion +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.js.config.* import org.jetbrains.kotlin.js.facade.MainCallParameters import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.resolve.CompilerEnvironment +import org.jetbrains.kotlin.resolve.TargetEnvironment import org.jetbrains.kotlin.serialization.js.JsModuleDescriptor import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil import org.jetbrains.kotlin.serialization.js.ModuleKind @@ -31,9 +32,9 @@ import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives.SOURCE_MAP_EMBED_SOURCES import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives.TYPED_ARRAYS import org.jetbrains.kotlin.test.directives.model.DirectivesContainer +import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives import org.jetbrains.kotlin.test.frontend.classic.moduleDescriptorProvider import org.jetbrains.kotlin.test.model.ArtifactKinds -import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives import org.jetbrains.kotlin.test.model.DependencyDescription import org.jetbrains.kotlin.test.model.DependencyRelation import org.jetbrains.kotlin.test.model.TestModule @@ -114,9 +115,11 @@ class JsEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigu fun getPostfixFile(module: TestModule): File? = getPrefixPostfixFile(module, prefix = false) - fun createJsConfig(project: Project, configuration: CompilerConfiguration): JsConfig { + fun createJsConfig( + project: Project, configuration: CompilerConfiguration, compilerEnvironment: TargetEnvironment = CompilerEnvironment + ): JsConfig { return JsConfig( - project, configuration, CompilerEnvironment, METADATA_CACHE, (JsConfig.JS_STDLIB + JsConfig.JS_KOTLIN_TEST).toSet() + project, configuration, compilerEnvironment, METADATA_CACHE, (JsConfig.JS_STDLIB + JsConfig.JS_KOTLIN_TEST).toSet() ) } diff --git a/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/native/FakeTopDownAnalyzerFacadeForNative.kt b/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/native/FakeTopDownAnalyzerFacadeForNative.kt index 8fc7986e641..00de85dfcf9 100644 --- a/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/native/FakeTopDownAnalyzerFacadeForNative.kt +++ b/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/native/FakeTopDownAnalyzerFacadeForNative.kt @@ -27,13 +27,15 @@ object FakeTopDownAnalyzerFacadeForNative { files: Collection, trace: BindingTrace, moduleContext: ModuleContext, - languageVersionSettings: LanguageVersionSettings + languageVersionSettings: LanguageVersionSettings, + compilerEnvironment: TargetEnvironment = CompilerEnvironment ): AnalysisResult { val analyzerForNative = createFakeTopDownAnalyzerForNative( moduleContext, trace, FileBasedDeclarationProviderFactory(moduleContext.storageManager, files), - languageVersionSettings + languageVersionSettings, + compilerEnvironment ) analyzerForNative.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files as Collection) @@ -45,7 +47,8 @@ private fun createFakeTopDownAnalyzerForNative( moduleContext: ModuleContext, bindingTrace: BindingTrace, declarationProviderFactory: DeclarationProviderFactory, - languageVersionSettings: LanguageVersionSettings + languageVersionSettings: LanguageVersionSettings, + compilerEnvironment: TargetEnvironment = CompilerEnvironment ): LazyTopDownAnalyzer = createContainer("FakeTopDownAnalyzerForNative", NativePlatformAnalyzerServices) { configureModule( moduleContext, @@ -59,7 +62,7 @@ private fun createFakeTopDownAnalyzerForNative( useInstance(declarationProviderFactory) useInstance(InlineConstTracker.DoNothing) - CompilerEnvironment.configure(this) + compilerEnvironment.configure(this) }.apply { val moduleDescriptor = get() moduleDescriptor.initialize(get().packageFragmentProvider) diff --git a/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit5CompilerTests.kt b/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit5CompilerTests.kt index 73b84ea2239..6dfc3fdc684 100644 --- a/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit5CompilerTests.kt +++ b/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit5CompilerTests.kt @@ -47,6 +47,13 @@ fun generateJUnit5CompilerTests(args: Array) { model("diagnostics/nativeTests") } + testClass { + model( + "diagnostics/testsWithMultiplatformCompositeAnalysis", + pattern = "^(.*)\\.kts?$", excludedPattern = excludedFirTestdataPattern + ) + } + testClass { model("diagnostics/foreignAnnotationsTests/tests", excludedPattern = excludedFirTestdataPattern) model("diagnostics/foreignAnnotationsTests/java8Tests", excludedPattern = excludedFirTestdataPattern)