diff --git a/compiler/frontend.common/src/org/jetbrains/kotlin/resolve/PlatformDependentAnalyzerServices.kt b/compiler/frontend.common/src/org/jetbrains/kotlin/resolve/PlatformDependentAnalyzerServices.kt index d35c52fb9bd..1e68bb3238d 100644 --- a/compiler/frontend.common/src/org/jetbrains/kotlin/resolve/PlatformDependentAnalyzerServices.kt +++ b/compiler/frontend.common/src/org/jetbrains/kotlin/resolve/PlatformDependentAnalyzerServices.kt @@ -53,7 +53,7 @@ abstract class PlatformDependentAnalyzerServices { ) ) - protected abstract fun computePlatformSpecificDefaultImports(storageManager: StorageManager, result: MutableList) + abstract fun computePlatformSpecificDefaultImports(storageManager: StorageManager, result: MutableList) open val excludedImports: List get() = emptyList() diff --git a/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt b/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt index e0ab82b9322..2785b5e5a70 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt @@ -59,7 +59,12 @@ fun StorageComponentContainer.configureModule( useInstance(platform) useInstance(analyzerServices) - useInstance(platform.componentPlatforms.singleOrNull()?.targetPlatformVersion ?: TargetPlatformVersion.NoVersion) + + val nonTrivialPlatformVersion = platform + .mapNotNull { it.targetPlatformVersion.takeIf { it != TargetPlatformVersion.NoVersion } } + .singleOrNull() + + useInstance(nonTrivialPlatformVersion ?: TargetPlatformVersion.NoVersion) analyzerServices.platformConfigurator.configureModuleComponents(this) analyzerServices.platformConfigurator.configureModuleDependentCheckers(this) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt index 48bd59e60ee..84c77cd6fc5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt @@ -100,7 +100,7 @@ abstract class PlatformConfiguratorBase( container.useImpl() } - private fun configureExtensionsAndCheckers(container: StorageComponentContainer) { + fun configureExtensionsAndCheckers(container: StorageComponentContainer) { with(container) { useInstanceIfNotNull(dynamicTypesSettings) additionalDeclarationCheckers.forEach { useInstance(it) } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CompositeResolverForModuleFactory.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CompositeResolverForModuleFactory.kt new file mode 100644 index 00000000000..ef08bebbd91 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CompositeResolverForModuleFactory.kt @@ -0,0 +1,257 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. 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("Duplicates") + +package org.jetbrains.kotlin.caches.resolve + +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.analyzer.* +import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters +import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices +import org.jetbrains.kotlin.analyzer.common.configureCommonSpecificComponents +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.container.* +import org.jetbrains.kotlin.context.ModuleContext +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.PackageFragmentProvider +import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.frontend.di.configureModule +import org.jetbrains.kotlin.frontend.di.configureStandardResolveComponents +import org.jetbrains.kotlin.frontend.java.di.configureJavaSpecificComponents +import org.jetbrains.kotlin.frontend.java.di.initializeJavaSpecificComponents +import org.jetbrains.kotlin.idea.project.IdeaEnvironment +import org.jetbrains.kotlin.incremental.components.LookupTracker +import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolver +import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolverImpl +import org.jetbrains.kotlin.load.kotlin.PackagePartProvider +import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.has +import org.jetbrains.kotlin.platform.idePlatformKind +import org.jetbrains.kotlin.platform.isCommon +import org.jetbrains.kotlin.platform.js.isJs +import org.jetbrains.kotlin.platform.jvm.JvmPlatform +import org.jetbrains.kotlin.platform.konan.KonanPlatform +import org.jetbrains.kotlin.platform.konan.KonanPlatforms +import org.jetbrains.kotlin.resolve.* +import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker +import org.jetbrains.kotlin.resolve.checkers.ExperimentalMarkerDeclarationAnnotationChecker +import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver +import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters +import org.jetbrains.kotlin.resolve.lazy.ResolveSession +import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory +import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService +import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragmentProvider +import org.jetbrains.kotlin.serialization.deserialization.MetadataPartProvider +import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil +import org.jetbrains.kotlin.serialization.js.createKotlinJavascriptPackageFragmentProvider +import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils + +class CompositeResolverForModuleFactory( + private val commonAnalysisParameters: CommonAnalysisParameters, + private val jvmAnalysisParameters: JvmPlatformParameters, + private val targetPlatform: TargetPlatform, + private val compilerServices: CompositeAnalyzerServices +) : ResolverForModuleFactory() { + override fun createResolverForModule( + moduleDescriptor: ModuleDescriptorImpl, + moduleContext: ModuleContext, + moduleContent: ModuleContent, + resolverForProject: ResolverForProject, + languageVersionSettings: LanguageVersionSettings + ): ResolverForModule { + val (moduleInfo, syntheticFiles, moduleContentScope) = moduleContent + val project = moduleContext.project + val declarationProviderFactory = DeclarationProviderFactoryService.createDeclarationProviderFactory( + project, moduleContext.storageManager, syntheticFiles, + moduleContentScope, + moduleInfo + ) + + val metadataPartProvider = commonAnalysisParameters.metadataPartProviderFactory(moduleContent) + val trace = CodeAnalyzerInitializer.getInstance(project).createTrace() + + + val moduleClassResolver = ModuleClassResolverImpl { javaClass -> + val referencedClassModule = jvmAnalysisParameters.moduleByJavaClass(javaClass) + // We don't have full control over idea resolve api so we allow for a situation which should not happen in Kotlin. + // For example, type in a java library can reference a class declared in a source root (is valid but rare case) + // Providing a fallback strategy in this case can hide future problems, so we should at least log to be able to diagnose those + + @Suppress("UNCHECKED_CAST") + val resolverForReferencedModule = referencedClassModule?.let { resolverForProject.tryGetResolverForModule(it as M) } + + val resolverForModule = resolverForReferencedModule?.takeIf { + referencedClassModule.platform.has() || referencedClassModule.platform == null + } ?: run { + // in case referenced class lies outside of our resolver, resolve the class as if it is inside our module + // this leads to java class being resolved several times + resolverForProject.resolverForModule(moduleInfo) + } + resolverForModule.componentProvider.get() + } + + val packagePartProvider = jvmAnalysisParameters.packagePartProviderFactory(moduleContent) + + val container = createContainerForCompositePlatform( + moduleContext, moduleContentScope, languageVersionSettings, targetPlatform, + compilerServices, trace, declarationProviderFactory, metadataPartProvider, + moduleClassResolver, packagePartProvider + ) + + val packageFragmentProviders = sequence { + yield(container.get().packageFragmentProvider) + + yieldAll(getCommonProvidersIfAny(container)) + yieldAll(getJsProvidersIfAny(moduleInfo, moduleContext, moduleDescriptor, container)) + yieldAll(getJvmProvidersIfAny(container)) + yieldAll(getKonanProvidersIfAny(moduleInfo, container)) + }.toList() + + return ResolverForModule(CompositePackageFragmentProvider(packageFragmentProviders), container) + } + + private fun getCommonProvidersIfAny(container: StorageComponentContainer): List = + if (targetPlatform.isCommon()) listOf(container.get()) else emptyList() + + private fun getJvmProvidersIfAny(container: StorageComponentContainer): List = + if (targetPlatform.has()) listOf(container.get().packageFragmentProvider) else emptyList() + + private fun getKonanProvidersIfAny(moduleInfo: ModuleInfo, container: StorageComponentContainer): List { + if (!targetPlatform.has()) return emptyList() + val resolution = KonanPlatforms.defaultKonanPlatform.idePlatformKind.resolution + + val konanProvider = resolution.createPlatformSpecificPackageFragmentProvider( + moduleInfo, + container.get(), + container.get(), + container.get() + ) ?: return emptyList() + + return listOf(konanProvider) + } + + private fun getJsProvidersIfAny( + moduleInfo: ModuleInfo, + moduleContext: ModuleContext, + moduleDescriptor: ModuleDescriptorImpl, + container: StorageComponentContainer + ): List { + if (moduleInfo !is LibraryModuleInfo || !moduleInfo.platform.isJs()) return emptyList() + + return moduleInfo.getLibraryRoots() + .flatMap { KotlinJavascriptMetadataUtils.loadMetadata(it) } + .filter { it.version.isCompatible() } + .map { metadata -> + val (header, packageFragmentProtos) = + KotlinJavascriptSerializationUtil.readModuleAsProto(metadata.body, metadata.version) + createKotlinJavascriptPackageFragmentProvider( + moduleContext.storageManager, moduleDescriptor, header, packageFragmentProtos, metadata.version, + container.get(), LookupTracker.DO_NOTHING + ) + } + } + + fun createContainerForCompositePlatform( + moduleContext: ModuleContext, + moduleContentScope: GlobalSearchScope, + languageVersionSettings: LanguageVersionSettings, + targetPlatform: TargetPlatform, + analyzerServices: CompositeAnalyzerServices, + trace: BindingTrace, + declarationProviderFactory: DeclarationProviderFactory, + metadataPartProvider: MetadataPartProvider, + // Guaranteed to be non-null for modules with JVM + moduleClassResolver: ModuleClassResolver?, + packagePartProvider: PackagePartProvider? + ): StorageComponentContainer = composeContainer("CompositePlatform") { + // Shared by all PlatformConfigurators + configureDefaultCheckers() + + // Specific for each PlatformConfigurator + for (configurator in analyzerServices.services.map { it.platformConfigurator as PlatformConfiguratorBase }) { + configurator.configureExtensionsAndCheckers(this) + } + + // Called by all normal containers set-ups + configureModule(moduleContext, targetPlatform, analyzerServices, trace, languageVersionSettings) + configureStandardResolveComponents() + useInstance(moduleContentScope) + useInstance(declarationProviderFactory) + + // Probably, should be in StandardResolveComponents, but + useInstance(VirtualFileFinderFactory.getInstance(moduleContext.project).create(moduleContentScope)) + useInstance(packagePartProvider!!) + + // JVM-specific + if (targetPlatform.has()) { + configureJavaSpecificComponents( + moduleContext, moduleClassResolver!!, languageVersionSettings, configureJavaClassFinder = null, + javaClassTracker = null, + useBuiltInsProvider = false + ) + } + + // Common-specific + if (targetPlatform.isCommon()) { + configureCommonSpecificComponents() + } + + IdeaEnvironment.configure(this) + }.apply { + if (targetPlatform.has()) { + initializeJavaSpecificComponents(trace) + } + } +} + +class CompositeAnalyzerServices(val services: List) : PlatformDependentAnalyzerServices() { + override val platformConfigurator: PlatformConfigurator = CompositePlatformConigurator(services.map { it.platformConfigurator }) + + override fun computePlatformSpecificDefaultImports(storageManager: StorageManager, result: MutableList) { + val intersectionOfDefaultImports = services.map { service -> + mutableListOf() + .apply { service.computePlatformSpecificDefaultImports(storageManager, this) } + .toSet() + }.safeIntersect() + + result.addAll(intersectionOfDefaultImports) + } + + override val defaultLowPriorityImports: List = services.map { it.defaultLowPriorityImports.toSet() }.safeIntersect() + + override val excludedImports: List = services.map { it.excludedImports.toSet() }.safeUnion() + + private fun List>.safeUnion(): List = + if (isEmpty()) emptyList() else reduce { first, second -> first.union(second) }.toList() + + private fun List>.safeIntersect(): List = + if (isEmpty()) emptyList() else reduce { first, second -> first.intersect(second) }.toList() +} + +class CompositePlatformConigurator(private val componentConfigurators: List) : PlatformConfigurator { + // TODO(dsavvinov): this is actually a hack. Review callers of that method, think about how to refactor it + // Unfortunately, clients of that container will inject additional services into them, + // without knowing about composite platform (see LocalClassifierAnalyzer), so we can't just use [createContainerForCompositePlatform] + // here. Hopefully, platformSpecific container won't at least make things worse. + override val platformSpecificContainer: StorageComponentContainer + get() = CommonPlatformAnalyzerServices.platformConfigurator.platformSpecificContainer + + override fun configureModuleComponents(container: StorageComponentContainer) { + componentConfigurators.forEach { it.configureModuleComponents(container) } + } + + override fun configureModuleDependentCheckers(container: StorageComponentContainer) { + // We (ab)use the fact that currently, platforms don't use that method, so the only injected compnent will be + // ExperimentalMarkerDeclarationAnnotationChecker. + // Unfortunately, it is declared in base class, so repeating call to 'configureModuleDependentCheckers' will lead + // to multiple registrrations. + container.useImpl() + } +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ProjectResolutionFacade.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ProjectResolutionFacade.kt index 3200c5ea065..177a8641f63 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ProjectResolutionFacade.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ProjectResolutionFacade.kt @@ -27,6 +27,8 @@ import org.jetbrains.kotlin.analyzer.* import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns +import org.jetbrains.kotlin.caches.resolve.CompositeAnalyzerServices +import org.jetbrains.kotlin.caches.resolve.CompositeResolverForModuleFactory import org.jetbrains.kotlin.caches.resolve.resolution import org.jetbrains.kotlin.context.GlobalContextImpl import org.jetbrains.kotlin.context.ProjectContext @@ -37,6 +39,8 @@ import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo import org.jetbrains.kotlin.idea.caches.project.getNullableModuleInfo import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider import org.jetbrains.kotlin.idea.project.IdeaEnvironment +import org.jetbrains.kotlin.idea.project.findAnalyzerServices +import org.jetbrains.kotlin.idea.project.useCompositeAnalysis import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl import org.jetbrains.kotlin.platform.idePlatformKind @@ -49,6 +53,7 @@ import org.jetbrains.kotlin.resolve.CompositeBindingContext import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult import org.jetbrains.kotlin.platform.DefaultIdeTargetPlatformKindProvider +import org.jetbrains.kotlin.platform.toTargetPlatform internal class ProjectResolutionFacade( private val debugString: String, @@ -149,7 +154,15 @@ internal class ProjectResolutionFacade( else -> PlatformAnalysisParameters.Empty } - platform.idePlatformKind.resolution.createResolverForModuleFactory(parameters, IdeaEnvironment, platform) + if (!project.useCompositeAnalysis) + platform.idePlatformKind.resolution.createResolverForModuleFactory(parameters, IdeaEnvironment, platform) + else + CompositeResolverForModuleFactory( + commonPlatformParameters, + jvmPlatformParameters, + modulePlatform!!, + CompositeAnalyzerServices(modulePlatform.componentPlatforms.map { it.toTargetPlatform().findAnalyzerServices }) + ) }, builtIns = builtIns, delegateResolver = delegateResolverForProject, diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/analyzerServices.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/analyzerServices.kt index 9d9b66358dc..0c996d7ca7c 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/analyzerServices.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/analyzerServices.kt @@ -5,13 +5,14 @@ package org.jetbrains.kotlin.idea.project -import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices +import org.jetbrains.kotlin.caches.resolve.CompositeAnalyzerServices import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices +import org.jetbrains.kotlin.platform.SimplePlatform import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.isCommon -import org.jetbrains.kotlin.platform.js.isJs -import org.jetbrains.kotlin.platform.jvm.isJvm -import org.jetbrains.kotlin.platform.konan.isNative +import org.jetbrains.kotlin.platform.js.JsPlatform +import org.jetbrains.kotlin.platform.jvm.JvmPlatform +import org.jetbrains.kotlin.platform.konan.KonanPlatform import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices import org.jetbrains.kotlin.resolve.konan.platform.NativePlatformAnalyzerServices @@ -20,9 +21,14 @@ import java.lang.IllegalStateException val TargetPlatform.findAnalyzerServices: PlatformDependentAnalyzerServices get() = when { - isJvm() -> JvmPlatformAnalyzerServices - isJs() -> JsPlatformAnalyzerServices - isNative() -> NativePlatformAnalyzerServices - isCommon() -> CommonPlatformAnalyzerServices - else -> throw IllegalStateException("Unknown platform $this") - } \ No newline at end of file + isCommon() -> CompositeAnalyzerServices(this.componentPlatforms.map { it.findAnalyzerServices }) + else -> single().findAnalyzerServices + } + +val SimplePlatform.findAnalyzerServices: PlatformDependentAnalyzerServices + get() = when (this) { + is JvmPlatform -> JvmPlatformAnalyzerServices + is JsPlatform -> JsPlatformAnalyzerServices + is KonanPlatform -> NativePlatformAnalyzerServices + else -> throw IllegalStateException("Unknown platform $this") + } \ No newline at end of file