diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 981fc6bd204..0239c403538 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -334,6 +334,8 @@ object DIAGNOSTICS_LIST : DiagnosticList() { val CYCLIC_GENERIC_UPPER_BOUND by error() val DEPRECATED_TYPE_PARAMETER_SYNTAX by error() + + val MISPLACED_TYPE_PARAMETER_CONSTRAINTS by warning() } val REFLECTION by object : DiagnosticGroup("Reflection") { diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index f41820f5b8b..12e3642a6de 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -242,6 +242,7 @@ object FirErrors { val RETURN_TYPE_MISMATCH by error2(SourceElementPositioningStrategies.WHOLE_ELEMENT) val CYCLIC_GENERIC_UPPER_BOUND by error0() val DEPRECATED_TYPE_PARAMETER_SYNTAX by error0() + val MISPLACED_TYPE_PARAMETER_CONSTRAINTS by warning0() // Reflection val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTypeParameterBoundsChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTypeParameterBoundsChecker.kt index 5637946990d..529d4ce9605 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTypeParameterBoundsChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTypeParameterBoundsChecker.kt @@ -42,6 +42,7 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() { checkBoundUniqueness(declaration, context, reporter) checkConflictingBounds(declaration, context, reporter) checkTypeAliasBound(declaration, containingDeclaration, context, reporter) + checkBoundsPlacement(declaration, context, reporter) } private fun checkFinalUpperBounds( @@ -147,6 +148,15 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() { } } + private fun checkBoundsPlacement(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) { + if (declaration.bounds.size < 2) return + + val (constraint, params) = declaration.bounds.partition { it.isInTypeConstraint() } + if (params.isNotEmpty() && constraint.isNotEmpty()) { + reporter.reportOn(declaration.source, FirErrors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS, context) + } + } + private fun KotlinTypeMarker.isRelated(context: TypeCheckerProviderContext, type: KotlinTypeMarker?): Boolean = isSubtypeOf(context, type) || isSupertypeOf(context, type) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 633449222a5..204c18f72fb 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -140,6 +140,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_COMPANION_OB import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.METHOD_OF_ANY_IMPLEMENTED_IN_INTERFACE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_VAL_ON_ANNOTATION_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MULTIPLE_VARARG_PARAMETERS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MUST_BE_INITIALIZED @@ -515,6 +516,11 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension { map.put(DEPRECATED_TYPE_PARAMETER_SYNTAX, "Type parameters must be placed before the name of the function") + map.put( + MISPLACED_TYPE_PARAMETER_CONSTRAINTS, + "If a type parameter has multiple constraints, they all need to be placed in the 'where' clause" + ) + // Reflection map.put( EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED, diff --git a/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.fir.kt b/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.fir.kt deleted file mode 100644 index 25f91dbcf10..00000000000 --- a/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.fir.kt +++ /dev/null @@ -1,23 +0,0 @@ -// See KT-9438: Enforce the Single Instantiation Inheritance Rule for type parameters - -interface A - -interface B - -interface D - -interface IncorrectF> where T : D - -interface CorrectF where T : D, T : D - -interface G - -interface IncorrectH>> where T : G> - -interface CorrectH where T : G>, T : G> - -interface incorrectJ>> where T : G> - -interface correctJ where T : G>, T : G> - -fun bar() where T : D, T : D {} diff --git a/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.kt b/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.kt index 6df85abf8c2..d704a93eded 100644 --- a/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.kt +++ b/compiler/testData/diagnostics/tests/generics/TypeParameterBounds.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // See KT-9438: Enforce the Single Instantiation Inheritance Rule for type parameters interface A diff --git a/compiler/testData/diagnostics/tests/generics/invalidArgumentsNumberInWhere.fir.kt b/compiler/testData/diagnostics/tests/generics/invalidArgumentsNumberInWhere.fir.kt index ea3c40512f7..e4853294bab 100644 --- a/compiler/testData/diagnostics/tests/generics/invalidArgumentsNumberInWhere.fir.kt +++ b/compiler/testData/diagnostics/tests/generics/invalidArgumentsNumberInWhere.fir.kt @@ -1,19 +1,19 @@ class A interface I0A> interface I1 where T : A -interface I2A> where T : A +interface I2<T : A> where T : A fun A> foo0() {} fun foo1() where E : A {} -fun A> foo2() where E : A {} +fun <E : A> foo2() where E : A {} val A> E.p1: Int get() = 1 val E.p2: Int where E : A get() = 1 -val A> E.p3: Int where E : A +val <E : A> E.p3: Int where E : A get() = 1 // See KT-8200 interface X -public class EnumAttributeX>(val klass: Class) where T : Enum +public class EnumAttribute<T : X>(val klass: Class) where T : Enum diff --git a/compiler/testData/diagnostics/tests/generics/unresolvedClassifierInWhere.fir.kt b/compiler/testData/diagnostics/tests/generics/unresolvedClassifierInWhere.fir.kt index 4a3180105e2..8bc935e1b32 100644 --- a/compiler/testData/diagnostics/tests/generics/unresolvedClassifierInWhere.fir.kt +++ b/compiler/testData/diagnostics/tests/generics/unresolvedClassifierInWhere.fir.kt @@ -1,14 +1,14 @@ interface I0Unresolved0> interface I1 where T : Unresolved1 -interface I2Unresolved2> where T : Unresolved3 +interface I2<T : Unresolved2> where T : Unresolved3 fun Unresolved4> foo0() {} fun foo1() where E : Unresolved5 {} -fun Unresolved6> foo2() where E : Unresolved7 {} +fun <E : Unresolved6> foo2() where E : Unresolved7 {} val Unresolved7> E.p1: Int get() = 1 val E.p2: Int where E : Unresolved8 get() = 1 -val Unresolved9> E.p3: Int where E : Unresolved10 +val <E : Unresolved9> E.p3: Int where E : Unresolved10 get() = 1 diff --git a/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.fir.kt b/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.fir.kt deleted file mode 100644 index 9df4235784b..00000000000 --- a/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.fir.kt +++ /dev/null @@ -1,13 +0,0 @@ -class Foo where T : Comparable { - fun foo(u: U): U where U: Comparable { - fun <T: Any> bar() where T: U {} - return u - } - - val U.foo: U? where U: Comparable - get() { return null } -} - -class Bar where U: Comparable { - -} diff --git a/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.kt b/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.kt index 83ea2ee4db9..c2cadddca10 100644 --- a/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.kt +++ b/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL class Foo<T : Cloneable> where T : Comparable { fun <U : Cloneable> foo(u: U): U where U: Comparable { fun <T: Any> bar() where T: U {} diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 3953dc86e25..589fea742ca 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -1030,6 +1030,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS) { firDiagnostic -> + MisplacedTypeParameterConstraintsImpl( + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED) { firDiagnostic -> ExtensionInClassReferenceNotAllowedImpl( firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a as FirCallableDeclaration), diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index b0594f94b9c..ab503de0f16 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -731,6 +731,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = DeprecatedTypeParameterSyntax::class } + abstract class MisplacedTypeParameterConstraints : KtFirDiagnostic() { + override val diagnosticClass get() = MisplacedTypeParameterConstraints::class + } + abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic() { override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class abstract val referencedDeclaration: KtCallableSymbol diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 7414fb17309..37db64d9847 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -1182,6 +1182,13 @@ internal class DeprecatedTypeParameterSyntaxImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class MisplacedTypeParameterConstraintsImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.MisplacedTypeParameterConstraints(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class ExtensionInClassReferenceNotAllowedImpl( override val referencedDeclaration: KtCallableSymbol, firDiagnostic: FirPsiDiagnostic<*>,