[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),
klibList: List<KotlinLibrary> = emptyList(),
explicitModuleDependencyList: List<ModuleDescriptorImpl> = emptyList(),
explicitModuleFriendsList: List<ModuleDescriptorImpl> = emptyList()
explicitModuleFriendsList: List<ModuleDescriptorImpl> = 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
)
@@ -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<ModulePath>
@@ -31,4 +33,4 @@ interface ModuleStructureOracle {
class ModulePath(val nodes: List<ModuleDescriptor>) {
constructor(vararg nodes: ModuleDescriptor) : this(nodes.toList())
}
}
@@ -33,7 +33,6 @@ abstract class TargetEnvironment(private val name: String) {
container.useInstance(BodyResolveCache.ThrowException)
container.useImpl<CompilerLocalDescriptorResolver>()
container.useImpl<BasicAbsentDescriptorHandler>()
container.useInstance(ModuleStructureOracle.SingleModule)
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.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<ClassicFrontendOutputArtifact>(testServices, FrontendKinds.ClassicFrontend) {
override val additionalServices: List<ServiceRegistrationData>
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<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 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<KtFile>,
dependentDescriptors: List<ModuleDescriptorImpl>,
compilerEnvironment: TargetEnvironment,
dependencyDescriptors: List<ModuleDescriptorImpl>,
friendsDescriptors: List<ModuleDescriptorImpl>,
hasCommonModules: Boolean
dependsOnDescriptors: List<ModuleDescriptorImpl>
): 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<KtFile>,
dependentDescriptors: List<ModuleDescriptorImpl>,
compilerEnviornment: TargetEnvironment,
dependencyDescriptors: List<ModuleDescriptorImpl>,
friendsDescriptors: List<ModuleDescriptorImpl>,
hasCommonModules: Boolean
dependsOnDescriptors: List<ModuleDescriptorImpl>
): 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<KtFile>,
dependentDescriptors: List<ModuleDescriptorImpl>
dependencyDescriptors: List<ModuleDescriptorImpl>
): 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<KtFile>,
dependentDescriptors: List<ModuleDescriptorImpl>,
dependencyDescriptors: List<ModuleDescriptorImpl>,
friendsDescriptors: List<ModuleDescriptorImpl>,
): 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<KtFile>,
dependencyDescriptors: List<ModuleDescriptorImpl>,
friendsDescriptors: List<ModuleDescriptorImpl>,
dependsOnDescriptors: List<ModuleDescriptorImpl>,
): 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<KtFile>,
): 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<ModuleDescriptorImpl>,
dependencyDescriptors: List<ModuleDescriptorImpl>,
friendsDescriptors: List<ModuleDescriptorImpl>,
dependsOnDescriptors: List<ModuleDescriptorImpl>,
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)
}
}
@@ -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 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()
)
}
@@ -27,13 +27,15 @@ object FakeTopDownAnalyzerFacadeForNative {
files: Collection<KtFile>,
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<com.intellij.psi.PsiElement>)
@@ -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<ModuleDescriptorImpl>()
moduleDescriptor.initialize(get<KotlinCodeAnalyzer>().packageFragmentProvider)
@@ -47,6 +47,13 @@ fun generateJUnit5CompilerTests(args: Array<String>) {
model("diagnostics/nativeTests")
}
testClass<AbstractDiagnosticsWithMultiplatformCompositeAnalysisTest> {
model(
"diagnostics/testsWithMultiplatformCompositeAnalysis",
pattern = "^(.*)\\.kts?$", excludedPattern = excludedFirTestdataPattern
)
}
testClass<AbstractForeignAnnotationsSourceJavaTest> {
model("diagnostics/foreignAnnotationsTests/tests", excludedPattern = excludedFirTestdataPattern)
model("diagnostics/foreignAnnotationsTests/java8Tests", excludedPattern = excludedFirTestdataPattern)