Improve type parameter bound diagnostic location

If there's only one erroneous bound (vast majority of cases), report it on the
bound; otherwise (to avoid reporting it several times) report on the type
parameter declaration
This commit is contained in:
Alexander Udalov
2015-12-10 21:19:31 +03:00
parent 39d9b35e27
commit 5e421b4024
4 changed files with 33 additions and 7 deletions
@@ -265,7 +265,7 @@ public interface Errors {
DiagnosticFactory0<KtTypeReference> DYNAMIC_UPPER_BOUND = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtTypeReference> UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtTypeReference> ONLY_ONE_CLASS_BOUND_ALLOWED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtTypeParameter> BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtElement> BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtTypeReference> REPEATED_BOUND = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<KtNamedDeclaration, TypeParameterDescriptor> CONFLICTING_UPPER_BOUNDS =
@@ -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<Pair<KtTypeReference, KotlinType?>> =
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)
}
}