From 94fe170b7b8e49f292b19922b32367a022afbad4 Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Wed, 11 Apr 2018 17:42:58 +0200 Subject: [PATCH] MPP: analyze platform sources with expectedBy common sources This allows to emulate current compiler behaviour Introduce CombinedModuleInfo which is a combination of several other modules intended to be analyzed together --- .../kotlin/analyzer/AnalyzerFacade.kt | 43 ++++++++++++------- .../idea/caches/project/IdeaModuleInfos.kt | 29 +++++++++++++ .../idea/caches/project/getModuleInfo.kt | 25 ++++++++++- .../idea/caches/project/multiplatformUtil.kt | 11 ++++- .../caches/resolve/ProjectResolutionFacade.kt | 2 +- .../idea/caches/resolve/packageOracles.kt | 17 ++++---- .../PluginDeclarationProviderFactory.kt | 7 ++- ...PluginDeclarationProviderFactoryService.kt | 5 ++- .../multiplatform/basic/jvm/jvm.kt | 3 +- 9 files changed, 109 insertions(+), 33 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt index 5acd3da94d6..ac1b78cbf83 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt @@ -116,7 +116,12 @@ class ResolverForProjectImpl( // Protected by ("projectContext.storageManager.lock") private val moduleInfoByDescriptor = mutableMapOf() - val modules = modules.toSet() + private val moduleInfoToResolvableInfo: Map = + modules.flatMap { module -> module.flatten().map { modulePart -> modulePart to module } }.toMap() as Map + + init { + assert(moduleInfoToResolvableInfo.values.toSet() == modules.toSet()) + } override fun tryGetResolverForModule(moduleInfo: M): ResolverForModule? { if (!isCorrectModuleInfo(moduleInfo)) { @@ -148,7 +153,7 @@ class ResolverForProjectImpl( private val resolverByModuleDescriptor = mutableMapOf() override val allModules: Collection by lazy { - this.modules + delegateResolver.allModules + this.moduleInfoToResolvableInfo.keys + delegateResolver.allModules } override val name: String @@ -212,19 +217,18 @@ class ResolverForProjectImpl( } private fun doGetDescriptorForModule(module: M): ModuleDescriptorImpl { - if (module in modules) { - return projectContext.storageManager.compute { - var moduleData = descriptorByModule.getOrPut(module) { - createModuleDescriptor(module) - } - if (moduleData.isOutOfDate()) { - moduleData = recreateModuleDescriptor(module) - } - moduleData.moduleDescriptor - } - } + val moduleFromThisResolver = moduleInfoToResolvableInfo[module] + ?: return delegateResolver.descriptorForModule(module) as ModuleDescriptorImpl - return delegateResolver.descriptorForModule(module) as ModuleDescriptorImpl + return projectContext.storageManager.compute { + var moduleData = descriptorByModule.getOrPut(moduleFromThisResolver) { + createModuleDescriptor(moduleFromThisResolver) + } + if (moduleData.isOutOfDate()) { + moduleData = recreateModuleDescriptor(moduleFromThisResolver) + } + moduleData.moduleDescriptor + } } private fun recreateModuleDescriptor(module: M): ModuleData { @@ -253,7 +257,7 @@ class ResolverForProjectImpl( } } -data class ModuleContent( +data class ModuleContent( val moduleInfo: M, val syntheticFiles: Collection, val moduleContentScope: GlobalSearchScope @@ -290,6 +294,15 @@ interface ModuleInfo { } } +interface CombinedModuleInfo : ModuleInfo { + val containedModules: List +} + +fun ModuleInfo.flatten(): List = when (this) { + is CombinedModuleInfo -> listOf(this) + containedModules + else -> listOf(this) +} + interface TrackableModuleInfo : ModuleInfo { fun createModificationTracker(): ModificationTracker } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/IdeaModuleInfos.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/IdeaModuleInfos.kt index 2656b51b9e4..a4873eb2716 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/IdeaModuleInfos.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/IdeaModuleInfos.kt @@ -25,6 +25,7 @@ import com.intellij.psi.util.CachedValuesManager import com.intellij.util.PathUtil import com.intellij.util.SmartList import org.jetbrains.jps.model.java.JavaSourceRootType +import org.jetbrains.kotlin.analyzer.CombinedModuleInfo import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.analyzer.TrackableModuleInfo import org.jetbrains.kotlin.caches.project.LibraryModuleInfo @@ -413,3 +414,31 @@ interface SourceForBinaryModuleInfo : IdeaModuleInfo { override val moduleOrigin: ModuleOrigin get() = ModuleOrigin.OTHER } + +class PlatformModuleInfo( + private val platformModule: ModuleSourceInfo, + private val commonModules: List +) : IdeaModuleInfo, CombinedModuleInfo, TrackableModuleInfo { + override val capabilities: Map, Any?> + get() = platformModule.capabilities + + override fun contentScope() = GlobalSearchScope.union(containedModules.map { it.contentScope() }.toTypedArray()) + + override val containedModules: List = listOf(platformModule) + commonModules + + override val platform: TargetPlatform? + get() = platformModule.platform + + override val moduleOrigin: ModuleOrigin + get() = platformModule.moduleOrigin + + override fun dependencies() = platformModule.dependencies() + + override val name: Name + get() = Name.special("") + + override fun createModificationTracker() = platformModule.createModificationTracker() +} + +fun IdeaModuleInfo.projectSourceModules(): List? = + (this as? ModuleSourceInfo)?.let(::listOf) ?: (this as? PlatformModuleInfo)?.containedModules \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/getModuleInfo.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/getModuleInfo.kt index a6067c04d24..424f0cbf72e 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/getModuleInfo.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/getModuleInfo.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected import org.jetbrains.kotlin.idea.util.isKotlinBinary import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.resolve.TargetPlatform import org.jetbrains.kotlin.script.getScriptDefinition import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.sure @@ -61,7 +62,7 @@ fun getLibrarySourcesModuleInfos(project: Project, virtualFile: VirtualFile) = virtualFile ) -fun collectAllModuleInfosFromIdeaModel(project: Project): List { +fun collectAllModuleInfosFromIdeaModel(project: Project, platform: TargetPlatform): List { val ideaModules = ModuleManager.getInstance(project).modules.toList() val modulesSourcesInfos = ideaModules.flatMap(Module::correspondingModuleInfos) @@ -87,9 +88,29 @@ fun collectAllModuleInfosFromIdeaModel(project: Project): List { ) } - return modulesSourcesInfos + librariesInfos + sdksInfos + return mergePlatformModules(modulesSourcesInfos, platform) + librariesInfos + sdksInfos } +private fun mergePlatformModules( + allModules: List, + platform: TargetPlatform +): List { + if (platform == TargetPlatform.Common) return allModules + + val platformModules = + allModules.flatMap { module -> + if (module is ModuleSourceInfo && module.platform == platform && module.expectedBy.isNotEmpty()) + listOf(module to module.expectedBy) + else emptyList() + }.map { (module, expectedBys) -> + PlatformModuleInfo(module, expectedBys) + } + + val rest = allModules - platformModules.flatMap { it.containedModules } + return rest + platformModules +} + + fun getScriptRelatedModuleInfo(project: Project, virtualFile: VirtualFile): ModuleSourceInfo? { val projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/multiplatformUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/multiplatformUtil.kt index 1559bada57f..b4c84379f81 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/multiplatformUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/multiplatformUtil.kt @@ -40,7 +40,11 @@ private fun Module.findImplementingModuleInfos(moduleSourceInfo: ModuleSourceInf val ModuleDescriptor.implementingDescriptors: List get() { - val moduleSourceInfo = getCapability(ModuleInfo.Capability) as? ModuleSourceInfo ?: return emptyList() + val moduleInfo = getCapability(ModuleInfo.Capability) + if (moduleInfo is PlatformModuleInfo) { + return listOf(this) + } + val moduleSourceInfo = moduleInfo as? ModuleSourceInfo ?: return emptyList() val module = moduleSourceInfo.module return module.cached(CachedValueProvider { val implementingModuleInfos = module.findImplementingModuleInfos(moduleSourceInfo) @@ -57,7 +61,10 @@ val ModuleDescriptor.implementingDescriptors: List val ModuleDescriptor.implementedDescriptors: List get() { - val moduleSourceInfo = getCapability(ModuleInfo.Capability) as? ModuleSourceInfo ?: return emptyList() + val moduleInfo = getCapability(ModuleInfo.Capability) + if (moduleInfo is PlatformModuleInfo) return listOf(this) + + val moduleSourceInfo = moduleInfo as? ModuleSourceInfo ?: return emptyList() return moduleSourceInfo.expectedBy.mapNotNull { KotlinCacheService.getInstance(moduleSourceInfo.module.project) 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 76c93f8fa47..4287661d591 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 @@ -102,7 +102,7 @@ internal class ProjectResolutionFacade( globalContext ) - val allModuleInfos = (allModules ?: collectAllModuleInfosFromIdeaModel(project)).toMutableSet() + val allModuleInfos = (allModules ?: collectAllModuleInfosFromIdeaModel(project, settings.platform)).toMutableSet() val syntheticFilesByModule = syntheticFiles.groupBy(KtFile::getModuleInfo) val syntheticFilesModules = syntheticFilesByModule.keys diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/packageOracles.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/packageOracles.kt index 2b71c292f06..1bbc6a4bc8e 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/packageOracles.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/packageOracles.kt @@ -24,7 +24,7 @@ import org.jetbrains.kotlin.analyzer.PackageOracleFactory import org.jetbrains.kotlin.idea.caches.PerModulePackageCacheService import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo import org.jetbrains.kotlin.idea.caches.project.ModuleOrigin -import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo +import org.jetbrains.kotlin.idea.caches.project.projectSourceModules import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.isSubpackageOf import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade @@ -37,11 +37,11 @@ class IdePackageOracleFactory(val project: Project) : PackageOracleFactory { return when (moduleInfo.platform) { JvmPlatform -> when (moduleInfo.moduleOrigin) { ModuleOrigin.LIBRARY -> JavaPackagesOracle(moduleInfo, project) - ModuleOrigin.MODULE -> JvmSourceOracle(moduleInfo as ModuleSourceInfo, project) + ModuleOrigin.MODULE -> JvmSourceOracle(moduleInfo, project) ModuleOrigin.OTHER -> PackageOracle.Optimistic } else -> when (moduleInfo.moduleOrigin) { - ModuleOrigin.MODULE -> KotlinSourceFilesOracle(moduleInfo as ModuleSourceInfo) + ModuleOrigin.MODULE -> KotlinSourceFilesOracle(moduleInfo, project) else -> PackageOracle.Optimistic // binaries for non-jvm platform need some oracles based on their structure } } @@ -54,17 +54,18 @@ class IdePackageOracleFactory(val project: Project) : PackageOracleFactory { override fun packageExists(fqName: FqName) = facade.findPackage(fqName.asString(), scope) != null } - private class KotlinSourceFilesOracle(private val moduleInfo: ModuleSourceInfo) : PackageOracle { - private val cacheService = ServiceManager.getService(moduleInfo.module.project, PerModulePackageCacheService::class.java) + private class KotlinSourceFilesOracle(moduleInfo: IdeaModuleInfo, private val project: Project) : PackageOracle { + private val cacheService = ServiceManager.getService(project, PerModulePackageCacheService::class.java) + private val sourceModules = moduleInfo.projectSourceModules() override fun packageExists(fqName: FqName): Boolean { - return cacheService.packageExists(fqName, moduleInfo) + return sourceModules?.any { cacheService.packageExists(fqName, it) } ?: false } } - private class JvmSourceOracle(moduleInfo: ModuleSourceInfo, project: Project) : PackageOracle { + private class JvmSourceOracle(moduleInfo: IdeaModuleInfo, project: Project) : PackageOracle { private val javaPackagesOracle = JavaPackagesOracle(moduleInfo, project) - private val kotlinSourceOracle = KotlinSourceFilesOracle(moduleInfo) + private val kotlinSourceOracle = KotlinSourceFilesOracle(moduleInfo, project) override fun packageExists(fqName: FqName) = javaPackagesOracle.packageExists(fqName) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/resolve/PluginDeclarationProviderFactory.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/resolve/PluginDeclarationProviderFactory.kt index 0a574517824..8029dc6a470 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/resolve/PluginDeclarationProviderFactory.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/resolve/PluginDeclarationProviderFactory.kt @@ -23,7 +23,9 @@ import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.stubs.StubIndex import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.idea.caches.PerModulePackageCacheService +import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo +import org.jetbrains.kotlin.idea.caches.project.projectSourceModules import org.jetbrains.kotlin.idea.stubindex.KotlinExactPackagesIndex import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil import org.jetbrains.kotlin.idea.stubindex.SubpackagesIndexService @@ -62,8 +64,9 @@ class PluginDeclarationProviderFactory( private fun stubBasedPackageExists(name: FqName): Boolean { // We're only looking for source-based declarations - val moduleSourceInfo = moduleInfo as? ModuleSourceInfo ?: return false - return PerModulePackageCacheService.getInstance(project).packageExists(name, moduleInfo) + return (moduleInfo as? IdeaModuleInfo)?.projectSourceModules() + ?.any { PerModulePackageCacheService.getInstance(project).packageExists(name, it) } + ?: false } private fun getStubBasedPackageMemberDeclarationProvider(name: FqName): PackageMemberDeclarationProvider? { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/resolve/PluginDeclarationProviderFactoryService.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/resolve/PluginDeclarationProviderFactoryService.kt index 427717b60b0..2773ecda6d2 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/resolve/PluginDeclarationProviderFactoryService.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/resolve/PluginDeclarationProviderFactoryService.kt @@ -19,7 +19,8 @@ package org.jetbrains.kotlin.idea.stubindex.resolve import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.analyzer.ModuleInfo -import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo +import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo +import org.jetbrains.kotlin.idea.caches.project.ModuleOrigin import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory @@ -35,7 +36,7 @@ class PluginDeclarationProviderFactoryService : DeclarationProviderFactoryServic filesScope: GlobalSearchScope, moduleInfo: ModuleInfo ): DeclarationProviderFactory { - if (syntheticFiles.isEmpty() && moduleInfo !is ModuleSourceInfo) { + if (syntheticFiles.isEmpty() && (moduleInfo as IdeaModuleInfo).moduleOrigin != ModuleOrigin.MODULE) { // No actual source declarations for libraries // Even in case of libraries sources they should be obtained through the classpath with subsequent decompiling // Anyway, we'll filter them out with `KotlinSourceFilterScope.sources` call below diff --git a/idea/testData/multiModuleHighlighting/multiplatform/basic/jvm/jvm.kt b/idea/testData/multiModuleHighlighting/multiplatform/basic/jvm/jvm.kt index c0bf33e72cd..866dbd2173f 100644 --- a/idea/testData/multiModuleHighlighting/multiplatform/basic/jvm/jvm.kt +++ b/idea/testData/multiModuleHighlighting/multiplatform/basic/jvm/jvm.kt @@ -6,10 +6,11 @@ expect class Their { +actual class Their { }