diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 4c36ab217ba..c5e6bc5ed2d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -265,7 +265,7 @@ public interface Errors { DiagnosticFactory0 DYNAMIC_UPPER_BOUND = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 ONLY_ONE_CLASS_BOUND_ALLOWED = DiagnosticFactory0.create(ERROR); - DiagnosticFactory0 BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 REPEATED_BOUND = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 CONFLICTING_UPPER_BOUNDS = diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt index a86441f6d16..219f68b7111 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt @@ -179,14 +179,38 @@ class DeclarationsChecker( } } - private fun checkOnlyOneTypeParameterBound(descriptor: TypeParameterDescriptor, declaration: KtTypeParameter) { + private fun checkOnlyOneTypeParameterBound( + descriptor: TypeParameterDescriptor, declaration: KtTypeParameter, owner: KtTypeParameterListOwner + ) { val upperBounds = descriptor.upperBounds val (boundsWhichAreTypeParameters, otherBounds) = upperBounds .map { type -> type.constructor } .partition { constructor -> constructor.declarationDescriptor is TypeParameterDescriptor } .let { pair -> pair.first.toSet() to pair.second.toSet() } - if (boundsWhichAreTypeParameters.size > 1 || (boundsWhichAreTypeParameters.isNotEmpty() && otherBounds.isNotEmpty())) { - trace.report(BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER.on(declaration)) + if (boundsWhichAreTypeParameters.size > 1 || (boundsWhichAreTypeParameters.size == 1 && otherBounds.isNotEmpty())) { + val reportOn = if (boundsWhichAreTypeParameters.size + otherBounds.size == 2) { + // If there's only one problematic bound (either 2 type parameter bounds, or 1 type parameter bound + 1 other bound), + // report the diagnostic on that bound + + val allBounds: List> = + owner.typeConstraints + .filter { constraint -> + constraint.subjectTypeParameterName?.getReferencedNameAsName() == declaration.nameAsName + } + .mapNotNull { constraint -> constraint.boundTypeReference } + .map { typeReference -> typeReference to trace.bindingContext.get(TYPE, typeReference) } + + val problematicBound = + allBounds.firstOrNull { bound -> bound.second?.constructor != boundsWhichAreTypeParameters.first() } + + problematicBound?.first ?: declaration + } + else { + // Otherwise report the diagnostic on the type parameter declaration + declaration + } + + trace.report(BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER.on(reportOn)) } } @@ -349,7 +373,7 @@ class DeclarationsChecker( } val typeParameterDescriptor = trace.get(TYPE_PARAMETER, typeParameter) ?: continue checkSupertypesForConsistency(typeParameterDescriptor, typeParameter) - checkOnlyOneTypeParameterBound(typeParameterDescriptor, typeParameter) + checkOnlyOneTypeParameterBound(typeParameterDescriptor, typeParameter, typeParameterListOwner) } } diff --git a/compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.kt b/compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.kt index d823ffc3e61..64c747bb07d 100644 --- a/compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.kt +++ b/compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.kt @@ -3,9 +3,10 @@ interface I2 open class C interface A1 where V : K, V : K -interface A2W> where W : K, W : V +interface A2 where W : K, W : V interface A3V> where V : I1, V : K, V : I2 interface A4<K, V> where K : I1, K : I2, K : C, K : V, V : I2, V : I1 fun f1() where V : K, V : K {} -fun W> f2() where W : K, W : V {} +fun f2() where W : K, W : V {} +fun W> f3() where W : K, W : V, W : Any {} diff --git a/compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.txt b/compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.txt index 3d3fa582646..a8da4e7def3 100644 --- a/compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.txt +++ b/compiler/testData/diagnostics/tests/generics/TypeParametersInTypeParameterBounds.txt @@ -2,6 +2,7 @@ package public fun f1(): kotlin.Unit where V : K public fun f2(): kotlin.Unit where W : V +public fun f3(): kotlin.Unit where W : V, W : kotlin.Any public interface A1 where V : K { public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean