From 5892bdf3f49704ed0d97b32425b4e2f6c014a0c9 Mon Sep 17 00:00:00 2001 From: Pavel Kirpichenkov Date: Mon, 15 Jun 2020 15:36:53 +0300 Subject: [PATCH] Extend import resolution for library-to-source analysis Use composite importing scope for references when resolution anchors are enabled. Composite scope provides additional descriptors from scope of resolution anchor module. Overriding old importing scope with a new one is not possible as it breaks library dependencies on other libraries, which are inaccessible in anchor scope (scope for sources). KT-24309 In Progress --- .../jetbrains/kotlin/config/AnalysisFlags.kt | 3 ++ .../kotlin/config/CommonConfigurationKeys.kt | 3 ++ .../resolve/QualifiedExpressionResolver.kt | 27 ++++++++++ .../jetbrains/kotlin/resolve/scopes/Scopes.kt | 49 +++++++++++++++++++ .../kotlin/descriptors/findClassInModule.kt | 3 +- .../resolve/ResolutionAnchorProvider.kt | 3 ++ .../KotlinIdeResolutionAnchorService.kt | 5 +- .../jetbrains/kotlin/idea/project/Platform.kt | 3 ++ 8 files changed, 94 insertions(+), 2 deletions(-) diff --git a/compiler/config/src/org/jetbrains/kotlin/config/AnalysisFlags.kt b/compiler/config/src/org/jetbrains/kotlin/config/AnalysisFlags.kt index 46a5700bc05..123e5bad772 100644 --- a/compiler/config/src/org/jetbrains/kotlin/config/AnalysisFlags.kt +++ b/compiler/config/src/org/jetbrains/kotlin/config/AnalysisFlags.kt @@ -47,4 +47,7 @@ object AnalysisFlags { @JvmStatic val reportErrorsOnIrDependencies by AnalysisFlag.Delegates.Boolean + + @JvmStatic + val libraryToSourceAnalysis by AnalysisFlag.Delegates.Boolean } diff --git a/compiler/config/src/org/jetbrains/kotlin/config/CommonConfigurationKeys.kt b/compiler/config/src/org/jetbrains/kotlin/config/CommonConfigurationKeys.kt index ff209598803..f407fd00e9b 100644 --- a/compiler/config/src/org/jetbrains/kotlin/config/CommonConfigurationKeys.kt +++ b/compiler/config/src/org/jetbrains/kotlin/config/CommonConfigurationKeys.kt @@ -61,3 +61,6 @@ var CompilerConfiguration.languageVersionSettings: LanguageVersionSettings val LanguageVersionSettings.isTypeRefinementEnabled: Boolean get() = getFlag(AnalysisFlags.useTypeRefinement) + +val LanguageVersionSettings.isLibraryToSourceAnalysisEnabled: Boolean + get() = getFlag(AnalysisFlags.libraryToSourceAnalysis) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolver.kt index a80287751e7..e30e64f6730 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolver.kt @@ -20,6 +20,7 @@ import com.intellij.psi.impl.source.DummyHolder import com.intellij.util.SmartList import org.jetbrains.kotlin.config.AnalysisFlags import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.config.isLibraryToSourceAnalysisEnabled import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.incremental.KotlinLookupLocation @@ -35,6 +36,7 @@ import org.jetbrains.kotlin.resolve.calls.CallExpressionElement import org.jetbrains.kotlin.resolve.calls.checkers.UnderscoreUsageChecker import org.jetbrains.kotlin.resolve.calls.unrollToLeftMostQualifiedExpression import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.scopes.CompositePrioritizedImportingScope import org.jetbrains.kotlin.resolve.scopes.ImportingScope import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.receivers.* @@ -221,6 +223,31 @@ class QualifiedExpressionResolver(val languageVersionSettings: LanguageVersionSe trace: BindingTrace, excludedImportNames: Collection, packageFragmentForVisibilityCheck: PackageFragmentDescriptor? + ): ImportingScope? { + fun processReferenceInContextOf(moduleDescriptor: ModuleDescriptor): ImportingScope? = + doProcessImportReference( + importDirective, + moduleDescriptor, + trace, + excludedImportNames, + packageFragmentForVisibilityCheck + ) + + val primaryImportingScope = processReferenceInContextOf(moduleDescriptor) + if (!languageVersionSettings.isLibraryToSourceAnalysisEnabled) return primaryImportingScope + + val resolutionAnchor = moduleDescriptor.getResolutionAnchorIfAny() ?: return primaryImportingScope + val secondaryImportingScope = processReferenceInContextOf(resolutionAnchor) ?: return primaryImportingScope + if (primaryImportingScope == null) return secondaryImportingScope + return CompositePrioritizedImportingScope(primaryImportingScope, secondaryImportingScope) + } + + private fun doProcessImportReference( + importDirective: KtImportInfo, + moduleDescriptor: ModuleDescriptor, + trace: BindingTrace, + excludedImportNames: Collection, + packageFragmentForVisibilityCheck: PackageFragmentDescriptor? ): ImportingScope? { // null if some error happened ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/Scopes.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/Scopes.kt index 12453861863..1fedfcfce58 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/Scopes.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/Scopes.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.utils.Printer +import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty // see utils/ScopeUtils.kt @@ -169,3 +170,51 @@ abstract class BaseImportingScope(parent: ImportingScope?) : BaseHierarchicalSco changeNamesForAliased: Boolean ): Collection = emptyList() } + +class CompositePrioritizedImportingScope( + private val primaryScope: ImportingScope, + private val secondaryScope: ImportingScope, +) : ImportingScope { + override val parent: ImportingScope? + get() = primaryScope.parent ?: secondaryScope.parent + + override fun getContributedPackage(name: Name): PackageViewDescriptor? { + return primaryScope.getContributedPackage(name) ?: secondaryScope.getContributedPackage(name) + } + + override fun getContributedDescriptors( + kindFilter: DescriptorKindFilter, + nameFilter: (Name) -> Boolean, + changeNamesForAliased: Boolean + ): Collection { + return primaryScope.getContributedDescriptors(kindFilter, nameFilter, changeNamesForAliased) + .ifEmpty { secondaryScope.getContributedDescriptors(kindFilter, nameFilter, changeNamesForAliased) } + } + + override fun computeImportedNames(): Set? { + val primaryNames = primaryScope.computeImportedNames() + val secondaryNames = secondaryScope.computeImportedNames() + return primaryNames?.union(secondaryNames.orEmpty()) ?: secondaryNames + } + + override fun printStructure(p: Printer) { + p.println(primaryScope::class.java.simpleName) + } + + override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? { + return primaryScope.getContributedClassifier(name, location) ?: secondaryScope.getContributedClassifier(name, location) + } + + override fun getContributedVariables(name: Name, location: LookupLocation): Collection { + return primaryScope.getContributedVariables(name, location).ifEmpty { + secondaryScope.getContributedVariables(name, location) + } + } + + override fun getContributedFunctions(name: Name, location: LookupLocation): Collection { + return primaryScope.getContributedFunctions(name, location).ifEmpty { + secondaryScope.getContributedFunctions(name, location) + } + } + +} \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/findClassInModule.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/findClassInModule.kt index 346b949075a..4dd0e293f09 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/findClassInModule.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/findClassInModule.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.descriptors import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.resolve.RESOLUTION_ANCHOR_PROVIDER_CAPABILITY +import org.jetbrains.kotlin.resolve.getResolutionAnchorIfAny fun ModuleDescriptor.findClassifierAcrossModuleDependencies(classId: ClassId): ClassifierDescriptor? = withAnchorFallback { val packageViewDescriptor = getPackage(classId.packageFqName) @@ -40,7 +41,7 @@ fun ModuleDescriptor.findClassifierAcrossModuleDependencies(classId: ClassId): C private inline fun ModuleDescriptor.withAnchorFallback( crossinline doSearch: ModuleDescriptor.() -> ClassifierDescriptor? ): ClassifierDescriptor? { - val anchor = getCapability(RESOLUTION_ANCHOR_PROVIDER_CAPABILITY)?.getResolutionAnchor(this) + val anchor = getResolutionAnchorIfAny() return if (anchor == null) doSearch() else doSearch() ?: anchor.doSearch() } diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/ResolutionAnchorProvider.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/ResolutionAnchorProvider.kt index 4b57e952478..0d628410af8 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/ResolutionAnchorProvider.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/ResolutionAnchorProvider.kt @@ -19,3 +19,6 @@ interface ResolutionAnchorProvider { } val RESOLUTION_ANCHOR_PROVIDER_CAPABILITY = ModuleCapability("ResolutionAnchorProvider") + +fun ModuleDescriptor.getResolutionAnchorIfAny(): ModuleDescriptor? = + getCapability(RESOLUTION_ANCHOR_PROVIDER_CAPABILITY)?.getResolutionAnchor(this) 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 2038be50237..a3f359c5f96 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 @@ -79,8 +79,11 @@ class KotlinIdeResolutionAnchorService( override fun getResolutionAnchor(moduleDescriptor: ModuleDescriptor): ModuleDescriptor? { if (!project.libraryToSourceAnalysisEnabled) return null + val moduleInfo = moduleDescriptor.moduleInfo ?: return null - val mapped = moduleToAnchor[moduleInfo] ?: return null + val keyModuleInfo = if (moduleInfo is SourceForBinaryModuleInfo) moduleInfo.binariesModuleInfo else moduleInfo + val mapped = moduleToAnchor[keyModuleInfo] ?: return null + return KotlinCacheService.getInstance(project) .getResolutionFacadeByModuleInfo(mapped, mapped.platform) ?.moduleDescriptor diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt index ef3ad3fb421..4aef8ab4f40 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt @@ -250,6 +250,9 @@ private fun MutableMap, Any>.initIDESpecificAnalysisSettings(pro if (KotlinMultiplatformAnalysisModeComponent.getMode(project) == KotlinMultiplatformAnalysisModeComponent.Mode.COMPOSITE) { put(AnalysisFlags.useTypeRefinement, true) } + if (KotlinLibraryToSourceAnalysisComponent.isEnabled(project)) { + put(AnalysisFlags.libraryToSourceAnalysis, true) + } put(AnalysisFlags.ideMode, true) }