[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
This commit is contained in:
sebastian.sellmair
2021-12-15 12:05:19 +01:00
committed by Space
parent 7771e5914d
commit 3598ba1d45
10 changed files with 329 additions and 107 deletions
@@ -95,10 +95,11 @@ object TopDownAnalyzerFacadeForJVM {
sourceModuleSearchScope: GlobalSearchScope = newModuleSearchScope(project, files), sourceModuleSearchScope: GlobalSearchScope = newModuleSearchScope(project, files),
klibList: List<KotlinLibrary> = emptyList(), klibList: List<KotlinLibrary> = emptyList(),
explicitModuleDependencyList: List<ModuleDescriptorImpl> = emptyList(), explicitModuleDependencyList: List<ModuleDescriptorImpl> = emptyList(),
explicitModuleFriendsList: List<ModuleDescriptorImpl> = emptyList() explicitModuleFriendsList: List<ModuleDescriptorImpl> = emptyList(),
explicitCompilerEnvironment: TargetEnvironment = CompilerEnvironment
): AnalysisResult { ): AnalysisResult {
val container = createContainer( val container = createContainer(
project, files, trace, configuration, packagePartProvider, declarationProviderFactory, CompilerEnvironment, project, files, trace, configuration, packagePartProvider, declarationProviderFactory, explicitCompilerEnvironment,
sourceModuleSearchScope, klibList, explicitModuleDependencyList = explicitModuleDependencyList, sourceModuleSearchScope, klibList, explicitModuleDependencyList = explicitModuleDependencyList,
explicitModuleFriendsList = explicitModuleFriendsList explicitModuleFriendsList = explicitModuleFriendsList
) )
@@ -5,10 +5,12 @@
package org.jetbrains.kotlin.resolve package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.container.DefaultImplementation
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@DefaultImplementation(ModuleStructureOracle.SingleModule::class)
interface ModuleStructureOracle { interface ModuleStructureOracle {
// May be faster than `findAllImplementingModules(module).isNotEmpty()` // May be faster than `findAllReversedDependsOnPaths(module).isNotEmpty()`
fun hasImplementingModules(module: ModuleDescriptor): Boolean fun hasImplementingModules(module: ModuleDescriptor): Boolean
fun findAllReversedDependsOnPaths(module: ModuleDescriptor): List<ModulePath> fun findAllReversedDependsOnPaths(module: ModuleDescriptor): List<ModulePath>
@@ -33,7 +33,6 @@ abstract class TargetEnvironment(private val name: String) {
container.useInstance(BodyResolveCache.ThrowException) container.useInstance(BodyResolveCache.ThrowException)
container.useImpl<CompilerLocalDescriptorResolver>() container.useImpl<CompilerLocalDescriptorResolver>()
container.useImpl<BasicAbsentDescriptorHandler>() container.useImpl<BasicAbsentDescriptorHandler>()
container.useInstance(ModuleStructureOracle.SingleModule)
container.useImpl<MainFunctionDetector.Factory.Ordinary>() container.useImpl<MainFunctionDetector.Factory.Ordinary>()
} }
} }
@@ -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
)
}
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.context.ProjectContext import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.context.withModule import org.jetbrains.kotlin.context.withModule
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider 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.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.frontend.java.di.createContainerForLazyResolveWithJava import org.jetbrains.kotlin.frontend.java.di.createContainerForLazyResolveWithJava
import org.jetbrains.kotlin.frontend.java.di.initJvmBuiltInsForTopDownAnalysis 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.ir.util.IrMessageLogger
import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS
import org.jetbrains.kotlin.js.config.JSConfigurationKeys 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.library.unresolvedDependencies
import org.jetbrains.kotlin.load.java.lazy.SingleModuleClassResolver import org.jetbrains.kotlin.load.java.lazy.SingleModuleClassResolver
import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager 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.jvm.isJvm
import org.jetbrains.kotlin.platform.konan.isNative import org.jetbrains.kotlin.platform.konan.isNative
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.CompilerEnvironment
import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer
import org.jetbrains.kotlin.resolve.TargetEnvironment
import org.jetbrains.kotlin.resolve.TopDownAnalysisMode import org.jetbrains.kotlin.resolve.TopDownAnalysisMode
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory 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.serialization.deserialization.MetadataPartProvider
import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.storage.StorageManager 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.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.*
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator
import org.jetbrains.kotlin.test.util.KtTestUtil import org.jetbrains.kotlin.test.util.KtTestUtil
import org.jetbrains.kotlin.types.typeUtil.closure
import org.jetbrains.kotlin.utils.addToStdlib.runIf import org.jetbrains.kotlin.utils.addToStdlib.runIf
import java.io.File import java.io.File
class ClassicFrontendFacade( class ClassicFrontendFacade(
testServices: TestServices testServices: TestServices,
) : FrontendFacade<ClassicFrontendOutputArtifact>(testServices, FrontendKinds.ClassicFrontend) { ) : FrontendFacade<ClassicFrontendOutputArtifact>(testServices, FrontendKinds.ClassicFrontend) {
override val additionalServices: List<ServiceRegistrationData> override val additionalServices: List<ServiceRegistrationData>
get() = listOf(service(::ModuleDescriptorProvider)) get() = listOf(service(::ModuleDescriptorProvider))
override fun analyze(module: TestModule): ClassicFrontendOutputArtifact { override fun analyze(module: TestModule): ClassicFrontendOutputArtifact {
val dependencyProvider = testServices.dependencyProvider
val moduleDescriptorProvider = testServices.moduleDescriptorProvider val moduleDescriptorProvider = testServices.moduleDescriptorProvider
val compilerConfigurationProvider = testServices.compilerConfigurationProvider val compilerConfigurationProvider = testServices.compilerConfigurationProvider
val packagePartProviderFactory = compilerConfigurationProvider.getPackagePartProviderFactory(module) val packagePartProviderFactory = compilerConfigurationProvider.getPackagePartProviderFactory(module)
val project = compilerConfigurationProvider.getProject(module) val project = compilerConfigurationProvider.getProject(module)
val configuration = compilerConfigurationProvider.getCompilerConfiguration(module) val configuration = compilerConfigurationProvider.getCompilerConfiguration(module)
val multiplatformConfiguration = MultiplatformAnalysisConfiguration(testServices)
val ktFilesMap = testServices.sourceFileProvider.getKtFilesForSourceFiles(module.files, project).toMutableMap() val ktFilesMap = multiplatformConfiguration.getKtFilesForForSourceFiles(project, module)
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<DependencyDescription>) {
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 ktFiles = ktFilesMap.values.toList() val ktFiles = ktFilesMap.values.toList()
setupJavacIfNeeded(module, ktFiles, configuration) setupJavacIfNeeded(module, ktFiles, configuration)
val analysisResult = performResolve( val analysisResult = performResolve(
module, module,
@@ -128,16 +97,17 @@ class ClassicFrontendFacade(
configuration, configuration,
packagePartProviderFactory, packagePartProviderFactory,
ktFiles, ktFiles,
dependentDescriptors, compilerEnvironment = multiplatformConfiguration.getCompilerEnvironment(module),
friendDescriptors, dependencyDescriptors = multiplatformConfiguration.getDependencyDescriptors(module),
hasCommonModules friendsDescriptors = multiplatformConfiguration.getFriendDescriptors(module),
dependsOnDescriptors = multiplatformConfiguration.getDependsOnDescriptors(module)
) )
moduleDescriptorProvider.replaceModuleDescriptorForModule(module, analysisResult.moduleDescriptor) moduleDescriptorProvider.replaceModuleDescriptorForModule(module, analysisResult.moduleDescriptor)
return ClassicFrontendOutputArtifact( return ClassicFrontendOutputArtifact(
ktFilesMap, ktFilesMap,
analysisResult, analysisResult,
project, project,
languageVersionSettings module.languageVersionSettings
) )
} }
@@ -160,28 +130,29 @@ class ClassicFrontendFacade(
configuration: CompilerConfiguration, configuration: CompilerConfiguration,
packagePartProviderFactory: (GlobalSearchScope) -> JvmPackagePartProvider, packagePartProviderFactory: (GlobalSearchScope) -> JvmPackagePartProvider,
files: List<KtFile>, files: List<KtFile>,
dependentDescriptors: List<ModuleDescriptorImpl>, compilerEnvironment: TargetEnvironment,
dependencyDescriptors: List<ModuleDescriptorImpl>,
friendsDescriptors: List<ModuleDescriptorImpl>, friendsDescriptors: List<ModuleDescriptorImpl>,
hasCommonModules: Boolean dependsOnDescriptors: List<ModuleDescriptorImpl>
): AnalysisResult { ): AnalysisResult {
val targetPlatform = module.targetPlatform val targetPlatform = module.targetPlatform
return when { return when {
targetPlatform.isJvm() -> performJvmModuleResolve( targetPlatform.isJvm() -> performJvmModuleResolve(
module, module, project, configuration, packagePartProviderFactory,
project, files, compilerEnvironment, dependencyDescriptors, friendsDescriptors, dependencyDescriptors
configuration,
packagePartProviderFactory,
files,
dependentDescriptors,
friendsDescriptors,
hasCommonModules
) )
targetPlatform.isJs() -> when { targetPlatform.isJs() -> when {
module.targetBackend?.isIR != true -> performJsModuleResolve(project, configuration, files, dependentDescriptors) module.targetBackend?.isIR != true -> performJsModuleResolve(
else -> performJsIrModuleResolve(module, project, configuration, files, dependentDescriptors, friendsDescriptors) project, configuration, compilerEnvironment, files, dependencyDescriptors
)
else -> performJsIrModuleResolve(
module, project, configuration, compilerEnvironment, files, dependencyDescriptors, friendsDescriptors
)
} }
targetPlatform.isNative() -> performNativeModuleResolve(module, project, files) targetPlatform.isNative() -> performNativeModuleResolve(
targetPlatform.isCommon() -> performCommonModuleResolve(module, files) module, project, compilerEnvironment, files, dependencyDescriptors, friendsDescriptors, dependsOnDescriptors
)
targetPlatform.isCommon() -> performCommonModuleResolve(module, compilerEnvironment, files)
else -> error("Should not be here") else -> error("Should not be here")
} }
} }
@@ -193,45 +164,48 @@ class ClassicFrontendFacade(
configuration: CompilerConfiguration, configuration: CompilerConfiguration,
packagePartProviderFactory: (GlobalSearchScope) -> JvmPackagePartProvider, packagePartProviderFactory: (GlobalSearchScope) -> JvmPackagePartProvider,
files: List<KtFile>, files: List<KtFile>,
dependentDescriptors: List<ModuleDescriptorImpl>, compilerEnviornment: TargetEnvironment,
dependencyDescriptors: List<ModuleDescriptorImpl>,
friendsDescriptors: List<ModuleDescriptorImpl>, friendsDescriptors: List<ModuleDescriptorImpl>,
hasCommonModules: Boolean dependsOnDescriptors: List<ModuleDescriptorImpl>
): AnalysisResult { ): AnalysisResult {
val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(project) val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(project)
configuration.getList(JVMConfigurationKeys.FRIEND_PATHS).forEach { moduleVisibilityManager.addFriendPath(it) } configuration.getList(JVMConfigurationKeys.FRIEND_PATHS).forEach { moduleVisibilityManager.addFriendPath(it) }
val moduleTrace = NoScopeRecordCliBindingTrace() val moduleTrace = NoScopeRecordCliBindingTrace()
if (!hasCommonModules) { if (module.dependsOnDependencies.isEmpty()) {
return TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration( return TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
project, project,
files, files,
moduleTrace, moduleTrace,
configuration.copy(), configuration.copy(),
packagePartProviderFactory, packagePartProviderFactory,
explicitModuleDependencyList = dependentDescriptors, explicitModuleDependencyList = dependencyDescriptors,
explicitModuleFriendsList = friendsDescriptors explicitModuleFriendsList = friendsDescriptors,
explicitCompilerEnvironment = compilerEnviornment
) )
} }
val moduleContentScope = GlobalSearchScope.allScope(project) val moduleContentScope = GlobalSearchScope.allScope(project)
val moduleClassResolver = SingleModuleClassResolver() 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) JvmBuiltIns(it, JvmBuiltIns.Kind.FROM_CLASS_LOADER)
} }
val moduleDescriptor = moduleContext.module as ModuleDescriptorImpl val moduleDescriptor = moduleContext.module as ModuleDescriptorImpl
val jvmTarget = configuration[JVM_TARGET] ?: JvmTarget.DEFAULT val jvmTarget = configuration[JVM_TARGET] ?: JvmTarget.DEFAULT
val container = createContainerForLazyResolveWithJava( val container = createContainerForLazyResolveWithJava(
JvmPlatforms.jvmPlatformByTargetVersion(jvmTarget), // TODO(dsavvinov): do not pass JvmTarget around jvmPlatform = JvmPlatforms.jvmPlatformByTargetVersion(jvmTarget), // TODO(dsavvinov): do not pass JvmTarget around
moduleContext, moduleContext = moduleContext,
moduleTrace, bindingTrace = moduleTrace,
FileBasedDeclarationProviderFactory(moduleContext.storageManager, files), declarationProviderFactory = FileBasedDeclarationProviderFactory(moduleContext.storageManager, files),
moduleContentScope, moduleContentScope = moduleContentScope,
moduleClassResolver, moduleClassResolver = moduleClassResolver,
CompilerEnvironment, LookupTracker.DO_NOTHING, targetEnvironment = compilerEnviornment,
ExpectActualTracker.DoNothing, lookupTracker = LookupTracker.DO_NOTHING,
InlineConstTracker.DoNothing, expectActualTracker = ExpectActualTracker.DoNothing,
packagePartProviderFactory(moduleContentScope), inlineConstTracker = InlineConstTracker.DoNothing,
module.languageVersionSettings, packagePartProvider = packagePartProviderFactory(moduleContentScope),
languageVersionSettings = module.languageVersionSettings,
useBuiltInsProvider = true useBuiltInsProvider = true
) )
@@ -257,17 +231,18 @@ class ClassicFrontendFacade(
private fun performJsModuleResolve( private fun performJsModuleResolve(
project: Project, project: Project,
configuration: CompilerConfiguration, configuration: CompilerConfiguration,
compilerEnvironment: TargetEnvironment,
files: List<KtFile>, files: List<KtFile>,
dependentDescriptors: List<ModuleDescriptorImpl> dependencyDescriptors: List<ModuleDescriptorImpl>
): AnalysisResult { ): AnalysisResult {
// `dependentDescriptors` - modules with source dependency kind // `dependencyDescriptors` - modules with source dependency kind
// 'jsConfig.moduleDescriptors' - modules with binary 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( return TopDownAnalyzerFacadeForJS.analyzeFiles(
files, files,
project = jsConfig.project, project = jsConfig.project,
configuration = jsConfig.configuration, configuration = jsConfig.configuration,
moduleDescriptors = dependentDescriptors + jsConfig.moduleDescriptors, moduleDescriptors = dependencyDescriptors + jsConfig.moduleDescriptors,
friendModuleDescriptors = jsConfig.friendModuleDescriptors, friendModuleDescriptors = jsConfig.friendModuleDescriptors,
targetEnvironment = jsConfig.targetEnvironment, targetEnvironment = jsConfig.targetEnvironment,
) )
@@ -310,15 +285,16 @@ class ClassicFrontendFacade(
module: TestModule, module: TestModule,
project: Project, project: Project,
configuration: CompilerConfiguration, configuration: CompilerConfiguration,
compilerEnvironment: TargetEnvironment,
files: List<KtFile>, files: List<KtFile>,
dependentDescriptors: List<ModuleDescriptorImpl>, dependencyDescriptors: List<ModuleDescriptorImpl>,
friendsDescriptors: List<ModuleDescriptorImpl>, friendsDescriptors: List<ModuleDescriptorImpl>,
): AnalysisResult { ): AnalysisResult {
val runtimeKlibsNames = JsEnvironmentConfigurator.getRuntimePathsForModule(module, testServices) val runtimeKlibsNames = JsEnvironmentConfigurator.getRuntimePathsForModule(module, testServices)
val runtimeKlibs = loadKlib(runtimeKlibsNames, configuration) val runtimeKlibs = loadKlib(runtimeKlibsNames, configuration)
val transitiveLibraries = JsEnvironmentConfigurator.getDependencies(module, testServices, DependencyRelation.RegularDependency) val transitiveLibraries = JsEnvironmentConfigurator.getDependencies(module, testServices, DependencyRelation.RegularDependency)
val friendLibraries = JsEnvironmentConfigurator.getDependencies(module, testServices, DependencyRelation.FriendDependency) val friendLibraries = JsEnvironmentConfigurator.getDependencies(module, testServices, DependencyRelation.FriendDependency)
val allDependencies = runtimeKlibs + dependentDescriptors + transitiveLibraries val allDependencies = runtimeKlibs + dependencyDescriptors + transitiveLibraries
val analyzer = AnalyzerWithCompilerReport(configuration) val analyzer = AnalyzerWithCompilerReport(configuration)
val builtInModuleDescriptor = allDependencies.firstNotNullOfOrNull { it.builtIns }?.builtInsModule val builtInModuleDescriptor = allDependencies.firstNotNullOfOrNull { it.builtIns }?.builtInsModule
@@ -329,7 +305,7 @@ class ClassicFrontendFacade(
configuration, configuration,
allDependencies, allDependencies,
friendsDescriptors + friendLibraries, friendsDescriptors + friendLibraries,
CompilerEnvironment, compilerEnvironment,
thisIsBuiltInsModule = builtInModuleDescriptor == null, thisIsBuiltInsModule = builtInModuleDescriptor == null,
customBuiltInsModule = builtInModuleDescriptor customBuiltInsModule = builtInModuleDescriptor
) )
@@ -341,31 +317,48 @@ class ClassicFrontendFacade(
private fun performNativeModuleResolve( private fun performNativeModuleResolve(
module: TestModule, module: TestModule,
project: Project, project: Project,
compilerEnvironment: TargetEnvironment,
files: List<KtFile>, files: List<KtFile>,
dependencyDescriptors: List<ModuleDescriptorImpl>,
friendsDescriptors: List<ModuleDescriptorImpl>,
dependsOnDescriptors: List<ModuleDescriptorImpl>,
): AnalysisResult { ): AnalysisResult {
val moduleTrace = NoScopeRecordCliBindingTrace() val moduleTrace = NoScopeRecordCliBindingTrace()
val moduleContext = createModuleContext(module, project, dependentDescriptors = emptyList(), friendsDescriptors = emptyList()) { val moduleContext = createModuleContext(
module, project,
dependencyDescriptors = dependencyDescriptors,
friendsDescriptors = friendsDescriptors,
dependsOnDescriptors = dependsOnDescriptors
) {
DefaultBuiltIns() DefaultBuiltIns()
} }
return FakeTopDownAnalyzerFacadeForNative.analyzeFilesWithGivenTrace( return FakeTopDownAnalyzerFacadeForNative.analyzeFilesWithGivenTrace(
files, files,
moduleTrace, moduleTrace,
moduleContext, moduleContext,
module.languageVersionSettings module.languageVersionSettings,
compilerEnvironment
) )
} }
private fun performCommonModuleResolve( private fun performCommonModuleResolve(
module: TestModule, module: TestModule,
compilerEnvironment: TargetEnvironment,
files: List<KtFile>, files: List<KtFile>,
): AnalysisResult { ): 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( return CommonResolverForModuleFactory.analyzeFiles(
files, files,
Name.special("<${module.name}>"), Name.special("<${module.name}>"),
dependOnBuiltIns = true, dependOnBuiltIns = true,
module.languageVersionSettings, module.languageVersionSettings,
module.targetPlatform, module.targetPlatform,
CompilerEnvironment, compilerEnvironment,
// TODO: add dependency manager // TODO: add dependency manager
) { ) {
// TODO // TODO
@@ -377,8 +370,9 @@ class ClassicFrontendFacade(
private fun createModuleContext( private fun createModuleContext(
module: TestModule, module: TestModule,
project: Project, project: Project,
dependentDescriptors: List<ModuleDescriptorImpl>, dependencyDescriptors: List<ModuleDescriptorImpl>,
friendsDescriptors: List<ModuleDescriptorImpl>, friendsDescriptors: List<ModuleDescriptorImpl>,
dependsOnDescriptors: List<ModuleDescriptorImpl>,
builtInsFactory: (StorageManager) -> KotlinBuiltIns, builtInsFactory: (StorageManager) -> KotlinBuiltIns,
): ModuleContext { ): ModuleContext {
val projectContext = ProjectContext(project, "test project context") val projectContext = ProjectContext(project, "test project context")
@@ -389,10 +383,18 @@ class ClassicFrontendFacade(
val dependencies = buildList { val dependencies = buildList {
add(moduleDescriptor) add(moduleDescriptor)
add(moduleDescriptor.builtIns.builtInsModule) 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) return projectContext.withModule(moduleDescriptor)
} }
} }
@@ -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<TestFile, KtFile>
fun getDependencyDescriptors(module: TestModule): List<ModuleDescriptorImpl>
fun getFriendDescriptors(module: TestModule): List<ModuleDescriptorImpl>
fun getDependsOnDescriptors(module: TestModule): List<ModuleDescriptorImpl>
}
/**
* 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<ModuleDescriptorImpl> {
return getDescriptors(
module.allDependencies - module.dependsOnDependencies.toSet(),
dependencyProvider, moduleDescriptorProvider
)
}
override fun getDependsOnDescriptors(module: TestModule): List<ModuleDescriptorImpl> {
return emptyList()
}
override fun getFriendDescriptors(module: TestModule): List<ModuleDescriptorImpl> {
return getDescriptors(
module.friendDependencies, dependencyProvider, moduleDescriptorProvider
)
}
override fun getKtFilesForForSourceFiles(project: Project, module: TestModule): Map<TestFile, KtFile> {
val ktFilesMap = sourceFileProvider.getKtFilesForSourceFiles(module.files, project).toMutableMap()
fun addDependsOnSources(dependencies: List<DependencyDescription>) {
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<TestFile, KtFile> {
return sourceFileProvider.getKtFilesForSourceFiles(module.files, project)
}
override fun getDependencyDescriptors(module: TestModule): List<ModuleDescriptorImpl> {
return getDescriptors(module.allDependencies, dependencyProvider, moduleDescriptorProvider)
}
override fun getDependsOnDescriptors(module: TestModule): List<ModuleDescriptorImpl> {
return getDescriptors(module.dependsOnDependencies, dependencyProvider, moduleDescriptorProvider)
}
override fun getFriendDescriptors(module: TestModule): List<ModuleDescriptorImpl> {
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<ModulePath> {
return emptyList() // <- Not supported, yet
}
override fun findAllDependsOnPaths(module: ModuleDescriptor): List<ModulePath> {
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<DependencyDescription>,
dependencyProvider: DependencyProvider,
moduleDescriptorProvider: ModuleDescriptorProvider
): List<ModuleDescriptorImpl> {
return dependencies.filter { it.kind == DependencyKind.Source }
.map { dependencyDescription -> dependencyProvider.getTestModule(dependencyDescription.moduleName) }
.map { dependencyModule -> moduleDescriptorProvider.getModuleDescriptor(dependencyModule) }
}
@@ -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"
}
}
}
@@ -8,16 +8,17 @@ package org.jetbrains.kotlin.test.services.configuration
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.MessageCollector 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.AnalysisFlag
import org.jetbrains.kotlin.config.AnalysisFlags.allowFullyQualifiedNameInKClass 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.config.LanguageVersion
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.js.config.* import org.jetbrains.kotlin.js.config.*
import org.jetbrains.kotlin.js.facade.MainCallParameters import org.jetbrains.kotlin.js.facade.MainCallParameters
import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.resolve.CompilerEnvironment 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.JsModuleDescriptor
import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil
import org.jetbrains.kotlin.serialization.js.ModuleKind 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.SOURCE_MAP_EMBED_SOURCES
import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives.TYPED_ARRAYS import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives.TYPED_ARRAYS
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer 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.frontend.classic.moduleDescriptorProvider
import org.jetbrains.kotlin.test.model.ArtifactKinds 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.DependencyDescription
import org.jetbrains.kotlin.test.model.DependencyRelation import org.jetbrains.kotlin.test.model.DependencyRelation
import org.jetbrains.kotlin.test.model.TestModule 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 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( 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()
) )
} }
@@ -27,13 +27,15 @@ object FakeTopDownAnalyzerFacadeForNative {
files: Collection<KtFile>, files: Collection<KtFile>,
trace: BindingTrace, trace: BindingTrace,
moduleContext: ModuleContext, moduleContext: ModuleContext,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings,
compilerEnvironment: TargetEnvironment = CompilerEnvironment
): AnalysisResult { ): AnalysisResult {
val analyzerForNative = createFakeTopDownAnalyzerForNative( val analyzerForNative = createFakeTopDownAnalyzerForNative(
moduleContext, trace, moduleContext, trace,
FileBasedDeclarationProviderFactory(moduleContext.storageManager, files), FileBasedDeclarationProviderFactory(moduleContext.storageManager, files),
languageVersionSettings languageVersionSettings,
compilerEnvironment
) )
analyzerForNative.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files as Collection<com.intellij.psi.PsiElement>) analyzerForNative.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files as Collection<com.intellij.psi.PsiElement>)
@@ -45,7 +47,8 @@ private fun createFakeTopDownAnalyzerForNative(
moduleContext: ModuleContext, moduleContext: ModuleContext,
bindingTrace: BindingTrace, bindingTrace: BindingTrace,
declarationProviderFactory: DeclarationProviderFactory, declarationProviderFactory: DeclarationProviderFactory,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings,
compilerEnvironment: TargetEnvironment = CompilerEnvironment
): LazyTopDownAnalyzer = createContainer("FakeTopDownAnalyzerForNative", NativePlatformAnalyzerServices) { ): LazyTopDownAnalyzer = createContainer("FakeTopDownAnalyzerForNative", NativePlatformAnalyzerServices) {
configureModule( configureModule(
moduleContext, moduleContext,
@@ -59,7 +62,7 @@ private fun createFakeTopDownAnalyzerForNative(
useInstance(declarationProviderFactory) useInstance(declarationProviderFactory)
useInstance(InlineConstTracker.DoNothing) useInstance(InlineConstTracker.DoNothing)
CompilerEnvironment.configure(this) compilerEnvironment.configure(this)
}.apply { }.apply {
val moduleDescriptor = get<ModuleDescriptorImpl>() val moduleDescriptor = get<ModuleDescriptorImpl>()
moduleDescriptor.initialize(get<KotlinCodeAnalyzer>().packageFragmentProvider) moduleDescriptor.initialize(get<KotlinCodeAnalyzer>().packageFragmentProvider)
@@ -47,6 +47,13 @@ fun generateJUnit5CompilerTests(args: Array<String>) {
model("diagnostics/nativeTests") model("diagnostics/nativeTests")
} }
testClass<AbstractDiagnosticsWithMultiplatformCompositeAnalysisTest> {
model(
"diagnostics/testsWithMultiplatformCompositeAnalysis",
pattern = "^(.*)\\.kts?$", excludedPattern = excludedFirTestdataPattern
)
}
testClass<AbstractForeignAnnotationsSourceJavaTest> { testClass<AbstractForeignAnnotationsSourceJavaTest> {
model("diagnostics/foreignAnnotationsTests/tests", excludedPattern = excludedFirTestdataPattern) model("diagnostics/foreignAnnotationsTests/tests", excludedPattern = excludedFirTestdataPattern)
model("diagnostics/foreignAnnotationsTests/java8Tests", excludedPattern = excludedFirTestdataPattern) model("diagnostics/foreignAnnotationsTests/java8Tests", excludedPattern = excludedFirTestdataPattern)