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
This commit is contained in:
Pavel V. Talanov
2018-04-11 17:42:58 +02:00
parent 94dfcba5c5
commit 94fe170b7b
9 changed files with 109 additions and 33 deletions
@@ -116,7 +116,12 @@ class ResolverForProjectImpl<M : ModuleInfo>(
// Protected by ("projectContext.storageManager.lock")
private val moduleInfoByDescriptor = mutableMapOf<ModuleDescriptorImpl, M>()
val modules = modules.toSet()
private val moduleInfoToResolvableInfo: Map<M, M> =
modules.flatMap { module -> module.flatten().map { modulePart -> modulePart to module } }.toMap() as Map<M, M>
init {
assert(moduleInfoToResolvableInfo.values.toSet() == modules.toSet())
}
override fun tryGetResolverForModule(moduleInfo: M): ResolverForModule? {
if (!isCorrectModuleInfo(moduleInfo)) {
@@ -148,7 +153,7 @@ class ResolverForProjectImpl<M : ModuleInfo>(
private val resolverByModuleDescriptor = mutableMapOf<ModuleDescriptor, ResolverForModule>()
override val allModules: Collection<M> by lazy {
this.modules + delegateResolver.allModules
this.moduleInfoToResolvableInfo.keys + delegateResolver.allModules
}
override val name: String
@@ -212,19 +217,18 @@ class ResolverForProjectImpl<M : ModuleInfo>(
}
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<M : ModuleInfo>(
}
}
data class ModuleContent<out M: ModuleInfo>(
data class ModuleContent<out M : ModuleInfo>(
val moduleInfo: M,
val syntheticFiles: Collection<KtFile>,
val moduleContentScope: GlobalSearchScope
@@ -290,6 +294,15 @@ interface ModuleInfo {
}
}
interface CombinedModuleInfo : ModuleInfo {
val containedModules: List<ModuleInfo>
}
fun ModuleInfo.flatten(): List<ModuleInfo> = when (this) {
is CombinedModuleInfo -> listOf(this) + containedModules
else -> listOf(this)
}
interface TrackableModuleInfo : ModuleInfo {
fun createModificationTracker(): ModificationTracker
}
@@ -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<ModuleSourceInfo>
) : IdeaModuleInfo, CombinedModuleInfo, TrackableModuleInfo {
override val capabilities: Map<ModuleDescriptor.Capability<*>, Any?>
get() = platformModule.capabilities
override fun contentScope() = GlobalSearchScope.union(containedModules.map { it.contentScope() }.toTypedArray())
override val containedModules: List<ModuleSourceInfo> = 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("<Platform module ${platformModule.displayedName} including ${commonModules.map { it.displayedName }}>")
override fun createModificationTracker() = platformModule.createModificationTracker()
}
fun IdeaModuleInfo.projectSourceModules(): List<ModuleSourceInfo>? =
(this as? ModuleSourceInfo)?.let(::listOf) ?: (this as? PlatformModuleInfo)?.containedModules
@@ -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<IdeaModuleInfo> {
fun collectAllModuleInfosFromIdeaModel(project: Project, platform: TargetPlatform): List<IdeaModuleInfo> {
val ideaModules = ModuleManager.getInstance(project).modules.toList()
val modulesSourcesInfos = ideaModules.flatMap(Module::correspondingModuleInfos)
@@ -87,9 +88,29 @@ fun collectAllModuleInfosFromIdeaModel(project: Project): List<IdeaModuleInfo> {
)
}
return modulesSourcesInfos + librariesInfos + sdksInfos
return mergePlatformModules(modulesSourcesInfos, platform) + librariesInfos + sdksInfos
}
private fun mergePlatformModules(
allModules: List<IdeaModuleInfo>,
platform: TargetPlatform
): List<IdeaModuleInfo> {
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)
@@ -40,7 +40,11 @@ private fun Module.findImplementingModuleInfos(moduleSourceInfo: ModuleSourceInf
val ModuleDescriptor.implementingDescriptors: List<ModuleDescriptor>
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<ModuleDescriptor>
val ModuleDescriptor.implementedDescriptors: List<ModuleDescriptor>
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)
@@ -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
@@ -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)
@@ -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? {
@@ -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
@@ -6,10 +6,11 @@ expect class <error descr="[NO_ACTUAL_FOR_EXPECT] Expected class 'His' has no ac
}
// NOTE: can declare expect and actual in platform module
expect class Their {
}
actual class <error descr="[ACTUAL_WITHOUT_EXPECT] Actual class 'Their' has no corresponding expected declaration">Their</error> {
actual class Their {
}