[test] Run checkers in metadata-like style to see what diagnostics metadata compilation omits

Review: https://jetbrains.team/p/kt/reviews/14807

Technically, *.ll.kt should have been covering that. But I see that
there slight differences
This commit is contained in:
Nikita Bobko
2024-03-04 14:51:21 +01:00
committed by Space Team
parent ab069cb7f4
commit 9d566465e6
30 changed files with 330 additions and 87 deletions
@@ -36,7 +36,7 @@ class NoFirCompilationErrorsHandler(testServices: TestServices) : FirAnalysisHan
val diagnosticsPerFile = testServices.firDiagnosticCollectorService.getFrontendDiagnosticsForModule(info)
for ((firFile, diagnostics) in diagnosticsPerFile) {
for (diagnostic in diagnostics) {
for (diagnostic in diagnostics.map { it.diagnostic }) {
if (diagnostic.severity == Severity.ERROR) {
hasError = true
if (!ignoreErrors) {
@@ -11,6 +11,9 @@ import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFa
import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory1
import org.jetbrains.kotlin.checkers.utils.TypeOfCall
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.config.AnalysisFlag
import org.jetbrains.kotlin.config.AnalysisFlags
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.diagnostics.*
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
@@ -135,22 +138,26 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
val firFile = info.mainFirFiles[file] ?: continue
var diagnostics = frontendDiagnosticsPerFile[firFile]
if (AdditionalFilesDirectives.CHECK_TYPE in currentModule.directives) {
diagnostics = diagnostics.filter { it.factory.name != FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.name }
diagnostics = diagnostics.filter { it.diagnostic.factory.name != FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.name }
}
if (LanguageSettingsDirectives.API_VERSION in currentModule.directives) {
diagnostics = diagnostics.filter { it.factory.name != FirErrors.NEWER_VERSION_IN_SINCE_KOTLIN.name }
diagnostics = diagnostics.filter { it.diagnostic.factory.name != FirErrors.NEWER_VERSION_IN_SINCE_KOTLIN.name }
}
val diagnosticsMetadataInfos =
diagnostics.diagnosticCodeMetaInfos(
currentModule, file,
diagnosticsService, globalMetadataInfoHandler,
lightTreeEnabled, lightTreeComparingModeEnabled,
forceRenderArguments,
)
val diagnosticsMetadataInfos = diagnostics
.groupBy({ it.kmpCompilationMode }, { it.diagnostic })
.flatMap { (kmpCompilation, diagnostics) ->
diagnostics.diagnosticCodeMetaInfos(
currentModule, file,
diagnosticsService, globalMetadataInfoHandler,
lightTreeEnabled, lightTreeComparingModeEnabled,
forceRenderArguments,
kmpCompilation
)
}
globalMetadataInfoHandler.addMetadataInfosForFile(file, diagnosticsMetadataInfos)
collectSyntaxDiagnostics(currentModule, file, firFile, lightTreeEnabled, lightTreeComparingModeEnabled, forceRenderArguments)
collectDebugInfoDiagnostics(currentModule, file, firFile, lightTreeEnabled, lightTreeComparingModeEnabled)
fullDiagnosticsRenderer.storeFullDiagnosticRender(module, diagnostics, file)
fullDiagnosticsRenderer.storeFullDiagnosticRender(module, diagnostics.map { it.diagnostic }, file)
}
}
}
@@ -170,7 +177,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
.toMetaInfos(
module,
testFile,
globalMetadataInfoHandler1 = globalMetadataInfoHandler,
globalMetadataInfoHandler = globalMetadataInfoHandler,
lightTreeEnabled,
lightTreeComparingModeEnabled,
forceRenderArguments,
@@ -185,7 +192,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
it.toMetaInfos(
module,
testFile,
globalMetadataInfoHandler1 = globalMetadataInfoHandler,
globalMetadataInfoHandler = globalMetadataInfoHandler,
lightTreeEnabled,
lightTreeComparingModeEnabled,
forceRenderArguments,
@@ -389,6 +396,7 @@ fun List<KtDiagnostic>.diagnosticCodeMetaInfos(
lightTreeEnabled: Boolean,
lightTreeComparingModeEnabled: Boolean,
forceRenderArguments: Boolean = false,
kmpCompilationMode: KmpCompilationMode? = null
): List<FirDiagnosticCodeMetaInfo> = flatMap { diagnostic ->
if (!diagnosticsService.shouldRenderDiagnostic(
module,
@@ -404,6 +412,7 @@ fun List<KtDiagnostic>.diagnosticCodeMetaInfos(
lightTreeEnabled,
lightTreeComparingModeEnabled,
forceRenderArguments,
kmpCompilationMode
)
}
@@ -587,13 +596,14 @@ class PsiLightTreeMetaInfoProcessor(testServices: TestServices) : AbstractTwoAtt
fun KtDiagnostic.toMetaInfos(
module: TestModule,
file: TestFile,
globalMetadataInfoHandler1: GlobalMetadataInfoHandler,
globalMetadataInfoHandler: GlobalMetadataInfoHandler,
lightTreeEnabled: Boolean,
lightTreeComparingModeEnabled: Boolean,
forceRenderArguments: Boolean = false
forceRenderArguments: Boolean = false,
kmpCompilationMode: KmpCompilationMode? = null
): List<FirDiagnosticCodeMetaInfo> = textRanges.map { range ->
val metaInfo = FirDiagnosticCodeMetaInfo(this, FirMetaInfoUtils.renderDiagnosticNoArgs, range)
val shouldRenderArguments = forceRenderArguments || globalMetadataInfoHandler1.getExistingMetaInfosForActualMetadata(file, metaInfo)
val shouldRenderArguments = forceRenderArguments || globalMetadataInfoHandler.getExistingMetaInfosForActualMetadata(file, metaInfo)
.any { it.description != null }
if (shouldRenderArguments) {
metaInfo.replaceRenderConfiguration(FirMetaInfoUtils.renderDiagnosticWithArgs)
@@ -611,10 +621,36 @@ fun KtDiagnostic.toMetaInfos(
else -> error("Should not be here")
}
}
if (kmpCompilationMode == KmpCompilationMode.METADATA) {
metaInfo.attributes += "METADATA"
}
metaInfo
}
typealias DiagnosticsMap = Multimap<FirFile, KtDiagnostic, List<KtDiagnostic>>
typealias DiagnosticsMap = Multimap<FirFile, DiagnosticWithKmpCompilationMode, List<DiagnosticWithKmpCompilationMode>>
data class DiagnosticWithKmpCompilationMode(val diagnostic: KtDiagnostic, val kmpCompilationMode: KmpCompilationMode)
/**
* There are two types of checkers (represented by [MppCheckerKind]):
* 1. Common checker. When a common checker analyzes a code, the checker doesn't see what are the actualizations for the `expect` declarations.
* 2. Platform checker. When a platform checker analyzes a code, the checker sees what are the actualizations for the `expect` declarations
* instead of the `expect` declarations themselves.
*
* KMP is compiled in two different modes (represented by [KmpCompilationMode]):
* 1. Metadata compilation. Metadata compilation compiles only non-platform fragments,
* and it runs both common and platform checkers on those non-platform fragments.
* But in testData, we only check diagnostics from platform checkers in non-platform fragments.
* Common checkers in non-platform targets are tested by "platform compilation" anyway
* 2. Platform compilation. Platform compilation compiles all the fragments (non-platform and platform),
* and it runs common checkers on non-platform fragments,
* and it runs platform checkers on platform fragments
*
* Please don't confuse "platform checker" and "platform compilation"
*/
enum class KmpCompilationMode {
METADATA, PLATFORM, LOW_LEVEL_API
}
open class FirDiagnosticCollectorService(val testServices: TestServices) : TestService {
private val cache: MutableMap<FirOutputArtifact, DiagnosticsMap> = mutableMapOf()
@@ -624,14 +660,14 @@ open class FirDiagnosticCollectorService(val testServices: TestServices) : TestS
}
fun containsErrors(info: FirOutputArtifact): Boolean {
return getFrontendDiagnosticsForModule(info).values.any { it.severity == Severity.ERROR }
return getFrontendDiagnosticsForModule(info).values.any { it.diagnostic.severity == Severity.ERROR }
}
private fun computeDiagnostics(info: FirOutputArtifact): ListMultimap<FirFile, KtDiagnostic> {
private fun computeDiagnostics(info: FirOutputArtifact): ListMultimap<FirFile, DiagnosticWithKmpCompilationMode> {
val allFiles = info.partsForDependsOnModules.flatMap { it.firFiles.values }
val platformPart = info.partsForDependsOnModules.last()
val lazyDeclarationResolver = platformPart.session.lazyDeclarationResolver
val result = listMultimapOf<FirFile, KtDiagnostic>()
val result = listMultimapOf<FirFile, DiagnosticWithKmpCompilationMode>()
lazyDeclarationResolver.disableLazyResolveContractChecksInside {
result += platformPart.session.runCheckers(
@@ -639,7 +675,7 @@ open class FirDiagnosticCollectorService(val testServices: TestServices) : TestS
allFiles,
DiagnosticReporterFactory.createPendingReporter(),
mppCheckerKind = MppCheckerKind.Platform
)
).mapValues { entry -> entry.value.map { DiagnosticWithKmpCompilationMode(it, KmpCompilationMode.PLATFORM) } }
for (part in info.partsForDependsOnModules) {
result += part.session.runCheckers(
@@ -647,7 +683,18 @@ open class FirDiagnosticCollectorService(val testServices: TestServices) : TestS
part.firFiles.values,
DiagnosticReporterFactory.createPendingReporter(),
mppCheckerKind = MppCheckerKind.Common
)
).mapValues { entry -> entry.value.map { DiagnosticWithKmpCompilationMode(it, KmpCompilationMode.PLATFORM) } }
}
for (part in info.partsForDependsOnModules.dropLast(1)) {
part.session.turnOnMetadataCompilationAnalysisFlag {
result += part.session.runCheckers(
part.firAnalyzerFacade.scopeSession,
part.firFiles.values,
DiagnosticReporterFactory.createPendingReporter(),
mppCheckerKind = MppCheckerKind.Platform
).mapValues { entry -> entry.value.map { DiagnosticWithKmpCompilationMode(it, KmpCompilationMode.METADATA) } }
}
}
}
@@ -655,4 +702,20 @@ open class FirDiagnosticCollectorService(val testServices: TestServices) : TestS
}
}
@OptIn(SessionConfiguration::class)
private fun FirSession.turnOnMetadataCompilationAnalysisFlag(body: () -> Unit) {
val originalLv = languageVersionSettings
val lv = object : LanguageVersionSettings by originalLv {
override fun <T> getFlag(flag: AnalysisFlag<T>): T =
@Suppress("UNCHECKED_CAST") // UNCHECKED_CAST is fine because metadataCompilation is boolean flag
if (flag == AnalysisFlags.metadataCompilation) true as T else originalLv.getFlag(flag)
}
register(FirLanguageSettingsComponent::class, FirLanguageSettingsComponent(lv))
try {
body()
} finally {
register(FirLanguageSettingsComponent::class, FirLanguageSettingsComponent(originalLv))
}
}
val TestServices.firDiagnosticCollectorService: FirDiagnosticCollectorService by TestServices.testServiceAccessor()