Clean up API of AbstractResolverForProject

Resolution anchors are IDE-specific.
Creating dedicated methods for them in AbstractResolverForProject leads to meaningless
implementation in non-IDE resolvers. Now this method is implemented only
for IDE resolver and accessed through JvmPlatformParameters.

#KT-24309 In Progress
This commit is contained in:
Pavel Kirpichenkov
2020-06-04 13:47:08 +03:00
parent 80a310540e
commit 8ed4424e3e
5 changed files with 41 additions and 25 deletions
@@ -38,7 +38,9 @@ import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory
class JvmPlatformParameters(
val packagePartProviderFactory: (ModuleContent<*>) -> PackagePartProvider,
val moduleByJavaClass: (JavaClass) -> ModuleInfo?
val moduleByJavaClass: (JavaClass) -> ModuleInfo?,
// params: referenced module info of target class, context module info of current resolver
val resolverForReferencedModule: ((ModuleInfo, ModuleInfo) -> ResolverForModule?)? = null,
) : PlatformAnalysisParameters
@@ -63,14 +65,21 @@ class JvmResolverForModuleFactory(
)
val moduleClassResolver = ModuleClassResolverImpl { javaClass ->
val referencedClassModule = (platformParameters as JvmPlatformParameters).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
val referencedClassModule = platformParameters.moduleByJavaClass(javaClass)
// A type in a java library can reference a class declared in a source root (is valid but rare case).
// Resolving such a class with Kotlin resolver for libraries is guaranteed to fail, as libraries can't
// have dependencies on the source roots. The chain of resolvers (sources -> libraries -> sdk) exists to prevent
// potentially slow repetitive analysis of the same libraries after modifications in sources. The only way to mitigate
// this restriction currently is to manually configure resolution anchors for known source-dependent libraries in a project.
// See also KT-24309
@Suppress("UNCHECKED_CAST")
val resolverForReferencedModule = referencedClassModule?.let {
resolverForProject.tryGetResolverForModuleWithResolutionAnchorFallback(it as M, moduleInfo)
val resolverForReferencedModule = referencedClassModule?.let { referencedModuleInfo ->
if (platformParameters.resolverForReferencedModule != null) {
platformParameters.resolverForReferencedModule.invoke(referencedModuleInfo, moduleInfo)
} else {
resolverForProject.tryGetResolverForModule(referencedModuleInfo as M)
}
}
val resolverForModule = resolverForReferencedModule?.takeIf {
@@ -111,12 +120,12 @@ class JvmResolverForModuleFactory(
)
providersForModule +=
PackageFragmentProviderExtension.getInstances(project)
.mapNotNull {
it.getPackageFragmentProvider(
project, moduleDescriptor, moduleContext.storageManager, trace, moduleInfo, lookupTracker
)
}
PackageFragmentProviderExtension.getInstances(project)
.mapNotNull {
it.getPackageFragmentProvider(
project, moduleDescriptor, moduleContext.storageManager, trace, moduleInfo, lookupTracker
)
}
return ResolverForModule(CompositePackageFragmentProvider(providersForModule), container)
}
@@ -64,9 +64,6 @@ abstract class AbstractResolverForProject<M : ModuleInfo>(
return resolverForModuleDescriptor(doGetDescriptorForModule(moduleInfo))
}
override fun tryGetResolverForModuleWithResolutionAnchorFallback(targetModuleInfo: M, referencingModuleInfo: M): ResolverForModule? =
tryGetResolverForModule(targetModuleInfo)
private fun setupModuleDescriptor(module: M, moduleDescriptor: ModuleDescriptorImpl) {
moduleDescriptor.setDependencies(
LazyModuleDependencies(
@@ -43,7 +43,6 @@ class ResolverForModule(
abstract class ResolverForProject<M : ModuleInfo> {
fun resolverForModule(moduleInfo: M): ResolverForModule = resolverForModuleDescriptor(descriptorForModule(moduleInfo))
abstract fun tryGetResolverForModule(moduleInfo: M): ResolverForModule?
abstract fun tryGetResolverForModuleWithResolutionAnchorFallback(targetModuleInfo: M, referencingModuleInfo: M): ResolverForModule?
abstract fun descriptorForModule(moduleInfo: M): ModuleDescriptor
abstract fun moduleInfoForModuleDescriptor(moduleDescriptor: ModuleDescriptor): M
abstract fun resolverForModuleDescriptor(descriptor: ModuleDescriptor): ResolverForModule
@@ -76,9 +75,6 @@ class EmptyResolverForProject<M : ModuleInfo> : ResolverForProject<M>() {
override val allModules: Collection<M> = listOf()
override fun diagnoseUnknownModuleInfo(infos: List<ModuleInfo>) = throw IllegalStateException("Should not be called for $infos")
override fun tryGetResolverForModuleWithResolutionAnchorFallback(targetModuleInfo: M, referencingModuleInfo: M) =
diagnoseUnknownModuleInfo(listOf(targetModuleInfo))
override fun moduleInfoForModuleDescriptor(descriptor: ModuleDescriptor): M {
throw IllegalStateException("$descriptor is not contained in this resolver")
}
@@ -35,7 +35,6 @@ import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.ResolutionAnchorProvider
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class IdeaResolverForProject(
debugName: String,
@@ -96,6 +95,12 @@ class IdeaResolverForProject(
moduleByJavaClass = { javaClass: JavaClass ->
val psiClass = (javaClass as JavaClassImpl).psi
psiClass.getPlatformModuleInfo(JvmPlatforms.unspecifiedJvmPlatform)?.platformModule ?: psiClass.getNullableModuleInfo()
},
resolverForReferencedModule = { targetModuleInfo, referencingModuleInfo ->
require(targetModuleInfo is IdeaModuleInfo && referencingModuleInfo is IdeaModuleInfo) {
"Unexpected modules passed through JvmPlatformParameters to IDE resolver ($targetModuleInfo, $referencingModuleInfo)"
}
tryGetResolverForModuleWithResolutionAnchorFallback(targetModuleInfo, referencingModuleInfo)
}
)
@@ -150,7 +155,7 @@ class IdeaResolverForProject(
}
}
override fun tryGetResolverForModuleWithResolutionAnchorFallback(
private fun tryGetResolverForModuleWithResolutionAnchorFallback(
targetModuleInfo: IdeaModuleInfo,
referencingModuleInfo: IdeaModuleInfo,
): ResolverForModule? {
@@ -158,9 +163,9 @@ class IdeaResolverForProject(
return getResolverForProjectUsingResolutionAnchor(targetModuleInfo, referencingModuleInfo)
}
private fun getResolverForProjectUsingResolutionAnchor(
targetModuleInfo: IdeaModuleInfo,
targetModuleInfo: IdeaModuleInfo,
referencingModuleInfo: IdeaModuleInfo
): ResolverForModule? {
val moduleDescriptorOfReferencingModule = descriptorByModule[referencingModuleInfo]?.moduleDescriptor
@@ -173,7 +178,9 @@ class IdeaResolverForProject(
?.getResolverForProject()
?: return null
if (resolverForProjectFromAnchorModule !is IdeaResolverForProject) return null
require(resolverForProjectFromAnchorModule is IdeaResolverForProject) {
"Resolution via anchor modules is expected to be used only from IDE resolvers"
}
return resolverForProjectFromAnchorModule.tryGetResolverForModule(targetModuleInfo)
}
@@ -21,6 +21,13 @@ import org.jetbrains.kotlin.idea.project.libraryToSourceAnalysisEnabled
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.resolve.ResolutionAnchorProvider
/**
* This component provides capabilities for correct highlighting for projects with source-dependent libraries.
* The issue with this kind of libraries is that their declarations are resolved by ResolverForProject
* that have no access to project sources by itself. The necessary path back to project sources can be provided
* manually for the libraries in project via resolution anchors. Anchor by itself is a source module which is mapped
* to a library and used during resolution as a fallback.
*/
@State(name = "KotlinIdeAnchorService", storages = [Storage("anchors.xml")])
class KotlinIdeResolutionAnchorService(
val project: Project