[FIR] Part 2. Setup running platform checkers in tests

^KT-58881
This commit is contained in:
Dmitriy Novozhilov
2024-01-09 11:54:12 +02:00
committed by Nikolay Lunyak
parent 727d2f46f8
commit c4e4776c5e
11 changed files with 107 additions and 38 deletions
@@ -22,7 +22,8 @@ import org.jetbrains.kotlin.utils.exceptions.withPsiEntry
internal class LLDiagnosticParameterChecker(testServices: TestServices) : FirAnalysisHandler(testServices) {
override fun processModule(module: TestModule, info: FirOutputArtifact) {
for (part in info.partsForDependsOnModules) {
val diagnostics = part.firAnalyzerFacade.runCheckers().values.flatten()
val facade = part.firAnalyzerFacade as LowLevelFirAnalyzerFacade
val diagnostics = facade.runCheckers().values.flatten()
for (diagnostic in diagnostics) {
checkDiagnosticIsSuitableForFirIde(diagnostic as KtPsiDiagnostic)
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDiagnosticsForFile
import org.jetbrains.kotlin.diagnostics.KtDiagnostic
import org.jetbrains.kotlin.fir.AbstractFirAnalyzerFacade
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.pipeline.FirResult
import org.jetbrains.kotlin.fir.pipeline.ModuleCompilerAnalyzedOutput
@@ -34,7 +35,8 @@ open class LowLevelFirAnalyzerFacade(
private var resolved: Boolean = false
override fun runCheckers(): Map<FirFile, List<KtDiagnostic>> {
// TODO: investigate
fun runCheckers(): Map<FirFile, List<KtDiagnostic>> {
if (!resolved) {
runResolution()
resolved = true
@@ -10,6 +10,8 @@ interface Multimap<K, out V, out C : Collection<V>> {
operator fun contains(key: K): Boolean
val keys: Set<K>
val values: Collection<V>
operator fun iterator(): Iterator<Map.Entry<K, C>>
}
interface MutableMultimap<K, V, C : Collection<V>> : Multimap<K, V, C> {
@@ -72,6 +74,11 @@ abstract class BaseMultimap<K, V, C : Collection<V>, MC : MutableCollection<V>>
override fun clear() {
map.clear()
}
override fun iterator(): Iterator<Map.Entry<K, C>> {
@Suppress("UNCHECKED_CAST")
return map.iterator() as Iterator<Map.Entry<K, C>>
}
}
class SetMultimap<K, V> : BaseMultimap<K, V, Set<V>, MutableSet<V>>() {
@@ -96,3 +103,9 @@ class ListMultimap<K, V> : BaseMultimap<K, V, List<V>, MutableList<V>>() {
fun <K, V> setMultimapOf(): SetMultimap<K, V> = SetMultimap()
fun <K, V> listMultimapOf(): ListMultimap<K, V> = ListMultimap()
operator fun <K, V> MutableMultimap<K, V, *>.plusAssign(map: Map<K, Collection<V>>) {
for ((key, values) in map) {
this.putAll(key, values)
}
}
@@ -23,7 +23,7 @@ fun FirSession.runResolution(firFiles: List<FirFile>): Pair<ScopeSession, List<F
fun FirSession.runCheckers(
scopeSession: ScopeSession,
firFiles: List<FirFile>,
firFiles: Collection<FirFile>,
reporter: BaseDiagnosticsCollector,
mppCheckerKind: MppCheckerKind = TODO()
): Map<FirFile, List<KtDiagnostic>> {
@@ -14,19 +14,28 @@ import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.IGNORE_FIR_DIA
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
import org.jetbrains.kotlin.test.frontend.fir.handlers.FirAnalysisHandler
import org.jetbrains.kotlin.test.frontend.fir.handlers.FirDiagnosticCollectorService
import org.jetbrains.kotlin.test.frontend.fir.handlers.firDiagnosticCollectorService
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.ServiceRegistrationData
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.service
class NoFirCompilationErrorsHandler(testServices: TestServices) : FirAnalysisHandler(testServices, failureDisablesNextSteps = true) {
override val directiveContainers: List<DirectivesContainer>
get() = listOf(CodegenTestDirectives)
override val additionalServices: List<ServiceRegistrationData>
get() = listOf(service(::FirDiagnosticCollectorService))
override fun processModule(module: TestModule, info: FirOutputArtifact) {
for (part in info.partsForDependsOnModules) {
var hasError = false
val ignoreErrors = IGNORE_FIR_DIAGNOSTICS in part.module.directives
for ((firFile, diagnostics) in part.firAnalyzerFacade.runCheckers()) {
val diagnosticsPerFile = testServices.firDiagnosticCollectorService.getFrontendDiagnosticsForModule(info)
for ((firFile, diagnostics) in diagnosticsPerFile) {
for (diagnostic in diagnostics) {
if (diagnostic.severity == Severity.ERROR) {
hasError = true
@@ -36,7 +45,7 @@ class NoFirCompilationErrorsHandler(testServices: TestServices) : FirAnalysisHan
val locationText = firFile.source?.psi?.containingFile?.let { psiFile ->
PsiDiagnosticUtils.atLocation(psiFile, range)
} ?: "${firFile.name}:$range"
throw IllegalStateException("${diagnostic.factory.name}: $diagnosticText at $locationText")
error("${diagnostic.factory.name}: $diagnosticText at $locationText")
}
}
}
@@ -32,13 +32,13 @@ import org.jetbrains.kotlin.library.unresolvedDependencies
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.test.backend.ir.IrBackendInput
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
import org.jetbrains.kotlin.test.frontend.fir.handlers.FirDiagnosticCollectorService
import org.jetbrains.kotlin.test.frontend.fir.handlers.firDiagnosticCollectorService
import org.jetbrains.kotlin.test.model.BackendKinds
import org.jetbrains.kotlin.test.model.Frontend2BackendConverter
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
import org.jetbrains.kotlin.test.services.libraryProvider
import org.jetbrains.kotlin.test.services.*
abstract class AbstractFir2IrNonJvmResultsConverter(
testServices: TestServices
@@ -52,11 +52,17 @@ abstract class AbstractFir2IrNonJvmResultsConverter(
protected abstract fun resolveLibraries(module: TestModule, compilerConfiguration: CompilerConfiguration): List<KotlinResolvedLibrary>
protected abstract val klibFactories: KlibMetadataFactories
final override val additionalServices: List<ServiceRegistrationData>
get() = listOf(service(::FirDiagnosticCollectorService))
final override fun transform(module: TestModule, inputArtifact: FirOutputArtifact): IrBackendInput? =
try {
transformInternal(module, inputArtifact)
} catch (e: Throwable) {
if (CodegenTestDirectives.IGNORE_FIR2IR_EXCEPTIONS_IF_FIR_CONTAINS_ERRORS in module.directives && inputArtifact.hasErrors) {
if (
CodegenTestDirectives.IGNORE_FIR2IR_EXCEPTIONS_IF_FIR_CONTAINS_ERRORS in module.directives &&
testServices.firDiagnosticCollectorService.containsErrors(inputArtifact)
) {
null
} else {
throw e
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.library.metadata.KlibMetadataFactories
import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.test.backend.ir.IrBackendInput
import org.jetbrains.kotlin.test.frontend.fir.handlers.firDiagnosticCollectorService
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.utils.metadataVersion
@@ -77,7 +78,7 @@ abstract class Fir2IrJsWasmResultsConverter(testServices: TestServices) : Abstra
sourceFiles,
compilerConfiguration.incrementalDataProvider?.getSerializedData(sourceFiles) ?: emptyList(),
diagnosticReporter,
inputArtifact.hasErrors,
testServices.firDiagnosticCollectorService.containsErrors(inputArtifact),
/*descriptorMangler = */null,
manglers.irMangler,
manglers.firMangler,
@@ -20,12 +20,16 @@ import org.jetbrains.kotlin.fir.pipeline.convertToIrAndActualize
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmIrMangler
import org.jetbrains.kotlin.test.backend.ir.IrBackendInput
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
import org.jetbrains.kotlin.test.frontend.fir.handlers.FirDiagnosticCollectorService
import org.jetbrains.kotlin.test.frontend.fir.handlers.firDiagnosticCollectorService
import org.jetbrains.kotlin.test.model.BackendKinds
import org.jetbrains.kotlin.test.model.Frontend2BackendConverter
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.ServiceRegistrationData
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
import org.jetbrains.kotlin.test.services.service
class Fir2IrJvmResultsConverter(
testServices: TestServices
@@ -34,6 +38,9 @@ class Fir2IrJvmResultsConverter(
FrontendKinds.FIR,
BackendKinds.IrBackend
) {
override val additionalServices: List<ServiceRegistrationData>
get() = listOf(service(::FirDiagnosticCollectorService))
override fun transform(
module: TestModule,
inputArtifact: FirOutputArtifact
@@ -41,7 +48,10 @@ class Fir2IrJvmResultsConverter(
return try {
transformInternal(module, inputArtifact)
} catch (e: Throwable) {
if (CodegenTestDirectives.IGNORE_FIR2IR_EXCEPTIONS_IF_FIR_CONTAINS_ERRORS in module.directives && inputArtifact.hasErrors) {
if (
CodegenTestDirectives.IGNORE_FIR2IR_EXCEPTIONS_IF_FIR_CONTAINS_ERRORS in module.directives &&
testServices.firDiagnosticCollectorService.containsErrors(inputArtifact)
) {
null
} else {
throw e
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.test.frontend.fir
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.fir.AbstractFirAnalyzerFacade
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirFile
@@ -29,12 +28,6 @@ abstract class FirOutputArtifact(val partsForDependsOnModules: List<FirOutputPar
get() = FrontendKinds.FIR
val mainFirFiles: Map<TestFile, FirFile> by lazy { allFirFiles.filterKeys { !it.isAdditional } }
val hasErrors: Boolean by lazy {
partsForDependsOnModules.any { part ->
part.firAnalyzerFacade.runCheckers().values.any { diagnostics -> diagnostics.any { it.severity == Severity.ERROR } }
}
}
}
class FirOutputArtifactImpl(parts: List<FirOutputPartForDependsOnModule>) : FirOutputArtifact(parts)
class FirOutputArtifactImpl(parts: List<FirOutputPartForDependsOnModule>) : FirOutputArtifact(parts)
@@ -15,10 +15,12 @@ import org.jetbrains.kotlin.diagnostics.*
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.builder.FirSyntaxErrors
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.pipeline.runCheckers
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
@@ -29,6 +31,10 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.lazyDeclarationResolver
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.util.ListMultimap
import org.jetbrains.kotlin.fir.util.Multimap
import org.jetbrains.kotlin.fir.util.listMultimapOf
import org.jetbrains.kotlin.fir.util.plusAssign
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.platform.isCommon
@@ -55,6 +61,7 @@ import org.jetbrains.kotlin.test.utils.AbstractTwoAttributesMetaInfoProcessor
import org.jetbrains.kotlin.test.utils.MultiModuleInfoDumper
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
import org.jetbrains.kotlin.utils.addToStdlib.getOrPut
class FullDiagnosticsRenderer(private val directive: SimpleDirective) {
private val dumper: MultiModuleInfoDumper = MultiModuleInfoDumper(moduleHeaderTemplate = "// -- Module: <%s> --")
@@ -98,7 +105,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
listOf(DiagnosticsDirectives)
override val additionalServices: List<ServiceRegistrationData> =
listOf(service(::DiagnosticsService))
listOf(service(::DiagnosticsService), service(::FirDiagnosticCollectorService))
private val fullDiagnosticsRenderer = FullDiagnosticsRenderer(DiagnosticsDirectives.RENDER_DIAGNOSTICS_FULL_TEXT)
@@ -107,11 +114,9 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
}
override fun processModule(module: TestModule, info: FirOutputArtifact) {
for (part in info.partsForDependsOnModules) {
val firAnalyzerFacade = part.firAnalyzerFacade
val lazyDeclarationResolver = firAnalyzerFacade.result.outputs.single().session.lazyDeclarationResolver
val diagnosticsPerFile = lazyDeclarationResolver.disableLazyResolveContractChecksInside { firAnalyzerFacade.runCheckers() }
val frontendDiagnosticsPerFile = testServices.firDiagnosticCollectorService.getFrontendDiagnosticsForModule(info)
for (part in info.partsForDependsOnModules) {
val currentModule = part.module
val lightTreeComparingModeEnabled = FirDiagnosticsDirectives.COMPARE_WITH_LIGHT_TREE in currentModule.directives
val lightTreeEnabled = currentModule.directives.singleValue(FirDiagnosticsDirectives.FIR_PARSER) == FirParser.LightTree
@@ -119,7 +124,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
for (file in currentModule.files) {
val firFile = info.mainFirFiles[file] ?: continue
var diagnostics = diagnosticsPerFile[firFile] ?: continue
var diagnostics = frontendDiagnosticsPerFile[firFile]
if (AdditionalFilesDirectives.CHECK_TYPE in currentModule.directives) {
diagnostics = diagnostics.filter { it.factory.name != FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.name }
}
@@ -598,3 +603,45 @@ fun KtDiagnostic.toMetaInfos(
metaInfo
}
typealias DiagnosticsMap = Multimap<FirFile, KtDiagnostic, List<KtDiagnostic>>
class FirDiagnosticCollectorService(val testServices: TestServices) : TestService {
private val cache: MutableMap<FirOutputArtifact, DiagnosticsMap> = mutableMapOf()
fun getFrontendDiagnosticsForModule(info: FirOutputArtifact): DiagnosticsMap {
return cache.getOrPut(info) { computeDiagnostics(info) }
}
fun containsErrors(info: FirOutputArtifact): Boolean {
return getFrontendDiagnosticsForModule(info).values.any { it.severity == Severity.ERROR }
}
private fun computeDiagnostics(info: FirOutputArtifact): ListMultimap<FirFile, KtDiagnostic> {
val allFiles = info.partsForDependsOnModules.flatMap { it.firFiles.values }
val platformPart = info.partsForDependsOnModules.last()
val lazyDeclarationResolver = platformPart.session.lazyDeclarationResolver
val result = listMultimapOf<FirFile, KtDiagnostic>()
lazyDeclarationResolver.disableLazyResolveContractChecksInside {
result += platformPart.session.runCheckers(
platformPart.firAnalyzerFacade.scopeSession,
allFiles,
DiagnosticReporterFactory.createPendingReporter(),
mppCheckerKind = MppCheckerKind.Platform
)
for (part in info.partsForDependsOnModules) {
result += part.session.runCheckers(
platformPart.firAnalyzerFacade.scopeSession,
part.firFiles.values,
DiagnosticReporterFactory.createPendingReporter(),
mppCheckerKind = MppCheckerKind.Common
)
}
}
return result
}
}
val TestServices.firDiagnosticCollectorService: FirDiagnosticCollectorService by TestServices.testServiceAccessor()
@@ -7,8 +7,6 @@ package org.jetbrains.kotlin.fir
import org.jetbrains.kotlin.KtSourceFile
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.diagnostics.KtDiagnostic
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.pipeline.*
import org.jetbrains.kotlin.fir.resolve.ScopeSession
@@ -19,8 +17,6 @@ abstract class AbstractFirAnalyzerFacade {
abstract val scopeSession: ScopeSession
abstract val result: FirResult
abstract fun runCheckers(): Map<FirFile, List<KtDiagnostic>>
abstract fun runResolution(): List<FirFile>
}
@@ -39,8 +35,6 @@ class FirAnalyzerFacade(
override val result: FirResult
get() = FirResult(listOf(ModuleCompilerAnalyzedOutput(session, scopeSession, firFiles!!)))
private var collectedDiagnostics: Map<FirFile, List<KtDiagnostic>>? = null
private fun buildRawFir() {
if (firFiles != null) return
firFiles = when (parser) {
@@ -55,11 +49,4 @@ class FirAnalyzerFacade(
_scopeSession = session.runResolution(firFiles!!).first
return firFiles!!
}
override fun runCheckers(): Map<FirFile, List<KtDiagnostic>> {
if (_scopeSession == null) runResolution()
if (collectedDiagnostics != null) return collectedDiagnostics!!
collectedDiagnostics = session.runCheckers(scopeSession, firFiles!!, DiagnosticReporterFactory.createPendingReporter())
return collectedDiagnostics!!
}
}