[FIR] Verify diagnostic messages don't reference non-existent parameters

This commit is contained in:
Kirill Rakhman
2023-04-26 16:19:27 +02:00
committed by Space Team
parent c956b4aeae
commit a818c543a9
5 changed files with 39 additions and 23 deletions
@@ -1843,7 +1843,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
) )
map.put( map.put(
WRONG_INVOCATION_KIND, WRONG_INVOCATION_KIND,
"{2} wrong invocation kind: given {3} case, but {4} case is possible", "{0} has wrong invocation kind: given {1} case, but {2} case is possible",
SYMBOL, SYMBOL,
TO_STRING, TO_STRING,
TO_STRING TO_STRING
@@ -5,17 +5,19 @@
package org.jetbrains.kotlin.diagnostics package org.jetbrains.kotlin.diagnostics
import com.google.common.annotations.VisibleForTesting
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticParameterRenderer import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticParameterRenderer
import org.jetbrains.kotlin.diagnostics.rendering.RenderingContext import org.jetbrains.kotlin.diagnostics.rendering.RenderingContext
import org.jetbrains.kotlin.diagnostics.rendering.renderParameter import org.jetbrains.kotlin.diagnostics.rendering.renderParameter
import java.text.MessageFormat import java.text.MessageFormat
sealed interface KtDiagnosticRenderer { sealed interface KtDiagnosticRenderer {
@VisibleForTesting val message: String
fun render(diagnostic: KtDiagnostic): String fun render(diagnostic: KtDiagnostic): String
fun renderParameters(diagnostic: KtDiagnostic): Array<out Any?> fun renderParameters(diagnostic: KtDiagnostic): Array<out Any?>
} }
class SimpleKtDiagnosticRenderer(private val message: String) : KtDiagnosticRenderer { class SimpleKtDiagnosticRenderer(override val message: String) : KtDiagnosticRenderer {
override fun render(diagnostic: KtDiagnostic): String { override fun render(diagnostic: KtDiagnostic): String {
require(diagnostic is KtSimpleDiagnostic) require(diagnostic is KtSimpleDiagnostic)
return message return message
@@ -28,7 +30,7 @@ class SimpleKtDiagnosticRenderer(private val message: String) : KtDiagnosticRend
} }
sealed class AbstractKtDiagnosticWithParametersRenderer( sealed class AbstractKtDiagnosticWithParametersRenderer(
protected val message: String final override val message: String
) : KtDiagnosticRenderer { ) : KtDiagnosticRenderer {
private val messageFormat = MessageFormat(message) private val messageFormat = MessageFormat(message)
@@ -5,29 +5,43 @@
package org.jetbrains.kotlin.test.utils package org.jetbrains.kotlin.test.utils
import org.jetbrains.kotlin.diagnostics.AbstractKtDiagnosticFactory import org.jetbrains.kotlin.diagnostics.*
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryForDeprecation import org.junit.Assert
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.reflect.full.memberProperties import kotlin.reflect.full.memberProperties
fun KtDiagnosticFactoryToRendererMap.checkMissingMessages(objectWithErrors: Any) { fun KtDiagnosticFactoryToRendererMap.verifyMessages(objectWithErrors: Any) {
for (property in objectWithErrors.javaClass.kotlin.memberProperties) { for (property in objectWithErrors::class.memberProperties) {
when (val factory = property.getter.call(objectWithErrors)) { when (val factory = property.getter.call(objectWithErrors)) {
is AbstractKtDiagnosticFactory -> { is AbstractKtDiagnosticFactory -> {
checkMissingMessagesForFactory(factory, property) verifyMessageForFactory(factory, property)
} }
is KtDiagnosticFactoryForDeprecation<*> -> { is KtDiagnosticFactoryForDeprecation<*> -> {
checkMissingMessagesForFactory(factory.warningFactory, property) verifyMessageForFactory(factory.warningFactory, property)
checkMissingMessagesForFactory(factory.errorFactory, property) verifyMessageForFactory(factory.errorFactory, property)
} }
else -> {} else -> {}
} }
} }
} }
fun KtDiagnosticFactoryToRendererMap.checkMissingMessagesForFactory(factory: AbstractKtDiagnosticFactory, property: KProperty<*>) { private val messageParameterRegex = """\{\d.*?}""".toRegex()
if (!containsKey(factory)) {
throw IllegalStateException("No default diagnostic renderer is provided for ${property.name}") fun KtDiagnosticFactoryToRendererMap.verifyMessageForFactory(factory: AbstractKtDiagnosticFactory, property: KProperty<*>) {
Assert.assertTrue("No default diagnostic renderer is provided for ${property.name}", containsKey(factory))
val renderer = get(factory)!!
val parameterCount = when (renderer) {
is KtDiagnosticWithParameters4Renderer<*, *, *, *> -> 4
is KtDiagnosticWithParameters3Renderer<*, *, *> -> 3
is KtDiagnosticWithParameters2Renderer<*, *> -> 2
is KtDiagnosticWithParameters1Renderer<*> -> 1
else -> 0
} }
}
for (parameter in messageParameterRegex.findAll(renderer.message)) {
val index = parameter.value.substring(1, 2).toInt()
Assert.assertTrue("Message for ${property.name} references wrong parameter {$index}", index < parameterCount)
}
}
@@ -15,16 +15,16 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrorsDefaultMessages import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrorsDefaultMessages
import org.jetbrains.kotlin.fir.builder.FirSyntaxErrors import org.jetbrains.kotlin.fir.builder.FirSyntaxErrors
import org.jetbrains.kotlin.fir.builder.FirSyntaxErrorsDefaultMessages import org.jetbrains.kotlin.fir.builder.FirSyntaxErrorsDefaultMessages
import org.jetbrains.kotlin.test.utils.checkMissingMessages import org.jetbrains.kotlin.test.utils.verifyMessages
import org.junit.Test import org.junit.Test
class DefaultMessagesTest { class DefaultMessagesTest {
@Test @Test
fun ensureAllMessagesPresent() { fun ensureAllMessagesPresent() {
FirErrorsDefaultMessages.MAP.checkMissingMessages(FirErrors) FirErrorsDefaultMessages.MAP.verifyMessages(FirErrors)
FirJvmErrorsDefaultMessages.MAP.checkMissingMessages(FirJvmErrors) FirJvmErrorsDefaultMessages.MAP.verifyMessages(FirJvmErrors)
FirJsErrorsDefaultMessages.MAP.checkMissingMessages(FirJsErrors) FirJsErrorsDefaultMessages.MAP.verifyMessages(FirJsErrors)
FirNativeErrorsDefaultMessages.MAP.checkMissingMessages(FirNativeErrors) FirNativeErrorsDefaultMessages.MAP.verifyMessages(FirNativeErrors)
FirSyntaxErrorsDefaultMessages.MAP.checkMissingMessages(FirSyntaxErrors) FirSyntaxErrorsDefaultMessages.MAP.verifyMessages(FirSyntaxErrors)
} }
} }
@@ -7,12 +7,12 @@ package org.jetbrains.kotlin.parcelize.test
import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtDefaultErrorMessagesParcelize import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtDefaultErrorMessagesParcelize
import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize
import org.jetbrains.kotlin.test.utils.checkMissingMessages import org.jetbrains.kotlin.test.utils.verifyMessages
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
class ParcelizeDefaultMessagesTest { class ParcelizeDefaultMessagesTest {
@Test @Test
fun ensureAllMessagesPresent() { fun ensureAllMessagesPresent() {
KtDefaultErrorMessagesParcelize.MAP.checkMissingMessages(KtErrorsParcelize) KtDefaultErrorMessagesParcelize.MAP.verifyMessages(KtErrorsParcelize)
} }
} }