From db1210fc671a14b3c10ae9e7b2a79f7b89e1fa6f Mon Sep 17 00:00:00 2001 From: Pavel Kirpichenkov Date: Thu, 23 Apr 2020 14:23:57 +0300 Subject: [PATCH] Introduce components for library-to-source resolution in IDE #KT-24309 In progress --- .../fir/descriptors/FirModuleDescriptor.kt | 4 + .../jvm/JvmResolverForModuleFactory.kt | 5 +- .../analyzer/AbstractResolverForProject.kt | 26 +++++- .../kotlin/analyzer/AnalyzerFacade.kt | 9 ++ .../kotlin/analyzer/ModuleResolverTracker.kt | 28 ++++++ .../kotlin/descriptors/ModuleDescriptor.kt | 3 + .../kotlin/descriptors/findClassInModule.kt | 17 ++-- .../descriptors/impl/ModuleDescriptorImpl.kt | 4 +- .../kotlin/resolve/AnchorProvider.kt | 14 +++ .../jetbrains/kotlin/types/ErrorUtils.java | 7 ++ .../resolve/IdeaModuleResolverTrackerImpl.kt | 38 +++++++++ .../caches/resolve/IdeaResolverForProject.kt | 34 +++++++- .../caches/resolve/KotlinIdeAnchorService.kt | 85 +++++++++++++++++++ .../caches/resolve/util/globalContextUtils.kt | 4 +- ...nchorSupportForLibraryAnalysisComponent.kt | 25 ++++++ .../DebugLabelPropertyDescriptorProvider.kt | 3 + .../messages/KotlinBundle.properties | 3 + idea/resources/META-INF/plugin-common.xml | 8 ++ ...orSupportForLibraryAnalysisToggleAction.kt | 28 ++++++ 19 files changed, 332 insertions(+), 13 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/analyzer/ModuleResolverTracker.kt create mode 100644 core/descriptors/src/org/jetbrains/kotlin/resolve/AnchorProvider.kt create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleResolverTrackerImpl.kt create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinIdeAnchorService.kt create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/KotlinAnchorSupportForLibraryAnalysisComponent.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/actions/internal/AnchorSupportForLibraryAnalysisToggleAction.kt diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/descriptors/FirModuleDescriptor.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/descriptors/FirModuleDescriptor.kt index ed9bc9d8ccf..c95f4a591ca 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/descriptors/FirModuleDescriptor.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/descriptors/FirModuleDescriptor.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.resolve.firSymbolProvider import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.resolve.AnchorProvider class FirModuleDescriptor(val session: FirSession) : ModuleDescriptor { override val builtIns: KotlinBuiltIns @@ -57,6 +58,9 @@ class FirModuleDescriptor(val session: FirSession) : ModuleDescriptor { } + override val anchorProvider: AnchorProvider + get() = AnchorProvider.Default + override fun getOriginal(): DeclarationDescriptor { return this } 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 38a9df196bb..2fb9884bb6a 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 @@ -29,7 +29,6 @@ import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolverImpl import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.load.kotlin.PackagePartProvider import org.jetbrains.kotlin.platform.jvm.isJvm -import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.resolve.TargetEnvironment @@ -70,7 +69,9 @@ class JvmResolverForModuleFactory( // Providing a fallback strategy in this case can hide future problems, so we should at least log to be able to diagnose those @Suppress("UNCHECKED_CAST") - val resolverForReferencedModule = referencedClassModule?.let { resolverForProject.tryGetResolverForModule(it as M) } + val resolverForReferencedModule = referencedClassModule?.let { + resolverForProject.tryGetResolverForModuleWithAnchorCheck(it as M, moduleInfo) + } val resolverForModule = resolverForReferencedModule?.takeIf { referencedClassModule.platform.isJvm() || referencedClassModule.platform == null diff --git a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AbstractResolverForProject.kt b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AbstractResolverForProject.kt index e79aaf675e7..945ee304a72 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AbstractResolverForProject.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AbstractResolverForProject.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.descriptors.PackageFragmentProvider import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.AnchorProvider abstract class AbstractResolverForProject( private val debugName: String, @@ -21,7 +22,8 @@ abstract class AbstractResolverForProject( modules: Collection, protected val fallbackModificationTracker: ModificationTracker? = null, private val delegateResolver: ResolverForProject = EmptyResolverForProject(), - private val packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory + private val packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory, + protected val anchorProvider: AnchorProvider = AnchorProvider.Default, ) : ResolverForProject() { protected class ModuleData( @@ -54,6 +56,13 @@ abstract class AbstractResolverForProject( abstract fun modulesContent(module: M): ModuleContent abstract fun builtInsForModule(module: M): KotlinBuiltIns abstract fun createResolverForModule(descriptor: ModuleDescriptor, moduleInfo: M): ResolverForModule + + protected open fun registerModuleDescriptorUpdate( + newDescriptor: ModuleDescriptor, + oldDescriptor: ModuleDescriptor?, + ) { + // Nothing by default + } override fun tryGetResolverForModule(moduleInfo: M): ResolverForModule? { if (!isCorrectModuleInfo(moduleInfo)) { @@ -62,6 +71,9 @@ abstract class AbstractResolverForProject( return resolverForModuleDescriptor(doGetDescriptorForModule(moduleInfo)) } + override fun tryGetResolverForModuleWithAnchorCheck(targetModuleInfo: M, referencingModuleInfo: M): ResolverForModule? = + tryGetResolverForModule(targetModuleInfo) + private fun setupModuleDescriptor(module: M, moduleDescriptor: ModuleDescriptorImpl) { moduleDescriptor.setDependencies( LazyModuleDependencies( @@ -122,6 +134,10 @@ abstract class AbstractResolverForProject( return doGetDescriptorForModule(moduleInfo) } + override fun moduleInfoForModuleDescriptor(moduleDescriptor: ModuleDescriptor): M { + return moduleInfoByDescriptor[moduleDescriptor] ?: delegateResolver.moduleInfoForModuleDescriptor(moduleDescriptor) + } + override fun diagnoseUnknownModuleInfo(infos: List): Nothing { DiagnoseUnknownModuleInfoReporter.report(name, infos, allModules) } @@ -142,6 +158,8 @@ abstract class AbstractResolverForProject( } if (moduleData.isOutOfDate()) { moduleData = recreateModuleDescriptor(moduleFromThisResolver) + } else { + registerModuleDescriptorUpdate(moduleData.moduleDescriptor, null) } moduleData.moduleDescriptor } @@ -158,6 +176,9 @@ abstract class AbstractResolverForProject( val moduleData = createModuleDescriptor(module) descriptorByModule[module] = moduleData + + registerModuleDescriptorUpdate(moduleData.moduleDescriptor, oldDescriptor) + return moduleData } @@ -168,7 +189,8 @@ abstract class AbstractResolverForProject( builtInsForModule(module), module.platform, module.capabilities, - module.stableName + module.stableName, + anchorProvider ) moduleInfoByDescriptor[moduleDescriptor] = module setupModuleDescriptor(module, moduleDescriptor) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt index e103f893e70..ed056b26f5a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt @@ -43,7 +43,9 @@ class ResolverForModule( abstract class ResolverForProject { fun resolverForModule(moduleInfo: M): ResolverForModule = resolverForModuleDescriptor(descriptorForModule(moduleInfo)) abstract fun tryGetResolverForModule(moduleInfo: M): ResolverForModule? + abstract fun tryGetResolverForModuleWithAnchorCheck(targetModuleInfo: M, referencingModuleInfo: M): ResolverForModule? abstract fun descriptorForModule(moduleInfo: M): ModuleDescriptor + abstract fun moduleInfoForModuleDescriptor(moduleDescriptor: ModuleDescriptor): M abstract fun resolverForModuleDescriptor(descriptor: ModuleDescriptor): ResolverForModule abstract fun diagnoseUnknownModuleInfo(infos: List): Nothing @@ -73,6 +75,13 @@ class EmptyResolverForProject : ResolverForProject() { override fun descriptorForModule(moduleInfo: M) = diagnoseUnknownModuleInfo(listOf(moduleInfo)) override val allModules: Collection = listOf() override fun diagnoseUnknownModuleInfo(infos: List) = throw IllegalStateException("Should not be called for $infos") + + override fun tryGetResolverForModuleWithAnchorCheck(targetModuleInfo: M, referencingModuleInfo: M) = + diagnoseUnknownModuleInfo(listOf(targetModuleInfo)) + + override fun moduleInfoForModuleDescriptor(descriptor: ModuleDescriptor): M { + throw IllegalStateException("$descriptor is not contained in this resolver") + } } data class ModuleContent( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/ModuleResolverTracker.kt b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/ModuleResolverTracker.kt new file mode 100644 index 00000000000..a8d94bc6822 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/ModuleResolverTracker.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2020 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.analyzer + +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.project.Project + +open class ModuleResolverTracker { + open fun registerModuleUpdate( + newModule: ModuleDescriptor, + oldModule: ModuleDescriptor?, + resolverForProject: ResolverForProject + ) { + } + + open fun findResolverForProjectByModuleDescriptor(moduleDescriptor: ModuleDescriptor): ResolverForProject? = null + + companion object { + val Default = ModuleResolverTracker() + + fun getInstance(project: Project): ModuleResolverTracker<*> = + ServiceManager.getService(project, ModuleResolverTracker::class.java) ?: Default + } +} \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/ModuleDescriptor.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/ModuleDescriptor.kt index 5df8bf5c95f..6d96831bc1a 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/ModuleDescriptor.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/ModuleDescriptor.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.resolve.AnchorProvider interface ModuleDescriptor : DeclarationDescriptor { override fun getContainingDeclaration(): DeclarationDescriptor? = null @@ -61,4 +62,6 @@ interface ModuleDescriptor : DeclarationDescriptor { val isValid: Boolean fun assertValid() + + val anchorProvider: AnchorProvider } diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/findClassInModule.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/findClassInModule.kt index ee39a8cc374..21b750739fc 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/findClassInModule.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/findClassInModule.kt @@ -19,21 +19,28 @@ package org.jetbrains.kotlin.descriptors import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.ClassId -fun ModuleDescriptor.findClassifierAcrossModuleDependencies(classId: ClassId): ClassifierDescriptor? { +fun ModuleDescriptor.findClassifierAcrossModuleDependencies(classId: ClassId): ClassifierDescriptor? = withAnchorFallback { val packageViewDescriptor = getPackage(classId.packageFqName) val segments = classId.relativeClassName.pathSegments() val topLevelClass = packageViewDescriptor.memberScope.getContributedClassifier( segments.first(), NoLookupLocation.FROM_DESERIALIZATION - ) ?: return null + ) ?: return@withAnchorFallback null var result = topLevelClass for (name in segments.subList(1, segments.size)) { - if (result !is ClassDescriptor) return null + if (result !is ClassDescriptor) return@withAnchorFallback null result = result.unsubstitutedInnerClassesScope .getContributedClassifier(name, NoLookupLocation.FROM_DESERIALIZATION) as? ClassDescriptor - ?: return null + ?: return@withAnchorFallback null } - return result + return@withAnchorFallback result +} + +private inline fun ModuleDescriptor.withAnchorFallback( + crossinline doSearch: ModuleDescriptor.() -> ClassifierDescriptor? +): ClassifierDescriptor? { + val anchor = anchorProvider.getAnchor(this) + return if (anchor == null) doSearch() else doSearch() ?: anchor.doSearch() } fun ModuleDescriptor.findClassAcrossModuleDependencies(classId: ClassId): ClassDescriptor? = diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt index accb16a0486..7faddf5b65b 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.resolve.AnchorProvider import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.types.checker.REFINER_CAPABILITY import org.jetbrains.kotlin.types.checker.Ref @@ -38,7 +39,8 @@ class ModuleDescriptorImpl @JvmOverloads constructor( // May be null in compiler context, should be not-null in IDE context override val platform: TargetPlatform? = null, capabilities: Map, Any?> = emptyMap(), - override val stableName: Name? = null + override val stableName: Name? = null, + override val anchorProvider: AnchorProvider = AnchorProvider.Default, ) : DeclarationDescriptorImpl(Annotations.EMPTY, moduleName), ModuleDescriptor { private val capabilities: Map, Any?> diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/AnchorProvider.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/AnchorProvider.kt new file mode 100644 index 00000000000..c6d6ac4e2b4 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/AnchorProvider.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2020 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.resolve + +import org.jetbrains.kotlin.descriptors.ModuleDescriptor + +abstract class AnchorProvider { + open fun getAnchor(moduleDescriptor: ModuleDescriptor): ModuleDescriptor? = null + + companion object Default : AnchorProvider() +} \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java b/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java index 4c1adc37fc1..a6a9d85c56d 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/ErrorUtils.java @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.incremental.components.LookupLocation; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.platform.TargetPlatform; +import org.jetbrains.kotlin.resolve.AnchorProvider; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter; import org.jetbrains.kotlin.resolve.scopes.MemberScope; @@ -53,6 +54,12 @@ public class ErrorUtils { static { ERROR_MODULE = new ModuleDescriptor() { + @NotNull + @Override + public AnchorProvider getAnchorProvider() { + return AnchorProvider.Default; + } + @Nullable @Override public T getCapability(@NotNull Capability capability) { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleResolverTrackerImpl.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleResolverTrackerImpl.kt new file mode 100644 index 00000000000..6e757561e15 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleResolverTrackerImpl.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2020 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.project.Project +import org.jetbrains.kotlin.analyzer.ModuleResolverTracker +import org.jetbrains.kotlin.analyzer.ResolverForProject +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo +import org.jetbrains.kotlin.idea.project.useAnchorServices + +class IdeaModuleResolverTrackerImpl(private val project: Project) : ModuleResolverTracker() { + private val resolversForModules by lazy { + mutableMapOf>() + } + + @Synchronized + override fun registerModuleUpdate( + newModule: ModuleDescriptor, + oldModule: ModuleDescriptor?, + resolverForProject: ResolverForProject + ) { + if (!project.useAnchorServices) return + + if (oldModule != null) resolversForModules.remove(oldModule) + resolversForModules[newModule] = resolverForProject + } + + @Synchronized + override fun findResolverForProjectByModuleDescriptor(moduleDescriptor: ModuleDescriptor): ResolverForProject? { + if (!project.useAnchorServices) return null + + return resolversForModules[moduleDescriptor] + } +} \ No newline at end of file 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 04fbbf94315..b26a7c4d800 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 @@ -31,9 +31,10 @@ 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.AnchorProvider import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters +import org.jetbrains.kotlin.utils.addToStdlib.safeAs class IdeaResolverForProject( debugName: String, @@ -51,7 +52,8 @@ class IdeaResolverForProject( modules, fallbackModificationTracker, delegateResolver, - ServiceManager.getService(projectContext.project, IdePackageOracleFactory::class.java) + ServiceManager.getService(projectContext.project, IdePackageOracleFactory::class.java), + ServiceManager.getService(projectContext.project, AnchorProvider::class.java) ) { private val builtInsCache: BuiltInsCache = (delegateResolver as? IdeaResolverForProject)?.builtInsCache ?: BuiltInsCache(projectContext, this) @@ -146,6 +148,34 @@ class IdeaResolverForProject( return@compute newBuiltIns } } + + override fun registerModuleDescriptorUpdate( + newDescriptor: ModuleDescriptor, + oldDescriptor: ModuleDescriptor? + ) { + ModuleResolverTracker + .getInstance(projectContext.project) + .safeAs() + ?.registerModuleUpdate(newDescriptor, oldDescriptor, this) + } + + override fun tryGetResolverForModuleWithAnchorCheck( + targetModuleInfo: IdeaModuleInfo, + referencingModuleInfo: IdeaModuleInfo, + ): ResolverForModule? { + tryGetResolverForModule(targetModuleInfo)?.let { return it } + + val moduleDescriptorOfReferencingModule = descriptorByModule[referencingModuleInfo]?.moduleDescriptor + ?: error("$referencingModuleInfo is not contained in this resolver, which means incorrect use of anchor-aware search") + + val anchorModuleDescriptor = anchorProvider.getAnchor(moduleDescriptorOfReferencingModule) ?: return null + + val moduleResolverTracker = + ModuleResolverTracker.getInstance(projectContext.project).safeAs() ?: return null + + return moduleResolverTracker.findResolverForProjectByModuleDescriptor(anchorModuleDescriptor) + ?.tryGetResolverForModule(targetModuleInfo) + } } interface BuiltInsCacheKey { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinIdeAnchorService.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinIdeAnchorService.kt new file mode 100644 index 00000000000..17f4488474b --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinIdeAnchorService.kt @@ -0,0 +1,85 @@ +/* + * Copyright 2010-2020 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.PersistentStateComponent +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage +import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.project.Project +import com.intellij.util.xmlb.XmlSerializerUtil +import org.jetbrains.kotlin.analyzer.ModuleInfo +import org.jetbrains.kotlin.analyzer.ModuleResolverTracker +import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications +import org.jetbrains.kotlin.caches.resolve.KotlinCacheService +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.idea.caches.project.* +import org.jetbrains.kotlin.idea.project.useAnchorServices +import org.jetbrains.kotlin.resolve.AnchorProvider + +@State(name = "KotlinIdeAnchorService", storages = [Storage("anchors.xml")]) +class KotlinIdeAnchorService( + val project: Project +) : AnchorProvider(), + PersistentStateComponent { + + data class State( + var moduleNameToAnchorName: Map = emptyMap() + ) + + @JvmField + var myState: State = State() + + private fun buildMapping(): Map { + val modulesByNames = getModuleInfosFromIdeaModel(project).associateBy { moduleInfo -> + when (moduleInfo) { + is LibraryInfo -> moduleInfo.library.name + is ModuleSourceInfo -> moduleInfo.module.name + else -> moduleInfo.name.asString() + } + } + + return myState.moduleNameToAnchorName.entries.mapNotNull { (moduleName, anchorName) -> + val module = modulesByNames[moduleName] ?: return@mapNotNull notFoundModule(moduleName) + val anchor = modulesByNames[anchorName] ?: return@mapNotNull notFoundModule(moduleName) + module to anchor + }.toMap() + } + + private fun notFoundModule(moduleName: String): Nothing? { + logger().warn("Module <${moduleName}> not found in project model") + return null + } + + private val moduleToAnchor: Map + get() = project.cacheInvalidatingOnRootModifications { + buildMapping() + } + + private fun getAnchorModuleDescriptorIfAny(moduleInfo: ModuleInfo): ModuleDescriptor? { + val mapped = moduleToAnchor[moduleInfo] ?: return null + return KotlinCacheService.getInstance(project) + .getResolutionFacadeByModuleInfo(mapped, mapped.platform) + ?.moduleDescriptor + } + + @Synchronized + override fun getState(): State = myState + + @Synchronized + override fun loadState(state: State) { + XmlSerializerUtil.copyBean(state, myState) + } + + @Synchronized + override fun getAnchor(moduleDescriptor: ModuleDescriptor): ModuleDescriptor? { + if (!project.useAnchorServices) return null + val resolver = ModuleResolverTracker.getInstance(project) + .findResolverForProjectByModuleDescriptor(moduleDescriptor) + ?: return null + return getAnchorModuleDescriptorIfAny(resolver.moduleInfoForModuleDescriptor(moduleDescriptor)) + } +} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/util/globalContextUtils.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/util/globalContextUtils.kt index ac0f831dada..ed336e0da30 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/util/globalContextUtils.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/util/globalContextUtils.kt @@ -6,8 +6,10 @@ package org.jetbrains.kotlin.idea.caches.resolve.util import com.intellij.openapi.project.Project +import com.intellij.openapi.util.registry.Registry import org.jetbrains.kotlin.context.GlobalContextImpl import org.jetbrains.kotlin.idea.caches.resolve.PlatformAnalysisSettings +import org.jetbrains.kotlin.idea.project.useAnchorServices import org.jetbrains.kotlin.idea.project.useCompositeAnalysis import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus import org.jetbrains.kotlin.storage.ExceptionTracker @@ -32,7 +34,7 @@ private fun GlobalContextImpl.contextWithNewLockAndCompositeExceptionTracker(deb } internal fun GlobalContextImpl.contextWithCompositeExceptionTracker(project: Project, debugName: String): GlobalContextImpl = - if (project.useCompositeAnalysis) { + if (project.useCompositeAnalysis || project.useAnchorServices) { this.contextWithCompositeExceptionTracker(debugName) } else { this.contextWithNewLockAndCompositeExceptionTracker(debugName) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/KotlinAnchorSupportForLibraryAnalysisComponent.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/KotlinAnchorSupportForLibraryAnalysisComponent.kt new file mode 100644 index 00000000000..443518094ae --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/KotlinAnchorSupportForLibraryAnalysisComponent.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2010-2020 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.project + +import com.intellij.ide.util.PropertiesComponent +import com.intellij.openapi.project.Project + +object KotlinAnchorSupportForLibraryAnalysisComponent { + private const val anchorSupportOption = "kotlin.use.anchor.services" + + @JvmStatic + fun setState(project: Project, isEnabled: Boolean) { + PropertiesComponent.getInstance(project).setValue(anchorSupportOption, isEnabled, false) + } + + @JvmStatic + fun isEnabled(project: Project): Boolean = + PropertiesComponent.getInstance(project).getBoolean(anchorSupportOption) +} + +val Project.useAnchorServices: Boolean + get() = KotlinAnchorSupportForLibraryAnalysisComponent.isEnabled(this) \ No newline at end of file diff --git a/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/DebugLabelPropertyDescriptorProvider.kt b/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/DebugLabelPropertyDescriptorProvider.kt index 24a2ae7cfae..7d678ace7af 100644 --- a/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/DebugLabelPropertyDescriptorProvider.kt +++ b/idea/jvm-debugger/jvm-debugger-evaluation/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/DebugLabelPropertyDescriptorProvider.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.psi.KtCodeFragment import org.jetbrains.kotlin.psi.externalDescriptors +import org.jetbrains.kotlin.resolve.AnchorProvider import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.Variance @@ -195,6 +196,8 @@ private object DebugLabelModuleDescriptor : DeclarationDescriptorImpl(Annotation get() = true override fun assertValid() {} + override val anchorProvider: AnchorProvider + get() = AnchorProvider.Default } internal class DebugLabelPropertyDescriptor( diff --git a/idea/resources-en/messages/KotlinBundle.properties b/idea/resources-en/messages/KotlinBundle.properties index 49b213e7250..f54fb01b67d 100644 --- a/idea/resources-en/messages/KotlinBundle.properties +++ b/idea/resources-en/messages/KotlinBundle.properties @@ -1918,6 +1918,8 @@ signature.preview=Signature Preview move.members.from=Move members from: open.moved.members.in.editor=Open moved members in editor to.fully.qualified.name=To (fully qualified name): +toggle.anchor.support.for.library.analysis=Toggle anchor support for library analysis +enable.components.for.library.to.source.analysis.in.kotlin=Enable components for library-to-source analysis in Kotlin action.Kotlin.StopScratch.text=Stop Scratch Execution action.Kotlin.StopScratch.description=Stop scratch execution @@ -1973,6 +1975,7 @@ action.ShowKotlinBytecode.text=Show Kotlin Bytecode action.ConfigureKotlinJsInProject.text=Configure Kotlin (JavaScript) in Project action.ConfigureKotlinInProject.text=Configure Kotlin in Project action.KotlinConsoleREPL.text=Kotlin REPL +action.AnchorSupportForLibraryAnalysisToggleAction.text=Toggle Anchor Support For Modules inspection.unused.unary.operator.display.name=Unused unary operator inspection.replace.guard.clause.with.function.call.display.name=Replace guard clause with kotlin's function call diff --git a/idea/resources/META-INF/plugin-common.xml b/idea/resources/META-INF/plugin-common.xml index e1d0bf0e26d..9a9b3b1d9d4 100644 --- a/idea/resources/META-INF/plugin-common.xml +++ b/idea/resources/META-INF/plugin-common.xml @@ -90,6 +90,8 @@ + + @@ -189,6 +191,12 @@ + + + + diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/internal/AnchorSupportForLibraryAnalysisToggleAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/internal/AnchorSupportForLibraryAnalysisToggleAction.kt new file mode 100644 index 00000000000..9a516e56be5 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/actions/internal/AnchorSupportForLibraryAnalysisToggleAction.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2020 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.actions.internal + +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.ToggleAction +import org.jetbrains.kotlin.idea.KotlinBundle +import org.jetbrains.kotlin.idea.project.KotlinAnchorSupportForLibraryAnalysisComponent +import org.jetbrains.kotlin.idea.project.useAnchorServices + +class AnchorSupportForLibraryAnalysisToggleAction : ToggleAction( + KotlinBundle.message("toggle.anchor.support.for.library.analysis"), + KotlinBundle.message("enable.components.for.library.to.source.analysis.in.kotlin"), + null +) { + override fun isSelected(e: AnActionEvent): Boolean = + e.project?.useAnchorServices == true + + override fun setSelected(e: AnActionEvent, state: Boolean) { + e.project?.let { + KotlinAnchorSupportForLibraryAnalysisComponent.setState(it, state) + } + } + +} \ No newline at end of file