From b08df32cac68efcf88e6f2cf2230563b121b21bd Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Wed, 2 Aug 2023 22:55:13 +0200 Subject: [PATCH] [FIR] Introduce OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY diagnostic ^KT-60892 --- .../diagnostics/KtFirDataClassConverters.kt | 6 ++++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 4 +++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 5 +++ .../diagnostics/FirDiagnosticsList.kt | 2 ++ .../fir/analysis/diagnostics/FirErrors.kt | 1 + .../checkers/CommonExpressionCheckers.kt | 1 + .../analysis/checkers/CommonTypeCheckers.kt | 1 + .../checkers/FirOptionalExpectationHelpers.kt | 19 ++++++++++++ ...FirOptionalExpectationExpressionChecker.kt | 26 ++++++++++++++++ .../type/FirOptionalExpectationTypeChecker.kt | 31 +++++++++++++++++++ .../diagnostics/FirErrorsDefaultMessages.kt | 5 +++ .../optionalExpectationDiagnostics.fir.kt | 12 +++---- 12 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirOptionalExpectationHelpers.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptionalExpectationExpressionChecker.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirOptionalExpectationTypeChecker.kt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index bbc51463141..e87c4d132dd 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -3618,6 +3618,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY) { firDiagnostic -> + OptionalDeclarationOutsideOfAnnotationEntryImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirErrors.INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION) { firDiagnostic -> InitializerRequiredForDestructuringDeclarationImpl( firDiagnostic as KtPsiDiagnostic, diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 0c5de6e4fba..ba2fa4f79e5 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -2533,6 +2533,10 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { val incompatibilityType: ExpectActualAnnotationsIncompatibilityType } + interface OptionalDeclarationOutsideOfAnnotationEntry : KtFirDiagnostic { + override val diagnosticClass get() = OptionalDeclarationOutsideOfAnnotationEntry::class + } + interface InitializerRequiredForDestructuringDeclaration : KtFirDiagnostic { override val diagnosticClass get() = InitializerRequiredForDestructuringDeclaration::class } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index d3dc377d163..2351ee24a77 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -3053,6 +3053,11 @@ internal class ActualAnnotationsNotMatchExpectImpl( token: KtLifetimeToken, ) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.ActualAnnotationsNotMatchExpect +internal class OptionalDeclarationOutsideOfAnnotationEntryImpl( + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.OptionalDeclarationOutsideOfAnnotationEntry + internal class InitializerRequiredForDestructuringDeclarationImpl( firDiagnostic: KtPsiDiagnostic, token: KtLifetimeToken, 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 afe990fb787..7d562fe9920 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 @@ -1236,6 +1236,8 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter("actualSymbol") parameter>("incompatibilityType") } + + val OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY by error() } val DESTRUCTING_DECLARATION by object : DiagnosticGroup("Destructuring declaration") { 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 04ac8d125b8..55eba6e89ce 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 @@ -649,6 +649,7 @@ object FirErrors { val EXPECT_ACTUAL_OPT_IN_ANNOTATION by error0(SourceElementPositioningStrategies.EXPECT_ACTUAL_MODIFIER) val ACTUAL_TYPEALIAS_TO_SPECIAL_ANNOTATION by error1(SourceElementPositioningStrategies.TYPEALIAS_TYPE_REFERENCE) val ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT by warning3, FirBasedSymbol<*>, ExpectActualAnnotationsIncompatibilityType>(SourceElementPositioningStrategies.DECLARATION_NAME_ONLY) + val OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY by error0() // Destructuring declaration val INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION by error0() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt index 6892ca78ed9..616de7c5493 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt @@ -71,6 +71,7 @@ object CommonExpressionCheckers : ExpressionCheckers() { FirUnderscoredTypeArgumentSyntaxChecker, FirContractNotFirstStatementChecker, FirProtectedConstructorNotInSuperCallChecker, + FirOptionalExpectationExpressionChecker ) override val propertyAccessExpressionCheckers: Set diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonTypeCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonTypeCheckers.kt index af3515efeaf..367b663b1e5 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonTypeCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonTypeCheckers.kt @@ -17,5 +17,6 @@ object CommonTypeCheckers : TypeCheckers() { FirUnsupportedDefaultValueInFunctionTypeParameterChecker, FirUnsupportedModifiersInFunctionTypeParameterChecker, FirStarProjectionModifierChecker, + FirOptionalExpectationTypeChecker ) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirOptionalExpectationHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirOptionalExpectationHelpers.kt new file mode 100644 index 00000000000..fbaa7e73f6a --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirOptionalExpectationHelpers.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2010-2023 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 + +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.hasAnnotation +import org.jetbrains.kotlin.fir.declarations.utils.isExpect +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.name.StandardClassIds + +fun FirRegularClassSymbol.isOptionalAnnotationClass(session: FirSession): Boolean { + return classKind == ClassKind.ANNOTATION_CLASS && + isExpect && + hasAnnotation(StandardClassIds.Annotations.OptionalExpectation, session) +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptionalExpectationExpressionChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptionalExpectationExpressionChecker.kt new file mode 100644 index 00000000000..ab21518ec66 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptionalExpectationExpressionChecker.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2023 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.expression + +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.isOptionalAnnotationClass +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.references.toResolvedConstructorSymbol +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.toRegularClassSymbol + +object FirOptionalExpectationExpressionChecker : FirFunctionCallChecker() { + override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) { + val constructorSymbol = expression.calleeReference.toResolvedConstructorSymbol() ?: return + val declarationClass = constructorSymbol.resolvedReturnTypeRef.coneType.toRegularClassSymbol(context.session) ?: return + if (!declarationClass.isOptionalAnnotationClass(context.session)) return + + reporter.reportOn(expression.source, FirErrors.OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY, context) + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirOptionalExpectationTypeChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirOptionalExpectationTypeChecker.kt new file mode 100644 index 00000000000..7e7e1fb6abd --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirOptionalExpectationTypeChecker.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2023 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.type + +import org.jetbrains.kotlin.KtFakeSourceElementKind +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.isOptionalAnnotationClass +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.types.ConeClassLikeType +import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.fir.types.coneTypeSafe +import org.jetbrains.kotlin.fir.types.toRegularClassSymbol + +object FirOptionalExpectationTypeChecker : FirTypeRefChecker() { + override fun check(typeRef: FirTypeRef, context: CheckerContext, reporter: DiagnosticReporter) { + val source = typeRef.source + if (source?.kind is KtFakeSourceElementKind) return + val classSymbol = typeRef.coneTypeSafe()?.toRegularClassSymbol(context.session) ?: return + if (!classSymbol.isOptionalAnnotationClass(context.session)) return + + val annotationContainer = context.annotationContainers.lastOrNull() + if (annotationContainer?.annotations?.any { it.typeRef == typeRef } == true) return + + reporter.reportOn(source, FirErrors.OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY, context) + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt index e20f234c51f..9f730da1186 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt @@ -435,6 +435,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULL_FOR_NONNULL_ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ONLY_ONE_CLASS_BOUND_ALLOWED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPERATOR_MODIFIER_REQUIRED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPERATOR_RENAMED_ON_IMPORT +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPT_IN_ARGUMENT_IS_NOT_MARKER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPT_IN_CAN_ONLY_BE_USED_AS_ANNOTATION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPT_IN_IS_NOT_ENABLED @@ -1921,6 +1922,10 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() { FirExpectActualAnnotationIncompatibilityDiagnosticRenderers.SYMBOL_RENDERER, FirExpectActualAnnotationIncompatibilityDiagnosticRenderers.INCOMPATIBILITY, ) + map.put( + OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY, + "Declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry" + ) // Destructuring declaration map.put(INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION, "Initializer required for destructuring declaration") diff --git a/compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.fir.kt index 1508c50ee28..48bd558f02a 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.fir.kt @@ -8,7 +8,7 @@ @OptionalExpectation expect annotation class A() -fun useInSignature(a: A) = a.toString() +fun useInSignature(a: A) = a.toString() @OptionalExpectation expect class NotAnAnnotationClass @@ -16,9 +16,9 @@ expect class NotAnAnnotationClass @OptionalExpectation annotation class NotAnExpectedClass -annotation class InOtherAnnotation(val a: A) +annotation class InOtherAnnotation(val a: A) -@InOtherAnnotation(A()) +@InOtherAnnotation(A()) fun useInOtherAnnotation() {} expect class C { @@ -29,11 +29,11 @@ expect class C { // MODULE: platform()()(common) // FILE: platform.kt -fun useInReturnType(): A? = null +fun useInReturnType(): A? = null -annotation class AnotherAnnotation(val a: A) +annotation class AnotherAnnotation(val a: A) -@AnotherAnnotation(A()) +@AnotherAnnotation(A()) fun useInAnotherAnnotation() {} actual class C {