diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt index 548c1a38616..ca67d3b2715 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.container.StorageComponentContainer import org.jetbrains.kotlin.container.useImpl import org.jetbrains.kotlin.descriptors.* 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.diagnostics.DiagnosticSink @@ -97,15 +98,24 @@ public object JvmPlatformConfigurator : PlatformConfigurator( public object RepeatableAnnotationChecker: AdditionalAnnotationChecker { override fun checkEntries(entries: List, actualTargets: List, trace: BindingTrace) { - val entryTypes: MutableSet = hashSetOf() + val entryTypesWithAnnotations = hashMapOf>() + for (entry in entries) { val descriptor = trace.get(BindingContext.ANNOTATION, entry) ?: continue val classDescriptor = TypeUtils.getClassDescriptor(descriptor.type) ?: continue - if (!entryTypes.add(descriptor.type) + + val useSiteTarget = entry.useSiteTarget?.getAnnotationUseSiteTarget() + val existingTargetsForAnnotation = entryTypesWithAnnotations.getOrPut(descriptor.type) { arrayListOf() } + val duplicateAnnotation = useSiteTarget in existingTargetsForAnnotation + || (existingTargetsForAnnotation.any { (it == null) != (useSiteTarget == null) }) + + if (duplicateAnnotation && classDescriptor.isRepeatableAnnotation() && classDescriptor.getAnnotationRetention() != KotlinRetention.SOURCE) { trace.report(ErrorsJvm.NON_SOURCE_REPEATED_ANNOTATION.on(entry)); } + + existingTargetsForAnnotation.add(useSiteTarget) } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt index a6b795a034d..3c05298eedb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt @@ -18,6 +18,8 @@ package org.jetbrains.kotlin.resolve import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getAnnotationEntries @@ -61,14 +63,23 @@ public class AnnotationChecker(private val additionalCheckers: Iterable, actualTargets: TargetList, trace: BindingTrace) { - val entryTypes: MutableSet = hashSetOf() + val entryTypesWithAnnotations = hashMapOf>() + for (entry in entries) { checkAnnotationEntry(entry, actualTargets, trace) val descriptor = trace.get(BindingContext.ANNOTATION, entry) ?: continue val classDescriptor = TypeUtils.getClassDescriptor(descriptor.type) ?: continue - if (!entryTypes.add(descriptor.type) && !classDescriptor.isRepeatableAnnotation()) { + + val useSiteTarget = entry.useSiteTarget?.getAnnotationUseSiteTarget() + val existingTargetsForAnnotation = entryTypesWithAnnotations.getOrPut(descriptor.type) { arrayListOf() } + val duplicateAnnotation = useSiteTarget in existingTargetsForAnnotation + || (existingTargetsForAnnotation.any { (it == null) != (useSiteTarget == null) }) + + if (duplicateAnnotation && !classDescriptor.isRepeatableAnnotation()) { trace.report(Errors.REPEATED_ANNOTATION.on(entry)); } + + existingTargetsForAnnotation.add(useSiteTarget) } additionalCheckers.forEach { it.checkEntries(entries, actualTargets.declarationSite, trace) } } diff --git a/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/repeatable.kt b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/repeatable.kt new file mode 100644 index 00000000000..9305c2614c0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/repeatable.kt @@ -0,0 +1,18 @@ +annotation(repeatable = true) class RepeatableAnn +annotation class Ann + +public class A(@param:Ann @Ann val x: Int, @param: RepeatableAnn @Ann val y: Int) { + + @field:Ann @property:Ann @RepeatableAnn @property:RepeatableAnn + val a: Int = 0 + + @Ann @field:Ann @property:Ann + val b: Int = 0 + + @field:RepeatableAnn @field:RepeatableAnn + val c: Int = 0 + + @property:RepeatableAnn @RepeatableAnn + val d: Int = 0 + +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/repeatable.txt b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/repeatable.txt new file mode 100644 index 00000000000..fedcce0db14 --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/repeatable.txt @@ -0,0 +1,28 @@ +package + +public final class A { + public constructor A(/*0*/ Ann() Ann() x: kotlin.Int, /*1*/ RepeatableAnn() Ann() y: kotlin.Int) + Ann() RepeatableAnn() Ann() RepeatableAnn() internal final val a: kotlin.Int = 0 + Ann() Ann() Ann() internal final val b: kotlin.Int = 0 + RepeatableAnn() RepeatableAnn() internal final val c: kotlin.Int = 0 + RepeatableAnn() RepeatableAnn() internal final val d: kotlin.Int = 0 + internal final val x: kotlin.Int + internal final val y: kotlin.Int + 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.annotation() internal final class Ann : kotlin.Annotation { + public constructor Ann() + 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.annotation(repeatable = true) internal final class RepeatableAnn : kotlin.Annotation { + public constructor RepeatableAnn() + 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index c5bf8e7f9bc..2bc5d0223a3 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -1232,6 +1232,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("repeatable.kt") + public void testRepeatable() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/repeatable.kt"); + doTest(fileName); + } + @TestMetadata("SetterAnnotations.kt") public void testSetterAnnotations() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/SetterAnnotations.kt");