[FIR] Add REPEATED_BOUND, CONFLICTING_UPPER_BOUNDS diagnostics

This commit is contained in:
Andrey Zinovyev
2021-04-09 07:38:39 +00:00
committed by Space
parent 81d4c19d8b
commit 37055c5a09
20 changed files with 132 additions and 94 deletions
@@ -38,15 +38,15 @@ fun test(z: Int, c: Char) {}
typealias BA = A
fun <T> kek(t: T) where T : (String) -> Any?, T : <!FINAL_UPPER_BOUND!>Char<!> {}
fun <T> kek(t: T) where T : () -> Boolean, T : <!FINAL_UPPER_BOUND!>String<!> {}
fun <<!CONFLICTING_UPPER_BOUNDS!>T<!>> kek(t: T) where T : (String) -> Any?, T : <!FINAL_UPPER_BOUND!>Char<!> {}
fun <<!CONFLICTING_UPPER_BOUNDS!>T<!>> kek(t: T) where T : () -> Boolean, T : <!FINAL_UPPER_BOUND!>String<!> {}
fun <T : <!FINAL_UPPER_BOUND!>Int<!>> kek(t: T) {}
fun lol(a: Array<Int>) {}
fun lol(a: Array<Boolean>) {}
<!CONFLICTING_OVERLOADS!>fun <T> mem(t: T)<!> where T : () -> Boolean, T : <!FINAL_UPPER_BOUND!>String<!> {}
<!CONFLICTING_OVERLOADS!>fun <T> mem(t: T)<!> where T : <!FINAL_UPPER_BOUND!>String<!>, T : () -> Boolean {}
<!CONFLICTING_OVERLOADS!>fun <<!CONFLICTING_UPPER_BOUNDS!>T<!>> mem(t: T)<!> where T : () -> Boolean, T : <!FINAL_UPPER_BOUND!>String<!> {}
<!CONFLICTING_OVERLOADS!>fun <<!CONFLICTING_UPPER_BOUNDS!>T<!>> mem(t: T)<!> where T : <!FINAL_UPPER_BOUND!>String<!>, T : () -> Boolean {}
class M {
companion <!REDECLARATION!>object<!> {}
@@ -294,15 +294,21 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
parameter<FirTypeParameterSymbol>("typeParameter")
}
val FINAL_UPPER_BOUND by warning<FirSourceElement, PsiElement> {
val FINAL_UPPER_BOUND by warning<FirSourceElement, KtTypeReference> {
parameter<ConeKotlinType>("type")
}
val UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE by error<FirSourceElement, PsiElement>()
val UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE by error<FirSourceElement, KtTypeReference>()
val BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER by error<FirSourceElement, PsiElement>()
val BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER by error<FirSourceElement, KtElement>()
val ONLY_ONE_CLASS_BOUND_ALLOWED by error<FirSourceElement, PsiElement>()
val ONLY_ONE_CLASS_BOUND_ALLOWED by error<FirSourceElement, KtTypeReference>()
val REPEATED_BOUND by error<FirSourceElement, KtTypeReference>()
val CONFLICTING_UPPER_BOUNDS by error<FirSourceElement, KtNamedDeclaration> {
parameter<FirTypeParameterSymbol>("typeParameter")
}
}
val REFLECTION by object : DiagnosticGroup("Reflection") {
@@ -220,10 +220,12 @@ object FirErrors {
val INNER_CLASS_OF_GENERIC_THROWABLE_SUBCLASS by error0<FirSourceElement, KtClassOrObject>(SourceElementPositioningStrategies.DECLARATION_NAME)
val KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE by error1<FirSourceElement, KtNamedDeclaration, FirTypeParameterSymbol>(SourceElementPositioningStrategies.DECLARATION_NAME)
val TYPE_PARAMETER_AS_REIFIED by error1<FirSourceElement, PsiElement, FirTypeParameterSymbol>()
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>()
val FINAL_UPPER_BOUND by warning1<FirSourceElement, KtTypeReference, ConeKotlinType>()
val UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE by error0<FirSourceElement, KtTypeReference>()
val BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER by error0<FirSourceElement, KtElement>()
val ONLY_ONE_CLASS_BOUND_ALLOWED by error0<FirSourceElement, KtTypeReference>()
val REPEATED_BOUND by error0<FirSourceElement, KtTypeReference>()
val CONFLICTING_UPPER_BOUNDS by error1<FirSourceElement, KtNamedDeclaration, FirTypeParameterSymbol>()
// Reflection
val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1<FirSourceElement, KtExpression, FirCallableDeclaration<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -7,18 +7,16 @@ 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.*
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
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.types.ConeTypeParameterType
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isExtensionFunctionType
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeCheckerProviderContext
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
@@ -48,7 +46,8 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
checkOnlyOneTypeParameterBound(declaration, context, reporter)
}
checkOnlyOneClassBound(declaration, context, reporter)
checkBoundUniqueness(declaration, context, reporter)
checkConflictingBounds(declaration, context, reporter)
}
private fun checkOnlyOneTypeParameterBound(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
@@ -78,15 +77,47 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
}
private fun checkOnlyOneClassBound(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
private fun checkBoundUniqueness(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
val seenClasses = mutableSetOf<FirRegularClass>()
val bounds = declaration.bounds.distinctBy { it.coneType }
bounds.forEach { bound ->
val allNonErrorBounds = declaration.bounds.filter { it !is FirErrorTypeRef }
val uniqueBounds = allNonErrorBounds.distinctBy { it.coneType.classId ?: it.coneType }
uniqueBounds.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)
}
}
}
allNonErrorBounds.minus(uniqueBounds).forEach { bound ->
reporter.reportOn(bound.source, FirErrors.REPEATED_BOUND, context)
}
}
private fun checkConflictingBounds(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.bounds.size < 2) return
fun anyConflictingTypes(types: List<ConeKotlinType>): Boolean {
types.forEach { type ->
if (!type.canHaveSubtypes(context.session)) {
types.forEach { otherType ->
if (type != otherType && !type.isRelated(context.session.typeContext, otherType)){
return true
}
}
}
}
return false
}
if (anyConflictingTypes(declaration.bounds.map { it.coneType })) {
reporter.reportOn(declaration.source, FirErrors.CONFLICTING_UPPER_BOUNDS, declaration.symbol, context)
}
}
private fun KotlinTypeMarker.isRelated(context: TypeCheckerProviderContext, type: KotlinTypeMarker?): Boolean =
isSubtypeOf(context, type) || isSupertypeOf(context, type)
}
@@ -58,6 +58,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTIO
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTION_ON_NULLABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONFLICTING_OVERLOADS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONFLICTING_PROJECTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONFLICTING_UPPER_BOUNDS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONSTRUCTOR_IN_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CONSTRUCTOR_IN_OBJECT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CYCLIC_CONSTRUCTOR_DELEGATION_CALL
@@ -184,6 +185,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_SETTER_
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_SINGLE_EXPRESSION_STRING_TEMPLATE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_VISIBILITY_MODIFIER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REIFIED_TYPE_IN_CATCH_CLAUSE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REPEATED_BOUND
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REPEATED_MODIFIER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RESERVED_MEMBER_INSIDE_INLINE_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY
@@ -457,7 +459,14 @@ 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")
map.put(ONLY_ONE_CLASS_BOUND_ALLOWED, "Only one of the upper bounds can be a class")
map.put(REPEATED_BOUND, "Type parameter already has this bound")
map.put(
CONFLICTING_UPPER_BOUNDS,
"Upper bounds of {0} have empty intersection",
SYMBOL
)
// Reflection
map.put(