FIR checker: fix UPPER_BOUND_VIOLATED

The diagnostics accepts one parameter for the expected upper bound for
the type parameter.
This commit is contained in:
Tianyu Geng
2021-03-29 17:23:37 -07:00
committed by Dmitriy Novozhilov
parent bd64237519
commit d7cae63cb0
11 changed files with 36 additions and 33 deletions
@@ -253,8 +253,7 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
val INFERENCE_ERROR by error<FirSourceElement, PsiElement>() val INFERENCE_ERROR by error<FirSourceElement, PsiElement>()
val PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT by error<FirSourceElement, PsiElement>() val PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT by error<FirSourceElement, PsiElement>()
val UPPER_BOUND_VIOLATED by error<FirSourceElement, PsiElement> { val UPPER_BOUND_VIOLATED by error<FirSourceElement, PsiElement> {
parameter<FirTypeParameterSymbol>("typeParameter") parameter<ConeKotlinType>("upperBound")
parameter<ConeKotlinType>("violatedType")
} }
val TYPE_ARGUMENTS_NOT_ALLOWED by error<FirSourceElement, PsiElement>() val TYPE_ARGUMENTS_NOT_ALLOWED by error<FirSourceElement, PsiElement>()
val WRONG_NUMBER_OF_TYPE_ARGUMENTS by error<FirSourceElement, PsiElement> { val WRONG_NUMBER_OF_TYPE_ARGUMENTS by error<FirSourceElement, PsiElement> {
@@ -197,7 +197,7 @@ object FirErrors {
val RECURSION_IN_IMPLICIT_TYPES by error0<FirSourceElement, PsiElement>() val RECURSION_IN_IMPLICIT_TYPES by error0<FirSourceElement, PsiElement>()
val INFERENCE_ERROR by error0<FirSourceElement, PsiElement>() val INFERENCE_ERROR by error0<FirSourceElement, PsiElement>()
val PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT by error0<FirSourceElement, PsiElement>() val PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT by error0<FirSourceElement, PsiElement>()
val UPPER_BOUND_VIOLATED by error2<FirSourceElement, PsiElement, FirTypeParameterSymbol, ConeKotlinType>() val UPPER_BOUND_VIOLATED by error1<FirSourceElement, PsiElement, ConeKotlinType>()
val TYPE_ARGUMENTS_NOT_ALLOWED by error0<FirSourceElement, PsiElement>() val TYPE_ARGUMENTS_NOT_ALLOWED by error0<FirSourceElement, PsiElement>()
val WRONG_NUMBER_OF_TYPE_ARGUMENTS by error2<FirSourceElement, PsiElement, Int, FirClassLikeSymbol<*>>() val WRONG_NUMBER_OF_TYPE_ARGUMENTS by error2<FirSourceElement, PsiElement, Int, FirClassLikeSymbol<*>>()
val NO_TYPE_ARGUMENTS_ON_RHS by error2<FirSourceElement, PsiElement, Int, FirClassLikeSymbol<*>>() val NO_TYPE_ARGUMENTS_ON_RHS by error2<FirSourceElement, PsiElement, Int, FirClassLikeSymbol<*>>()
@@ -68,8 +68,9 @@ object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() {
return@forEach return@forEach
} }
if (!satisfiesBounds(proto, actual.coneType, substitutor, context.session.typeContext)) { val upperBound = getSubstitutedUpperBound(proto, substitutor, context.session.typeContext)
reporter.reportOn(actual.source, proto, actual.coneType, context) if (upperBound != null && !satisfiesBounds(upperBound, actual.coneType, context.session.typeContext)) {
reporter.reportOn(actual.source, upperBound, context)
return return
} }
@@ -164,7 +165,7 @@ object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() {
val satisfiesBounds = AbstractTypeChecker.isSubtypeOf(typeSystemContext, target, intersection) val satisfiesBounds = AbstractTypeChecker.isSubtypeOf(typeSystemContext, target, intersection)
if (!satisfiesBounds) { if (!satisfiesBounds) {
reporter.reportOn(functionCall.source, proto, actual, context) reporter.reportOn(functionCall.source, intersection, context)
return return
} }
} }
@@ -212,9 +213,10 @@ object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() {
) )
parameterPairs.forEach { (proto, actual) -> parameterPairs.forEach { (proto, actual) ->
if (!satisfiesBounds(proto, actual.type, substitutor, typeSystemContext)) { val upperBound = getSubstitutedUpperBound(proto, substitutor, typeSystemContext)
if (upperBound != null && !satisfiesBounds(upperBound, actual.type, typeSystemContext)) {
// should report on the parameter instead! // should report on the parameter instead!
reporter.reportOn(reportTarget, proto, actual, context) reporter.reportOn(reportTarget, upperBound, context)
return true return true
} }
@@ -233,25 +235,30 @@ object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() {
* bounds of the prototypeSymbol. * bounds of the prototypeSymbol.
*/ */
private fun satisfiesBounds( private fun satisfiesBounds(
prototypeSymbol: FirTypeParameterSymbol, upperBound: ConeKotlinType,
target: ConeKotlinType, target: ConeKotlinType,
substitutor: ConeSubstitutor,
typeSystemContext: ConeTypeContext typeSystemContext: ConeTypeContext
): Boolean { ): Boolean {
var intersection = typeSystemContext.intersectTypes( return AbstractTypeChecker.isSubtypeOf(typeSystemContext, target, upperBound, stubTypesEqualToAnything = false)
prototypeSymbol.fir.bounds.map { it.coneType } }
).safeAs<ConeKotlinType>() ?: return true
intersection = substitutor.substituteOrSelf(intersection) private fun getSubstitutedUpperBound(
return AbstractTypeChecker.isSubtypeOf(typeSystemContext, target, intersection, stubTypesEqualToAnything = false) prototypeSymbol: FirTypeParameterSymbol,
substitutor: ConeSubstitutor,
typeSystemContext: ConeTypeContext
): ConeKotlinType? {
val intersection = typeSystemContext.intersectTypes(
prototypeSymbol.fir.bounds.map { it.coneType }
).safeAs<ConeKotlinType>() ?: return null
return substitutor.substituteOrSelf(intersection)
} }
private fun DiagnosticReporter.reportOn( private fun DiagnosticReporter.reportOn(
source: FirSourceElement?, source: FirSourceElement?,
proto: FirTypeParameterSymbol,
actual: ConeKotlinType, actual: ConeKotlinType,
context: CheckerContext context: CheckerContext
) { ) {
reportOn(source, FirErrors.UPPER_BOUND_VIOLATED, proto, actual, context) reportOn(source, FirErrors.UPPER_BOUND_VIOLATED, actual, context)
} }
} }
@@ -404,7 +404,7 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
map.put(RECURSION_IN_IMPLICIT_TYPES, "Recursion in implicit types") map.put(RECURSION_IN_IMPLICIT_TYPES, "Recursion in implicit types")
map.put(INFERENCE_ERROR, "Inference error") map.put(INFERENCE_ERROR, "Inference error")
map.put(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT, "Projections are not allowed on type arguments of functions and properties") map.put(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT, "Projections are not allowed on type arguments of functions and properties")
map.put(UPPER_BOUND_VIOLATED, "Type argument is not within its bounds: should be subtype of ''{0}''", TO_STRING, TO_STRING) map.put(UPPER_BOUND_VIOLATED, "Type argument is not within its bounds: should be subtype of ''{0}''", RENDER_TYPE)
map.put(TYPE_ARGUMENTS_NOT_ALLOWED, "Type arguments are not allowed for type parameters") // * map.put(TYPE_ARGUMENTS_NOT_ALLOWED, "Type arguments are not allowed for type parameters") // *
map.put( map.put(
WRONG_NUMBER_OF_TYPE_ARGUMENTS, WRONG_NUMBER_OF_TYPE_ARGUMENTS,
@@ -772,8 +772,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
} }
add(FirErrors.UPPER_BOUND_VIOLATED) { firDiagnostic -> add(FirErrors.UPPER_BOUND_VIOLATED) { firDiagnostic ->
UpperBoundViolatedImpl( UpperBoundViolatedImpl(
firSymbolBuilder.classifierBuilder.buildTypeParameterSymbol(firDiagnostic.a.fir), firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b),
firDiagnostic as FirPsiDiagnostic<*>, firDiagnostic as FirPsiDiagnostic<*>,
token, token,
) )
@@ -553,8 +553,7 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract class UpperBoundViolated : KtFirDiagnostic<PsiElement>() { abstract class UpperBoundViolated : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = UpperBoundViolated::class override val diagnosticClass get() = UpperBoundViolated::class
abstract val typeParameter: KtTypeParameterSymbol abstract val upperBound: KtType
abstract val violatedType: KtType
} }
abstract class TypeArgumentsNotAllowed : KtFirDiagnostic<PsiElement>() { abstract class TypeArgumentsNotAllowed : KtFirDiagnostic<PsiElement>() {
@@ -883,8 +883,7 @@ internal class ProjectionOnNonClassTypeArgumentImpl(
} }
internal class UpperBoundViolatedImpl( internal class UpperBoundViolatedImpl(
override val typeParameter: KtTypeParameterSymbol, override val upperBound: KtType,
override val violatedType: KtType,
firDiagnostic: FirPsiDiagnostic<*>, firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken, override val token: ValidityToken,
) : KtFirDiagnostic.UpperBoundViolated(), KtAbstractFirDiagnostic<PsiElement> { ) : KtFirDiagnostic.UpperBoundViolated(), KtAbstractFirDiagnostic<PsiElement> {
+1 -1
View File
@@ -4,7 +4,7 @@
class Pair<A, B> class Pair<A, B>
abstract class C<T : B<Int>, X : (B<Char>) -> Pair<B<Any>, B<A>>>() : B<Any>() { // 2 errors abstract class C<T : B<Int>, X : (B<Char>) -> Pair<B<Any>, B<A>>>() : B<Any>() { // 2 errors
val a = B<<error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol@1f33f4d4'">Char</error>>() // error val a = B<<error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'A'">Char</error>>() // error
abstract val x : (B<Char>) -> B<Any> abstract val x : (B<Char>) -> B<Any>
} }
+3 -3
View File
@@ -1,10 +1,10 @@
fun test() { fun test() {
foo<<error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol@23ae790a'">Int?</error>>() foo<<error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'kotlin/Any'">Int?</error>>()
foo<Int>() foo<Int>()
bar<Int?>() bar<Int?>()
bar<Int>() bar<Int>()
bar<<error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol@ca50b2f'">Double?</error>>() bar<<error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'kotlin/Int?'">Double?</error>>()
bar<<error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol@ca50b2f'">Double</error>>() bar<<error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'kotlin/Int?'">Double</error>>()
1.<error descr="[INAPPLICABLE_CANDIDATE] Inapplicable candidate(s): /buzz">buzz</error><Double>() 1.<error descr="[INAPPLICABLE_CANDIDATE] Inapplicable candidate(s): /buzz">buzz</error><Double>()
} }
+3 -3
View File
@@ -6,10 +6,10 @@
class C : A<C>() class C : A<C>()
val a = B<C>() val a = B<C>()
val a1 = B<<error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol@29263cc3'">Int</error>>() val a1 = B<<error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'A<kotlin/Int>'">Int</error>>()
class X<A, B : A>() class X<A, B : A>()
val b = X<Any, X<A<C>, C>>() val b = X<Any, X<A<C>, C>>()
val b0 = X<Any, <error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol@2826285f'">Any?</error>>() val b0 = X<Any, <error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'kotlin/Any'">Any?</error>>()
val b1 = X<Any, <error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol@2826285f'">X<A<C>, String></error>>() val b1 = X<Any, <error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'A<C>'">X<A<C>, String></error>>()
+2 -2
View File
@@ -30,8 +30,8 @@ class Test1<T>()
} }
fun test() { fun test() {
Test1<<error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol@4c663d20'">B</error>>() Test1<<error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'it(Jet87/A & Jet87/B)'">B</error>>()
Test1<<error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol@4c663d20'">A</error>>() Test1<<error descr="[UPPER_BOUND_VIOLATED] Type argument is not within its bounds: should be subtype of 'it(Jet87/A & Jet87/B)'">A</error>>()
Test1<C>() Test1<C>()
} }