Add FIR version of JspecifyDiagnosticComplianceHandler

This commit is contained in:
pyos
2021-08-10 12:34:58 +02:00
committed by Mikhail Glukhikh
parent 608b88996a
commit cd26ef2bb5
2 changed files with 68 additions and 43 deletions
@@ -5,64 +5,89 @@
package org.jetbrains.kotlin.test.frontend.classic.handlers
import org.jetbrains.kotlin.codeMetaInfo.model.CodeMetaInfo
import org.jetbrains.kotlin.codeMetaInfo.model.DiagnosticCodeMetaInfo
import org.jetbrains.kotlin.codeMetaInfo.model.JspecifyMarkerCodeMetaInfo
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.load.java.JSPECIFY_ANNOTATIONS_PACKAGE
import org.jetbrains.kotlin.load.java.ReportLevel
import org.jetbrains.kotlin.load.java.getDefaultReportLevelForAnnotation
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.test.directives.ForeignAnnotationsDirectives
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
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.FirDiagnosticCodeMetaInfo
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.globalMetadataInfoHandler
import org.jetbrains.kotlin.test.services.sourceFileProvider
// Not that this diagnostic handler should be included only with `ClassicDiagnosticsHandler` and go after it
class JspecifyDiagnosticComplianceHandler(testServices: TestServices) : ClassicFrontendAnalysisHandler(testServices) {
override fun processModule(module: TestModule, info: ClassicFrontendOutputArtifact) {
val jspecifyMode = module.directives[ForeignAnnotationsDirectives.JSPECIFY_STATE].singleOrNull()
?: getDefaultReportLevelForAnnotation(JSPECIFY_ANNOTATIONS_PACKAGE)
private fun TestServices.generateJspecifyMetadataInfos(
module: TestModule, files: Iterable<TestFile>, diagnosticKind: (CodeMetaInfo) -> Any?
) {
val jspecifyMode = module.directives[ForeignAnnotationsDirectives.JSPECIFY_STATE].singleOrNull()
?: getDefaultReportLevelForAnnotation(JSPECIFY_ANNOTATIONS_PACKAGE)
val diagnosticsToJspecifyMarksForMode = diagnosticsToJspecifyMarks[jspecifyMode] ?: return
for ((testFile, ktFile) in info.allKtFiles) {
val reportedDiagnostics =
testServices.globalMetadataInfoHandler.getReportedMetaInfosForFile(testFile).filterIsInstance<DiagnosticCodeMetaInfo>()
for (metaInfo in reportedDiagnostics) {
val jspecifyMark = diagnosticsToJspecifyMarks.getValue(jspecifyMode)[metaInfo.diagnostic.factory] ?: continue
val fileLines = ktFile.text.lines()
val fileLinePositions =
fileLines.map { it.length }.runningReduce { sumLength, length -> sumLength + length + 1 }
val lineIndexToPasteJspecifyMark = fileLinePositions.indexOfLast { it < metaInfo.start }
val positionToPasteJspecifyMark = fileLinePositions[lineIndexToPasteJspecifyMark]
val offset = fileLines[lineIndexToPasteJspecifyMark + 1].takeWhile { it == ' ' }.length
testServices.globalMetadataInfoHandler.addMetadataInfosForFile(
testFile,
listOf(JspecifyMarkerCodeMetaInfo(positionToPasteJspecifyMark, positionToPasteJspecifyMark, offset, jspecifyMark))
)
}
for (testFile in files) {
val fileLines = sourceFileProvider.getContentOfSourceFile(testFile).lines()
val fileLinePositions =
fileLines.map { it.length }.runningReduce { sumLength, length -> sumLength + length + 1 }
val newMetaInfos = globalMetadataInfoHandler.getReportedMetaInfosForFile(testFile).mapNotNull { metaInfo ->
val diagnostic = diagnosticKind(metaInfo) ?: return@mapNotNull null
val jspecifyMark = diagnosticsToJspecifyMarksForMode[diagnostic] ?: return@mapNotNull null
val lineIndexToPasteJspecifyMark = fileLinePositions.indexOfLast { it < metaInfo.start }
val positionToPasteJspecifyMark = fileLinePositions[lineIndexToPasteJspecifyMark]
val offset = fileLines[lineIndexToPasteJspecifyMark + 1].takeWhile { it == ' ' }.length
JspecifyMarkerCodeMetaInfo(positionToPasteJspecifyMark, positionToPasteJspecifyMark, offset, jspecifyMark)
}
globalMetadataInfoHandler.addMetadataInfosForFile(testFile, newMetaInfos)
}
}
internal val diagnosticsToJspecifyMarks = mapOf(
ReportLevel.WARN to mapOf(
ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS to "jspecify_nullness_mismatch",
ErrorsJvm.UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS to "jspecify_nullness_mismatch",
ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS to "jspecify_nullness_mismatch",
ErrorsJvm.RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS to "jspecify_nullness_mismatch",
// TODO: list FIR warnings (they don't exist yet)
),
ReportLevel.STRICT to mapOf(
Errors.TYPE_MISMATCH to "jspecify_nullness_mismatch",
Errors.NULL_FOR_NONNULL_TYPE to "jspecify_nullness_mismatch",
Errors.NOTHING_TO_OVERRIDE to "jspecify_nullness_mismatch",
Errors.RETURN_TYPE_MISMATCH_ON_OVERRIDE to "jspecify_nullness_mismatch",
Errors.UPPER_BOUND_VIOLATED to "jspecify_nullness_mismatch",
Errors.UNSAFE_CALL to "jspecify_nullness_mismatch",
FirErrors.ARGUMENT_TYPE_MISMATCH to "jspecify_nullness_mismatch",
FirErrors.NULL_FOR_NONNULL_TYPE to "jspecify_nullness_mismatch",
FirErrors.NOTHING_TO_OVERRIDE to "jspecify_nullness_mismatch",
FirErrors.RETURN_TYPE_MISMATCH_ON_OVERRIDE to "jspecify_nullness_mismatch",
FirErrors.UPPER_BOUND_VIOLATED to "jspecify_nullness_mismatch",
FirErrors.UNSAFE_CALL to "jspecify_nullness_mismatch",
),
ReportLevel.IGNORE to emptyMap()
)
class JspecifyDiagnosticComplianceHandler(testServices: TestServices) : ClassicFrontendAnalysisHandler(testServices) {
override fun processModule(module: TestModule, info: ClassicFrontendOutputArtifact) =
testServices.generateJspecifyMetadataInfos(module, info.allKtFiles.keys) {
(it as? DiagnosticCodeMetaInfo)?.diagnostic?.factory
}
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
}
companion object {
val diagnosticsToJspecifyMarks = mapOf(
ReportLevel.WARN to mapOf(
ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS to "jspecify_nullness_mismatch",
ErrorsJvm.UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS to "jspecify_nullness_mismatch",
ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS to "jspecify_nullness_mismatch",
ErrorsJvm.RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS to "jspecify_nullness_mismatch",
),
ReportLevel.STRICT to mapOf(
Errors.TYPE_MISMATCH to "jspecify_nullness_mismatch",
Errors.NULL_FOR_NONNULL_TYPE to "jspecify_nullness_mismatch",
Errors.NOTHING_TO_OVERRIDE to "jspecify_nullness_mismatch",
Errors.RETURN_TYPE_MISMATCH_ON_OVERRIDE to "jspecify_nullness_mismatch",
Errors.UPPER_BOUND_VIOLATED to "jspecify_nullness_mismatch",
Errors.UNSAFE_CALL to "jspecify_nullness_mismatch",
),
ReportLevel.IGNORE to emptyMap()
)
}
}
class FirJspecifyDiagnosticComplianceHandler(testServices: TestServices) : FirAnalysisHandler(testServices) {
override fun processModule(module: TestModule, info: FirOutputArtifact) =
testServices.generateJspecifyMetadataInfos(module, info.allFirFiles.keys) {
(it as? FirDiagnosticCodeMetaInfo)?.diagnostic?.factory
}
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
}
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.test.preprocessors
import org.jetbrains.kotlin.test.frontend.classic.handlers.JspecifyDiagnosticComplianceHandler.Companion.diagnosticsToJspecifyMarks
import org.jetbrains.kotlin.test.frontend.classic.handlers.diagnosticsToJspecifyMarks
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.services.*