[FIR] Improve UPPER_BOUND_VIOLATED message

Suggest removing explicit type arguments when the upper bound is a
captured type since the only way to satisfy the upper bounds is
by letting the type variable be inferred to its bound.

#KT-65681 Fixed
This commit is contained in:
Kirill Rakhman
2024-02-09 16:41:43 +01:00
committed by Space Team
parent 91e4837c95
commit 58264e177f
11 changed files with 31 additions and 6 deletions
@@ -2152,6 +2152,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
UpperBoundViolatedImpl(
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b),
firDiagnostic.c,
firDiagnostic as KtPsiDiagnostic,
token,
)
@@ -1533,6 +1533,7 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = UpperBoundViolated::class
val expectedUpperBound: KtType
val actualUpperBound: KtType
val extraMessage: String
}
interface UpperBoundViolatedInTypealiasExpansion : KtFirDiagnostic<PsiElement> {
@@ -1839,6 +1839,7 @@ internal class ProjectionOnNonClassTypeArgumentImpl(
internal class UpperBoundViolatedImpl(
override val expectedUpperBound: KtType,
override val actualUpperBound: KtType,
override val extraMessage: String,
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.UpperBoundViolated
@@ -691,6 +691,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val UPPER_BOUND_VIOLATED by error<PsiElement> {
parameter<ConeKotlinType>("expectedUpperBound")
parameter<ConeKotlinType>("actualUpperBound")
parameter<String>("extraMessage")
}
val UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION by error<PsiElement> {
parameter<ConeKotlinType>("expectedUpperBound")
@@ -433,7 +433,7 @@ object FirErrors {
val RECURSION_IN_IMPLICIT_TYPES: KtDiagnosticFactory0 by error0<PsiElement>()
val INFERENCE_ERROR: KtDiagnosticFactory0 by error0<PsiElement>()
val PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT: KtDiagnosticFactory0 by error0<PsiElement>()
val UPPER_BOUND_VIOLATED: KtDiagnosticFactory2<ConeKotlinType, ConeKotlinType> by error2<PsiElement, ConeKotlinType, ConeKotlinType>()
val UPPER_BOUND_VIOLATED: KtDiagnosticFactory3<ConeKotlinType, ConeKotlinType, String> by error3<PsiElement, ConeKotlinType, ConeKotlinType, String>()
val UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION: KtDiagnosticFactory2<ConeKotlinType, ConeKotlinType> by error2<PsiElement, ConeKotlinType, ConeKotlinType>()
val TYPE_ARGUMENTS_NOT_ALLOWED: KtDiagnosticFactory1<String> by error1<PsiElement, String>()
val TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED: KtDiagnosticFactory0 by error0<PsiElement>()
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import kotlin.reflect.KClass
/**
@@ -203,11 +204,17 @@ fun checkUpperBoundViolated(
stubTypesEqualToAnything = true
)
) {
val factory = when {
isReportExpansionError && argumentTypeRef == null -> FirErrors.UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION
else -> FirErrors.UPPER_BOUND_VIOLATED
if (isReportExpansionError && argumentTypeRef == null) {
reporter.reportOn(
argumentSource, FirErrors.UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION, upperBound, argumentType.type, context
)
} else {
val extraMessage = if(upperBound is ConeCapturedType) "Consider removing the explicit type arguments" else ""
reporter.reportOn(
argumentSource, FirErrors.UPPER_BOUND_VIOLATED,
upperBound, argumentType.type, extraMessage, context
)
}
reporter.reportOn(argumentSource, factory, upperBound, argumentType.type, context)
} else {
// Only check if the original check was successful to prevent duplicate diagnostics
val additionalUpperBound = additionalUpperBoundsProvider?.getAdditionalUpperBound(upperBound)
@@ -1319,7 +1319,13 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
map.put(INFERENCE_ERROR, "Inference error.")
map.put(ILLEGAL_PROJECTION_USAGE, "Illegal projection usage.")
map.put(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT, "Projections on type arguments of functions and properties are prohibited.")
map.put(UPPER_BOUND_VIOLATED, "Type argument is not within its bounds: should be subtype of ''{0}''.", RENDER_TYPE, RENDER_TYPE)
map.put(
UPPER_BOUND_VIOLATED,
"Type argument is not within its bounds: should be subtype of ''{0}''.{2}",
RENDER_TYPE,
RENDER_TYPE,
OPTIONAL_SENTENCE
)
map.put(
UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION,
"Type argument is not within its bounds: should be subtype of ''{0}''.",
@@ -0,0 +1,5 @@
/capturedUpperBound.fir.kt:(100,120): error: Type argument is not within its bounds: should be subtype of 'CapturedType(*)'. Consider removing the explicit type arguments.
/capturedUpperBound.fir.kt:(136,150): error: Type argument is not within its bounds: should be subtype of 'CapturedType(*)'. Consider removing the explicit type arguments.
/capturedUpperBound.fir.kt:(166,182): error: Type argument is not within its bounds: should be subtype of 'CapturedType(*)'. Consider removing the explicit type arguments.
@@ -1,4 +1,5 @@
// ISSUE: KT-65712
// RENDER_DIAGNOSTICS_FULL_TEXT
fun test(a: BodySpec<List<*>, *>) {
a.value<<!UPPER_BOUND_VIOLATED!>BodySpec<List<*>, *><!>>()
@@ -1,4 +1,5 @@
// ISSUE: KT-65712
// RENDER_DIAGNOSTICS_FULL_TEXT
fun test(a: BodySpec<List<*>, *>) {
a.value<BodySpec<List<*>, *>>()