[FIR] Add more type params checks
VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED
This commit is contained in:
committed by
TeamCityServer
parent
fb1b253d1e
commit
14fe570a00
+4
@@ -318,6 +318,10 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
|
||||
parameter<FirDeclaration>("typeParametersOwner")
|
||||
}
|
||||
|
||||
val BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED by error<FirSourceElement, KtTypeReference>()
|
||||
|
||||
val REIFIED_TYPE_PARAMETER_NO_INLINE by error<FirSourceElement, PsiElement>()
|
||||
|
||||
val RETURN_TYPE_MISMATCH by error<FirSourceElement, KtExpression>(PositioningStrategy.WHOLE_ELEMENT) {
|
||||
parameter<ConeKotlinType>("expected")
|
||||
parameter<ConeKotlinType>("actual")
|
||||
|
||||
@@ -235,6 +235,8 @@ object FirErrors {
|
||||
val REPEATED_BOUND by error0<FirSourceElement, KtTypeReference>()
|
||||
val CONFLICTING_UPPER_BOUNDS by error1<FirSourceElement, KtNamedDeclaration, FirTypeParameterSymbol>()
|
||||
val NAME_IN_CONSTRAINT_IS_NOT_A_TYPE_PARAMETER by error2<FirSourceElement, KtSimpleNameExpression, Name, FirDeclaration>()
|
||||
val BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED by error0<FirSourceElement, KtTypeReference>()
|
||||
val REIFIED_TYPE_PARAMETER_NO_INLINE by error0<FirSourceElement, PsiElement>()
|
||||
val RETURN_TYPE_MISMATCH by error2<FirSourceElement, KtExpression, ConeKotlinType, ConeKotlinType>(SourceElementPositioningStrategies.WHOLE_ELEMENT)
|
||||
|
||||
// Reflection
|
||||
|
||||
+1
-7
@@ -36,14 +36,8 @@ object FirConflictingProjectionChecker : FirBasicDeclarationChecker() {
|
||||
checkTypeRef(it, context, reporter)
|
||||
}
|
||||
}
|
||||
is FirTypeAlias -> {
|
||||
for (it in declaration.typeParameters) {
|
||||
if (it.variance != Variance.INVARIANT) {
|
||||
reporter.reportOn(it.source, FirErrors.VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED, context)
|
||||
}
|
||||
}
|
||||
is FirTypeAlias ->
|
||||
checkTypeRef(declaration.expandedTypeRef, context, reporter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+44
-13
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.fir.FirRealSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
@@ -28,19 +29,11 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
||||
)
|
||||
|
||||
override fun check(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val containingDeclaration = context.containingDeclarations.lastOrNull()
|
||||
val containingDeclaration = context.containingDeclarations.lastOrNull() ?: return
|
||||
if (containingDeclaration is FirConstructor) return
|
||||
if (containingDeclaration is FirSimpleFunction && containingDeclaration.isOverride) return
|
||||
if (containingDeclaration is FirProperty && containingDeclaration.isOverride) return
|
||||
|
||||
declaration.symbol.fir.bounds.forEach { bound ->
|
||||
if (!bound.coneType.canHaveSubtypes(context.session)) {
|
||||
reporter.reportOn(bound.source, FirErrors.FINAL_UPPER_BOUND, bound.coneType, context)
|
||||
}
|
||||
if (bound.isExtensionFunctionType(context.session)) {
|
||||
reporter.reportOn(bound.source, FirErrors.UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE, context)
|
||||
}
|
||||
}
|
||||
checkFinalUpperBounds(declaration, containingDeclaration, context, reporter)
|
||||
checkExtensionFunctionTypeBound(declaration, context, reporter)
|
||||
|
||||
if (containingDeclaration.safeAs<FirMemberDeclaration>()?.isInlineOnly() != true) {
|
||||
checkOnlyOneTypeParameterBound(declaration, context, reporter)
|
||||
@@ -48,6 +41,44 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
||||
|
||||
checkBoundUniqueness(declaration, context, reporter)
|
||||
checkConflictingBounds(declaration, context, reporter)
|
||||
checkTypeAliasBound(declaration, containingDeclaration, context, reporter)
|
||||
}
|
||||
|
||||
private fun checkFinalUpperBounds(
|
||||
declaration: FirTypeParameter,
|
||||
containingDeclaration: FirDeclaration,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
if (containingDeclaration is FirSimpleFunction && containingDeclaration.isOverride) return
|
||||
if (containingDeclaration is FirProperty && containingDeclaration.isOverride) return
|
||||
|
||||
declaration.symbol.fir.bounds.forEach { bound ->
|
||||
if (!bound.coneType.canHaveSubtypes(context.session)) {
|
||||
reporter.reportOn(bound.source, FirErrors.FINAL_UPPER_BOUND, bound.coneType, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkExtensionFunctionTypeBound(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
declaration.symbol.fir.bounds.forEach { bound ->
|
||||
if (bound.isExtensionFunctionType(context.session)) {
|
||||
reporter.reportOn(bound.source, FirErrors.UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkTypeAliasBound(
|
||||
declaration: FirTypeParameter,
|
||||
containingDeclaration: FirDeclaration,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
if (containingDeclaration is FirTypeAlias) {
|
||||
declaration.bounds.filter { it.source?.kind == FirRealSourceElementKind }.forEach { bound ->
|
||||
reporter.reportOn(bound.source, FirErrors.BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkOnlyOneTypeParameterBound(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
@@ -81,7 +112,7 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
||||
val seenClasses = mutableSetOf<FirRegularClass>()
|
||||
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) {
|
||||
@@ -102,7 +133,7 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
||||
types.forEach { type ->
|
||||
if (!type.canHaveSubtypes(context.session)) {
|
||||
types.forEach { otherType ->
|
||||
if (type != otherType && !type.isRelated(context.session.typeContext, otherType)){
|
||||
if (type != otherType && !type.isRelated(context.session.typeContext, otherType)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
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.FirProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
|
||||
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
object FirTypeParameterVarianceChecker : FirTypeParameterChecker() {
|
||||
|
||||
override fun check(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val containingDeclaration = context.containingDeclarations.lastOrNull()
|
||||
if (declaration.variance != Variance.INVARIANT &&
|
||||
(containingDeclaration is FirSimpleFunction ||
|
||||
containingDeclaration is FirTypeAlias ||
|
||||
containingDeclaration is FirProperty)
|
||||
) {
|
||||
reporter.reportOn(declaration.source, FirErrors.VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
@@ -92,6 +92,12 @@ abstract class AbstractDiagnosticCollector(
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitTypeAlias(typeAlias: FirTypeAlias, data: Nothing?) {
|
||||
withSuppressedDiagnostics(typeAlias) {
|
||||
visitWithDeclaration(typeAlias)
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitJump(loopJump: FirLoopJump) {
|
||||
withSuppressedDiagnostics(loopJump) {
|
||||
loopJump.runComponents()
|
||||
|
||||
+1
@@ -93,5 +93,6 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
|
||||
override val typeParameterCheckers: Set<FirTypeParameterChecker> = setOf(
|
||||
FirTypeParameterBoundsChecker,
|
||||
FirTypeParameterVarianceChecker,
|
||||
)
|
||||
}
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
fun <in T> f() {
|
||||
|
||||
}
|
||||
|
||||
fun <out T> g() {
|
||||
|
||||
}
|
||||
|
||||
fun <out T, in X, Y> h() {
|
||||
|
||||
}
|
||||
|
||||
val <out T> T.x: Int
|
||||
get() = 1
|
||||
|
||||
val <in T> T.y: Int
|
||||
get() = 1
|
||||
compiler/testData/diagnostics/tests/declarationChecks/VarianceOnFunctionAndPropertyTypeParameters.kt
Vendored
+2
-1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
fun <<!VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED!>in<!> T> f() {
|
||||
|
||||
}
|
||||
@@ -14,4 +15,4 @@ val <<!VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED!>out<!> T> T.x: Int
|
||||
get() = 1
|
||||
|
||||
val <<!VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED!>in<!> T> T.y: Int
|
||||
get() = 1
|
||||
get() = 1
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ fun test3() {
|
||||
}
|
||||
|
||||
class A {
|
||||
fun <out <!REPEATED_MODIFIER!>out<!> T> bar() {
|
||||
fun <<!VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED!>out<!> <!REPEATED_MODIFIER!>out<!> T> bar() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
|
||||
|
||||
typealias R<T: List<R>> = List<T>
|
||||
typealias R<T: <!BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED!>List<R><!>> = List<T>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// !DIAGNOSTICS: -UNUSED_TYPEALIAS_PARAMETER
|
||||
|
||||
typealias WithVariance<<!VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED!>in<!> X, <!VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED!>out<!> Y> = Int
|
||||
typealias WithBounds1<T : T> = Int
|
||||
typealias WithBounds2<X : Y, Y : X> = Int
|
||||
typealias WithBounds1<T : <!BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED!>T<!>> = Int
|
||||
typealias WithBounds2<X : <!BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED!>Y<!>, Y : <!BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED!>X<!>> = Int
|
||||
|
||||
typealias WithBounds3<X> <!SYNTAX!>where X : Any<!> = Int
|
||||
|
||||
|
||||
+12
@@ -986,6 +986,18 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED) { firDiagnostic ->
|
||||
BoundOnTypeAliasParameterNotAllowedImpl(
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.REIFIED_TYPE_PARAMETER_NO_INLINE) { firDiagnostic ->
|
||||
ReifiedTypeParameterNoInlineImpl(
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.RETURN_TYPE_MISMATCH) { firDiagnostic ->
|
||||
ReturnTypeMismatchImpl(
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
|
||||
|
||||
+8
@@ -701,6 +701,14 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
abstract val typeParametersOwner: KtSymbol
|
||||
}
|
||||
|
||||
abstract class BoundOnTypeAliasParameterNotAllowed : KtFirDiagnostic<KtTypeReference>() {
|
||||
override val diagnosticClass get() = BoundOnTypeAliasParameterNotAllowed::class
|
||||
}
|
||||
|
||||
abstract class ReifiedTypeParameterNoInline : KtFirDiagnostic<PsiElement>() {
|
||||
override val diagnosticClass get() = ReifiedTypeParameterNoInline::class
|
||||
}
|
||||
|
||||
abstract class ReturnTypeMismatch : KtFirDiagnostic<KtExpression>() {
|
||||
override val diagnosticClass get() = ReturnTypeMismatch::class
|
||||
abstract val expected: KtType
|
||||
|
||||
+14
@@ -1131,6 +1131,20 @@ internal class NameInConstraintIsNotATypeParameterImpl(
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class BoundOnTypeAliasParameterNotAllowedImpl(
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.BoundOnTypeAliasParameterNotAllowed(), KtAbstractFirDiagnostic<KtTypeReference> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ReifiedTypeParameterNoInlineImpl(
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.ReifiedTypeParameterNoInline(), KtAbstractFirDiagnostic<PsiElement> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ReturnTypeMismatchImpl(
|
||||
override val expected: KtType,
|
||||
override val actual: KtType,
|
||||
|
||||
Reference in New Issue
Block a user