[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:
Ilya Kirillov
2022-08-06 15:00:07 +02:00
parent f39f1da70d
commit 56e6d5d6b9
6 changed files with 59 additions and 32 deletions
@@ -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)
@@ -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()
} }
@@ -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 }
}
} }
@@ -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