diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirUtils.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirUtils.kt index 140ac09deda..6c0567b1112 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirUtils.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirUtils.kt @@ -6,7 +6,7 @@ package org.jetbrains.kotlin.analysis.api.fir import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationApplication -import org.jetbrains.kotlin.analysis.api.fir.annotations.fullyExpandedClassId +import org.jetbrains.kotlin.fir.declarations.fullyExpandedClassId import org.jetbrains.kotlin.analysis.api.fir.annotations.mapAnnotationParameters import org.jetbrains.kotlin.analysis.api.fir.evaluate.FirAnnotationValueConverter import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/KtFirAnnotationListForDeclaration.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/KtFirAnnotationListForDeclaration.kt index 04383e82ce1..007a4e0dab2 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/KtFirAnnotationListForDeclaration.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/KtFirAnnotationListForDeclaration.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.analysis.api.impl.base.annotations.KtEmptyAnnotation import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.fullyExpandedClassId import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.name.ClassId diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/KtFirAnnotationListForType.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/KtFirAnnotationListForType.kt index c8cc8a7818a..edca47c828a 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/KtFirAnnotationListForType.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/KtFirAnnotationListForType.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.analysis.api.impl.base.annotations.KtEmptyAnnotation import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.fullyExpandedClassId import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.customAnnotations import org.jetbrains.kotlin.name.ClassId diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/firAnnotationUtils.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/firAnnotationUtils.kt index 98cc0d83a49..b489a520a88 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/firAnnotationUtils.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/firAnnotationUtils.kt @@ -90,6 +90,3 @@ internal fun mapAnnotationParameters(annotation: FirAnnotation, session: FirSess return resultMap } - -internal fun FirAnnotation.fullyExpandedClassId(useSiteSession: FirSession): ClassId? = - coneClassLikeType?.fullyExpandedType(useSiteSession)?.classId 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 725f6a741e6..5a6eecfba71 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 @@ -4178,6 +4178,14 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirJvmErrors.REDUNDANT_REPEATABLE_ANNOTATION) { firDiagnostic -> + RedundantRepeatableAnnotationImpl( + firDiagnostic.a, + firDiagnostic.b, + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirJvmErrors.LOCAL_JVM_RECORD) { firDiagnostic -> LocalJvmRecordImpl( 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 4d256c0adba..63ccb78d583 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 @@ -2908,6 +2908,12 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = PositionedValueArgumentForJavaAnnotation::class } + abstract class RedundantRepeatableAnnotation : KtFirDiagnostic() { + override val diagnosticClass get() = RedundantRepeatableAnnotation::class + abstract val kotlinRepeatable: FqName + abstract val javaRepeatable: FqName + } + abstract class LocalJvmRecord : KtFirDiagnostic() { override val diagnosticClass get() = LocalJvmRecord::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 c5cbcf53615..62fcd7f518a 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 @@ -3514,6 +3514,13 @@ internal class PositionedValueArgumentForJavaAnnotationImpl( override val token: KtLifetimeToken, ) : KtFirDiagnostic.PositionedValueArgumentForJavaAnnotation(), KtAbstractFirDiagnostic +internal class RedundantRepeatableAnnotationImpl( + override val kotlinRepeatable: FqName, + override val javaRepeatable: FqName, + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.RedundantRepeatableAnnotation(), KtAbstractFirDiagnostic + internal class LocalJvmRecordImpl( override val firDiagnostic: KtPsiDiagnostic, override val token: KtLifetimeToken, diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 639555369c1..b3a70e2968d 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -34866,6 +34866,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/JvmSyntheticOnDelegate.kt"); } + @Test + @TestMetadata("multipleRepeatables.kt") + public void testMultipleRepeatables() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/multipleRepeatables.kt"); + } + @Test @TestMetadata("qualifiedCallValue.kt") public void testQualifiedCallValue() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 44194f49689..d3bc51441c7 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -34866,6 +34866,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/JvmSyntheticOnDelegate.kt"); } + @Test + @TestMetadata("multipleRepeatables.kt") + public void testMultipleRepeatables() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/multipleRepeatables.kt"); + } + @Test @TestMetadata("qualifiedCallValue.kt") public void testQualifiedCallValue() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index f97e0afd7dc..2409ee23ea2 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -34866,6 +34866,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/JvmSyntheticOnDelegate.kt"); } + @Test + @TestMetadata("multipleRepeatables.kt") + public void testMultipleRepeatables() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/multipleRepeatables.kt"); + } + @Test @TestMetadata("qualifiedCallValue.kt") public void testQualifiedCallValue() throws Exception { diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt index 1bc6025a1cb..0f5b94c91ae 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt @@ -79,6 +79,11 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") { val JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES by error() val POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION by error() + + val REDUNDANT_REPEATABLE_ANNOTATION by warning { + parameter("kotlinRepeatable") + parameter("javaRepeatable") + } } val SUPER by object : DiagnosticGroup("Super") { diff --git a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt index a0fa6747afa..361bfb6aa54 100644 --- a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt +++ b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt @@ -71,6 +71,7 @@ object FirJvmErrors { val JVM_PACKAGE_NAME_MUST_BE_VALID_NAME by error0() val JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES by error0() val POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION by error0() + val REDUNDANT_REPEATABLE_ANNOTATION by warning2() // Super val SUPER_CALL_WITH_DEFAULT_PARAMETERS by error1() diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt index 0b102e92f90..1b7da98b92f 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt @@ -67,6 +67,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERRIDE_CANNOT_BE_STATIC import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.REDUNDANT_REPEATABLE_ANNOTATION import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.REPEATABLE_ANNOTATION_HAS_NESTED_CLASS_NAMED_CONTAINER import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.REPEATABLE_CONTAINER_HAS_NON_DEFAULT_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.REPEATABLE_CONTAINER_HAS_SHORTER_RETENTION @@ -285,6 +286,12 @@ object FirJvmErrorsDefaultMessages : BaseDiagnosticRendererFactory() { JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE, "Java SAM interface constructor references are prohibited" ) + map.put( + REDUNDANT_REPEATABLE_ANNOTATION, + "Please, remove the ''{0}'' annotation, as ''{1}'' is already enough", + TO_STRING, + TO_STRING, + ) map.checkMissingMessages(FirJvmErrors) } diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmDeclarationCheckers.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmDeclarationCheckers.kt index 32a7e96b921..e0adbd24ca9 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmDeclarationCheckers.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmDeclarationCheckers.kt @@ -18,6 +18,7 @@ object JvmDeclarationCheckers : DeclarationCheckers() { FirJvmStaticChecker, FirRepeatableAnnotationChecker, FirJvmInvalidAndDangerousCharactersChecker, + FirJvmRedundantRepeatableChecker, ) override val classCheckers: Set diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmRedundantRepeatableChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmRedundantRepeatableChecker.kt new file mode 100644 index 00000000000..6cda8bc75ac --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmRedundantRepeatableChecker.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2022 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.jvm.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.diagnostics.jvm.FirJvmErrors +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.expressions.classId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.StandardClassIds.Annotations.Repeatable +import org.jetbrains.kotlin.name.StandardClassIds.Annotations.JvmRepeatable +import org.jetbrains.kotlin.name.StandardClassIds.Annotations.Java + +object FirJvmRedundantRepeatableChecker : FirBasicDeclarationChecker() { + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + val kotlinRepeatable = declaration.annotations.find { it.fullyExpandedClassId(context.session) == Repeatable } + + val javaRepeatable = declaration.annotations.find { + val expandedClassId = it.fullyExpandedClassId(context.session) + expandedClassId == JvmRepeatable || expandedClassId == Java.Repeatable + } + + if (kotlinRepeatable != null && javaRepeatable != null) { + reporter.reportOn( + kotlinRepeatable.source, + FirJvmErrors.REDUNDANT_REPEATABLE_ANNOTATION, + kotlinRepeatable.classId?.asSingleFqName() ?: FqName.ROOT, + javaRepeatable.classId?.asSingleFqName() ?: FqName.ROOT, + context + ) + } + } +} + diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirRepeatableAnnotationChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirRepeatableAnnotationChecker.kt index 89ac62cbb0d..d8a3d4aa80d 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirRepeatableAnnotationChecker.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirRepeatableAnnotationChecker.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclaratio import org.jetbrains.kotlin.fir.analysis.checkers.getAllowedAnnotationTargets import org.jetbrains.kotlin.fir.analysis.checkers.getAnnotationRetention import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors import org.jetbrains.kotlin.fir.analysis.jvm.checkers.isJvm6 import org.jetbrains.kotlin.fir.declarations.* @@ -24,6 +25,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.classId import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.resolve.defaultType +import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.scopes.getSingleClassifier import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol @@ -51,7 +53,8 @@ object FirRepeatableAnnotationChecker : FirBasicDeclarationChecker() { val annotationClass = session.symbolProvider.getClassLikeSymbolByClassId(annotationClassId) ?: continue val useSiteTarget = annotation.useSiteTarget - val existingTargetsForAnnotation = annotationsMap.getOrPut(annotation.annotationTypeRef.coneType) { arrayListOf() } + val expandedType = annotation.annotationTypeRef.coneType.fullyExpandedType(context.session) + val existingTargetsForAnnotation = annotationsMap.getOrPut(expandedType) { arrayListOf() } val duplicateAnnotation = useSiteTarget in existingTargetsForAnnotation || existingTargetsForAnnotation.any { (it == null) != (useSiteTarget == null) } 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 78a8215a83b..65a15946d08 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 @@ -185,7 +185,8 @@ fun checkRepeatedAnnotation( for (annotation in annotations) { val useSiteTarget = annotation.useSiteTarget ?: annotationContainer?.getDefaultUseSiteTarget(annotation, context) - val existingTargetsForAnnotation = annotationsMap.getOrPut(annotation.annotationTypeRef.coneType) { arrayListOf() } + val expandedType = annotation.annotationTypeRef.coneType.fullyExpandedType(context.session) + val existingTargetsForAnnotation = annotationsMap.getOrPut(expandedType) { arrayListOf() } checkRepeatedAnnotation(useSiteTarget, existingTargetsForAnnotation, annotation, context, reporter) existingTargetsForAnnotation.add(useSiteTarget) diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt index 8070daa213b..72160bc8be5 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.references.FirErrorNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference +import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolvedSymbol import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag @@ -223,3 +224,6 @@ fun hasLowPriorityAnnotation(annotations: List) = annotations.any val lookupTag = it.annotationTypeRef.coneTypeSafe()?.lookupTag ?: return@any false lookupTag.classId == LOW_PRIORITY_IN_OVERLOAD_RESOLUTION_CLASS_ID } + +fun FirAnnotation.fullyExpandedClassId(useSiteSession: FirSession): ClassId? = + coneClassLikeType?.fullyExpandedType(useSiteSession)?.classId diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/RepeatableAnnotationChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/RepeatableAnnotationChecker.kt index 3014402a271..53d445a8175 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/RepeatableAnnotationChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/RepeatableAnnotationChecker.kt @@ -12,10 +12,7 @@ import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor -import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget -import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention -import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget +import org.jetbrains.kotlin.descriptors.annotations.* import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.lexer.KtTokens @@ -73,6 +70,15 @@ class RepeatableAnnotationChecker( javaRepeatable != null -> checkJavaRepeatableAnnotationDeclaration(javaRepeatable, annotationClass, trace) kotlinRepeatable != null -> checkKotlinRepeatableAnnotationDeclaration(kotlinRepeatable, annotationClass, trace) } + if (javaRepeatable != null && kotlinRepeatable != null) { + trace.report( + ErrorsJvm.REDUNDANT_REPEATABLE_ANNOTATION.on( + kotlinRepeatable.entry, + kotlinRepeatable.descriptor.abbreviationFqName ?: FqName.ROOT, + javaRepeatable.descriptor.abbreviationFqName ?: FqName.ROOT, + ) + ) + } } } } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java index a562c8bdae7..314ec7f724d 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java @@ -232,6 +232,9 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { MAP.put(JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE, "Java SAM interface constructor references are prohibited"); MAP.put(ENUM_DECLARING_CLASS_DEPRECATED, "Enum.declaringClass is deprecated, use declaringJavaClass instead or cast receiver to java.lang.Enum explicitly"); + + MAP.put(REDUNDANT_REPEATABLE_ANNOTATION, + "Please, remove the ''{0}'' annotation, as ''{1}'' is already enough", TO_STRING, TO_STRING); } @NotNull diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java index 2e330653085..edd7eff6845 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java @@ -210,6 +210,8 @@ public interface ErrorsJvm { DiagnosticFactoryForDeprecation0 ENUM_DECLARING_CLASS_DEPRECATED = DiagnosticFactoryForDeprecation0.create(LanguageFeature.ProhibitEnumDeclaringClass); + DiagnosticFactory2 REDUNDANT_REPEATABLE_ANNOTATION = DiagnosticFactory2.create(WARNING); + @SuppressWarnings("UnusedDeclaration") Object _initializer = new Object() { { diff --git a/compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_5.kt b/compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_5.kt index cbbe9d23344..d3158e6f010 100644 --- a/compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_5.kt +++ b/compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_5.kt @@ -14,7 +14,7 @@ annotation class B1 { } annotation class D1(val value: Array) -@Repeatable +@Repeatable @java.lang.annotation.Repeatable(D2::class) annotation class B2 { class Container diff --git a/compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_6.kt b/compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_6.kt index c2b5217774a..e7e466f7fe0 100644 --- a/compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_6.kt +++ b/compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_6.kt @@ -14,7 +14,7 @@ annotation class B1 { } annotation class D1(val value: Array) -@Repeatable +@Repeatable @java.lang.annotation.Repeatable(D2::class) annotation class B2 { class Container diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/multipleRepeatables.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/multipleRepeatables.kt new file mode 100644 index 00000000000..86ce9a4b9d4 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/multipleRepeatables.kt @@ -0,0 +1,27 @@ +// FIR_IDENTICAL +// FULL_JDK + +@kotlin.annotation.Repeatable +@java.lang.annotation.Repeatable(AContainer::class) +annotation class A +annotation class AContainer(val value: Array) + +@kotlin.annotation.Repeatable +@kotlin.jvm.JvmRepeatable(BContainer::class) +annotation class B +annotation class BContainer(val value: Array) + +@kotlin.annotation.Repeatable +@kotlin.annotation.Repeatable +@kotlin.jvm.JvmRepeatable(CContainer::class) +@java.lang.annotation.Repeatable(CContainer::class) +annotation class C +annotation class CContainer(val value: Array) + +typealias AlphaRepeatable = kotlin.annotation.Repeatable +typealias BetaRepeatable = kotlin.jvm.JvmRepeatable + +@AlphaRepeatable +@BetaRepeatable(DContainer::class) +annotation class D +annotation class DContainer(val value: Array) diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/multipleRepeatables.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/multipleRepeatables.txt new file mode 100644 index 00000000000..e37f2be4051 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/multipleRepeatables.txt @@ -0,0 +1,64 @@ +package + +@kotlin.annotation.Repeatable @java.lang.annotation.Repeatable(value = AContainer::class) public final annotation class A : kotlin.Annotation { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final annotation class AContainer : kotlin.Annotation { + public constructor AContainer(/*0*/ value: kotlin.Array) + public final val value: kotlin.Array + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@kotlin.annotation.Repeatable @kotlin.jvm.JvmRepeatable /* = java.lang.annotation.Repeatable */(value = BContainer::class) public final annotation class B : kotlin.Annotation { + public constructor B() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final annotation class BContainer : kotlin.Annotation { + public constructor BContainer(/*0*/ value: kotlin.Array) + public final val value: kotlin.Array + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@kotlin.annotation.Repeatable @kotlin.annotation.Repeatable @kotlin.jvm.JvmRepeatable /* = java.lang.annotation.Repeatable */(value = CContainer::class) @java.lang.annotation.Repeatable(value = CContainer::class) public final annotation class C : kotlin.Annotation { + public constructor C() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final annotation class CContainer : kotlin.Annotation { + public constructor CContainer(/*0*/ value: kotlin.Array) + public final val value: kotlin.Array + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@AlphaRepeatable /* = kotlin.annotation.Repeatable */ @BetaRepeatable /* = java.lang.annotation.Repeatable */(value = DContainer::class) public final annotation class D : kotlin.Annotation { + public constructor D() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final annotation class DContainer : kotlin.Annotation { + public constructor DContainer(/*0*/ value: kotlin.Array) + public final val value: kotlin.Array + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} +public typealias AlphaRepeatable = kotlin.annotation.Repeatable +public typealias BetaRepeatable = kotlin.jvm.JvmRepeatable + diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 5e964348421..5ed95ca965e 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -34956,6 +34956,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/JvmSyntheticOnDelegate.kt"); } + @Test + @TestMetadata("multipleRepeatables.kt") + public void testMultipleRepeatables() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/multipleRepeatables.kt"); + } + @Test @TestMetadata("qualifiedCallValue.kt") public void testQualifiedCallValue() throws Exception {