[FIR] Introduce OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY diagnostic

^KT-60892
This commit is contained in:
Ivan Kochurkin
2023-08-02 22:55:13 +02:00
committed by Space Team
parent dc1f1fe979
commit b08df32cac
12 changed files with 107 additions and 6 deletions
@@ -3618,6 +3618,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token, token,
) )
} }
add(FirErrors.OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY) { firDiagnostic ->
OptionalDeclarationOutsideOfAnnotationEntryImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION) { firDiagnostic -> add(FirErrors.INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION) { firDiagnostic ->
InitializerRequiredForDestructuringDeclarationImpl( InitializerRequiredForDestructuringDeclarationImpl(
firDiagnostic as KtPsiDiagnostic, firDiagnostic as KtPsiDiagnostic,
@@ -2533,6 +2533,10 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
val incompatibilityType: ExpectActualAnnotationsIncompatibilityType<FirAnnotation> val incompatibilityType: ExpectActualAnnotationsIncompatibilityType<FirAnnotation>
} }
interface OptionalDeclarationOutsideOfAnnotationEntry : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = OptionalDeclarationOutsideOfAnnotationEntry::class
}
interface InitializerRequiredForDestructuringDeclaration : KtFirDiagnostic<KtDestructuringDeclaration> { interface InitializerRequiredForDestructuringDeclaration : KtFirDiagnostic<KtDestructuringDeclaration> {
override val diagnosticClass get() = InitializerRequiredForDestructuringDeclaration::class override val diagnosticClass get() = InitializerRequiredForDestructuringDeclaration::class
} }
@@ -3053,6 +3053,11 @@ internal class ActualAnnotationsNotMatchExpectImpl(
token: KtLifetimeToken, token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtElement>(firDiagnostic, token), KtFirDiagnostic.ActualAnnotationsNotMatchExpect ) : KtAbstractFirDiagnostic<KtElement>(firDiagnostic, token), KtFirDiagnostic.ActualAnnotationsNotMatchExpect
internal class OptionalDeclarationOutsideOfAnnotationEntryImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.OptionalDeclarationOutsideOfAnnotationEntry
internal class InitializerRequiredForDestructuringDeclarationImpl( internal class InitializerRequiredForDestructuringDeclarationImpl(
firDiagnostic: KtPsiDiagnostic, firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken, token: KtLifetimeToken,
@@ -1236,6 +1236,8 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<Symbol>("actualSymbol") parameter<Symbol>("actualSymbol")
parameter<ExpectActualAnnotationsIncompatibilityType<FirAnnotation>>("incompatibilityType") parameter<ExpectActualAnnotationsIncompatibilityType<FirAnnotation>>("incompatibilityType")
} }
val OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY by error<PsiElement>()
} }
val DESTRUCTING_DECLARATION by object : DiagnosticGroup("Destructuring declaration") { val DESTRUCTING_DECLARATION by object : DiagnosticGroup("Destructuring declaration") {
@@ -649,6 +649,7 @@ object FirErrors {
val EXPECT_ACTUAL_OPT_IN_ANNOTATION by error0<KtNamedDeclaration>(SourceElementPositioningStrategies.EXPECT_ACTUAL_MODIFIER) val EXPECT_ACTUAL_OPT_IN_ANNOTATION by error0<KtNamedDeclaration>(SourceElementPositioningStrategies.EXPECT_ACTUAL_MODIFIER)
val ACTUAL_TYPEALIAS_TO_SPECIAL_ANNOTATION by error1<KtTypeAlias, ClassId>(SourceElementPositioningStrategies.TYPEALIAS_TYPE_REFERENCE) val ACTUAL_TYPEALIAS_TO_SPECIAL_ANNOTATION by error1<KtTypeAlias, ClassId>(SourceElementPositioningStrategies.TYPEALIAS_TYPE_REFERENCE)
val ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT by warning3<KtElement, FirBasedSymbol<*>, FirBasedSymbol<*>, ExpectActualAnnotationsIncompatibilityType<FirAnnotation>>(SourceElementPositioningStrategies.DECLARATION_NAME_ONLY) val ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT by warning3<KtElement, FirBasedSymbol<*>, FirBasedSymbol<*>, ExpectActualAnnotationsIncompatibilityType<FirAnnotation>>(SourceElementPositioningStrategies.DECLARATION_NAME_ONLY)
val OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY by error0<PsiElement>()
// Destructuring declaration // Destructuring declaration
val INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION by error0<KtDestructuringDeclaration>() val INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION by error0<KtDestructuringDeclaration>()
@@ -71,6 +71,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
FirUnderscoredTypeArgumentSyntaxChecker, FirUnderscoredTypeArgumentSyntaxChecker,
FirContractNotFirstStatementChecker, FirContractNotFirstStatementChecker,
FirProtectedConstructorNotInSuperCallChecker, FirProtectedConstructorNotInSuperCallChecker,
FirOptionalExpectationExpressionChecker
) )
override val propertyAccessExpressionCheckers: Set<FirPropertyAccessExpressionChecker> override val propertyAccessExpressionCheckers: Set<FirPropertyAccessExpressionChecker>
@@ -17,5 +17,6 @@ object CommonTypeCheckers : TypeCheckers() {
FirUnsupportedDefaultValueInFunctionTypeParameterChecker, FirUnsupportedDefaultValueInFunctionTypeParameterChecker,
FirUnsupportedModifiersInFunctionTypeParameterChecker, FirUnsupportedModifiersInFunctionTypeParameterChecker,
FirStarProjectionModifierChecker, FirStarProjectionModifierChecker,
FirOptionalExpectationTypeChecker
) )
} }
@@ -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)
}
@@ -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)
}
}
@@ -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<ConeClassLikeType>()?.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)
}
}
@@ -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.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_MODIFIER_REQUIRED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPERATOR_RENAMED_ON_IMPORT 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_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_CAN_ONLY_BE_USED_AS_ANNOTATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPT_IN_IS_NOT_ENABLED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPT_IN_IS_NOT_ENABLED
@@ -1921,6 +1922,10 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
FirExpectActualAnnotationIncompatibilityDiagnosticRenderers.SYMBOL_RENDERER, FirExpectActualAnnotationIncompatibilityDiagnosticRenderers.SYMBOL_RENDERER,
FirExpectActualAnnotationIncompatibilityDiagnosticRenderers.INCOMPATIBILITY, FirExpectActualAnnotationIncompatibilityDiagnosticRenderers.INCOMPATIBILITY,
) )
map.put(
OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY,
"Declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry"
)
// Destructuring declaration // Destructuring declaration
map.put(INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION, "Initializer required for destructuring declaration") map.put(INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION, "Initializer required for destructuring declaration")
@@ -8,7 +8,7 @@
@OptionalExpectation @OptionalExpectation
expect annotation class A() expect annotation class A()
fun useInSignature(a: A) = a.toString() fun useInSignature(a: <!OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY!>A<!>) = a.toString()
<!NO_ACTUAL_FOR_EXPECT{JVM}!><!WRONG_ANNOTATION_TARGET!>@OptionalExpectation<!> <!NO_ACTUAL_FOR_EXPECT{JVM}!><!WRONG_ANNOTATION_TARGET!>@OptionalExpectation<!>
expect class NotAnAnnotationClass<!> expect class NotAnAnnotationClass<!>
@@ -16,9 +16,9 @@ expect class NotAnAnnotationClass<!>
@OptionalExpectation @OptionalExpectation
annotation class NotAnExpectedClass annotation class NotAnExpectedClass
annotation class InOtherAnnotation(val a: A) annotation class InOtherAnnotation(val a: <!OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY!>A<!>)
@InOtherAnnotation(A()) @InOtherAnnotation(<!OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY!>A()<!>)
fun useInOtherAnnotation() {} fun useInOtherAnnotation() {}
expect class C { expect class C {
@@ -29,11 +29,11 @@ expect class C {
// MODULE: platform()()(common) // MODULE: platform()()(common)
// FILE: platform.kt // FILE: platform.kt
fun useInReturnType(): A? = null fun useInReturnType(): <!OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY!>A?<!> = null
annotation class AnotherAnnotation(val a: A) annotation class AnotherAnnotation(val a: <!OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY!>A<!>)
@AnotherAnnotation(A()) @AnotherAnnotation(<!OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY!>A()<!>)
fun useInAnotherAnnotation() {} fun useInAnotherAnnotation() {}
actual class C { actual class C {