diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmResolverForModuleFactory.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmResolverForModuleFactory.kt index 467360d6c28..7afacd0d561 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmResolverForModuleFactory.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmResolverForModuleFactory.kt @@ -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) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AbstractResolverForProject.kt b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AbstractResolverForProject.kt index 7e2372df907..41cf335de7e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AbstractResolverForProject.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AbstractResolverForProject.kt @@ -64,9 +64,6 @@ abstract class AbstractResolverForProject( return resolverForModuleDescriptor(doGetDescriptorForModule(moduleInfo)) } - override fun tryGetResolverForModuleWithResolutionAnchorFallback(targetModuleInfo: M, referencingModuleInfo: M): ResolverForModule? = - tryGetResolverForModule(targetModuleInfo) - private fun setupModuleDescriptor(module: M, moduleDescriptor: ModuleDescriptorImpl) { moduleDescriptor.setDependencies( LazyModuleDependencies( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt index d91a7905e31..ea41e2b9ba2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt @@ -43,7 +43,6 @@ class ResolverForModule( abstract class ResolverForProject { 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 : ResolverForProject() { override val allModules: Collection = listOf() override fun diagnoseUnknownModuleInfo(infos: List) = 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") } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaResolverForProject.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaResolverForProject.kt index fccaf5fcf97..6018b0533c4 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaResolverForProject.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaResolverForProject.kt @@ -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) } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinIdeResolutionAnchorService.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinIdeResolutionAnchorService.kt index 16d08170d4f..2038be50237 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinIdeResolutionAnchorService.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinIdeResolutionAnchorService.kt @@ -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