diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index d24053ec716..f5a1d2a6491 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -268,6 +268,8 @@ public interface Errors { DiagnosticFactory0 CYCLIC_GENERIC_UPPER_BOUND = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 MISPLACED_TYPE_PARAMETER_CONSTRAITS = DiagnosticFactory0.create(WARNING); + // Members DiagnosticFactory2 CONFLICTING_OVERLOADS = diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 1cda341b04e..42ba06dc288 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -496,6 +496,8 @@ public class DefaultErrorMessages { MAP.put(DEPRECATED_TYPE_PARAMETER_SYNTAX, "Placing function type parameters after the function name is deprecated"); + MAP.put(MISPLACED_TYPE_PARAMETER_CONSTRAITS, "If a type parameter has multiple constraints, they all need to be placed in the 'where' clause"); + MAP.put(TYPE_VARIANCE_CONFLICT, "Type parameter {0} is declared as ''{1}'' but occurs in ''{2}'' position in type {3}", new MultiRenderer() { @NotNull diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java index b9e262b3e4c..d2986fe328e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java @@ -279,6 +279,7 @@ public class DeclarationsChecker { private void checkClass(BodiesResolveContext c, JetClass aClass, ClassDescriptorWithResolutionScopes classDescriptor) { checkOpenMembers(classDescriptor); checkTypeParameters(aClass); + checkTypeParameterConstraints(aClass); if (aClass.isInterface()) { checkConstructorInTrait(aClass); @@ -336,6 +337,27 @@ public class DeclarationsChecker { } } + private void checkTypeParameterConstraints(JetTypeParameterListOwner typeParameterListOwner) { + List constraints = typeParameterListOwner.getTypeConstraints(); + if (!constraints.isEmpty()) { + for (JetTypeParameter typeParameter : typeParameterListOwner.getTypeParameters()) { + if (typeParameter.getExtendsBound() != null && hasConstraints(typeParameter, constraints)) { + trace.report(MISPLACED_TYPE_PARAMETER_CONSTRAITS.on(typeParameter)); + } + } + } + } + + private static boolean hasConstraints(JetTypeParameter typeParameter, List constraints) { + for (JetTypeConstraint constraint : constraints) { + JetSimpleNameExpression parameterName = constraint.getSubjectTypeParameterName(); + if (parameterName != null && parameterName.getText().equals(typeParameter.getName())) { + return true; + } + } + return false; + } + private void checkConstructorInTrait(JetClass klass) { JetPrimaryConstructor primaryConstructor = klass.getPrimaryConstructor(); if (primaryConstructor != null) { @@ -377,6 +399,7 @@ public class DeclarationsChecker { checkPropertyLateInit(property, propertyDescriptor); checkPropertyInitializer(property, propertyDescriptor); checkAccessors(property, propertyDescriptor); + checkTypeParameterConstraints(property); checkPropertyExposedType(property, propertyDescriptor); } @@ -572,6 +595,7 @@ public class DeclarationsChecker { typeParameterList.getTextRange().getStartOffset() > nameIdentifier.getTextRange().getStartOffset()) { trace.report(DEPRECATED_TYPE_PARAMETER_SYNTAX.on(typeParameterList)); } + checkTypeParameterConstraints(function); DeclarationDescriptor containingDescriptor = functionDescriptor.getContainingDeclaration(); boolean hasAbstractModifier = function.hasModifier(JetTokens.ABSTRACT_KEYWORD); diff --git a/compiler/testData/diagnostics/tests/MultipleBounds.kt b/compiler/testData/diagnostics/tests/MultipleBounds.kt index 243940985b5..f7e40d83c73 100644 --- a/compiler/testData/diagnostics/tests/MultipleBounds.kt +++ b/compiler/testData/diagnostics/tests/MultipleBounds.kt @@ -9,9 +9,9 @@ interface B { } interface G { - val boo: Double where X : B + val boo: Double where X : A, X : B val bal: Double where A : B - val bas: Double where X : B + val bas: Double where Y : B, X : B } class C() : A(), B @@ -20,8 +20,9 @@ class D() { companion object : A(), B {} } -class Test1() +class Test1() where + T : A, T : B, B : T // error { @@ -47,10 +48,11 @@ class BarFoo> class Buzz where T : Bar<Int>, T : nioho class XFoo> -class Y<T : Foo> where T : Bar +class Y<T> where T : Foo, T : Bar -fun test2(t : T) +fun test2(t : T) where + T : A, T : B, B : T { diff --git a/compiler/testData/diagnostics/tests/duplicateJvmSignature/erasure/typeParameterWithTwoBounds.kt b/compiler/testData/diagnostics/tests/duplicateJvmSignature/erasure/typeParameterWithTwoBounds.kt index ceadd5a3234..426fa852797 100644 --- a/compiler/testData/diagnostics/tests/duplicateJvmSignature/erasure/typeParameterWithTwoBounds.kt +++ b/compiler/testData/diagnostics/tests/duplicateJvmSignature/erasure/typeParameterWithTwoBounds.kt @@ -3,6 +3,6 @@ interface Foo interface Bar -fun foo(x: T): T where T: Bar {null!!} +fun foo(x: T): T where T: Foo, T: Bar {null!!} fun foo(x: Foo): Foo {null!!} fun foo(x: Bar): Bar {null!!} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.kt b/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.kt index 775f12ec20c..f49b4f20388 100644 --- a/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.kt +++ b/compiler/testData/diagnostics/tests/inference/upperBounds/doNotInferFromBoundsOnly.kt @@ -34,7 +34,7 @@ fun test4() { //-------------- -fun emptyStrangeMap2(t: T): Map where R: A = throw Exception("$t") +fun emptyStrangeMap2(t: T): Map where R: T, R: A = throw Exception("$t") fun test5(a: A) { emptyStrangeMap2(a) diff --git a/compiler/testData/diagnostics/tests/nullableTypes/baseWithNullableUpperBound.kt b/compiler/testData/diagnostics/tests/nullableTypes/baseWithNullableUpperBound.kt index 829d88a7ad1..4fb7a6bcd0d 100644 --- a/compiler/testData/diagnostics/tests/nullableTypes/baseWithNullableUpperBound.kt +++ b/compiler/testData/diagnostics/tests/nullableTypes/baseWithNullableUpperBound.kt @@ -3,10 +3,10 @@ fun nonMisleadingNullable( nnn: NNN? ) {} -fun twoBounds( +fun twoBounds( tb: TWO_BOUNDS? -) where TWO_BOUNDS : NN {} +) where TWO_BOUNDS: Any, TWO_BOUNDS : NN {} fun misleadingNullableSimple( t: T?, @@ -15,10 +15,10 @@ fun misleadingNullableSimple( ind: INDIRECT? ) {} -fun misleadingNullableMultiBound( +fun misleadingNullableMultiBound( fb: FIRST_BOUND?, sb: SECOND_BOUND? -) where FIRST_BOUND: Any, SECOND_BOUND: Any? { +) where FIRST_BOUND: Any?, FIRST_BOUND: Any, SECOND_BOUND: Any, SECOND_BOUND: Any? { } fun interactionWithRedundant(t: T??) {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.kt b/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.kt new file mode 100644 index 00000000000..fb1f7e0d9c8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.kt @@ -0,0 +1,12 @@ +class Foo<T : Cloneable> where T : Comparable { + fun <U : Cloneable> foo(u: U): U where U: Comparable { + return u + } + + val <U : Cloneable> U.foo: U? where U: Comparable + get() { return null } +} + +class Bar where U: Comparable { + +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.txt b/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.txt new file mode 100644 index 00000000000..8a21dbb265a --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.txt @@ -0,0 +1,17 @@ +package + +public final class Bar> { + public constructor Bar>() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Foo where T : kotlin.Comparable { + public constructor Foo() where T : kotlin.Comparable + public final val U.foo: U? where U : kotlin.Comparable + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(/*0*/ u: U): U where U : kotlin.Comparable + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/typeParameters/upperBoundCannotBeArray.kt b/compiler/testData/diagnostics/tests/typeParameters/upperBoundCannotBeArray.kt index f13d141e0d4..df667b56306 100644 --- a/compiler/testData/diagnostics/tests/typeParameters/upperBoundCannotBeArray.kt +++ b/compiler/testData/diagnostics/tests/typeParameters/upperBoundCannotBeArray.kt @@ -1,6 +1,6 @@ fun <A : Array> f1() {} fun A : Array> f2() {} -fun A : Array> f3() where A : Array {} +fun A> f3() where A : Array, A : Array {} fun <T : IntArray> f4() {} @@ -9,7 +9,7 @@ fun <T> f5() where T : Array {} val <T : Array> v = "" class C2A : Array> -interface C3A : Array> where A : Array +interface C3A> where A : Array, A : Array fun foo() { class C1<A : Array> { diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 955e30bf3a6..55a90116ea1 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -15534,6 +15534,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("misplacedConstraints.kt") + public void testMisplacedConstraints() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.kt"); + doTest(fileName); + } + @TestMetadata("upperBoundCannotBeArray.kt") public void testUpperBoundCannotBeArray() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typeParameters/upperBoundCannotBeArray.kt");