[FIR] Add BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER diagnostic

This commit is contained in:
Andrey Zinovyev
2021-04-07 08:19:31 +00:00
committed by Space
parent b0f1ddc91e
commit 0f04fac5b2
10 changed files with 76 additions and 8 deletions
@@ -299,6 +299,8 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
}
val UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE by error<FirSourceElement, PsiElement>()
val BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER by error<FirSourceElement, PsiElement>()
}
val REFLECTION by object : DiagnosticGroup("Reflection") {
@@ -222,6 +222,7 @@ object FirErrors {
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>()
// Reflection
val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1<FirSourceElement, KtExpression, FirCallableDeclaration<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -31,6 +31,8 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtModifierList
import org.jetbrains.kotlin.psi.KtParameter.VAL_VAR_TOKEN_SET
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
@@ -40,6 +42,8 @@ import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeCheckerProviderContext
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
private val INLINE_ONLY_ANNOTATION_CLASS_ID = ClassId.topLevel(FqName("kotlin.internal.InlineOnly"))
internal fun FirClass<*>.unsubstitutedScope(context: CheckerContext) =
this.unsubstitutedScope(context.sessionHolder.session, context.sessionHolder.scopeSession, withForcedTypeCalculator = false)
@@ -397,3 +401,5 @@ private fun lowerThanBound(context: ConeInferenceContext, argument: ConeKotlinTy
}
return false
}
fun FirMemberDeclaration.isInlineOnly(): Boolean = isInline && hasAnnotation(INLINE_ONLY_ANNOTATION_CLASS_ID)
@@ -5,14 +5,19 @@
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.fir.analysis.checkers.canHaveSubtypes
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.isInlineOnly
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.utils.addToStdlib.safeAs
object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
@@ -30,5 +35,36 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
reporter.reportOn(bound.source, FirErrors.UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE, context)
}
}
if (containingDeclaration.safeAs<FirMemberDeclaration>()?.isInlineOnly() != true) {
checkOnlyOneTypeParameterBound(declaration, context, reporter)
}
}
private fun checkOnlyOneTypeParameterBound(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
val bounds = declaration.bounds.distinctBy { it.coneType }
val (boundWithParam, otherBounds) = bounds.partition { it.coneType is ConeTypeParameterType }
if (boundWithParam.size > 1 || (boundWithParam.size == 1 && otherBounds.isNotEmpty())) {
// 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
//take TypeConstraint bounds only to report on the same point as old FE
val constraintBounds = bounds.filter { it.isInTypeConstraint() }.toSet()
val reportOn =
if (bounds.size == 2) {
val boundDecl = otherBounds.firstOrNull() ?: boundWithParam.last()
if (constraintBounds.contains(boundDecl)) boundDecl.source
else declaration.source
} else {
declaration.source
}
reporter.reportOn(reportOn, FirErrors.BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER, context)
}
}
private fun FirTypeRef.isInTypeConstraint(): Boolean {
val source = source ?: return false
return source.treeStructure.getParent(source.lighterASTNode)?.tokenType == KtNodeTypes.TYPE_CONSTRAINT
}
}
@@ -42,6 +42,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ARRAY_EQUALITY_OP
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ASSIGNED_VALUE_IS_NEVER_READ
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ASSIGN_OPERATOR_AMBIGUITY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.BACKING_FIELD_IN_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.BREAK_OR_CONTINUE_OUTSIDE_A_LOOP
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CALLABLE_REFERENCE_LHS_NOT_A_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR
@@ -450,6 +451,11 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
)
map.put(UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE, "Extension function type can not be used as an upper bound")
map.put(
BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER,
"Type parameter cannot have any other bounds if it's bounded by another type parameter"
)
// Reflection
map.put(
EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED,