diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index b06f114faf6..5877dc37fc6 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -324,6 +324,8 @@ object DIAGNOSTICS_LIST : DiagnosticList() { val TYPE_PARAMETERS_NOT_ALLOWED by error() + val TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER by error() + val RETURN_TYPE_MISMATCH by error(PositioningStrategy.WHOLE_ELEMENT) { parameter("expected") parameter("actual") diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 06393686bd7..7abd37a721a 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -238,6 +238,7 @@ object FirErrors { val BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED by error0() val REIFIED_TYPE_PARAMETER_NO_INLINE by error0() val TYPE_PARAMETERS_NOT_ALLOWED by error0() + val TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER by error0() val RETURN_TYPE_MISMATCH by error2(SourceElementPositioningStrategies.WHOLE_ELEMENT) // Reflection diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPropertyTypeParametersChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPropertyTypeParametersChecker.kt new file mode 100644 index 00000000000..4c745aa96fe --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPropertyTypeParametersChecker.kt @@ -0,0 +1,37 @@ +/* + * 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.scopes.impl.toConeType +import org.jetbrains.kotlin.fir.types.* + +object FirPropertyTypeParametersChecker : FirPropertyChecker() { + + override fun check(declaration: FirProperty, context: CheckerContext, reporter: DiagnosticReporter) { + val boundsByName = declaration.typeParameters.map { it.name to it.bounds }.toMap() + val usedTypes = HashSet() + fun collectAllTypes(type: ConeKotlinType) { + if (usedTypes.add(type)) { + type.typeArguments.forEach { it.type?.let(::collectAllTypes) } + if (type is ConeTypeParameterType) { + boundsByName[type.lookupTag.name]?.forEach { collectAllTypes(it.coneType) } + } + } + } + declaration.receiverTypeRef?.let { collectAllTypes(it.coneType) } + + val usedNames = usedTypes.filterIsInstance().map { it.lookupTag.name } + declaration.typeParameters.filterNot { usedNames.contains(it.name) }.forEach { danglingParam -> + reporter.reportOn(danglingParam.source, FirErrors.TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER, context) + } + } + +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 714705d8277..bf299aaac89 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -229,6 +229,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETERS_N import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_AS_REIFIED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_IN_CATCH_CLAUSE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_IS_NOT_AN_EXPRESSION +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_ON_LHS_OF_DOT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNEXPECTED_SAFE_CALL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNINITIALIZED_ENUM_COMPANION @@ -504,6 +505,8 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension { map.put(TYPE_PARAMETERS_NOT_ALLOWED, "Type parameters are not allowed here") + map.put(TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER, "Type parameter of a property must be used in its receiver type") + map.put(RETURN_TYPE_MISMATCH, "Return type mismatch: expected {0}, actual {1}", RENDER_TYPE, RENDER_TYPE) // Reflection diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt index 4cb7b76638f..6beb6a9fdc7 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt @@ -42,6 +42,8 @@ object CommonDeclarationCheckers : DeclarationCheckers() { FirDestructuringDeclarationChecker, FirConstPropertyChecker, FirPropertyAccessorChecker, + FirPropertyTypeParametersChecker, + FirPropertyAccessorChecker, FirInitializerTypeMismatchChecker ) diff --git a/compiler/testData/diagnostics/tests/MultipleBounds.fir.kt b/compiler/testData/diagnostics/tests/MultipleBounds.fir.kt index ee395cf250d..cad212e0616 100644 --- a/compiler/testData/diagnostics/tests/MultipleBounds.fir.kt +++ b/compiler/testData/diagnostics/tests/MultipleBounds.fir.kt @@ -9,9 +9,9 @@ interface B { } interface G { - val boo: Double where X : A, X : B - val bal: Double where A : B - val bas: Double where Y : B, X : B + val <X> boo: Double where X : A, X : B + val <A> bal: Double where A : B + val <Y> bas: Double where Y : B, X : B } class C() : A(), B @@ -66,4 +66,4 @@ val t1 = test2(A()) val t2 = test2(C()) val t3 = test2(C()) -val x : Int = 0 +val <T, B : T> x : Int = 0 diff --git a/compiler/testData/diagnostics/tests/inference/kt11963.fir.kt b/compiler/testData/diagnostics/tests/inference/kt11963.fir.kt index 2bb0af2999b..682e90dfb85 100644 --- a/compiler/testData/diagnostics/tests/inference/kt11963.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/kt11963.fir.kt @@ -1 +1 @@ -val KClass.something> abc +val <T : KClass.something> abc diff --git a/compiler/testData/diagnostics/tests/override/Generics.fir.kt b/compiler/testData/diagnostics/tests/override/Generics.fir.kt index 54f1c4b3d24..6c0e076e169 100644 --- a/compiler/testData/diagnostics/tests/override/Generics.fir.kt +++ b/compiler/testData/diagnostics/tests/override/Generics.fir.kt @@ -43,7 +43,7 @@ abstract class MyAbstractClass1 : MyTrait, MyAbstractClass() { class MyIllegalGenericClass1 : MyTrait, MyAbstractClass() {} class MyIllegalGenericClass2(r : R) : MyTrait, MyAbstractClass() { override fun foo(r: R) = r - override val pr : R = r + override val <T> pr : R = r } class MyIllegalClass1 : MyTrait, MyAbstractClass() {} abstract class MyLegalAbstractClass1 : MyTrait, MyAbstractClass() {} @@ -51,10 +51,10 @@ abstract class MyLegalAbstractClass1 : MyTrait, MyAbstractClass() { class MyIllegalClass2(t : T) : MyTrait, MyAbstractClass() { fun foo(t: T) = t fun bar(t: T) = t - val pr : T = t + val <R> pr : T = t } abstract class MyLegalAbstractClass2(t : T) : MyTrait, MyAbstractClass() { fun foo(t: T) = t fun bar(t: T) = t - val pr : T = t -} \ No newline at end of file + val <R> pr : T = t +} diff --git a/compiler/testData/diagnostics/tests/typeParameters/extFunctionTypeAsUpperBound.fir.kt b/compiler/testData/diagnostics/tests/typeParameters/extFunctionTypeAsUpperBound.fir.kt deleted file mode 100644 index e920d6cb9fa..00000000000 --- a/compiler/testData/diagnostics/tests/typeParameters/extFunctionTypeAsUpperBound.fir.kt +++ /dev/null @@ -1,7 +0,0 @@ -fun Int.() -> String> foo() {} - -val Int.() -> String> bar = fun (x: Int): String { return x.toString() } - -class A where T : Double.(Int) -> Unit - -interface BT.() -> Unit> diff --git a/compiler/testData/diagnostics/tests/typeParameters/extFunctionTypeAsUpperBound.kt b/compiler/testData/diagnostics/tests/typeParameters/extFunctionTypeAsUpperBound.kt index 2f21553c3df..1406da7a750 100644 --- a/compiler/testData/diagnostics/tests/typeParameters/extFunctionTypeAsUpperBound.kt +++ b/compiler/testData/diagnostics/tests/typeParameters/extFunctionTypeAsUpperBound.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL fun Int.() -> String> foo() {} val <T: Int.() -> String> bar = fun (x: Int): String { return x.toString() } diff --git a/compiler/testData/diagnostics/tests/typeParameters/functionTypeAsUpperBound.fir.kt b/compiler/testData/diagnostics/tests/typeParameters/functionTypeAsUpperBound.fir.kt deleted file mode 100644 index edd3dccff91..00000000000 --- a/compiler/testData/diagnostics/tests/typeParameters/functionTypeAsUpperBound.fir.kt +++ /dev/null @@ -1,7 +0,0 @@ -fun String> foo() {} - -val kotlin.String> bar = fun (x: Int): String { return x.toString() } - -class A where T : () -> Unit, U : (Int) -> Double, V : (T, U) -> U - -interface B Unit> \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typeParameters/functionTypeAsUpperBound.kt b/compiler/testData/diagnostics/tests/typeParameters/functionTypeAsUpperBound.kt index cab01fc0082..8eea4df311e 100644 --- a/compiler/testData/diagnostics/tests/typeParameters/functionTypeAsUpperBound.kt +++ b/compiler/testData/diagnostics/tests/typeParameters/functionTypeAsUpperBound.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL fun String> foo() {} val <T: (kotlin.Int) -> kotlin.String> bar = fun (x: Int): String { return x.toString() } diff --git a/compiler/testData/diagnostics/tests/typeParameters/propertyTypeParameters.fir.kt b/compiler/testData/diagnostics/tests/typeParameters/propertyTypeParameters.fir.kt deleted file mode 100644 index 93872b99e62..00000000000 --- a/compiler/testData/diagnostics/tests/typeParameters/propertyTypeParameters.fir.kt +++ /dev/null @@ -1,44 +0,0 @@ -// !DIAGNOSTICS: -REDUNDANT_PROJECTION -CONFLICTING_PROJECTION - -interface G - -val T.a: Int - get() = 3 - -val Map.b: String - get() = "asds" - -val G.c: Int get() = 5 - -val List>.d: Int get() = 6 - -val G.e: T? - get() = null - -val List>>.f: Int get() = 7 - -val List>>.g: Int get() = 7 -val List>>.h: Int get() = 7 - -val List>>.i: Int get() = 7 - -var p = 1 - -class C { - val T1.a: Int get() = 3 - val T2.b: Int get() = 3 - val E.c: Int get() = 3 - val Map.d: Int get() = 3 - val Map.e: Int get() = 3 -} - -val > T.z1: Int - get() = 4 - -interface D> - -val > X.z2: Int - get() = 4 - -val D<*>.z3: Int - get() = 4 \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typeParameters/propertyTypeParameters.kt b/compiler/testData/diagnostics/tests/typeParameters/propertyTypeParameters.kt index e407ece6ca0..eacac05936b 100644 --- a/compiler/testData/diagnostics/tests/typeParameters/propertyTypeParameters.kt +++ b/compiler/testData/diagnostics/tests/typeParameters/propertyTypeParameters.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -REDUNDANT_PROJECTION -CONFLICTING_PROJECTION interface G diff --git a/compiler/testData/diagnostics/tests/typeParameters/propertyTypeParametersWithUpperBounds.fir.kt b/compiler/testData/diagnostics/tests/typeParameters/propertyTypeParametersWithUpperBounds.fir.kt deleted file mode 100644 index 4f3ea1b2bca..00000000000 --- a/compiler/testData/diagnostics/tests/typeParameters/propertyTypeParametersWithUpperBounds.fir.kt +++ /dev/null @@ -1,28 +0,0 @@ - -val K.a: Int get() = 4 - -val K.b: Int where T : K - get() = 4 - -val K.c: Int where T : List - get() = 4 - -val K.d: Int where K : T - get() = 4 - -val K.e: Int where K : List - get() = 4 - -interface G -val G.x1: Int where T : G - get() = 4 - - -val Z.x2: Int where X : Y, Z : Y - get() = 4 - -val , Z: List>> Z.x3: Int - get() = 5 - -val >, Z> Map>.x4: Int - get() = 5 \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typeParameters/propertyTypeParametersWithUpperBounds.kt b/compiler/testData/diagnostics/tests/typeParameters/propertyTypeParametersWithUpperBounds.kt index b7a28d8748e..4b308c37efa 100644 --- a/compiler/testData/diagnostics/tests/typeParameters/propertyTypeParametersWithUpperBounds.kt +++ b/compiler/testData/diagnostics/tests/typeParameters/propertyTypeParametersWithUpperBounds.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL val <T : K, K> K.a: Int get() = 4 diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 3b9b3b422db..cb1ebe9609d 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -1004,6 +1004,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER) { firDiagnostic -> + TypeParameterOfPropertyNotUsedInReceiverImpl( + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.RETURN_TYPE_MISMATCH) { firDiagnostic -> ReturnTypeMismatchImpl( firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index ffbd5b2ff4a..177910908fe 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -713,6 +713,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = TypeParametersNotAllowed::class } + abstract class TypeParameterOfPropertyNotUsedInReceiver : KtFirDiagnostic() { + override val diagnosticClass get() = TypeParameterOfPropertyNotUsedInReceiver::class + } + abstract class ReturnTypeMismatch : KtFirDiagnostic() { override val diagnosticClass get() = ReturnTypeMismatch::class abstract val expected: KtType diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 5c27c82301c..8461c478647 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -1152,6 +1152,13 @@ internal class TypeParametersNotAllowedImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class TypeParameterOfPropertyNotUsedInReceiverImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.TypeParameterOfPropertyNotUsedInReceiver(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class ReturnTypeMismatchImpl( override val expected: KtType, override val actual: KtType,