[AA] Add KtClass-based KotlinSealedInheritorsProvider

- The compiler's `SealedClassInheritorsProvider` should not be exposed
  outside LL FIR, as it is an internal compiler component and also
  exposes `FirRegularClass`. `KotlinSealedInheritorsProvider` is an
  Analysis API provider for sealed inheritors that accepts a `KtClass`
  instead.

^KT-64718 fixed
This commit is contained in:
Marco Pennekamp
2024-01-22 15:37:15 +01:00
committed by Space Team
parent e001fa4e72
commit 9a4bf0601e
8 changed files with 106 additions and 44 deletions
@@ -0,0 +1,36 @@
/*
* 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.providers
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtClass
/**
* [KotlinSealedInheritorsProvider] provides a list of direct inheritors for sealed [KtClass]es (which may be a class or an interface). It
* may be created with [KotlinSealedInheritorsProviderFactory].
*
* Implementations of this service should consider caching the results. The [KotlinSealedInheritorsProvider] is tied to the lifetime of its
* owning session, so it will be invalidated automatically with the session.
*/
public interface KotlinSealedInheritorsProvider {
/**
* Returns the sealed inheritors of [ktClass], which may be a class or an interface. [ktClass] is guaranteed to be sealed.
*/
public fun getSealedInheritors(ktClass: KtClass): List<ClassId>
}
public interface KotlinSealedInheritorsProviderFactory {
/**
* Creates a [KotlinSealedInheritorsProvider] for a session.
*/
public fun createSealedInheritorsProvider(): KotlinSealedInheritorsProvider
public companion object {
public fun getInstance(project: Project): KotlinSealedInheritorsProviderFactory? =
project.getService(KotlinSealedInheritorsProviderFactory::class.java)
}
}
@@ -17,7 +17,7 @@ import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.FirSt
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtStaticProjectStructureProvider
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.LLFirStandaloneLibrarySymbolProviderFactory
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.LLSealedInheritorsProviderFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirLibrarySymbolProviderFactory
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.analysis.project.structure.builder.KtModuleProviderBuilder
@@ -140,9 +140,9 @@ public class StandaloneAnalysisAPISessionBuilder(
registerService(KotlinPackageProviderMerger::class.java, KotlinStaticPackageProviderMerger(this))
registerService(
FirSealedClassInheritorsProcessorFactory::class.java,
object : FirSealedClassInheritorsProcessorFactory() {
override fun createSealedClassInheritorsProvider(): SealedClassInheritorsProvider {
LLSealedInheritorsProviderFactory::class.java,
object : LLSealedInheritorsProviderFactory {
override fun createSealedInheritorsProvider(): SealedClassInheritorsProvider {
return SealedClassInheritorsProviderImpl
}
}
@@ -0,0 +1,56 @@
/*
* 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.analysis.low.level.api.fir.api.services
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.providers.KotlinSealedInheritorsProvider
import org.jetbrains.kotlin.analysis.providers.KotlinSealedInheritorsProviderFactory
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProvider
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtClass
interface LLSealedInheritorsProviderFactory {
/**
* Creates a [SealedClassInheritorsProvider] for a session. A new [SealedClassInheritorsProvider] instance should be created on every
* call.
*/
fun createSealedInheritorsProvider(): SealedClassInheritorsProvider
}
/**
* Creates a [SealedClassInheritorsProvider] for a session.
*
* Requires either [LLSealedInheritorsProviderFactory] or [KotlinSealedInheritorsProviderFactory] to be registered as a project service.
*/
internal fun createSealedInheritorsProvider(project: Project): SealedClassInheritorsProvider =
createFromLLProvider(project)
?: createFromAnalysisApiProvider(project)
?: error(
"""
Expected one of the following services to be registered with $project:
- ${LLSealedInheritorsProviderFactory::class.simpleName}
- ${KotlinSealedInheritorsProviderFactory::class.simpleName}
""".trimIndent()
)
private fun createFromLLProvider(project: Project): SealedClassInheritorsProvider? =
project.getService(LLSealedInheritorsProviderFactory::class.java)?.createSealedInheritorsProvider()
private fun createFromAnalysisApiProvider(project: Project): SealedClassInheritorsProvider? =
KotlinSealedInheritorsProviderFactory.getInstance(project)
?.createSealedInheritorsProvider()
?.let(::LLSealedInheritorsProviderByAnalysisApiProvider)
private class LLSealedInheritorsProviderByAnalysisApiProvider(
private val provider: KotlinSealedInheritorsProvider,
) : SealedClassInheritorsProvider() {
override fun getSealedClassInheritors(firClass: FirRegularClass): List<ClassId> {
val ktClass = firClass.psi as? KtClass ?: return emptyList()
return provider.getSealedInheritors(ktClass)
}
}
@@ -1,27 +0,0 @@
/*
* 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.analysis.low.level.api.fir.api.services
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProvider
import org.jetbrains.kotlin.fir.resolve.transformers.FirSealedClassInheritorsProcessor
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
abstract class FirSealedClassInheritorsProcessorFactory {
/**
* Creates [SealedClassInheritorsProvider] for a module.
* This function should crate a new [SealedClassInheritorsProvider] instance on every call.
*/
abstract fun createSealedClassInheritorsProvider(): SealedClassInheritorsProvider
}
/**
* Creates [SealedClassInheritorsProvider] for a module.
* This function should crate a new [SealedClassInheritorsProvider] instance on every call.
*/
internal fun Project.createSealedInheritorsProvider(): SealedClassInheritorsProvider =
getService(FirSealedClassInheritorsProcessorFactory::class.java).createSealedClassInheritorsProvider()
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.IdeSessionComponents
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.createSealedInheritorsProvider
@@ -39,7 +38,7 @@ import org.jetbrains.kotlin.fir.session.FirSessionConfigurator
internal fun LLFirSession.registerIdeComponents(project: Project) {
register(IdeSessionComponents::class, IdeSessionComponents.create())
register(FirCachesFactory::class, FirThreadSafeCachesFactory)
register(SealedClassInheritorsProvider::class, project.createSealedInheritorsProvider())
register(SealedClassInheritorsProvider::class, createSealedInheritorsProvider(project))
register(FirExceptionHandler::class, LLFirExceptionHandler)
register(CodeFragmentScopeProvider::class, CodeFragmentScopeProvider(this))
createResolveExtensionTool()?.let {
@@ -8,8 +8,8 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirResolveSessionService
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.services.LLFirSealedClassInheritorsProcessorFactoryForTests
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.LLSealedInheritorsProviderFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.services.LLSealedInheritorsProviderFactoryForTests
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
import org.jetbrains.kotlin.analysis.test.framework.project.structure.getKtFiles
import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider
@@ -19,7 +19,6 @@ import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.transformers.FirSealedClassInheritorsProcessor
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.directives.model.DirectiveApplicability
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
import org.jetbrains.kotlin.test.services.PreAnalysisHandler
@@ -55,8 +54,7 @@ class SealedClassesInheritorsCaclulatorPreAnalysisHandler(
val tmpFirResolveSession = LLFirResolveSessionService.getInstance(project).getFirResolveSessionNoCaching(ktModule)
val firFiles = ktFiles.map { it.getOrBuildFirFile(tmpFirResolveSession) }
val sealedInheritors = collectSealedClassInheritors(firFiles, tmpFirResolveSession)
val provider =
project.getService(FirSealedClassInheritorsProcessorFactory::class.java) as LLFirSealedClassInheritorsProcessorFactoryForTests
val provider = project.getService(LLSealedInheritorsProviderFactory::class.java) as LLSealedInheritorsProviderFactoryForTests
provider.registerInheritors(ktModule, sealedInheritors)
}
}
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.services
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.LLSealedInheritorsProviderFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData
import org.jetbrains.kotlin.analysis.project.structure.KtDanglingFileModule
import org.jetbrains.kotlin.analysis.project.structure.KtModule
@@ -16,14 +16,14 @@ import org.jetbrains.kotlin.fir.declarations.sealedInheritorsAttr
import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.name.ClassId
internal class LLFirSealedClassInheritorsProcessorFactoryForTests : FirSealedClassInheritorsProcessorFactory() {
internal class LLSealedInheritorsProviderFactoryForTests : LLSealedInheritorsProviderFactory {
private val inheritorsByModule = mutableMapOf<KtModule, Map<ClassId, List<ClassId>>>()
fun registerInheritors(ktModule: KtModule, inheritors: Map<ClassId, List<ClassId>>) {
inheritorsByModule[ktModule] = inheritors
}
override fun createSealedClassInheritorsProvider(): SealedClassInheritorsProvider {
override fun createSealedInheritorsProvider(): SealedClassInheritorsProvider {
return SealedClassInheritorsProviderForTests(inheritorsByModule)
}
}
@@ -8,8 +8,8 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.test.base
import com.intellij.mock.MockApplication
import com.intellij.mock.MockProject
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.FirStandaloneServiceRegistrar
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.services.LLFirSealedClassInheritorsProcessorFactoryForTests
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.LLSealedInheritorsProviderFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.services.LLSealedInheritorsProviderFactoryForTests
import org.jetbrains.kotlin.analysis.low.level.api.fir.services.NoOpKtCompilerPluginsProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.services.PackagePartProviderTestImpl
import org.jetbrains.kotlin.analysis.project.structure.KtCompilerPluginsProvider
@@ -28,7 +28,7 @@ object AnalysisApiFirTestServiceRegistrar : AnalysisApiTestServiceRegistrar() {
project.apply {
FirStandaloneServiceRegistrar.registerProjectServices(project)
registerService(FirSealedClassInheritorsProcessorFactory::class.java, LLFirSealedClassInheritorsProcessorFactoryForTests())
registerService(LLSealedInheritorsProviderFactory::class.java, LLSealedInheritorsProviderFactoryForTests())
registerService(PackagePartProviderFactory::class.java, PackagePartProviderTestImpl(testServices))
registerService(KtCompilerPluginsProvider::class.java, NoOpKtCompilerPluginsProvider)
}