[AA Standalone] Implement KotlinDirectInheritorsProvider
- We are relying on static indexing to find candidates for sealed inheritors, hence the extension to the index. - The direct usage of `KotlinStaticDeclarationProviderFactory` in `KotlinStandaloneDirectInheritorsProvider` is not pretty, but a proper design requires making the static index available as a service and moving "static" services to the Standalone API (from AA providers). ^KT-66013
This commit is contained in:
committed by
Space Team
parent
9bed2e974b
commit
b2639a469b
+5
-20
@@ -19,13 +19,12 @@ import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolOrigin
|
|||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtValueParameterSymbol
|
import org.jetbrains.kotlin.analysis.api.symbols.KtValueParameterSymbol
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes
|
|
||||||
import org.jetbrains.kotlin.fir.scopes.*
|
import org.jetbrains.kotlin.fir.scopes.*
|
||||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||||
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
||||||
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.unwrapFakeOverrides
|
import org.jetbrains.kotlin.fir.unwrapFakeOverrides
|
||||||
|
import org.jetbrains.kotlin.analysis.api.fir.utils.isSubClassOf
|
||||||
|
|
||||||
internal class KtFirSymbolDeclarationOverridesProvider(
|
internal class KtFirSymbolDeclarationOverridesProvider(
|
||||||
override val analysisSession: KtFirAnalysisSession,
|
override val analysisSession: KtFirAnalysisSession,
|
||||||
@@ -168,37 +167,23 @@ internal class KtFirSymbolDeclarationOverridesProvider(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun isSubClassOf(subClass: KtClassOrObjectSymbol, superClass: KtClassOrObjectSymbol): Boolean {
|
override fun isSubClassOf(subClass: KtClassOrObjectSymbol, superClass: KtClassOrObjectSymbol): Boolean {
|
||||||
return isSubClassOf(subClass, superClass, checkDeep = true)
|
return isSubClassOf(subClass, superClass, allowIndirectSubtyping = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isDirectSubClassOf(subClass: KtClassOrObjectSymbol, superClass: KtClassOrObjectSymbol): Boolean {
|
override fun isDirectSubClassOf(subClass: KtClassOrObjectSymbol, superClass: KtClassOrObjectSymbol): Boolean {
|
||||||
return isSubClassOf(subClass, superClass, checkDeep = false)
|
return isSubClassOf(subClass, superClass, allowIndirectSubtyping = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSubClassOf(subClass: KtClassOrObjectSymbol, superClass: KtClassOrObjectSymbol, checkDeep: Boolean): Boolean {
|
private fun isSubClassOf(subClass: KtClassOrObjectSymbol, superClass: KtClassOrObjectSymbol, allowIndirectSubtyping: Boolean): Boolean {
|
||||||
require(subClass is KtFirSymbol<*>)
|
require(subClass is KtFirSymbol<*>)
|
||||||
require(superClass is KtFirSymbol<*>)
|
require(superClass is KtFirSymbol<*>)
|
||||||
|
|
||||||
if (subClass == superClass) return false
|
if (subClass == superClass) return false
|
||||||
subClass.firSymbol.lazyResolveToPhase(FirResolvePhase.SUPER_TYPES)
|
|
||||||
return isSubClassOf(
|
return isSubClassOf(
|
||||||
subClass = subClass.firSymbol.fir as FirClass,
|
subClass = subClass.firSymbol.fir as FirClass,
|
||||||
superClass = superClass.firSymbol.fir as FirClass,
|
superClass = superClass.firSymbol.fir as FirClass,
|
||||||
checkDeep
|
allowIndirectSubtyping,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isSubClassOf(subClass: FirClass, superClass: FirClass, checkDeep: Boolean): Boolean {
|
|
||||||
if (subClass.superConeTypes.any { it.toRegularClassSymbol(rootModuleSession) == superClass.symbol }) return true
|
|
||||||
if (!checkDeep) return false
|
|
||||||
subClass.superConeTypes.forEach { superType ->
|
|
||||||
val superOfSub = superType.toRegularClassSymbol(rootModuleSession) ?: return@forEach
|
|
||||||
superOfSub.lazyResolveToPhase(FirResolvePhase.SUPER_TYPES)
|
|
||||||
if (isSubClassOf(superOfSub.fir, superClass, checkDeep = true)) return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getIntersectionOverriddenSymbols(symbol: KtCallableSymbol): List<KtCallableSymbol> {
|
override fun getIntersectionOverriddenSymbols(symbol: KtCallableSymbol): List<KtCallableSymbol> {
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2024 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.analysis.api.fir.utils
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
||||||
|
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether [subClass] is a strict subtype of [superClass]. Resolves [subClass] to [FirResolvePhase.SUPER_TYPES].
|
||||||
|
*/
|
||||||
|
fun isSubClassOf(subClass: FirClass, superClass: FirClass, allowIndirectSubtyping: Boolean = true): Boolean {
|
||||||
|
subClass.lazyResolveToPhase(FirResolvePhase.SUPER_TYPES)
|
||||||
|
|
||||||
|
val session = subClass.moduleData.session
|
||||||
|
if (subClass.superConeTypes.any { it.toRegularClassSymbol(session) == superClass.symbol }) return true
|
||||||
|
if (!allowIndirectSubtyping) return false
|
||||||
|
|
||||||
|
subClass.superConeTypes.forEach { superType ->
|
||||||
|
val superOfSub = superType.toRegularClassSymbol(session) ?: return@forEach
|
||||||
|
if (isSubClassOf(superOfSub.fir, superClass, allowIndirectSubtyping = true)) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
+6
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.analysis.providers.impl
|
package org.jetbrains.kotlin.analysis.providers.impl
|
||||||
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
|
||||||
internal class KotlinStaticDeclarationIndex {
|
internal class KotlinStaticDeclarationIndex {
|
||||||
@@ -16,4 +17,9 @@ internal class KotlinStaticDeclarationIndex {
|
|||||||
internal val typeAliasMap: MutableMap<FqName, MutableSet<KtTypeAlias>> = mutableMapOf()
|
internal val typeAliasMap: MutableMap<FqName, MutableSet<KtTypeAlias>> = mutableMapOf()
|
||||||
internal val topLevelFunctionMap: MutableMap<FqName, MutableSet<KtNamedFunction>> = mutableMapOf()
|
internal val topLevelFunctionMap: MutableMap<FqName, MutableSet<KtNamedFunction>> = mutableMapOf()
|
||||||
internal val topLevelPropertyMap: MutableMap<FqName, MutableSet<KtProperty>> = mutableMapOf()
|
internal val topLevelPropertyMap: MutableMap<FqName, MutableSet<KtProperty>> = mutableMapOf()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows quickly finding [KtClassOrObject]s which have a given simple name as a supertype. The map may contain local classes as well.
|
||||||
|
*/
|
||||||
|
internal val classesBySupertypeName: MutableMap<Name, MutableSet<KtClassOrObject>> = mutableMapOf()
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-3
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
|
|||||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||||
import org.jetbrains.kotlin.name.*
|
import org.jetbrains.kotlin.name.*
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getSuperNames
|
||||||
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
|
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
|
||||||
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
|
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
|
||||||
import org.jetbrains.kotlin.psi.stubs.impl.*
|
import org.jetbrains.kotlin.psi.stubs.impl.*
|
||||||
@@ -203,7 +204,7 @@ public class KotlinStaticDeclarationProviderFactory(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitClassOrObject(classOrObject: KtClassOrObject) {
|
override fun visitClassOrObject(classOrObject: KtClassOrObject) {
|
||||||
addToClassMap(classOrObject)
|
indexClassOrObject(classOrObject)
|
||||||
super.visitClassOrObject(classOrObject)
|
super.visitClassOrObject(classOrObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,6 +237,11 @@ public class KotlinStaticDeclarationProviderFactory(
|
|||||||
}.add(script)
|
}.add(script)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun indexClassOrObject(classOrObject: KtClassOrObject) {
|
||||||
|
addToClassMap(classOrObject)
|
||||||
|
indexSupertypeNames(classOrObject)
|
||||||
|
}
|
||||||
|
|
||||||
private fun addToClassMap(classOrObject: KtClassOrObject) {
|
private fun addToClassMap(classOrObject: KtClassOrObject) {
|
||||||
classOrObject.getClassId()?.let { classId ->
|
classOrObject.getClassId()?.let { classId ->
|
||||||
index.classMap.computeIfAbsent(classId.packageFqName) {
|
index.classMap.computeIfAbsent(classId.packageFqName) {
|
||||||
@@ -244,6 +250,14 @@ public class KotlinStaticDeclarationProviderFactory(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun indexSupertypeNames(classOrObject: KtClassOrObject) {
|
||||||
|
classOrObject.getSuperNames().forEach { superName ->
|
||||||
|
index.classesBySupertypeName
|
||||||
|
.computeIfAbsent(Name.identifier(superName)) { mutableSetOf() }
|
||||||
|
.add(classOrObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun addToTypeAliasMap(typeAlias: KtTypeAlias) {
|
private fun addToTypeAliasMap(typeAlias: KtTypeAlias) {
|
||||||
typeAlias.getClassId()?.let { classId ->
|
typeAlias.getClassId()?.let { classId ->
|
||||||
index.typeAliasMap.computeIfAbsent(classId.packageFqName) {
|
index.typeAliasMap.computeIfAbsent(classId.packageFqName) {
|
||||||
@@ -307,12 +321,12 @@ public class KotlinStaticDeclarationProviderFactory(
|
|||||||
private fun indexStub(stub: StubElement<*>) {
|
private fun indexStub(stub: StubElement<*>) {
|
||||||
when (stub) {
|
when (stub) {
|
||||||
is KotlinClassStubImpl -> {
|
is KotlinClassStubImpl -> {
|
||||||
addToClassMap(stub.psi)
|
indexClassOrObject(stub.psi)
|
||||||
// member functions and properties
|
// member functions and properties
|
||||||
stub.childrenStubs.forEach(::indexStub)
|
stub.childrenStubs.forEach(::indexStub)
|
||||||
}
|
}
|
||||||
is KotlinObjectStubImpl -> {
|
is KotlinObjectStubImpl -> {
|
||||||
addToClassMap(stub.psi)
|
indexClassOrObject(stub.psi)
|
||||||
// member functions and properties
|
// member functions and properties
|
||||||
stub.childrenStubs.forEach(::indexStub)
|
stub.childrenStubs.forEach(::indexStub)
|
||||||
}
|
}
|
||||||
@@ -399,6 +413,9 @@ public class KotlinStaticDeclarationProviderFactory(
|
|||||||
}
|
}
|
||||||
|
|
||||||
public fun getAllKtClasses(): List<KtClassOrObject> = index.classMap.values.flattenTo(mutableListOf())
|
public fun getAllKtClasses(): List<KtClassOrObject> = index.classMap.values.flattenTo(mutableListOf())
|
||||||
|
|
||||||
|
public fun getDirectInheritorCandidates(baseClassName: Name): Set<KtClassOrObject> =
|
||||||
|
index.classesBySupertypeName[baseClassName].orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+4
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals
|
|||||||
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSessionProvider
|
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSessionProvider
|
||||||
import org.jetbrains.kotlin.analysis.api.fir.references.ReadWriteAccessCheckerFirImpl
|
import org.jetbrains.kotlin.analysis.api.fir.references.ReadWriteAccessCheckerFirImpl
|
||||||
import org.jetbrains.kotlin.analysis.api.session.KtAnalysisSessionProvider
|
import org.jetbrains.kotlin.analysis.api.session.KtAnalysisSessionProvider
|
||||||
|
import org.jetbrains.kotlin.analysis.api.standalone.base.providers.KotlinStandaloneDirectInheritorsProvider
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirGlobalResolveComponents
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirGlobalResolveComponents
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirInternals
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirInternals
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirResolveSessionService
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirResolveSessionService
|
||||||
@@ -24,6 +25,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionCach
|
|||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionConfigurator
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionConfigurator
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionInvalidationService
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionInvalidationService
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization.LLStubBasedLibrarySymbolProviderFactory
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization.LLStubBasedLibrarySymbolProviderFactory
|
||||||
|
import org.jetbrains.kotlin.analysis.providers.KotlinDirectInheritorsProvider
|
||||||
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
|
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
|
||||||
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
|
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
|
||||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||||
@@ -63,6 +65,8 @@ object FirStandaloneServiceRegistrar : AnalysisApiStandaloneServiceRegistrar {
|
|||||||
LLFirSessionInvalidationService.getInstance(project).subscribeToModificationEvents()
|
LLFirSessionInvalidationService.getInstance(project).subscribeToModificationEvents()
|
||||||
|
|
||||||
registerService(LLFirDeclarationModificationService::class.java)
|
registerService(LLFirDeclarationModificationService::class.java)
|
||||||
|
|
||||||
|
registerService(KotlinDirectInheritorsProvider::class.java, KotlinStandaloneDirectInheritorsProvider(project))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+99
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2024 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.analysis.api.standalone.base.providers
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import org.jetbrains.kotlin.analysis.api.fir.utils.isSubClassOf
|
||||||
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirInternals
|
||||||
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionCache
|
||||||
|
import org.jetbrains.kotlin.analysis.project.structure.KtDanglingFileModule
|
||||||
|
import org.jetbrains.kotlin.analysis.project.structure.KtModule
|
||||||
|
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
|
||||||
|
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProviderFactory
|
||||||
|
import org.jetbrains.kotlin.analysis.providers.KotlinDirectInheritorsProvider
|
||||||
|
import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticDeclarationProviderFactory
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.psi.KtClass
|
||||||
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.contains
|
||||||
|
import kotlin.collections.filter
|
||||||
|
|
||||||
|
@OptIn(LLFirInternals::class, SymbolInternals::class)
|
||||||
|
class KotlinStandaloneDirectInheritorsProvider(private val project: Project) : KotlinDirectInheritorsProvider {
|
||||||
|
private val staticDeclarationProviderFactory by lazy {
|
||||||
|
KotlinDeclarationProviderFactory.getInstance(project) as? KotlinStaticDeclarationProviderFactory
|
||||||
|
?: error(
|
||||||
|
"`${KotlinStandaloneDirectInheritorsProvider::class.simpleName}` expects the following declaration provider factory to be" +
|
||||||
|
" registered: `${KotlinStaticDeclarationProviderFactory::class.simpleName}`"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDirectKotlinInheritors(
|
||||||
|
ktClass: KtClass,
|
||||||
|
scope: GlobalSearchScope,
|
||||||
|
includeLocalInheritors: Boolean,
|
||||||
|
): Iterable<KtClassOrObject> {
|
||||||
|
val classId = ktClass.getClassId() ?: return emptyList()
|
||||||
|
val possibleInheritors = staticDeclarationProviderFactory.getDirectInheritorCandidates(classId.shortClassName)
|
||||||
|
|
||||||
|
if (possibleInheritors.isEmpty()) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The index provides candidates from an original module, not dangling files. If we resolve the supertypes of a candidate in the
|
||||||
|
// context of its session, we will resolve to FIR classes from non-dangling, original modules. If `ktClass` is inside a dangling
|
||||||
|
// file, the FIR class for `ktClass` will come from the dangling module. So we'd compare the original FIR class for the supertype
|
||||||
|
// with the dangling FIR class for `ktClass`, resulting in a mismatch. To avoid such incompatible comparisons, we need to resolve
|
||||||
|
// `ktClass` to the original FIR class.
|
||||||
|
//
|
||||||
|
// Note that this means we don't support providing inheritors based on the dangling file yet, for example if an inheritor was added
|
||||||
|
// or removed only in the dangling file.
|
||||||
|
val baseKtModule = when (val ktModule = ProjectStructureProvider.getModule(project, ktClass, contextualModule = null)) {
|
||||||
|
is KtDanglingFileModule -> ktModule.contextModule
|
||||||
|
else -> ktModule
|
||||||
|
}
|
||||||
|
|
||||||
|
val baseFirClass = ktClass.toFirSymbol(classId, baseKtModule)?.fir as? FirClass ?: return emptyList()
|
||||||
|
return possibleInheritors.filter { isValidInheritor(it, baseFirClass, scope, includeLocalInheritors) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isValidInheritor(
|
||||||
|
candidate: KtClassOrObject,
|
||||||
|
baseFirClass: FirClass,
|
||||||
|
scope: GlobalSearchScope,
|
||||||
|
includeLocalInheritors: Boolean,
|
||||||
|
): Boolean {
|
||||||
|
if (!includeLocalInheritors && candidate.isLocal) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!scope.contains(candidate)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val candidateClassId = candidate.getClassId() ?: return false
|
||||||
|
val candidateKtModule = ProjectStructureProvider.getModule(project, candidate, contextualModule = null)
|
||||||
|
val candidateFirSymbol = candidate.toFirSymbol(candidateClassId, candidateKtModule) ?: return false
|
||||||
|
val candidateFirClass = candidateFirSymbol.fir as? FirClass ?: return false
|
||||||
|
|
||||||
|
// `KotlinDirectInheritorsProvider`'s interface guarantees that `getDirectKotlinInheritors` is only called from lazy resolution to
|
||||||
|
// `SEALED_CLASS_INHERITORS` or later, so `isSubClassOf` resolving to `SUPER_TYPES` is legal.
|
||||||
|
return isSubClassOf(candidateFirClass, baseFirClass, allowIndirectSubtyping = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtClassOrObject.toFirSymbol(classId: ClassId, ktModule: KtModule): FirClassLikeSymbol<*>? {
|
||||||
|
// Using a resolve session/source-preferred session will cause class stubs from binary libraries to be AST-loaded in IDE mode tests,
|
||||||
|
// which results in an exception since we don't have a decompiler for them. See KT-64898, KT-64899, and KT-64900. If not for these
|
||||||
|
// issues, we would be able to use `analyze` instead of custom session logic.
|
||||||
|
val session = LLFirSessionCache.getInstance(project).getSession(ktModule, preferBinary = true)
|
||||||
|
return session.symbolProvider.getClassLikeSymbolByClassId(classId)
|
||||||
|
}
|
||||||
|
}
|
||||||
-1
@@ -10,7 +10,6 @@ import com.intellij.openapi.Disposable
|
|||||||
import com.intellij.openapi.application.Application
|
import com.intellij.openapi.application.Application
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem
|
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.FirStandaloneServiceRegistrar
|
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.FirStandaloneServiceRegistrar
|
||||||
|
|||||||
Reference in New Issue
Block a user