[FIR] Add BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER diagnostic
This commit is contained in:
+2
@@ -299,6 +299,8 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE by error<FirSourceElement, PsiElement>()
|
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") {
|
val REFLECTION by object : DiagnosticGroup("Reflection") {
|
||||||
|
|||||||
@@ -222,6 +222,7 @@ object FirErrors {
|
|||||||
val TYPE_PARAMETER_AS_REIFIED by error1<FirSourceElement, PsiElement, FirTypeParameterSymbol>()
|
val TYPE_PARAMETER_AS_REIFIED by error1<FirSourceElement, PsiElement, FirTypeParameterSymbol>()
|
||||||
val FINAL_UPPER_BOUND by warning1<FirSourceElement, PsiElement, ConeKotlinType>()
|
val FINAL_UPPER_BOUND by warning1<FirSourceElement, PsiElement, ConeKotlinType>()
|
||||||
val UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE by error0<FirSourceElement, PsiElement>()
|
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
|
// Reflection
|
||||||
val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1<FirSourceElement, KtExpression, FirCallableDeclaration<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
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.fir.types.*
|
||||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
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.KtModifierList
|
||||||
import org.jetbrains.kotlin.psi.KtParameter.VAL_VAR_TOKEN_SET
|
import org.jetbrains.kotlin.psi.KtParameter.VAL_VAR_TOKEN_SET
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
|
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.types.model.TypeCheckerProviderContext
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
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) =
|
internal fun FirClass<*>.unsubstitutedScope(context: CheckerContext) =
|
||||||
this.unsubstitutedScope(context.sessionHolder.session, context.sessionHolder.scopeSession, withForcedTypeCalculator = false)
|
this.unsubstitutedScope(context.sessionHolder.session, context.sessionHolder.scopeSession, withForcedTypeCalculator = false)
|
||||||
|
|
||||||
@@ -397,3 +401,5 @@ private fun lowerThanBound(context: ConeInferenceContext, argument: ConeKotlinTy
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun FirMemberDeclaration.isInlineOnly(): Boolean = isInline && hasAnnotation(INLINE_ONLY_ANNOTATION_CLASS_ID)
|
||||||
|
|||||||
+36
@@ -5,14 +5,19 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
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.canHaveSubtypes
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
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.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
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.coneType
|
||||||
import org.jetbrains.kotlin.fir.types.isExtensionFunctionType
|
import org.jetbrains.kotlin.fir.types.isExtensionFunctionType
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
||||||
|
|
||||||
@@ -30,5 +35,36 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
|||||||
reporter.reportOn(bound.source, FirErrors.UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE, context)
|
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
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -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.ASSIGNED_VALUE_IS_NEVER_READ
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ASSIGN_OPERATOR_AMBIGUITY
|
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.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.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_LHS_NOT_A_CLASS
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR
|
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(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
|
// Reflection
|
||||||
map.put(
|
map.put(
|
||||||
EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED,
|
EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED,
|
||||||
|
|||||||
+6
-6
@@ -3,13 +3,13 @@ interface I2
|
|||||||
open class C
|
open class C
|
||||||
|
|
||||||
interface A1<K, V> where V : K, V : K
|
interface A1<K, V> where V : K, V : K
|
||||||
interface A2<K, V, W> where W : K, W : V
|
interface A2<K, V, W> where W : K, W : <!BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER!>V<!>
|
||||||
interface A3<K, V> where V : I1, V : K, V : I2
|
interface A3<K, <!BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER!>V<!>> where V : I1, V : K, V : I2
|
||||||
interface A4<K, V> where K : I1, K : I2, K : C, K : V, V : I2, V : I1
|
interface A4<<!BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER!>K<!>, V> where K : I1, K : I2, K : C, K : V, V : I2, V : I1
|
||||||
|
|
||||||
fun <K, V> f1() where V : K, V : K {}
|
fun <K, V> f1() where V : K, V : K {}
|
||||||
fun <K, V, W> f2() where W : K, W : V {
|
fun <K, V, W> f2() where W : K, W : <!BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER!>V<!> {
|
||||||
fun <T> f3() where T : K, T : V {}
|
fun <T> f3() where T : K, T : <!BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER!>V<!> {}
|
||||||
fun <T> f4() where T : K, T : K {}
|
fun <T> f4() where T : K, T : K {}
|
||||||
}
|
}
|
||||||
fun <K, V, W> f3() where W : K, W : V, W : Any {}
|
fun <K, V, <!BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER!>W<!>> f3() where W : K, W : V, W : Any {}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
class Foo<T : Cloneable> where T : Comparable<T> {
|
class Foo<T : Cloneable> where T : Comparable<T> {
|
||||||
fun <U : Cloneable> foo(u: U): U where U: Comparable<U> {
|
fun <U : Cloneable> foo(u: U): U where U: Comparable<U> {
|
||||||
fun <T: Any> bar() where T: U {}
|
fun <<!BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER!>T: Any<!>> bar() where T: U {}
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
@@ -920,6 +920,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
|||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
add(FirErrors.BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER) { firDiagnostic ->
|
||||||
|
BoundsNotAllowedIfBoundedByTypeParameterImpl(
|
||||||
|
firDiagnostic as FirPsiDiagnostic<*>,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
add(FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED) { firDiagnostic ->
|
add(FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED) { firDiagnostic ->
|
||||||
ExtensionInClassReferenceNotAllowedImpl(
|
ExtensionInClassReferenceNotAllowedImpl(
|
||||||
firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a as FirCallableDeclaration),
|
firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a as FirCallableDeclaration),
|
||||||
|
|||||||
+4
@@ -656,6 +656,10 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
override val diagnosticClass get() = UpperBoundIsExtensionFunctionType::class
|
override val diagnosticClass get() = UpperBoundIsExtensionFunctionType::class
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class BoundsNotAllowedIfBoundedByTypeParameter : KtFirDiagnostic<PsiElement>() {
|
||||||
|
override val diagnosticClass get() = BoundsNotAllowedIfBoundedByTypeParameter::class
|
||||||
|
}
|
||||||
|
|
||||||
abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic<KtExpression>() {
|
abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic<KtExpression>() {
|
||||||
override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class
|
override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class
|
||||||
abstract val referencedDeclaration: KtCallableSymbol
|
abstract val referencedDeclaration: KtCallableSymbol
|
||||||
|
|||||||
+7
@@ -1056,6 +1056,13 @@ internal class UpperBoundIsExtensionFunctionTypeImpl(
|
|||||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class BoundsNotAllowedIfBoundedByTypeParameterImpl(
|
||||||
|
firDiagnostic: FirPsiDiagnostic<*>,
|
||||||
|
override val token: ValidityToken,
|
||||||
|
) : KtFirDiagnostic.BoundsNotAllowedIfBoundedByTypeParameter(), KtAbstractFirDiagnostic<PsiElement> {
|
||||||
|
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||||
|
}
|
||||||
|
|
||||||
internal class ExtensionInClassReferenceNotAllowedImpl(
|
internal class ExtensionInClassReferenceNotAllowedImpl(
|
||||||
override val referencedDeclaration: KtCallableSymbol,
|
override val referencedDeclaration: KtCallableSymbol,
|
||||||
firDiagnostic: FirPsiDiagnostic<*>,
|
firDiagnostic: FirPsiDiagnostic<*>,
|
||||||
|
|||||||
Reference in New Issue
Block a user