KT-44821 [Sealed Interfaces]: when-exhaustiveness failure in IDE

Compiler check for 'when' exhaustiveness requires that module
descriptors of a sealed class and its inheritors are the same (reference
identity matters). Prior to this commit and under some conditions they
were not. Details follow below.

Resolution related data structures (resolution facades) are organized
into trees (sdks, libs, and modules have their own nodes/facades,
module/class descriptors are stored inside). And the trees themselves
are put into a map associating so called PlatformAnalysisSettings and
GlobalFacades (plays a role of a root). PlatformAnalysisSettings is an
abstraction describing target platform and sdk of a module. The more
combinations exist for a project the more facades are used. Please, see
KotlinCacheService for more details.

So why a module can have multiple ModuleDescriptor-s?
Every tree mentioned above is an isolated resolution environment
containing its own instances of the outer world descriptors. Say, if a
project has modules X, Y, Z and we consider X then all three might have
their own vision of X, i.e. 3 descriptors exist at a time.

What descriptor instance does compiler get?
The path starts when the user opens a file in the editor and
highlighting pass starts (see usages of
ResolutionUtils#analyzeWithAllCompilerChecks). Module descriptor stored
in the resolution tree of the file's module gets injected into the
compiler's context. Starting from this moment compiler sees other
modules through the prism of a single resolution facade (tree).
Descriptors residing outside are alien.

This commit allows IdeSealedClassInheritorsProvider to figure out what
PlatformAnalysisSettings are associated with the resolution facade (read
ModuleDescriptor) seen by the compiler. Later on the same facade is used
to provide correct instances of sealed inheritors back to the compiler.
This commit is contained in:
Andrei Klunnyi
2021-02-19 18:42:59 +01:00
parent 6ebd4b954c
commit f45af5ea0e
8 changed files with 105 additions and 63 deletions
@@ -12,8 +12,6 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.RESOLUTION_ANCHOR_PROVIDER_CAPABILITY
import org.jetbrains.kotlin.resolve.ResolutionAnchorProvider
import org.jetbrains.kotlin.utils.checkWithAttachment import org.jetbrains.kotlin.utils.checkWithAttachment
abstract class AbstractResolverForProject<M : ModuleInfo>( abstract class AbstractResolverForProject<M : ModuleInfo>(
@@ -22,8 +20,7 @@ abstract class AbstractResolverForProject<M : ModuleInfo>(
modules: Collection<M>, modules: Collection<M>,
protected val fallbackModificationTracker: ModificationTracker? = null, protected val fallbackModificationTracker: ModificationTracker? = null,
private val delegateResolver: ResolverForProject<M> = EmptyResolverForProject(), private val delegateResolver: ResolverForProject<M> = EmptyResolverForProject(),
private val packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory, private val packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory
protected val resolutionAnchorProvider: ResolutionAnchorProvider = ResolutionAnchorProvider.Default,
) : ResolverForProject<M>() { ) : ResolverForProject<M>() {
protected class ModuleData( protected class ModuleData(
@@ -196,13 +193,15 @@ abstract class AbstractResolverForProject<M : ModuleInfo>(
return moduleData return moduleData
} }
protected open fun getAdditionalCapabilities(): Map<ModuleCapability<*>, Any?> = emptyMap()
private fun createModuleDescriptor(module: M): ModuleData { private fun createModuleDescriptor(module: M): ModuleData {
val moduleDescriptor = ModuleDescriptorImpl( val moduleDescriptor = ModuleDescriptorImpl(
module.name, module.name,
projectContext.storageManager, projectContext.storageManager,
builtInsForModule(module), builtInsForModule(module),
module.platform, module.platform,
module.capabilities + listOf(RESOLUTION_ANCHOR_PROVIDER_CAPABILITY to resolutionAnchorProvider), module.capabilities + getAdditionalCapabilities(),
module.stableName, module.stableName,
) )
moduleInfoByDescriptor[moduleDescriptor] = module moduleInfoByDescriptor[moduleDescriptor] = module
@@ -9,13 +9,7 @@ import org.jetbrains.kotlin.descriptors.ModuleCapability
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
interface ResolutionAnchorProvider { interface ResolutionAnchorProvider {
fun getResolutionAnchor(moduleDescriptor: ModuleDescriptor): ModuleDescriptor? = null fun getResolutionAnchor(moduleDescriptor: ModuleDescriptor): ModuleDescriptor?
companion object {
val Default: ResolutionAnchorProvider = object : ResolutionAnchorProvider {
override fun getResolutionAnchor(moduleDescriptor: ModuleDescriptor): ModuleDescriptor? = null
}
}
} }
val RESOLUTION_ANCHOR_PROVIDER_CAPABILITY = ModuleCapability<ResolutionAnchorProvider>("ResolutionAnchorProvider") val RESOLUTION_ANCHOR_PROVIDER_CAPABILITY = ModuleCapability<ResolutionAnchorProvider>("ResolutionAnchorProvider")
@@ -21,8 +21,8 @@ import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.diagnostics.KotlinSuppressCache import org.jetbrains.kotlin.resolve.diagnostics.KotlinSuppressCache
interface KotlinCacheService { interface KotlinCacheService {
@@ -36,4 +36,6 @@ interface KotlinCacheService {
fun getSuppressionCache(): KotlinSuppressCache fun getSuppressionCache(): KotlinSuppressCache
fun getResolutionFacadeByModuleInfo(moduleInfo: ModuleInfo, platform: TargetPlatform): ResolutionFacade? fun getResolutionFacadeByModuleInfo(moduleInfo: ModuleInfo, platform: TargetPlatform): ResolutionFacade?
fun getResolutionFacadeByModuleInfo(moduleInfo: ModuleInfo, settings: PlatformAnalysisSettings): ResolutionFacade?
} }
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2021 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.caches.resolve
/**
* Regulates which sources should be analyzed together.
*
* There are exactly two descendants, which are in strong one-to-one correspondence with [ResolutionModeComponent.Mode] (meaning
* that after checking value of ResolutionMode, it's safe to downcast settings instance to the respective type):
* - [PlatformAnalysisSettingsImpl] should be used iff we're working under [Mode.SEPARATE], and will create separate
* facade for each platforms, sdk, builtIns settings and other stuff.
* This is the old and stable mode, which should be used by default.
*
* - [CompositeAnalysisSettings] should be used iff we're working under [Mode.COMPOSITE], and will analyze all sources
* together, in one facade.
* This mode is new and experimental, and works only together with TypeRefinement facilities in the compiler's frontend.
* This mode is currently enabled only for HMPP projects
*/
interface PlatformAnalysisSettings
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.idea.caches.resolve package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.ModificationTracker import com.intellij.openapi.util.ModificationTracker
import org.jetbrains.kotlin.analyzer.* import org.jetbrains.kotlin.analyzer.*
import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters
@@ -14,6 +13,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.caches.resolve.* import org.jetbrains.kotlin.caches.resolve.*
import org.jetbrains.kotlin.context.ProjectContext import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.context.withModule import org.jetbrains.kotlin.context.withModule
import org.jetbrains.kotlin.descriptors.ModuleCapability
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.idea.caches.project.* import org.jetbrains.kotlin.idea.caches.project.*
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.RESOLUTION_ANCHOR_PROVIDER_CAPABILITY
import org.jetbrains.kotlin.resolve.ResolutionAnchorProvider import org.jetbrains.kotlin.resolve.ResolutionAnchorProvider
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
@@ -42,20 +43,34 @@ class IdeaResolverForProject(
private val syntheticFilesByModule: Map<IdeaModuleInfo, Collection<KtFile>>, private val syntheticFilesByModule: Map<IdeaModuleInfo, Collection<KtFile>>,
delegateResolver: ResolverForProject<IdeaModuleInfo>, delegateResolver: ResolverForProject<IdeaModuleInfo>,
fallbackModificationTracker: ModificationTracker? = null, fallbackModificationTracker: ModificationTracker? = null,
// TODO(dsavvinov): this is needed only for non-composite analysis, extract separate resolver implementation instead private val settings: PlatformAnalysisSettings
private val constantSdkDependencyIfAny: SdkInfo? = null
) : AbstractResolverForProject<IdeaModuleInfo>( ) : AbstractResolverForProject<IdeaModuleInfo>(
debugName, debugName,
projectContext, projectContext,
modules, modules,
fallbackModificationTracker, fallbackModificationTracker,
delegateResolver, delegateResolver,
ServiceManager.getService(projectContext.project, IdePackageOracleFactory::class.java), ServiceManager.getService(projectContext.project, IdePackageOracleFactory::class.java)
ServiceManager.getService(projectContext.project, ResolutionAnchorProvider::class.java)
) { ) {
companion object {
val PLATFORM_ANALYSIS_SETTINGS = ModuleCapability<PlatformAnalysisSettings>("PlatformAnalysisSettings")
}
private val resolutionAnchorProvider = ServiceManager.getService(projectContext.project, ResolutionAnchorProvider::class.java)
private val constantSdkDependencyIfAny: SdkInfo? =
if (settings is PlatformAnalysisSettingsImpl) settings.sdk?.let { SdkInfo(projectContext.project, it) } else null
private val builtInsCache: BuiltInsCache = private val builtInsCache: BuiltInsCache =
(delegateResolver as? IdeaResolverForProject)?.builtInsCache ?: BuiltInsCache(projectContext, this) (delegateResolver as? IdeaResolverForProject)?.builtInsCache ?: BuiltInsCache(projectContext, this)
override fun getAdditionalCapabilities(): Map<ModuleCapability<*>, Any?> {
return super.getAdditionalCapabilities() +
(PLATFORM_ANALYSIS_SETTINGS to settings) +
(RESOLUTION_ANCHOR_PROVIDER_CAPABILITY to resolutionAnchorProvider)
}
override fun sdkDependency(module: IdeaModuleInfo): SdkInfo? { override fun sdkDependency(module: IdeaModuleInfo): SdkInfo? {
if (projectContext.project.useCompositeAnalysis) { if (projectContext.project.useCompositeAnalysis) {
require(constantSdkDependencyIfAny == null) { "Shouldn't pass SDK dependency manually for composite analysis mode" } require(constantSdkDependencyIfAny == null) { "Shouldn't pass SDK dependency manually for composite analysis mode" }
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.analyzer.ResolverForProject.Companion.resolverForSdk
import org.jetbrains.kotlin.analyzer.ResolverForProject.Companion.resolverForSpecialInfoName import org.jetbrains.kotlin.analyzer.ResolverForProject.Companion.resolverForSpecialInfoName
import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.caches.resolve.PlatformAnalysisSettings
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.context.GlobalContext import org.jetbrains.kotlin.context.GlobalContext
import org.jetbrains.kotlin.context.GlobalContextImpl import org.jetbrains.kotlin.context.GlobalContextImpl
@@ -66,41 +67,24 @@ import org.jetbrains.kotlin.utils.addToStdlib.sumByLong
internal val LOG = Logger.getInstance(KotlinCacheService::class.java) internal val LOG = Logger.getInstance(KotlinCacheService::class.java)
/**
* Regulates which sources should be analyzed together.
*
* There are exactly two descendants, which are in strong one-to-one correspondence with [ResolutionModeComponent.Mode] (meaning
* that after checking value of ResolutionMode, it's safe to downcast settings instance to the respective type):
* - [PlatformAnalysisSettingsImpl] should be used iff we're working under [Mode.SEPARATE], and will create separate
* facade for each platforms, sdk, builtIns settings and other stuff.
* This is the old and stable mode, which should be used by default.
*
* - [CompositeAnalysisSettings] should be used iff we're working under [Mode.COMPOSITE], and will analyze all sources
* together, in one facade.
* This mode is new and experimental, and works only together with TypeRefinement facilities in the compiler's frontend.
* This mode is currently enabled only for HMPP projects
*/
sealed class PlatformAnalysisSettings {
companion object {
fun create(
project: Project,
platform: TargetPlatform,
sdk: Sdk?,
isAdditionalBuiltInFeaturesSupported: Boolean
) = if (project.useCompositeAnalysis)
CompositeAnalysisSettings
else
PlatformAnalysisSettingsImpl(platform, sdk, isAdditionalBuiltInFeaturesSupported)
}
}
data class PlatformAnalysisSettingsImpl( data class PlatformAnalysisSettingsImpl(
val platform: TargetPlatform, val platform: TargetPlatform,
val sdk: Sdk?, val sdk: Sdk?,
val isAdditionalBuiltInFeaturesSupported: Boolean, val isAdditionalBuiltInFeaturesSupported: Boolean,
) : PlatformAnalysisSettings() ) : PlatformAnalysisSettings
object CompositeAnalysisSettings : PlatformAnalysisSettings
fun createPlatformAnalysisSettings(
project: Project,
platform: TargetPlatform,
sdk: Sdk?,
isAdditionalBuiltInFeaturesSupported: Boolean
) = if (project.useCompositeAnalysis)
CompositeAnalysisSettings
else
PlatformAnalysisSettingsImpl(platform, sdk, isAdditionalBuiltInFeaturesSupported)
object CompositeAnalysisSettings : PlatformAnalysisSettings()
class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService { class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
override fun getResolutionFacade(elements: List<KtElement>): ResolutionFacade { override fun getResolutionFacade(elements: List<KtElement>): ResolutionFacade {
@@ -144,7 +128,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
): ProjectResolutionFacade { ): ProjectResolutionFacade {
val sdk = dependenciesModuleInfo.sdk val sdk = dependenciesModuleInfo.sdk
val platform = JvmPlatforms.defaultJvmPlatform // TODO: Js scripts? val platform = JvmPlatforms.defaultJvmPlatform // TODO: Js scripts?
val settings = PlatformAnalysisSettings.create(project, platform, sdk, true) val settings = createPlatformAnalysisSettings(project, platform, sdk, true)
val dependenciesForScriptDependencies = listOf( val dependenciesForScriptDependencies = listOf(
LibraryModificationTracker.getInstance(project), LibraryModificationTracker.getInstance(project),
@@ -217,7 +201,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
) )
} }
private fun IdeaModuleInfo.platformSettings(targetPlatform: TargetPlatform) = PlatformAnalysisSettings.create( private fun IdeaModuleInfo.platformSettings(targetPlatform: TargetPlatform) = createPlatformAnalysisSettings(
this@KotlinCacheServiceImpl.project, targetPlatform, sdk, this@KotlinCacheServiceImpl.project, targetPlatform, sdk,
supportsAdditionalBuiltInsMembers(this@KotlinCacheServiceImpl.project) supportsAdditionalBuiltInsMembers(this@KotlinCacheServiceImpl.project)
) )
@@ -469,6 +453,13 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
private fun getResolutionFacadeByModuleInfo(moduleInfo: IdeaModuleInfo, platform: TargetPlatform): ResolutionFacade { private fun getResolutionFacadeByModuleInfo(moduleInfo: IdeaModuleInfo, platform: TargetPlatform): ResolutionFacade {
val settings = moduleInfo.platformSettings(platform) val settings = moduleInfo.platformSettings(platform)
return getResolutionFacadeByModuleInfoAndSettings(moduleInfo, settings)
}
private fun getResolutionFacadeByModuleInfoAndSettings(
moduleInfo: IdeaModuleInfo,
settings: PlatformAnalysisSettings
): ResolutionFacade {
val projectFacade = when (moduleInfo) { val projectFacade = when (moduleInfo) {
is ScriptDependenciesInfo.ForProject, is ScriptDependenciesInfo.ForProject,
is ScriptDependenciesSourceInfo.ForProject -> facadeForScriptDependenciesForProject is ScriptDependenciesSourceInfo.ForProject -> facadeForScriptDependenciesForProject
@@ -481,6 +472,11 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
override fun getResolutionFacadeByModuleInfo(moduleInfo: ModuleInfo, platform: TargetPlatform): ResolutionFacade? = override fun getResolutionFacadeByModuleInfo(moduleInfo: ModuleInfo, platform: TargetPlatform): ResolutionFacade? =
(moduleInfo as? IdeaModuleInfo)?.let { getResolutionFacadeByModuleInfo(it, platform) } (moduleInfo as? IdeaModuleInfo)?.let { getResolutionFacadeByModuleInfo(it, platform) }
override fun getResolutionFacadeByModuleInfo(moduleInfo: ModuleInfo, settings: PlatformAnalysisSettings): ResolutionFacade? {
val ideaModuleInfo = moduleInfo as? IdeaModuleInfo ?: return null
return getResolutionFacadeByModuleInfoAndSettings(ideaModuleInfo, settings)
}
private fun Collection<KtFile>.filterNotInProjectSource(moduleInfo: IdeaModuleInfo): Set<KtFile> { private fun Collection<KtFile>.filterNotInProjectSource(moduleInfo: IdeaModuleInfo): Set<KtFile> {
return mapNotNull { return mapNotNull {
if (it is KtCodeFragment) it.getContextFile() else it if (it is KtCodeFragment) it.getContextFile() else it
@@ -14,6 +14,7 @@ import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.CachedValuesManager
import com.intellij.util.containers.SLRUCache import com.intellij.util.containers.SLRUCache
import org.jetbrains.kotlin.analyzer.* import org.jetbrains.kotlin.analyzer.*
import org.jetbrains.kotlin.caches.resolve.PlatformAnalysisSettings
import org.jetbrains.kotlin.context.GlobalContextImpl import org.jetbrains.kotlin.context.GlobalContextImpl
import org.jetbrains.kotlin.context.withProject import org.jetbrains.kotlin.context.withProject
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -116,17 +117,15 @@ internal class ProjectResolutionFacade(
val modulesToCreateResolversFor = allModuleInfos.filter(moduleFilter) val modulesToCreateResolversFor = allModuleInfos.filter(moduleFilter)
val resolverForProject = IdeaResolverForProject( return IdeaResolverForProject(
resolverDebugName, resolverDebugName,
globalContext.withProject(project), globalContext.withProject(project),
modulesToCreateResolversFor, modulesToCreateResolversFor,
syntheticFilesByModule, syntheticFilesByModule,
delegateResolverForProject, delegateResolverForProject,
if (invalidateOnOOCB) KotlinModificationTrackerService.getInstance(project).outOfBlockModificationTracker else null, if (invalidateOnOOCB) KotlinModificationTrackerService.getInstance(project).outOfBlockModificationTracker else null,
constantSdkDependencyIfAny = if (settings is PlatformAnalysisSettingsImpl) settings.sdk?.let { SdkInfo(project, it) } else null settings
) )
return resolverForProject
} }
internal fun resolverForModuleInfo(moduleInfo: IdeaModuleInfo) = cachedResolverForProject.resolverForModule(moduleInfo) internal fun resolverForModuleInfo(moduleInfo: IdeaModuleInfo) = cachedResolverForProject.resolverForModule(moduleInfo)
@@ -6,20 +6,27 @@
package org.jetbrains.kotlin.idea.compiler package org.jetbrains.kotlin.idea.compiler
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.psi.* import com.intellij.openapi.project.Project
import com.intellij.psi.search.* import com.intellij.psi.JavaDirectoryService
import com.intellij.psi.PsiPackage
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.PackageScope
import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.searches.ClassInheritorsSearch import com.intellij.psi.search.searches.ClassInheritorsSearch
import com.intellij.psi.search.searches.ClassInheritorsSearch.SearchParameters import com.intellij.psi.search.searches.ClassInheritorsSearch.SearchParameters
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.moduleInfo import org.jetbrains.kotlin.analyzer.moduleInfo
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.containingPackage import org.jetbrains.kotlin.descriptors.containingPackage
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
import org.jetbrains.kotlin.idea.caches.project.implementedDescriptors import org.jetbrains.kotlin.idea.caches.project.implementedDescriptors
import org.jetbrains.kotlin.idea.caches.resolve.util.javaResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.IdeaResolverForProject.Companion.PLATFORM_ANALYSIS_SETTINGS
import org.jetbrains.kotlin.idea.caches.resolve.util.resolveToDescriptor import org.jetbrains.kotlin.idea.caches.resolve.util.resolveToDescriptor
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.module import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClass
@@ -36,6 +43,9 @@ object IdeSealedClassInheritorsProvider : SealedClassInheritorsProvider() {
): Collection<ClassDescriptor> { ): Collection<ClassDescriptor> {
val sealedKtClass = sealedClass.findPsi() as? KtClass ?: return emptyList() val sealedKtClass = sealedClass.findPsi() as? KtClass ?: return emptyList()
val project = sealedKtClass.project
val moduleDescriptor = sealedClass.module
val searchScope: SearchScope = if (allowSealedInheritorsInDifferentFilesOfSamePackage) { val searchScope: SearchScope = if (allowSealedInheritorsInDifferentFilesOfSamePackage) {
val module = sealedKtClass.module ?: return emptyList() val module = sealedKtClass.module ?: return emptyList()
@@ -46,7 +56,7 @@ object IdeSealedClassInheritorsProvider : SealedClassInheritorsProvider() {
val mppAwareSearchScope = GlobalSearchScope.union(modulesScope) val mppAwareSearchScope = GlobalSearchScope.union(modulesScope)
val containingPackage = sealedClass.containingPackage() ?: return emptyList() val containingPackage = sealedClass.containingPackage() ?: return emptyList()
val psiPackage = KotlinJavaPsiFacade.getInstance(sealedKtClass.project) val psiPackage = KotlinJavaPsiFacade.getInstance(project)
.findPackage(containingPackage.asString(), GlobalSearchScope.moduleScope(module)) .findPackage(containingPackage.asString(), GlobalSearchScope.moduleScope(module))
?: getPackageViaDirectoryService(sealedKtClass) ?: getPackageViaDirectoryService(sealedKtClass)
?: return emptyList() ?: return emptyList()
@@ -57,15 +67,14 @@ object IdeSealedClassInheritorsProvider : SealedClassInheritorsProvider() {
GlobalSearchScope.fileScope(sealedKtClass.containingFile) // Kotlin version prior to 1.5 GlobalSearchScope.fileScope(sealedKtClass.containingFile) // Kotlin version prior to 1.5
} }
val kotlinAsJavaSupport = KotlinAsJavaSupport.getInstance(sealedKtClass.project) val kotlinAsJavaSupport = KotlinAsJavaSupport.getInstance(project)
val lightClass = sealedKtClass.toLightClass() ?: kotlinAsJavaSupport.getFakeLightClass(sealedKtClass) val lightClass = sealedKtClass.toLightClass() ?: kotlinAsJavaSupport.getFakeLightClass(sealedKtClass)
val searchParameters = SearchParameters(lightClass, searchScope, false, true, false) val searchParameters = SearchParameters(lightClass, searchScope, false, true, false)
val resolutionFacade = getResolutionFacade(moduleDescriptor, project) ?: return emptyList()
return ClassInheritorsSearch.search(searchParameters) return ClassInheritorsSearch.search(searchParameters)
.map mapper@{ .mapNotNull { it.resolveToDescriptor(resolutionFacade) }
val resolutionFacade = it.javaResolutionFacade() ?: return@mapper null
it.resolveToDescriptor(resolutionFacade)
}.filterNotNull()
.sortedBy(ClassDescriptor::getName) // order needs to be stable (at least for tests) .sortedBy(ClassDescriptor::getName) // order needs to be stable (at least for tests)
} }
@@ -78,4 +87,10 @@ object IdeSealedClassInheritorsProvider : SealedClassInheritorsProvider() {
val directory = ktClass.containingFile.containingDirectory ?: return null val directory = ktClass.containingFile.containingDirectory ?: return null
return JavaDirectoryService.getInstance().getPackage(directory) return JavaDirectoryService.getInstance().getPackage(directory)
} }
}
private fun getResolutionFacade(moduleDescriptor: ModuleDescriptor, project: Project): ResolutionFacade? {
val analysisSettings = moduleDescriptor.getCapability(PLATFORM_ANALYSIS_SETTINGS) ?: return null
val moduleInfo = moduleDescriptor.getCapability(ModuleInfo.Capability) ?: return null
return KotlinCacheService.getInstance(project).getResolutionFacadeByModuleInfo(moduleInfo, analysisSettings)
} }