[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,
)
}
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,
@@ -2533,6 +2533,10 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
val incompatibilityType: ExpectActualAnnotationsIncompatibilityType<FirAnnotation>
}
interface OptionalDeclarationOutsideOfAnnotationEntry : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = OptionalDeclarationOutsideOfAnnotationEntry::class
}
interface InitializerRequiredForDestructuringDeclaration : KtFirDiagnostic<KtDestructuringDeclaration> {
override val diagnosticClass get() = InitializerRequiredForDestructuringDeclaration::class
}
@@ -3053,6 +3053,11 @@ internal class ActualAnnotationsNotMatchExpectImpl(
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtElement>(firDiagnostic, token), KtFirDiagnostic.ActualAnnotationsNotMatchExpect
internal class OptionalDeclarationOutsideOfAnnotationEntryImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.OptionalDeclarationOutsideOfAnnotationEntry
internal class InitializerRequiredForDestructuringDeclarationImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
@@ -1236,6 +1236,8 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<Symbol>("actualSymbol")
parameter<ExpectActualAnnotationsIncompatibilityType<FirAnnotation>>("incompatibilityType")
}
val OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY by error<PsiElement>()
}
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 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 OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY by error0<PsiElement>()
// Destructuring declaration
val INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION by error0<KtDestructuringDeclaration>()
@@ -71,6 +71,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
FirUnderscoredTypeArgumentSyntaxChecker,
FirContractNotFirstStatementChecker,
FirProtectedConstructorNotInSuperCallChecker,
FirOptionalExpectationExpressionChecker
)
override val propertyAccessExpressionCheckers: Set<FirPropertyAccessExpressionChecker>
@@ -17,5 +17,6 @@ object CommonTypeCheckers : TypeCheckers() {
FirUnsupportedDefaultValueInFunctionTypeParameterChecker,
FirUnsupportedModifiersInFunctionTypeParameterChecker,
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.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")
@@ -8,7 +8,7 @@
@OptionalExpectation
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<!>
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: <!OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY!>A<!>)
@InOtherAnnotation(A())
@InOtherAnnotation(<!OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY!>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(): <!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() {}
actual class C {