[FIR] Add ONLY_ONE_CLASS_BOUND_ALLOWED diagnostic
This commit is contained in:
+2
@@ -301,6 +301,8 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
|
||||
val UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE by error<FirSourceElement, PsiElement>()
|
||||
|
||||
val BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER by error<FirSourceElement, PsiElement>()
|
||||
|
||||
val ONLY_ONE_CLASS_BOUND_ALLOWED by error<FirSourceElement, PsiElement>()
|
||||
}
|
||||
|
||||
val REFLECTION by object : DiagnosticGroup("Reflection") {
|
||||
|
||||
@@ -223,6 +223,7 @@ object FirErrors {
|
||||
val FINAL_UPPER_BOUND by warning1<FirSourceElement, PsiElement, ConeKotlinType>()
|
||||
val UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE by error0<FirSourceElement, PsiElement>()
|
||||
val BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER by error0<FirSourceElement, PsiElement>()
|
||||
val ONLY_ONE_CLASS_BOUND_ALLOWED by error0<FirSourceElement, PsiElement>()
|
||||
|
||||
// Reflection
|
||||
val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1<FirSourceElement, KtExpression, FirCallableDeclaration<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
|
||||
+22
@@ -6,9 +6,11 @@
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.canHaveSubtypes
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isInlineOnly
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClass
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
@@ -21,6 +23,12 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
||||
|
||||
private val classKinds = setOf(
|
||||
ClassKind.CLASS,
|
||||
ClassKind.ENUM_CLASS,
|
||||
ClassKind.OBJECT
|
||||
)
|
||||
|
||||
override fun check(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val containingDeclaration = context.containingDeclarations.lastOrNull()
|
||||
if (containingDeclaration is FirConstructor) return
|
||||
@@ -39,6 +47,8 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
||||
if (containingDeclaration.safeAs<FirMemberDeclaration>()?.isInlineOnly() != true) {
|
||||
checkOnlyOneTypeParameterBound(declaration, context, reporter)
|
||||
}
|
||||
|
||||
checkOnlyOneClassBound(declaration, context, reporter)
|
||||
}
|
||||
|
||||
private fun checkOnlyOneTypeParameterBound(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
@@ -67,4 +77,16 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
||||
return source.treeStructure.getParent(source.lighterASTNode)?.tokenType == KtNodeTypes.TYPE_CONSTRAINT
|
||||
}
|
||||
|
||||
|
||||
private fun checkOnlyOneClassBound(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val seenClasses = mutableSetOf<FirRegularClass>()
|
||||
val bounds = declaration.bounds.distinctBy { it.coneType }
|
||||
bounds.forEach { bound ->
|
||||
bound.coneType.toRegularClass(context.session)?.let { clazz ->
|
||||
if (classKinds.contains(clazz.classKind) && seenClasses.add(clazz) && seenClasses.size > 1) {
|
||||
reporter.reportOn(bound.source, FirErrors.ONLY_ONE_CLASS_BOUND_ALLOWED, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -154,6 +154,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_THIS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_VALUE_FOR_PARAMETER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_TYPE_IN_CLASS_LITERAL_LHS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_TYPE_OF_ANNOTATION_MEMBER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ONLY_ONE_CLASS_BOUND_ALLOWED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OTHER_ERROR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OVERLOAD_RESOLUTION_AMBIGUITY
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OVERRIDING_FINAL_MEMBER
|
||||
@@ -456,6 +457,8 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
"Type parameter cannot have any other bounds if it's bounded by another type parameter"
|
||||
)
|
||||
|
||||
map.put(ONLY_ONE_CLASS_BOUND_ALLOWED,"Only one of the upper bounds can be a class")
|
||||
|
||||
// Reflection
|
||||
map.put(
|
||||
EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED,
|
||||
|
||||
@@ -48,7 +48,7 @@ class Bar<T : <!FINAL_UPPER_BOUND!>Foo<!>>
|
||||
class Buzz<T> where T : <!FINAL_UPPER_BOUND!>Bar<Int><!>, T : <!UNRESOLVED_REFERENCE!>nioho<!>
|
||||
|
||||
class X<T : <!FINAL_UPPER_BOUND!>Foo<!>>
|
||||
class Y<T> where T : <!FINAL_UPPER_BOUND!>Foo<!>, T : <!FINAL_UPPER_BOUND!>Bar<Foo><!>
|
||||
class Y<T> where T : <!FINAL_UPPER_BOUND!>Foo<!>, T : <!FINAL_UPPER_BOUND, ONLY_ONE_CLASS_BOUND_ALLOWED!>Bar<Foo><!>
|
||||
|
||||
fun <T> test2(t : T)
|
||||
where
|
||||
|
||||
+9
-9
@@ -2,17 +2,17 @@ open class C1
|
||||
open class C2
|
||||
open class C3 : C2()
|
||||
|
||||
class A1<T> where T : C1, T : C2
|
||||
class A2<T> where T : C1, T : C2, T : C3
|
||||
class A3<T> where T : C2, T : C3
|
||||
class A4<T> where T : C3, T : C2
|
||||
class A1<T> where T : C1, T : <!ONLY_ONE_CLASS_BOUND_ALLOWED!>C2<!>
|
||||
class A2<T> where T : C1, T : <!ONLY_ONE_CLASS_BOUND_ALLOWED!>C2<!>, T : <!ONLY_ONE_CLASS_BOUND_ALLOWED!>C3<!>
|
||||
class A3<T> where T : C2, T : <!ONLY_ONE_CLASS_BOUND_ALLOWED!>C3<!>
|
||||
class A4<T> where T : C3, T : <!ONLY_ONE_CLASS_BOUND_ALLOWED!>C2<!>
|
||||
|
||||
fun <T> f1() where T : C1, T : C2, T : C3 {}
|
||||
fun <T> f2() where T : C2, T : C3 {}
|
||||
fun <T> f3() where T : C3, T : C2 {}
|
||||
fun <T> f1() where T : C1, T : <!ONLY_ONE_CLASS_BOUND_ALLOWED!>C2<!>, T : <!ONLY_ONE_CLASS_BOUND_ALLOWED!>C3<!> {}
|
||||
fun <T> f2() where T : C2, T : <!ONLY_ONE_CLASS_BOUND_ALLOWED!>C3<!> {}
|
||||
fun <T> f3() where T : C3, T : <!ONLY_ONE_CLASS_BOUND_ALLOWED!>C2<!> {}
|
||||
|
||||
enum class E1
|
||||
class A5<T> where T : C1, T : E1
|
||||
class A5<T> where T : C1, T : <!ONLY_ONE_CLASS_BOUND_ALLOWED!>E1<!>
|
||||
|
||||
object O1
|
||||
class A6<T> where T : <!FINAL_UPPER_BOUND!>O1<!>, T : C2
|
||||
class A6<T> where T : <!FINAL_UPPER_BOUND!>O1<!>, T : <!ONLY_ONE_CLASS_BOUND_ALLOWED!>C2<!>
|
||||
|
||||
+6
@@ -926,6 +926,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.ONLY_ONE_CLASS_BOUND_ALLOWED) { firDiagnostic ->
|
||||
OnlyOneClassBoundAllowedImpl(
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED) { firDiagnostic ->
|
||||
ExtensionInClassReferenceNotAllowedImpl(
|
||||
firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a as FirCallableDeclaration),
|
||||
|
||||
+4
@@ -660,6 +660,10 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = BoundsNotAllowedIfBoundedByTypeParameter::class
|
||||
}
|
||||
|
||||
abstract class OnlyOneClassBoundAllowed : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = OnlyOneClassBoundAllowed::class
|
||||
}
|
||||
|
||||
abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic<KtExpression>() {
|
||||
override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class
|
||||
abstract val referencedDeclaration: KtCallableSymbol
|
||||
|
||||
+7
@@ -1063,6 +1063,13 @@ internal class BoundsNotAllowedIfBoundedByTypeParameterImpl(
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class OnlyOneClassBoundAllowedImpl(
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.OnlyOneClassBoundAllowed(), KtAbstractFirDiagnostic<PsiElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ExtensionInClassReferenceNotAllowedImpl(
|
||||
override val referencedDeclaration: KtCallableSymbol,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ class Bar<T : Foo>
|
||||
class Buzz<T> where T : Bar<Int>, T : <error descr="[UNRESOLVED_REFERENCE] Unresolved reference: nioho">nioho</error>
|
||||
|
||||
class X<T : Foo>
|
||||
class Y<T> where T : Foo, T : Bar<Foo>
|
||||
class Y<T> where T : Foo, T : <error descr="[ONLY_ONE_CLASS_BOUND_ALLOWED] Only one of the upper bounds can be a class">Bar<Foo></error>
|
||||
|
||||
fun <T> test2(t : T)
|
||||
where
|
||||
|
||||
Reference in New Issue
Block a user