[LL] Implement a common LLSealedInheritorsProvider
- The new `LLSealedInheritorsProvider` is based on the previous sealed inheritors provider implementation in the IDE. It uses the new direct inheritors provider and the module dependents provider to implement the same functionality that was previously confined to the IDE. With this design we avoid duplication of complex logic such as the KMP handling in `searchInheritors`. - The implementation is designed to work in the production Standalone mode and the aforementioned services have already been implemented for Standalone in prior commits. Now we can get rid of the problematic `SealedClassInheritorsProviderForTests` and tests should more closely match production behavior. - In IDE mode tests, `LLSealedInheritorsProvider` is used with Standalone Analysis API provider implementations. This is in line with the rest of the test infrastructure, where Standalone AA providers are generally used, as IDE providers aren't available. - `KotlinSealedInheritorsProvider` is made obsolete by the common sealed inheritors provider. ^KT-66013 fixed ^KT-64505 fixed
This commit is contained in:
committed by
Space Team
parent
48229f7faa
commit
1374bc8e2d
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
-12
@@ -16,7 +16,6 @@ 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.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
|
||||
@@ -32,8 +31,6 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreProjectEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.setupIdeaStandaloneExecution
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
|
||||
import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProvider
|
||||
import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProviderImpl
|
||||
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
@@ -137,15 +134,6 @@ public class StandaloneAnalysisAPISessionBuilder(
|
||||
)
|
||||
registerService(KotlinPackageProviderMerger::class.java, KotlinStaticPackageProviderMerger(this))
|
||||
|
||||
registerService(
|
||||
LLSealedInheritorsProviderFactory::class.java,
|
||||
object : LLSealedInheritorsProviderFactory {
|
||||
override fun createSealedInheritorsProvider(): SealedClassInheritorsProvider {
|
||||
return SealedClassInheritorsProviderImpl
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
registerService(
|
||||
PackagePartProviderFactory::class.java,
|
||||
KotlinStaticPackagePartProviderFactory(packagePartProvider)
|
||||
|
||||
+1
-1
@@ -52,8 +52,8 @@ MODULE_FRAGMENT
|
||||
CLASS CLASS name:Operation modality:SEALED visibility:public superTypes:[kotlin.Any]
|
||||
sealedSubclasses:
|
||||
CLASS CLASS name:Add modality:FINAL visibility:public superTypes:[<root>.Operation]
|
||||
CLASS CLASS name:Subtract modality:FINAL visibility:public superTypes:[<root>.Operation]
|
||||
CLASS CLASS name:Negate modality:FINAL visibility:public superTypes:[<root>.Operation]
|
||||
CLASS CLASS name:Subtract modality:FINAL visibility:public superTypes:[<root>.Operation]
|
||||
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.Operation
|
||||
CLASS CLASS name:Add modality:FINAL visibility:public superTypes:[<root>.Operation]
|
||||
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.Operation.Add
|
||||
|
||||
+6
-6
@@ -1,7 +1,7 @@
|
||||
Diagnostics from elements:
|
||||
for PSI element of type KtConstructorCalleeExpression at (34,2-12)
|
||||
NO_VALUE_FOR_PARAMETER text ranges: [(115,125)]
|
||||
PSI: KtConstructorCalleeExpression at (34,2-12)
|
||||
for PSI element of type KtNameReferenceExpression at (32,8-10)
|
||||
UNRESOLVED_REFERENCE text ranges: [(94,96)]
|
||||
PSI: KtNameReferenceExpression at (32,8-10)
|
||||
for PSI element of type KtConstructorCalleeExpression at (33,2-12)
|
||||
NO_VALUE_FOR_PARAMETER text ranges: [(114,124)]
|
||||
PSI: KtConstructorCalleeExpression at (33,2-12)
|
||||
for PSI element of type KtNameReferenceExpression at (31,8-10)
|
||||
UNRESOLVED_REFERENCE text ranges: [(93,95)]
|
||||
PSI: KtNameReferenceExpression at (31,8-10)
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// ALLOW_KOTLIN_PACKAGE
|
||||
// DISABLE_SEALED_INHERITOR_CALCULATOR
|
||||
// MODULE: lib1
|
||||
// FILE: anno.kt
|
||||
package kotlin.annotation
|
||||
|
||||
+12
-12
@@ -1,13 +1,13 @@
|
||||
Diagnostics from elements:
|
||||
for PSI element of type KtCallExpression at (24,20) - (27,2)
|
||||
TYPE_MISMATCH text ranges: [(98,160)]
|
||||
PSI: KtCallExpression at (24,20) - (27,2)
|
||||
for PSI element of type KtLambdaExpression at (24,24) - (27,2)
|
||||
TYPE_MISMATCH text ranges: [(102,160)]
|
||||
PSI: KtLambdaExpression at (24,24) - (27,2)
|
||||
for PSI element of type KtNameReferenceExpression at (25,5-17)
|
||||
UNRESOLVED_REFERENCE text ranges: [(108,120)]
|
||||
PSI: KtNameReferenceExpression at (25,5-17)
|
||||
for PSI element of type KtNameReferenceExpression at (26,5-34)
|
||||
UNRESOLVED_REFERENCE text ranges: [(127,156)]
|
||||
PSI: KtNameReferenceExpression at (26,5-34)
|
||||
for PSI element of type KtCallExpression at (23,20) - (26,2)
|
||||
TYPE_MISMATCH text ranges: [(97,159)]
|
||||
PSI: KtCallExpression at (23,20) - (26,2)
|
||||
for PSI element of type KtLambdaExpression at (23,24) - (26,2)
|
||||
TYPE_MISMATCH text ranges: [(101,159)]
|
||||
PSI: KtLambdaExpression at (23,24) - (26,2)
|
||||
for PSI element of type KtNameReferenceExpression at (24,5-17)
|
||||
UNRESOLVED_REFERENCE text ranges: [(107,119)]
|
||||
PSI: KtNameReferenceExpression at (24,5-17)
|
||||
for PSI element of type KtNameReferenceExpression at (25,5-34)
|
||||
UNRESOLVED_REFERENCE text ranges: [(126,155)]
|
||||
PSI: KtNameReferenceExpression at (25,5-34)
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// DISABLE_SEALED_INHERITOR_CALCULATOR
|
||||
// MODULE: lib1
|
||||
// FILE: dependency.kt
|
||||
package usage
|
||||
|
||||
-56
@@ -1,56 +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 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)
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -8,11 +8,11 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.createSealedInheritorsProvider
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.compile.CodeFragmentScopeProvider
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.fir.caches.FirThreadSafeCachesFactory
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirIdePredicateBasedProvider
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirIdeRegisteredPluginAnnotations
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLSealedInheritorsProvider
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions.LLFirNonEmptyResolveExtensionTool
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions.LLFirResolveExtensionTool
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession
|
||||
@@ -37,7 +37,7 @@ import org.jetbrains.kotlin.fir.session.FirSessionConfigurator
|
||||
@SessionConfiguration
|
||||
internal fun LLFirSession.registerIdeComponents(project: Project) {
|
||||
register(FirCachesFactory::class, FirThreadSafeCachesFactory)
|
||||
register(SealedClassInheritorsProvider::class, createSealedInheritorsProvider(project))
|
||||
register(SealedClassInheritorsProvider::class, LLSealedInheritorsProvider(project))
|
||||
register(FirExceptionHandler::class, LLFirExceptionHandler)
|
||||
register(CodeFragmentScopeProvider::class, CodeFragmentScopeProvider(this))
|
||||
registerResolveExtensionTool()
|
||||
|
||||
+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.low.level.api.fir.providers
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KotlinModuleDependentsProvider
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtDanglingFileModule
|
||||
import org.jetbrains.kotlin.analysis.providers.KotlinDirectInheritorsProvider
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProvider
|
||||
import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProviderInternals
|
||||
import org.jetbrains.kotlin.fir.declarations.sealedInheritorsAttr
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.classId
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.collections.filter
|
||||
import kotlin.collections.map
|
||||
import kotlin.collections.mapNotNull
|
||||
import kotlin.collections.plus
|
||||
import kotlin.collections.sortedBy
|
||||
import kotlin.let
|
||||
|
||||
/**
|
||||
* [LLSealedInheritorsProvider] is the LL FIR implementation of [SealedClassInheritorsProvider] for both the IDE and Standalone mode.
|
||||
*/
|
||||
@OptIn(SealedClassInheritorsProviderInternals::class)
|
||||
internal class LLSealedInheritorsProvider(private val project: Project) : SealedClassInheritorsProvider() {
|
||||
val cache = ConcurrentHashMap<ClassId, List<ClassId>>()
|
||||
|
||||
override fun getSealedClassInheritors(firClass: FirRegularClass): List<ClassId> {
|
||||
// Classes from binary libraries which are deserialized from class files (but not stubs) will have their `sealedInheritorsAttr` set
|
||||
// from metadata.
|
||||
firClass.sealedInheritorsAttr?.let { return it.value }
|
||||
|
||||
val classId = firClass.classId
|
||||
|
||||
// Local classes cannot be sealed.
|
||||
if (classId.isLocal) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
return cache.computeIfAbsent(classId) { searchInheritors(firClass) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Some notes about the search:
|
||||
*
|
||||
* - A Java class cannot legally extend a sealed Kotlin class (even in the same package), so we don't need to search for Java class
|
||||
* inheritors.
|
||||
* - Technically, we could use a package scope to narrow the search, but the search is already sufficiently narrow because it uses
|
||||
* supertype indices and is confined to the current `KtModule` in most cases (except for 'expect' classes). Finding a `PsiPackage`
|
||||
* for a `PackageScope` is not cheap, hence the decision to avoid it. If a `PackageScope` is needed in the future, it'd be best to
|
||||
* extract a `PackageNameScope` which operates just with the qualified package name, to avoid `PsiPackage`. (At the time of writing,
|
||||
* this is possible with the implementation of `PackageScope`.)
|
||||
* - We ignore local classes to avoid lazy resolve contract violations.
|
||||
* See KT-63795.
|
||||
* - For `expect` declarations, the search scope includes all modules with a dependsOn dependency on the containing module.
|
||||
* At the same time, `actual` declarations are restricted to the same module and require no special handling.
|
||||
* See KT-45842.
|
||||
* - KMP libraries are not yet supported.
|
||||
* See KT-65591.
|
||||
*/
|
||||
private fun searchInheritors(firClass: FirClass): List<ClassId> {
|
||||
val ktClass = firClass.psi as? KtClass ?: return emptyList()
|
||||
|
||||
val ktModule = when (val classKtModule = firClass.llFirModuleData.ktModule) {
|
||||
is KtDanglingFileModule -> classKtModule.contextModule
|
||||
else -> classKtModule
|
||||
}
|
||||
|
||||
// `FirClass.isExpect` does not depend on the `STATUS` phase because it's already set during FIR building.
|
||||
val scope = if (firClass.isExpect) {
|
||||
val refinementDependents = KotlinModuleDependentsProvider.getInstance(project).getRefinementDependents(ktModule)
|
||||
GlobalSearchScope.union(refinementDependents.map { it.contentScope } + ktModule.contentScope)
|
||||
} else {
|
||||
ktModule.contentScope
|
||||
}
|
||||
|
||||
return searchInScope(ktClass, firClass.classId, scope)
|
||||
}
|
||||
|
||||
private fun searchInScope(ktClass: KtClass, classId: ClassId, scope: GlobalSearchScope): List<ClassId> =
|
||||
KotlinDirectInheritorsProvider.getInstance(project)
|
||||
.getDirectKotlinInheritors(ktClass, scope, includeLocalInheritors = false)
|
||||
.mapNotNull { it.getClassId() }
|
||||
.filter { it.packageFqName == classId.packageFqName }
|
||||
// Enforce a deterministic order on the result, e.g. for stable test output.
|
||||
.sortedBy { it.toString() }
|
||||
.ifEmpty { emptyList() }
|
||||
}
|
||||
+3
-3
@@ -21,11 +21,11 @@ internal object LLFirSealedClassInheritorsLazyResolver : LLFirLazyResolver(FirRe
|
||||
/**
|
||||
* This resolver is responsible for [SEALED_CLASS_INHERITORS][FirResolvePhase.SEALED_CLASS_INHERITORS] phase.
|
||||
*
|
||||
* LL FIR use [LLSealedInheritorsProviderByAnalysisApiProvider][org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.LLSealedInheritorsProviderByAnalysisApiProvider]
|
||||
* LL FIR uses [LLSealedInheritorsProvider][org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLSealedInheritorsProvider]
|
||||
* instead of the compiler [SealedClassInheritorsProviderImpl][org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProviderImpl],
|
||||
* so it does nothing during this phase as sealed class hierarchy will be provided later on demand.
|
||||
* so it does nothing during this phase as sealed inheritors will be provided later on demand.
|
||||
*
|
||||
* @see org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.LLSealedInheritorsProviderFactory
|
||||
* @see org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLSealedInheritorsProvider
|
||||
* @see org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProvider
|
||||
* @see FirResolvePhase.SEALED_CLASS_INHERITORS
|
||||
*/
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// SKIP_WHEN_OUT_OF_CONTENT_ROOT
|
||||
// DISABLE_SEALED_INHERITOR_CALCULATOR
|
||||
// FILE: main.kt
|
||||
fun resol<caret>veMe(i: JavaInterface) = i.id
|
||||
|
||||
|
||||
Vendored
-1
@@ -1,5 +1,4 @@
|
||||
// SKIP_WHEN_OUT_OF_CONTENT_ROOT
|
||||
// DISABLE_SEALED_INHERITOR_CALCULATOR
|
||||
// FILE: main.kt
|
||||
fun reso<caret>lveMe(i: JavaInterface) = i.id
|
||||
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// SKIP_WHEN_OUT_OF_CONTENT_ROOT
|
||||
// DISABLE_SEALED_INHERITOR_CALCULATOR
|
||||
// MODULE: lib1
|
||||
// FILE: dependency.kt
|
||||
package usage
|
||||
|
||||
-2
@@ -52,8 +52,6 @@ abstract class AbstractLowLevelCompilerBasedTest : AbstractCompilerBasedTest() {
|
||||
configureTest(this)
|
||||
defaultConfiguration(this)
|
||||
registerAnalysisApiBaseTestServices(disposable, FirLowLevelCompilerBasedTestConfigurator)
|
||||
useDirectives(SealedClassesInheritorsCalculatorPreAnalysisHandler.Directives)
|
||||
usePreAnalysisHandlers(::SealedClassesInheritorsCalculatorPreAnalysisHandler)
|
||||
useAdditionalServices(service<FirDiagnosticCollectorService>(::AnalysisApiFirDiagnosticCollectorService))
|
||||
|
||||
firHandlersStep {
|
||||
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* 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.low.level.api.fir.compiler.based
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
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.sessions.LLFirSessionCache
|
||||
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.impl.KotlinStaticDeclarationProviderFactory
|
||||
import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider
|
||||
import org.jetbrains.kotlin.analysis.test.framework.project.structure.mainModules
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
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.KtClassOrObject
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectiveApplicability
|
||||
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
|
||||
import org.jetbrains.kotlin.test.services.PreAnalysisHandler
|
||||
import org.jetbrains.kotlin.test.services.TestModuleStructure
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
|
||||
|
||||
class SealedClassesInheritorsCalculatorPreAnalysisHandler(
|
||||
testServices: TestServices,
|
||||
) : PreAnalysisHandler(testServices) {
|
||||
|
||||
override fun preprocessModuleStructure(moduleStructure: TestModuleStructure) {
|
||||
}
|
||||
|
||||
// Manually process all inheritors of sealed classes so that SealedClassInheritorsProviderTestImpl can work correctly for tests.
|
||||
// In the actual IDE, SealedClassInheritorsProviderIdeImpl works by finding inheritors from the index instead of doing a
|
||||
// preprocessing of all files. Therefore, the IDE does not rely on such a pre-analysis pass of all files in the module.
|
||||
override fun prepareSealedClassInheritors(moduleStructure: TestModuleStructure) {
|
||||
if (Directives.DISABLE_SEALED_INHERITOR_CALCULATOR in moduleStructure.allDirectives) {
|
||||
return
|
||||
}
|
||||
|
||||
val project = testServices.compilerConfigurationProvider.getProject(moduleStructure.modules.first())
|
||||
|
||||
val sealedInheritorsProviderFactory =
|
||||
project.getService(LLSealedInheritorsProviderFactory::class.java) as? LLSealedInheritorsProviderFactoryForTests
|
||||
?: return
|
||||
|
||||
val declarationProviderFactory = KotlinDeclarationProviderFactory.getInstance(project) as KotlinStaticDeclarationProviderFactory
|
||||
val projectStructureProvider = project.getService(ProjectStructureProvider::class.java)
|
||||
|
||||
val allKtClasses = declarationProviderFactory.getAllKtClasses()
|
||||
val ktClassesByKtModule = allKtClasses.groupBy { projectStructureProvider.getModule(it, contextualModule = null) }
|
||||
|
||||
for (ktTestModule in testServices.ktModuleProvider.mainModules) {
|
||||
val ktClasses = ktClassesByKtModule[ktTestModule.ktModule] ?: continue
|
||||
val sealedInheritors = collectSealedInheritorClassIds(ktTestModule.ktModule, ktClasses, project)
|
||||
|
||||
sealedInheritorsProviderFactory.registerInheritors(ktTestModule.ktModule, sealedInheritors)
|
||||
}
|
||||
|
||||
// We request and cache sessions while collecting sealed inheritors. To not interfere with the test, we should invalidate all
|
||||
// sessions. Note that, while we could get uncached sessions from `LLFirSessionCache`, dependency sessions will still be cached, so
|
||||
// that is not really an alternative to invalidating sessions.
|
||||
LLFirSessionCache.getInstance(project).removeAllSessions(includeLibraryModules = true)
|
||||
}
|
||||
|
||||
private fun collectSealedInheritorClassIds(
|
||||
ktModule: KtModule,
|
||||
ktClasses: List<KtClassOrObject>,
|
||||
project: Project,
|
||||
): Map<ClassId, List<ClassId>> {
|
||||
val sealedInheritorsByFirClass = mutableMapOf<FirRegularClass, MutableList<ClassId>>()
|
||||
|
||||
ktClasses.forEach { ktClass ->
|
||||
val classId = ktClass.getClassId() ?: return@forEach
|
||||
|
||||
// Using a resolve session/source-preferred session will cause class stubs from binary libraries to be decompiled (which
|
||||
// results in an exception since we don't have a decompiler for them).
|
||||
val firSession = LLFirSessionCache.getInstance(project).getSession(ktModule, preferBinary = true)
|
||||
val firClassSymbol = firSession.symbolProvider.getClassLikeSymbolByClassId(classId) ?: return@forEach
|
||||
firClassSymbol.lazyResolveToPhaseRecursively(FirResolvePhase.TYPES)
|
||||
|
||||
val inheritorsCollector = FirSealedClassInheritorsProcessor.InheritorsCollector(firSession)
|
||||
firClassSymbol.fir.accept(inheritorsCollector, sealedInheritorsByFirClass)
|
||||
}
|
||||
|
||||
return buildMap {
|
||||
sealedInheritorsByFirClass.forEach { firClass, classIds ->
|
||||
put(firClass.symbol.classId, classIds.distinct())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Directives : SimpleDirectivesContainer() {
|
||||
val DISABLE_SEALED_INHERITOR_CALCULATOR by directive(
|
||||
description = "Disable mock sealed class inheritor calculation",
|
||||
applicability = DirectiveApplicability.Global
|
||||
)
|
||||
}
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.services
|
||||
|
||||
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
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProvider
|
||||
import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProviderInternals
|
||||
import org.jetbrains.kotlin.fir.declarations.sealedInheritorsAttr
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.classId
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
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 createSealedInheritorsProvider(): SealedClassInheritorsProvider {
|
||||
return SealedClassInheritorsProviderForTests(inheritorsByModule)
|
||||
}
|
||||
}
|
||||
|
||||
private class SealedClassInheritorsProviderForTests(
|
||||
private val inheritorsByModule: Map<KtModule, Map<ClassId, List<ClassId>>>
|
||||
) : SealedClassInheritorsProvider() {
|
||||
@OptIn(SealedClassInheritorsProviderInternals::class)
|
||||
override fun getSealedClassInheritors(firClass: FirRegularClass): List<ClassId> {
|
||||
val ktModule = firClass.llFirModuleData.ktModule
|
||||
val relevantModule = when (ktModule) {
|
||||
is KtDanglingFileModule -> ktModule.contextModule
|
||||
else -> ktModule
|
||||
}
|
||||
|
||||
// If the module is absent in the map, it's either a binary library that wasn't stub-indexed (e.g. in Standalone mode) or the module
|
||||
// doesn't have any `.kt` files, in which case there cannot be sealed inheritors. `sealedInheritorsAttr` covers the binary library
|
||||
// case, as class-based deserialization sets this attribute.
|
||||
return inheritorsByModule[relevantModule]?.get(firClass.classId) ?: firClass.sealedInheritorsAttr?.value ?: emptyList()
|
||||
}
|
||||
}
|
||||
-3
@@ -8,8 +8,6 @@ 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.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
|
||||
@@ -30,7 +28,6 @@ object AnalysisApiFirTestServiceRegistrar : AnalysisApiTestServiceRegistrar() {
|
||||
FirStandaloneServiceRegistrar.registerProjectServices(project)
|
||||
|
||||
project.apply {
|
||||
registerService(LLSealedInheritorsProviderFactory::class.java, LLSealedInheritorsProviderFactoryForTests())
|
||||
registerService(PackagePartProviderFactory::class.java, PackagePartProviderTestImpl(testServices))
|
||||
registerService(KtCompilerPluginsProvider::class.java, NoOpKtCompilerPluginsProvider)
|
||||
}
|
||||
|
||||
-3
@@ -10,7 +10,6 @@ import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiBaseTestServiceRegistrar
|
||||
import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiDecompiledCodeTestServiceRegistrar
|
||||
import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiLibraryBaseTestServiceRegistrar
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.SealedClassesInheritorsCalculatorPreAnalysisHandler
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.AnalysisApiFirTestServiceRegistrar
|
||||
import org.jetbrains.kotlin.analysis.test.framework.project.structure.KtLibrarySourceTestModuleFactory
|
||||
import org.jetbrains.kotlin.analysis.test.framework.project.structure.KtTestModuleFactory
|
||||
@@ -40,8 +39,6 @@ object AnalysisApiFirLibrarySourceTestConfigurator : AnalysisApiTestConfigurator
|
||||
useAdditionalService<KtTestModuleFactory> { KtLibrarySourceTestModuleFactory }
|
||||
useAdditionalService<TestModuleCompiler> { DispatchingTestModuleCompiler() }
|
||||
useAdditionalService { AnalysisApiIndexingConfiguration(AnalysisApiBinaryLibraryIndexingMode.INDEX_STUBS) }
|
||||
useDirectives(SealedClassesInheritorsCalculatorPreAnalysisHandler.Directives)
|
||||
usePreAnalysisHandlers(::SealedClassesInheritorsCalculatorPreAnalysisHandler)
|
||||
useConfigurators(
|
||||
::AnalysisApiJvmEnvironmentConfigurator,
|
||||
::JsEnvironmentConfigurator,
|
||||
|
||||
-3
@@ -9,7 +9,6 @@ import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiBaseTestServiceRegistrar
|
||||
import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiDecompiledCodeTestServiceRegistrar
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.SealedClassesInheritorsCalculatorPreAnalysisHandler
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.AnalysisApiFirTestServiceRegistrar
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.configureOptionalTestCompilerPlugin
|
||||
import org.jetbrains.kotlin.analysis.test.framework.project.structure.KtTestModuleProjectStructure
|
||||
@@ -34,8 +33,6 @@ abstract class AnalysisApiFirSourceLikeTestConfigurator(override val analyseInDe
|
||||
override fun configureTest(builder: TestConfigurationBuilder, disposable: Disposable) {
|
||||
builder.apply {
|
||||
useAdditionalService { AnalysisApiIndexingConfiguration(AnalysisApiBinaryLibraryIndexingMode.INDEX_STUBS) }
|
||||
useDirectives(SealedClassesInheritorsCalculatorPreAnalysisHandler.Directives)
|
||||
usePreAnalysisHandlers(::SealedClassesInheritorsCalculatorPreAnalysisHandler)
|
||||
configureOptionalTestCompilerPlugin()
|
||||
useConfigurators(
|
||||
::CommonEnvironmentConfigurator,
|
||||
|
||||
Reference in New Issue
Block a user