[FIR] Add more type params checks

VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED
BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED
This commit is contained in:
Andrey Zinovyev
2021-04-11 19:33:12 +03:00
committed by TeamCityServer
parent fb1b253d1e
commit 14fe570a00
15 changed files with 128 additions and 42 deletions
@@ -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
@@ -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)
}
}
}
@@ -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
}
}
@@ -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)
}
}
}
@@ -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()
@@ -93,5 +93,6 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
override val typeParameterCheckers: Set<FirTypeParameterChecker> = setOf(
FirTypeParameterBoundsChecker,
FirTypeParameterVarianceChecker,
)
}
@@ -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
@@ -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
@@ -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,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
@@ -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),
@@ -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
@@ -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,