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 89f2c72b04e..237892459a9 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 @@ -1007,6 +1007,18 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirJsErrors.RUNTIME_ANNOTATION_NOT_SUPPORTED) { firDiagnostic -> + RuntimeAnnotationNotSupportedImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } + add(FirJsErrors.RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION) { firDiagnostic -> + RuntimeAnnotationOnExternalDeclarationImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirErrors.OPT_IN_USAGE) { firDiagnostic -> OptInUsageImpl( firDiagnostic.a, 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 8d192db81b7..353b5bc165f 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 @@ -724,6 +724,14 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = NestedJsModuleProhibited::class } + abstract class RuntimeAnnotationNotSupported : KtFirDiagnostic() { + override val diagnosticClass get() = RuntimeAnnotationNotSupported::class + } + + abstract class RuntimeAnnotationOnExternalDeclaration : KtFirDiagnostic() { + override val diagnosticClass get() = RuntimeAnnotationOnExternalDeclaration::class + } + abstract class OptInUsage : KtFirDiagnostic() { override val diagnosticClass get() = OptInUsage::class abstract val optInMarkerFqName: FqName 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 4fea5e1402f..264a2f14688 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 @@ -870,6 +870,16 @@ internal class NestedJsModuleProhibitedImpl( override val token: KtLifetimeToken, ) : KtFirDiagnostic.NestedJsModuleProhibited(), KtAbstractFirDiagnostic +internal class RuntimeAnnotationNotSupportedImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.RuntimeAnnotationNotSupported(), KtAbstractFirDiagnostic + +internal class RuntimeAnnotationOnExternalDeclarationImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.RuntimeAnnotationOnExternalDeclaration(), KtAbstractFirDiagnostic + internal class OptInUsageImpl( override val optInMarkerFqName: FqName, override val message: String, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJsDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJsDiagnosticsList.kt index 5f1843c8964..c3d4baf2c93 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJsDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJsDiagnosticsList.kt @@ -22,6 +22,8 @@ object JS_DIAGNOSTICS_LIST : DiagnosticList("FirJsErrors") { val JS_MODULE_PROHIBITED_ON_VAR by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) val JS_MODULE_PROHIBITED_ON_NON_NATIVE by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) val NESTED_JS_MODULE_PROHIBITED by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) + val RUNTIME_ANNOTATION_NOT_SUPPORTED by warning(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) + val RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) } val SUPERTYPES by object : DiagnosticGroup("Supertypes") { diff --git a/compiler/fir/checkers/checkers.js/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrors.kt b/compiler/fir/checkers/checkers.js/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrors.kt index 2854eed3b99..5e65eabd5e9 100644 --- a/compiler/fir/checkers/checkers.js/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrors.kt +++ b/compiler/fir/checkers/checkers.js/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrors.kt @@ -26,6 +26,8 @@ object FirJsErrors { val JS_MODULE_PROHIBITED_ON_VAR by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) val JS_MODULE_PROHIBITED_ON_NON_NATIVE by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) val NESTED_JS_MODULE_PROHIBITED by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) + val RUNTIME_ANNOTATION_NOT_SUPPORTED by warning0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) + val RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) // Supertypes val WRONG_MULTIPLE_INHERITANCE by error1>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrorsDefaultMessages.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrorsDefaultMessages.kt index 4a796db3e72..25d75b68284 100644 --- a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrorsDefaultMessages.kt @@ -17,6 +17,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_MODULE_PR import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_JS_MODULE_PROHIBITED import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE +import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.RUNTIME_ANNOTATION_NOT_SUPPORTED +import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_JS_QUALIFIER import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_MULTIPLE_INHERITANCE @@ -44,6 +46,11 @@ object FirJsErrorsDefaultMessages : BaseDiagnosticRendererFactory() { FirDiagnosticRenderers.SYMBOL ) map.put(CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION, "This property can only be used from external declarations") + map.put(RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION, "Runtime annotation can't be put on external declaration") + map.put( + RUNTIME_ANNOTATION_NOT_SUPPORTED, + "Reflection is not supported in JavaScript target, therefore you won't be able to read this annotation in run-time" + ) map.checkMissingMessages(FirJsErrors) } diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt index 12dbc3115bf..3e3526e0dfe 100644 --- a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt @@ -23,6 +23,7 @@ object JsDeclarationCheckers : DeclarationCheckers() { override val basicDeclarationCheckers: Set get() = setOf( FirJsModuleChecker, + FirJsRuntimeAnnotationChecker, ) override val classCheckers: Set diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsRuntimeAnnotationChecker.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsRuntimeAnnotationChecker.kt new file mode 100644 index 00000000000..795c9c17deb --- /dev/null +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsRuntimeAnnotationChecker.kt @@ -0,0 +1,33 @@ +/* + * 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.js.checkers.declaration + +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.declaration.FirBasicDeclarationChecker +import org.jetbrains.kotlin.fir.analysis.checkers.getExplicitAnnotationRetention +import org.jetbrains.kotlin.fir.analysis.js.checkers.isEffectivelyExternal +import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration +import org.jetbrains.kotlin.fir.expressions.coneClassLikeType +import org.jetbrains.kotlin.fir.resolve.toSymbol + +object FirJsRuntimeAnnotationChecker : FirBasicDeclarationChecker() { + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + for (annotation in declaration.annotations) { + val annotationClass = annotation.coneClassLikeType?.lookupTag?.toSymbol(context.session) ?: continue + if (annotationClass.getExplicitAnnotationRetention(context.session) != AnnotationRetention.RUNTIME) continue + + if (declaration is FirMemberDeclaration && declaration.symbol.isEffectivelyExternal(context)) { + reporter.reportOn(annotation.source, FirJsErrors.RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION, context) + } else { + reporter.reportOn(annotation.source, FirJsErrors.RUNTIME_ANNOTATION_NOT_SUPPORTED, context) + } + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirAnnotationHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirAnnotationHelpers.kt index 99a41873c3e..34a9d146e24 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirAnnotationHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirAnnotationHelpers.kt @@ -10,19 +10,18 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.reportOn -import org.jetbrains.kotlin.fir.FirAnnotationContainer -import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.impl.FirPrimaryConstructor import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.references.resolved +import org.jetbrains.kotlin.fir.references.toResolvedEnumEntrySymbol import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol @@ -36,12 +35,15 @@ fun FirRegularClass.getRetention(session: FirSession): AnnotationRetention { return getRetentionAnnotation(session)?.getRetention() ?: AnnotationRetention.RUNTIME } -fun FirAnnotation.getRetention(): AnnotationRetention { - val retentionArgument = findArgumentByName(ParameterNames.retentionValue) as? FirQualifiedAccessExpression - ?: return AnnotationRetention.RUNTIME - val retentionName = retentionArgument.calleeReference.resolved?.name?.asString() - ?: return AnnotationRetention.RUNTIME - return AnnotationRetention.values().firstOrNull { it.name == retentionName } ?: AnnotationRetention.RUNTIME +private fun FirAnnotation.getRetention(): AnnotationRetention? { + val propertyAccess = findArgumentByName(ParameterNames.retentionValue) as? FirQualifiedAccessExpression + val callableId = propertyAccess?.calleeReference?.toResolvedEnumEntrySymbol()?.callableId ?: return null + + if (callableId.classId != StandardClassIds.AnnotationRetention) { + return null + } + + return AnnotationRetention.values().firstOrNull { it.name == callableId.callableName.asString() } } private val defaultAnnotationTargets = KotlinTarget.DEFAULT_TARGET_SET @@ -141,8 +143,12 @@ fun FirClassLikeSymbol<*>.containsRepeatableAnnotation(session: FirSession): Boo return false } +fun FirClassLikeSymbol<*>.getExplicitAnnotationRetention(session: FirSession): AnnotationRetention? { + return getAnnotationByClassId(StandardClassIds.Annotations.Retention, session)?.getRetention() +} + fun FirClassLikeSymbol<*>.getAnnotationRetention(session: FirSession): AnnotationRetention { - return getAnnotationByClassId(StandardClassIds.Annotations.Retention, session)?.getRetention() ?: AnnotationRetention.RUNTIME + return getExplicitAnnotationRetention(session) ?: AnnotationRetention.RUNTIME } fun FirAnnotationContainer.getDefaultUseSiteTarget( diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/references/FirReferenceUtils.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/references/FirReferenceUtils.kt index 1d6bba01f3a..85909039621 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/references/FirReferenceUtils.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/references/FirReferenceUtils.kt @@ -56,6 +56,10 @@ fun FirReference.toResolvedConstructorSymbol(discardErrorReference: Boolean = fa return this.toResolvedSymbol(discardErrorReference) } +fun FirReference.toResolvedEnumEntrySymbol(discardErrorReference: Boolean = false): FirEnumEntrySymbol? { + return this.toResolvedSymbol(discardErrorReference) +} + @OptIn(ExperimentalContracts::class) fun FirReference.isError(): Boolean { contract { diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/runtimeAnnotations.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/runtimeAnnotations.fir.kt deleted file mode 100644 index 243dcf104a3..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/runtimeAnnotations.fir.kt +++ /dev/null @@ -1,59 +0,0 @@ -@Retention(AnnotationRetention.BINARY) -annotation class X - -@Retention(AnnotationRetention.RUNTIME) -annotation class Y - -@X -external class A { - @X - fun f() - - @X - val p: Int - - @get:X - val r: Int -} - -@Y -external class B { - @Y - fun f() - - @Y - val p: Int - - @get:Y - val r: Int -} - -@X -class C { - @X - fun f() {} - - @X - val p: Int = 0 - - val q: Int - @X get() = 0 - - @get:X - val r: Int = 0 -} - -@Y -class D { - @Y - fun f() {} - - @Y - val p: Int = 0 - - val q: Int - @Y get() = 0 - - @get:Y - val r: Int = 0 -} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/runtimeAnnotations.kt b/compiler/testData/diagnostics/testsWithJsStdLib/runtimeAnnotations.kt index f86a1069c39..677bda33ca1 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/runtimeAnnotations.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/runtimeAnnotations.kt @@ -1,3 +1,5 @@ +// FIR_IDENTICAL +// !DIAGNOSTICS: +RUNTIME_ANNOTATION_NOT_SUPPORTED @Retention(AnnotationRetention.BINARY) annotation class X