Introduce AbstractResolverForProject

Previously, ResolverForProjectImpl had multiple callbacks in
constructor. Some of those callbacks were used only to overcome module
visibility and provide an ability to inject IDE-specific logic into
compiler (ResolverForProject is in the 'compiler'-module)

This commit introduces abstract class which implements
environment-independent logic (previously, this logic had been stored in
ResolverForProjectImpl) with several abstract met hods (previously,
callbacks). Then, we provide few concrete implementations of
AbstractResolverForProject with clear semantics:

- IdeaResolverForProject: resolver used in IDE, where we have indices,
  oracles, multiple modules, etc.
- ResolverForSingleModuleProject: resolver for project with only one
  module, commonly used for CLI compiler/tests
- one anonymous implementation for MultimoduleTests

This refactoring achieves several things:
- now it is easier to see what kinds of ResolverForProject you might see
  in some particular environment (previously, one had to inspect all
  call-sites of constructor)
- we can easily add IDE-specific logic in IdeaResolverForProject without
  adding noisy callbacks (which most probably wouldn't have any other
  non-trivial implementations)
This commit is contained in:
Dmitry Savvinov
2019-09-23 18:44:58 +03:00
parent 48719c4050
commit bfacc1a3c5
8 changed files with 253 additions and 162 deletions
@@ -0,0 +1,124 @@
/*
* Copyright 2010-2019 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.idea.caches.resolve
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.util.ModificationTracker
import org.jetbrains.kotlin.analyzer.*
import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
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.ProjectContext
import org.jetbrains.kotlin.context.withModule
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.caches.project.findSdkAcrossDependencies
import org.jetbrains.kotlin.idea.caches.project.getNullableModuleInfo
import org.jetbrains.kotlin.idea.caches.project.getPlatformModuleInfo
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
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.platform.toTargetPlatform
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
class IdeaResolverForProject(
debugName: String,
projectContext: ProjectContext,
private val modules: Collection<IdeaModuleInfo>,
private val syntheticFilesByModule: Map<IdeaModuleInfo, Collection<KtFile>>,
delegateResolver: ResolverForProject<IdeaModuleInfo>,
private val builtInsCache: BuiltInsCache,
fallbackModificationTracker: ModificationTracker? = null,
private val isReleaseCoroutines: Boolean? = null,
// TODO(dsavvinov): this is needed only for non-composite analysis, extract separate resolver implementation instead
private val constantSdkDependencyIfAny: IdeaModuleInfo? = null
) : AbstractResolverForProject<IdeaModuleInfo>(
debugName,
projectContext,
modules,
fallbackModificationTracker,
delegateResolver,
ServiceManager.getService(projectContext.project, IdePackageOracleFactory::class.java)
) {
override fun sdkDependency(module: IdeaModuleInfo): IdeaModuleInfo? {
if (projectContext.project.useCompositeAnalysis) {
require(constantSdkDependencyIfAny == null) { "Shouldn't pass SDK dependency manually for composite analysis mode" }
}
return constantSdkDependencyIfAny ?: module.findSdkAcrossDependencies()
}
override fun modulesContent(module: IdeaModuleInfo): ModuleContent<IdeaModuleInfo> =
ModuleContent(module, syntheticFilesByModule[module] ?: emptyList(), module.contentScope())
override fun builtInsForModule(module: IdeaModuleInfo): KotlinBuiltIns {
require(module in modules)
val key = module.platform.idePlatformKind.resolution.getKeyForBuiltIns(module)
return builtInsCache.getOrPut(key) { throw IllegalStateException("Can't find builtIns by key $key for module $module") }
}
override fun createResolverForModule(descriptor: ModuleDescriptor, moduleInfo: IdeaModuleInfo): ResolverForModule {
val moduleContent = ModuleContent(moduleInfo, syntheticFilesByModule[moduleInfo] ?: listOf(), moduleInfo.contentScope())
val languageVersionSettings =
IDELanguageSettingsProvider.getLanguageVersionSettings(moduleInfo, projectContext.project, isReleaseCoroutines)
val resolverForModuleFactory = getResolverForModuleFactory(moduleInfo)
return resolverForModuleFactory.createResolverForModule(
descriptor as ModuleDescriptorImpl,
projectContext.withModule(descriptor),
moduleContent,
this,
languageVersionSettings
)
}
private fun getResolverForModuleFactory(moduleInfo: IdeaModuleInfo): ResolverForModuleFactory {
val platform = moduleInfo.platform
val jvmPlatformParameters = JvmPlatformParameters(
packagePartProviderFactory = { IDEPackagePartProvider(it.moduleContentScope) },
moduleByJavaClass = { javaClass: JavaClass ->
val psiClass = (javaClass as JavaClassImpl).psi
psiClass.getPlatformModuleInfo(JvmPlatforms.unspecifiedJvmPlatform)?.platformModule ?: psiClass.getNullableModuleInfo()
}
)
val commonPlatformParameters = CommonAnalysisParameters(
metadataPartProviderFactory = { IDEPackagePartProvider(it.moduleContentScope) }
)
return if (!projectContext.project.useCompositeAnalysis) {
val parameters = when {
platform.isJvm() -> jvmPlatformParameters
platform.isCommon() -> commonPlatformParameters
else -> PlatformAnalysisParameters.Empty
}
platform.idePlatformKind.resolution.createResolverForModuleFactory(parameters, IdeaEnvironment, platform)
} else {
CompositeResolverForModuleFactory(
commonPlatformParameters,
jvmPlatformParameters,
platform,
CompositeAnalyzerServices(platform.componentPlatforms.map { it.toTargetPlatform().findAnalyzerServices })
)
}
}
}
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.util.CachedValueProvider
@@ -24,36 +23,18 @@ import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.util.containers.SLRUCache
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
import org.jetbrains.kotlin.context.withProject
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.caches.project.*
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
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
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,
@@ -101,7 +82,6 @@ internal class ProjectResolutionFacade(
private fun computeModuleResolverProvider(): ResolverForProject<IdeaModuleInfo> {
val delegateResolverForProject: ResolverForProject<IdeaModuleInfo>
val delegateBuiltIns: KotlinBuiltIns?
if (reuseDataFrom != null) {
delegateResolverForProject = reuseDataFrom.cachedResolverForProject
@@ -119,61 +99,16 @@ internal class ProjectResolutionFacade(
val modulesToCreateResolversFor = allModuleInfos.filter(moduleFilter)
val modulesContentFactory = { module: IdeaModuleInfo ->
ModuleContent(module, syntheticFilesByModule[module] ?: listOf(), module.contentScope())
}
val jvmPlatformParameters = JvmPlatformParameters(
packagePartProviderFactory = { IDEPackagePartProvider(it.moduleContentScope) },
moduleByJavaClass = { javaClass: JavaClass ->
val psiClass = (javaClass as JavaClassImpl).psi
psiClass.getPlatformModuleInfo(JvmPlatforms.unspecifiedJvmPlatform)?.platformModule ?: psiClass.getNullableModuleInfo()
}
)
val commonPlatformParameters = CommonAnalysisParameters(
metadataPartProviderFactory = { IDEPackagePartProvider(it.moduleContentScope) }
)
val resolverForProject = ResolverForProjectImpl(
val resolverForProject = IdeaResolverForProject(
resolverDebugName,
projectContext,
modulesToCreateResolversFor,
modulesContentFactory,
moduleLanguageSettingsProvider = IDELanguageSettingsProvider,
resolverForModuleFactoryByPlatform = { modulePlatform ->
val platform = modulePlatform ?: DefaultIdeTargetPlatformKindProvider.defaultPlatform
val parameters = when {
platform.isJvm() -> jvmPlatformParameters
platform.isCommon() -> commonPlatformParameters
else -> PlatformAnalysisParameters.Empty
}
if (!project.useCompositeAnalysis)
platform.idePlatformKind.resolution.createResolverForModuleFactory(parameters, IdeaEnvironment, platform)
else
CompositeResolverForModuleFactory(
commonPlatformParameters,
jvmPlatformParameters,
modulePlatform!!,
CompositeAnalyzerServices(modulePlatform.componentPlatforms.map { it.toTargetPlatform().findAnalyzerServices })
)
},
builtInsProvider = { module ->
require(module in modulesToCreateResolversFor)
val key = module.platform.idePlatformKind.resolution.getKeyForBuiltIns(module)
builtInsCache.getOrPut(key) { throw IllegalStateException("Can't find builtIns by key $key for module $module") }
},
delegateResolver = delegateResolverForProject,
sdkDependency = { module ->
if (settings is PlatformAnalysisSettingsImpl)
settings.sdk?.let { SdkInfo(project, it) }
else
module.findSdkAcrossDependencies()
},
packageOracleFactory = ServiceManager.getService(project, IdePackageOracleFactory::class.java),
invalidateOnOOCB = invalidateOnOOCB,
isReleaseCoroutines = settings.isReleaseCoroutines
syntheticFilesByModule,
delegateResolverForProject,
builtInsCache,
if (invalidateOnOOCB) KotlinModificationTrackerService.getInstance(project).outOfBlockModificationTracker else null,
settings.isReleaseCoroutines,
constantSdkDependencyIfAny = if (settings is PlatformAnalysisSettingsImpl) settings.sdk?.let { SdkInfo(project, it) } else null
)
// Fill builtInsCache