[Analysis API] do not preresolve all FirDeclarations to TYPES in tests
Previously, we resolved all declaration to the TYPES phase to collect their sealed inheritors and thus not really checking lazy resolution to TYPES. Now sealed class inheritors collection happens in separate LLFirResolveSession
This commit is contained in:
+1
@@ -47,6 +47,7 @@ abstract class AbstractCompilerBasedTestForFir : AbstractCompilerBasedTest() {
|
|||||||
configureTest()
|
configureTest()
|
||||||
defaultConfiguration(this)
|
defaultConfiguration(this)
|
||||||
registerAnalysisApiBaseTestServices(disposable, FirLowLevelCompilerBasedTestConfigurator)
|
registerAnalysisApiBaseTestServices(disposable, FirLowLevelCompilerBasedTestConfigurator)
|
||||||
|
usePreAnalysisHandlers(::SealedClassesInheritorsCaclulatorPreAnalysisHandler)
|
||||||
|
|
||||||
firHandlersStep {
|
firHandlersStep {
|
||||||
useHandlers(::LLDiagnosticParameterChecker)
|
useHandlers(::LLDiagnosticParameterChecker)
|
||||||
|
|||||||
-9
@@ -30,7 +30,6 @@ class LowLevelFirAnalyzerFacade(
|
|||||||
get() = ScopeSession()
|
get() = ScopeSession()
|
||||||
|
|
||||||
override fun runCheckers(): Map<FirFile, List<KtDiagnostic>> {
|
override fun runCheckers(): Map<FirFile, List<KtDiagnostic>> {
|
||||||
findSealedInheritors()
|
|
||||||
return allFirFiles.values.associateWith { firFile ->
|
return allFirFiles.values.associateWith { firFile ->
|
||||||
val ktFile = firFile.psi as KtFile
|
val ktFile = firFile.psi as KtFile
|
||||||
val diagnostics = ktFile.collectDiagnosticsForFile(firResolveSession, diagnosticCheckerFilter)
|
val diagnostics = ktFile.collectDiagnosticsForFile(firResolveSession, diagnosticCheckerFilter)
|
||||||
@@ -39,14 +38,6 @@ class LowLevelFirAnalyzerFacade(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findSealedInheritors() {
|
|
||||||
allFirFiles.values.forEach { firFile ->
|
|
||||||
firFile.ensureResolved(FirResolvePhase.SUPER_TYPES)
|
|
||||||
}
|
|
||||||
val sealedProcessor = FirSealedClassInheritorsProcessor(allFirFiles.values.first().moduleData.session, ScopeSession())
|
|
||||||
sealedProcessor.process(allFirFiles.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun runResolution(): List<FirFile> = shouldNotBeCalled()
|
override fun runResolution(): List<FirFile> = shouldNotBeCalled()
|
||||||
override fun convertToIr(fir2IrExtensions: Fir2IrExtensions): Fir2IrResult = shouldNotBeCalled()
|
override fun convertToIr(fir2IrExtensions: Fir2IrExtensions): Fir2IrResult = shouldNotBeCalled()
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-19
@@ -5,15 +5,19 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based
|
package org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based
|
||||||
|
|
||||||
|
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.getOrBuildFirFile
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getFirResolveSession
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory
|
||||||
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.createFirResolveSessionForNoCaching
|
||||||
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.services.LLFirSealedClassInheritorsProcessorFactoryForTests
|
||||||
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
|
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
|
||||||
import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider
|
import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.FirSealedClassInheritorsProcessor
|
import org.jetbrains.kotlin.fir.resolve.transformers.FirSealedClassInheritorsProcessor
|
||||||
import org.jetbrains.kotlin.fir.symbols.ensureResolved
|
import org.jetbrains.kotlin.fir.symbols.ensureResolved
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.test.services.PreAnalysisHandler
|
import org.jetbrains.kotlin.test.services.PreAnalysisHandler
|
||||||
import org.jetbrains.kotlin.test.services.TestModuleStructure
|
import org.jetbrains.kotlin.test.services.TestModuleStructure
|
||||||
@@ -27,30 +31,37 @@ class SealedClassesInheritorsCaclulatorPreAnalysisHandler(
|
|||||||
override fun preprocessModuleStructure(moduleStructure: TestModuleStructure) {
|
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 do 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) {
|
override fun prepareSealedClassInheritors(moduleStructure: TestModuleStructure) {
|
||||||
val ktFilesByModule = moduleStructure.modules.associateWith { testModule ->
|
val ktFilesByModule = moduleStructure.modules.associateWith { testModule ->
|
||||||
testServices.ktModuleProvider.getModuleFiles(testModule).filterIsInstance<KtFile>()
|
testServices.ktModuleProvider.getModuleFiles(testModule).filterIsInstance<KtFile>()
|
||||||
}
|
}
|
||||||
|
|
||||||
ktFilesByModule.forEach { (testModule, ktFiles) ->
|
for ((testModule, ktFiles) in ktFilesByModule) {
|
||||||
|
if (ktFiles.isEmpty()) continue
|
||||||
val project = testServices.compilerConfigurationProvider.getProject(testModule)
|
val project = testServices.compilerConfigurationProvider.getProject(testModule)
|
||||||
// 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 do a
|
|
||||||
// preprocessing of all files. Therefore, the IDE does not rely on such a pre-analysis pass of all files in the module.
|
|
||||||
val ktModuleProvider = project.getService(ProjectStructureProvider::class.java)
|
val ktModuleProvider = project.getService(ProjectStructureProvider::class.java)
|
||||||
val allFirFiles = mutableListOf<FirFile>()
|
val ktModule = ktFiles.map(ktModuleProvider::getKtModuleForKtElement).distinct().single()
|
||||||
ktFiles.forEach { ktFile ->
|
|
||||||
val ktModule = ktModuleProvider.getKtModuleForKtElement(ktFile)
|
val tmpFirResolveSession = createFirResolveSessionForNoCaching(ktModule, project)
|
||||||
val firResolveSession = ktModule.getFirResolveSession(project)
|
val firFiles = ktFiles.map { it.getOrBuildFirFile(tmpFirResolveSession) }
|
||||||
allFirFiles.add(ktFile.getOrBuildFirFile(firResolveSession))
|
val sealedInheritors = collectSealedClassInheritors(firFiles, tmpFirResolveSession)
|
||||||
}
|
val provider =
|
||||||
allFirFiles.groupBy { it.moduleData.session }.forEach { (session, firFiles) ->
|
project.getService(FirSealedClassInheritorsProcessorFactory::class.java) as LLFirSealedClassInheritorsProcessorFactoryForTests
|
||||||
for (firFile in firFiles) {
|
provider.registerInheritors(ktModule, sealedInheritors)
|
||||||
firFile.ensureResolved(FirResolvePhase.TYPES)
|
|
||||||
}
|
|
||||||
val sealedProcessor = FirSealedClassInheritorsProcessor(session, ScopeSession())
|
|
||||||
sealedProcessor.process(firFiles)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun collectSealedClassInheritors(
|
||||||
|
firFiles: List<FirFile>,
|
||||||
|
tmpFirResolveSession: LLFirResolveSession
|
||||||
|
): Map<ClassId, List<ClassId>> {
|
||||||
|
firFiles.forEach { it.ensureResolved(FirResolvePhase.TYPES) }
|
||||||
|
val inheritorsCollector = FirSealedClassInheritorsProcessor.InheritorsCollector(tmpFirResolveSession.useSiteFirSession)
|
||||||
|
val sealedClassInheritorsMap = mutableMapOf<FirRegularClass, MutableList<ClassId>>()
|
||||||
|
firFiles.forEach { it.accept(inheritorsCollector, sealedClassInheritorsMap) }
|
||||||
|
return sealedClassInheritorsMap.mapKeys { (firClass, _) -> firClass.symbol.classId }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-3
@@ -6,11 +6,32 @@
|
|||||||
package org.jetbrains.kotlin.analysis.low.level.api.fir.services
|
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.FirSealedClassInheritorsProcessorFactory
|
||||||
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData
|
||||||
|
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.SealedClassInheritorsProvider
|
||||||
import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProviderImpl
|
import org.jetbrains.kotlin.fir.declarations.utils.classId
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
|
||||||
internal class LLFirSealedClassInheritorsProcessorFactoryForTests : FirSealedClassInheritorsProcessorFactory() {
|
internal class LLFirSealedClassInheritorsProcessorFactoryForTests : FirSealedClassInheritorsProcessorFactory() {
|
||||||
override fun createSealedClassInheritorsProvider(): SealedClassInheritorsProvider {
|
private val inheritorsByModule = mutableMapOf<KtModule, Map<ClassId, List<ClassId>>>()
|
||||||
return SealedClassInheritorsProviderImpl
|
|
||||||
|
fun registerInheritors(ktModule: KtModule, inheritors: Map<ClassId, List<ClassId>>) {
|
||||||
|
inheritorsByModule[ktModule] = inheritors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun createSealedClassInheritorsProvider(): SealedClassInheritorsProvider {
|
||||||
|
return SealedClassInheritorsProviderForTests(inheritorsByModule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SealedClassInheritorsProviderForTests(
|
||||||
|
private val inheritorsByModule: Map<KtModule, Map<ClassId, List<ClassId>>>
|
||||||
|
) : SealedClassInheritorsProvider() {
|
||||||
|
override fun getSealedClassInheritors(firClass: FirRegularClass): List<ClassId> {
|
||||||
|
val ktModule = firClass.llFirModuleData.ktModule
|
||||||
|
val inheritorsForModuleMap = inheritorsByModule.getValue(ktModule)
|
||||||
|
return inheritorsForModuleMap[firClass.classId].orEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -81,6 +81,10 @@ class TestRunner(private val testConfiguration: TestConfiguration) {
|
|||||||
preprocessor.preprocessModuleStructure(moduleStructure)
|
preprocessor.preprocessModuleStructure(moduleStructure)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testConfiguration.preAnalysisHandlers.forEach { preprocessor ->
|
||||||
|
preprocessor.prepareSealedClassInheritors(moduleStructure)
|
||||||
|
}
|
||||||
|
|
||||||
for (module in modules) {
|
for (module in modules) {
|
||||||
val shouldProcessNextModules = processModule(module, dependencyProvider)
|
val shouldProcessNextModules = processModule(module, dependencyProvider)
|
||||||
if (!shouldProcessNextModules) break
|
if (!shouldProcessNextModules) break
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// FIR_IDE_IGNORE
|
|
||||||
// FIR_IDENTICAL
|
// FIR_IDENTICAL
|
||||||
// MODULE: m1
|
// MODULE: m1
|
||||||
// FILE: test/Foo.java
|
// FILE: test/Foo.java
|
||||||
|
|||||||
Reference in New Issue
Block a user