Introduce components for library-to-source resolution in IDE

#KT-24309 In progress
This commit is contained in:
Pavel Kirpichenkov
2020-04-23 14:23:57 +03:00
parent 0b2c9ff77a
commit db1210fc67
19 changed files with 332 additions and 13 deletions
@@ -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<IdeaModuleInfo>() {
private val resolversForModules by lazy {
mutableMapOf<ModuleDescriptor, ResolverForProject<IdeaModuleInfo>>()
}
@Synchronized
override fun registerModuleUpdate(
newModule: ModuleDescriptor,
oldModule: ModuleDescriptor?,
resolverForProject: ResolverForProject<IdeaModuleInfo>
) {
if (!project.useAnchorServices) return
if (oldModule != null) resolversForModules.remove(oldModule)
resolversForModules[newModule] = resolverForProject
}
@Synchronized
override fun findResolverForProjectByModuleDescriptor(moduleDescriptor: ModuleDescriptor): ResolverForProject<IdeaModuleInfo>? {
if (!project.useAnchorServices) return null
return resolversForModules[moduleDescriptor]
}
}
@@ -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<IdeaModuleResolverTrackerImpl>()
?.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<IdeaModuleResolverTrackerImpl>() ?: return null
return moduleResolverTracker.findResolverForProjectByModuleDescriptor(anchorModuleDescriptor)
?.tryGetResolverForModule(targetModuleInfo)
}
}
interface BuiltInsCacheKey {
@@ -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<KotlinIdeAnchorService.State> {
data class State(
var moduleNameToAnchorName: Map<String, String> = emptyMap()
)
@JvmField
var myState: State = State()
private fun buildMapping(): Map<ModuleInfo, ModuleInfo> {
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<KotlinIdeAnchorService>().warn("Module <${moduleName}> not found in project model")
return null
}
private val moduleToAnchor: Map<ModuleInfo, ModuleInfo>
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))
}
}
@@ -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)
@@ -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)