[FIR][FE 1.0] KT-47933: Warn about redundant @Repeatable
Merge-request: KT-MR-7318
Merged-by: Nikolay Lunyak <Nikolay.Lunyak@jetbrains.com>
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 140ac09..6c0567b 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 04383e8..007a4e0 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.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 c8cc8a7..edca47c 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.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 98cc0d8..b489a52 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 @@
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 725f6a7..5a6eecf 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 @@
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 4d256c0..63ccb78 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 @@
override val diagnosticClass get() = PositionedValueArgumentForJavaAnnotation::class
}
+ abstract class RedundantRepeatableAnnotation : KtFirDiagnostic<KtAnnotationEntry>() {
+ override val diagnosticClass get() = RedundantRepeatableAnnotation::class
+ abstract val kotlinRepeatable: FqName
+ abstract val javaRepeatable: FqName
+ }
+
abstract class LocalJvmRecord : KtFirDiagnostic<PsiElement>() {
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 c5cbcf5..62fcd7f 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 @@
override val token: KtLifetimeToken,
) : KtFirDiagnostic.PositionedValueArgumentForJavaAnnotation(), KtAbstractFirDiagnostic<KtExpression>
+internal class RedundantRepeatableAnnotationImpl(
+ override val kotlinRepeatable: FqName,
+ override val javaRepeatable: FqName,
+ override val firDiagnostic: KtPsiDiagnostic,
+ override val token: KtLifetimeToken,
+) : KtFirDiagnostic.RedundantRepeatableAnnotation(), KtAbstractFirDiagnostic<KtAnnotationEntry>
+
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 6395553..b3a70e2 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
@@ -34867,6 +34867,12 @@
}
@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 {
runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/qualifiedCallValue.kt");
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 44194f4..d3bc514 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
@@ -34867,6 +34867,12 @@
}
@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 {
runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/qualifiedCallValue.kt");
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 f97e0af..2409ee2 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
@@ -34867,6 +34867,12 @@
}
@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 {
runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/qualifiedCallValue.kt");
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 1bc6025..0f5b94c 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 @@
val JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES by error<KtAnnotationEntry>()
val POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION by error<KtExpression>()
+
+ val REDUNDANT_REPEATABLE_ANNOTATION by warning<KtAnnotationEntry> {
+ parameter<FqName>("kotlinRepeatable")
+ parameter<FqName>("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 a0fa674..361bfb6 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 @@
val JVM_PACKAGE_NAME_MUST_BE_VALID_NAME by error0<KtAnnotationEntry>()
val JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES by error0<KtAnnotationEntry>()
val POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION by error0<KtExpression>()
+ val REDUNDANT_REPEATABLE_ANNOTATION by warning2<KtAnnotationEntry, FqName, FqName>()
// Super
val SUPER_CALL_WITH_DEFAULT_PARAMETERS by error1<PsiElement, String>()
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 0b102e9..1b7da98 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_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 @@
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 32a7e96..e0adbd2 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 @@
FirJvmStaticChecker,
FirRepeatableAnnotationChecker,
FirJvmInvalidAndDangerousCharactersChecker,
+ FirJvmRedundantRepeatableChecker,
)
override val classCheckers: Set<FirClassChecker>
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 0000000..6cda8bc
--- /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 89ac62c..d8a3d4a 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.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.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 @@
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 78a8215..65a1594 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 @@
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 8070daa..72160bc 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.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 @@
val lookupTag = it.annotationTypeRef.coneTypeSafe<ConeClassLikeType>()?.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 3014402..53d445a 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.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 @@
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 a562c8b..314ec7f 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 @@
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 2e33065..edd7eff 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 @@
DiagnosticFactoryForDeprecation0<PsiElement> ENUM_DECLARING_CLASS_DEPRECATED =
DiagnosticFactoryForDeprecation0.create(LanguageFeature.ProhibitEnumDeclaringClass);
+ DiagnosticFactory2<KtAnnotationEntry, FqName, FqName> 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 cbbe9d2..d3158e6 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 D1(val value: Array<B1>)
-@Repeatable
+<!REDUNDANT_REPEATABLE_ANNOTATION!>@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 c2b5217..e7e466f 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 D1(val value: Array<B1>)
-@Repeatable
+<!REDUNDANT_REPEATABLE_ANNOTATION!>@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 0000000..86ce9a4
--- /dev/null
+++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/multipleRepeatables.kt
@@ -0,0 +1,27 @@
+// FIR_IDENTICAL
+// FULL_JDK
+
+<!REDUNDANT_REPEATABLE_ANNOTATION!>@kotlin.annotation.Repeatable<!>
+@java.lang.annotation.Repeatable(AContainer::class)
+annotation class A
+annotation class AContainer(val value: Array<A>)
+
+<!REDUNDANT_REPEATABLE_ANNOTATION!>@kotlin.annotation.Repeatable<!>
+@kotlin.jvm.JvmRepeatable(BContainer::class)
+annotation class B
+annotation class BContainer(val value: Array<B>)
+
+<!REDUNDANT_REPEATABLE_ANNOTATION!>@kotlin.annotation.Repeatable<!>
+<!REPEATED_ANNOTATION!>@kotlin.annotation.Repeatable<!>
+@kotlin.jvm.JvmRepeatable(CContainer::class)
+<!REPEATED_ANNOTATION!>@java.lang.annotation.Repeatable(CContainer::class)<!>
+annotation class C
+annotation class CContainer(val value: Array<C>)
+
+typealias AlphaRepeatable = kotlin.annotation.Repeatable
+typealias BetaRepeatable = kotlin.jvm.JvmRepeatable
+
+<!REDUNDANT_REPEATABLE_ANNOTATION!>@AlphaRepeatable<!>
+@BetaRepeatable(DContainer::class)
+annotation class D
+annotation class DContainer(val value: Array<D>)
diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/multipleRepeatables.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/multipleRepeatables.txt
new file mode 100644
index 0000000..e37f2be
--- /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<A>)
+ public final val value: kotlin.Array<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
+}
+
+@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<B>)
+ public final val value: kotlin.Array<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
+}
+
+@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<C>)
+ public final val value: kotlin.Array<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
+}
+
+@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<D>)
+ public final val value: kotlin.Array<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 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 5e96434..5ed95ca 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
@@ -34957,6 +34957,12 @@
}
@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 {
runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/qualifiedCallValue.kt");