diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 9485852ea50..52fb2b16aeb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -213,6 +213,7 @@ public interface Errors { DiagnosticFactory2 WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET = DiagnosticFactory2.create(ERROR); DiagnosticFactory1 WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE = DiagnosticFactory1.create(WARNING); DiagnosticFactory0 REPEATED_ANNOTATION = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 REPEATED_ANNOTATION_WARNING = DiagnosticFactory0.create(WARNING); DiagnosticFactory0 NON_SOURCE_ANNOTATION_ON_INLINED_LAMBDA_EXPRESSION = DiagnosticFactory0.create(ERROR); // Annotations diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 9e526610ded..cf6de2b4264 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -128,6 +128,7 @@ public class DefaultErrorMessages { MAP.put(WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE, "Use of this annotation with target ''type'' and use site target ''@{0}'' is deprecated", TO_STRING); MAP.put(REPEATED_ANNOTATION, "This annotation is not repeatable"); + MAP.put(REPEATED_ANNOTATION_WARNING, "This annotation is not repeatable"); MAP.put(NON_SOURCE_ANNOTATION_ON_INLINED_LAMBDA_EXPRESSION, "The lambda expression here is an inlined argument so this annotation cannot be stored anywhere"); MAP.put(INAPPLICABLE_TARGET_ON_PROPERTY, "''@{0}:'' annotations could be applied only to property declarations", TO_STRING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt index 78712c80403..1bdf4387b12 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.resolve import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor @@ -16,6 +17,7 @@ import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.* import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getAnnotationEntries @@ -36,6 +38,9 @@ class AnnotationChecker( fun check(annotated: KtAnnotated, trace: BindingTrace, descriptor: DeclarationDescriptor? = null) { val actualTargets = getActualTargetList(annotated, descriptor, trace.bindingContext) checkEntries(annotated.annotationEntries, actualTargets, trace, annotated) + if (annotated is KtProperty) { + checkPropertyUseSiteTargetAnnotations(annotated, trace) + } if (annotated is KtCallableDeclaration) { annotated.typeReference?.let { check(it, trace) } annotated.receiverTypeReference?.let { check(it, trace) } @@ -72,6 +77,33 @@ class AnnotationChecker( } } + private fun checkPropertyUseSiteTargetAnnotations(property: KtProperty, trace: BindingTrace) { + fun List?.getDescriptors() = this?.mapNotNull { trace.get(BindingContext.ANNOTATION, it)?.annotationClass } ?: listOf() + + val reportError = languageVersionSettings.supportsFeature(LanguageFeature.ProhibitRepeatedUseSiteTargetAnnotations) + + val propertyAnnotations = mapOf( + AnnotationUseSiteTarget.PROPERTY_GETTER to property.getter?.annotationEntries.getDescriptors(), + AnnotationUseSiteTarget.PROPERTY_SETTER to property.setter?.annotationEntries.getDescriptors(), + AnnotationUseSiteTarget.SETTER_PARAMETER to property.setter?.parameter?.annotationEntries.getDescriptors() + ) + + for (entry in property.annotationEntries) { + val descriptor = trace.get(BindingContext.ANNOTATION, entry) ?: continue + val classDescriptor = descriptor.annotationClass ?: continue + + val useSiteTarget = entry.useSiteTarget?.getAnnotationUseSiteTarget() ?: property.getDefaultUseSiteTarget(descriptor) + val existingAnnotations = propertyAnnotations.get(useSiteTarget) ?: continue + if (classDescriptor in existingAnnotations && !classDescriptor.isRepeatableAnnotation()) { + if (reportError) { + trace.reportDiagnosticOnce(Errors.REPEATED_ANNOTATION.on(entry)) + } else { + trace.report(Errors.REPEATED_ANNOTATION_WARNING.on(entry)) + } + } + } + } + private fun KtAnnotated?.getImplicitUseSiteTargetList(): List = when (this) { is KtParameter -> if (ownerFunction is KtPrimaryConstructor) UseSiteTargetsList.T_CONSTRUCTOR_PARAMETER else emptyList() diff --git a/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638.kt b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638.kt index 0d89b156977..747913875ee 100644 --- a/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638.kt +++ b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638.kt @@ -1,21 +1,22 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER +// !LANGUAGE: -ProhibitRepeatedUseSiteTargetAnnotations // Ann is not repeatable annotation class Ann(val x: Int) -@get:Ann(10) +@get:Ann(10) val a: String @Ann(20) get() = "foo" -@set:Ann(10) +@set:Ann(10) var b: String = "" @Ann(20) set(value) { field = value } -@setparam:Ann(10) +@setparam:Ann(10) var c = " " set(@Ann(20) x) {} -@get:Ann(10) -@get:Ann(20) +@get:Ann(10) +@get:Ann(20) val d: String @Ann(30) @Ann(40) get() = "foo" \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638.txt b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638.txt index 17e27f6efff..e82fc10eab6 100644 --- a/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638.txt +++ b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638.txt @@ -1,6 +1,9 @@ package -@setparam:Ann(x = 20) @setparam:Ann(x = 10) public var a: kotlin.String +@get:Ann(x = 10) @get:Ann(x = 20) public val a: kotlin.String +@set:Ann(x = 10) @set:Ann(x = 20) public var b: kotlin.String +@setparam:Ann(x = 20) @setparam:Ann(x = 10) public var c: kotlin.String +@get:Ann(x = 10) @get:Ann(x = 20) @get:Ann(x = 30) @get:Ann(x = 40) public val d: kotlin.String public final annotation class Ann : kotlin.Annotation { public constructor Ann(/*0*/ x: kotlin.Int) diff --git a/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638_after.kt b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638_after.kt new file mode 100644 index 00000000000..db821b5d811 --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638_after.kt @@ -0,0 +1,22 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +// !LANGUAGE: +ProhibitRepeatedUseSiteTargetAnnotations + +// Ann is not repeatable +annotation class Ann(val x: Int) + +@get:Ann(10) +val a: String + @Ann(20) get() = "foo" + +@set:Ann(10) +var b: String = "" + @Ann(20) set(value) { field = value } + +@setparam:Ann(10) +var c = " " + set(@Ann(20) x) {} + +@get:Ann(10) +@get:Ann(20) +val d: String + @Ann(30) @Ann(40) get() = "foo" \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638_after.txt b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638_after.txt new file mode 100644 index 00000000000..e82fc10eab6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638_after.txt @@ -0,0 +1,14 @@ +package + +@get:Ann(x = 10) @get:Ann(x = 20) public val a: kotlin.String +@set:Ann(x = 10) @set:Ann(x = 20) public var b: kotlin.String +@setparam:Ann(x = 20) @setparam:Ann(x = 10) public var c: kotlin.String +@get:Ann(x = 10) @get:Ann(x = 20) @get:Ann(x = 30) @get:Ann(x = 40) public val d: kotlin.String + +public final annotation class Ann : kotlin.Annotation { + public constructor Ann(/*0*/ x: kotlin.Int) + public final val x: 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index daf07034f5f..8e248001f5f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -1449,6 +1449,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638.kt"); } + @TestMetadata("kt26638_after.kt") + public void testKt26638_after() throws Exception { + runTest("compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638_after.kt"); + } + @TestMetadata("ParamAnnotations.kt") public void testParamAnnotations() throws Exception { runTest("compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/ParamAnnotations.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 47099c94b9b..e055be1294f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -1444,6 +1444,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638.kt"); } + @TestMetadata("kt26638_after.kt") + public void testKt26638_after() throws Exception { + runTest("compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/kt26638_after.kt"); + } + @TestMetadata("ParamAnnotations.kt") public void testParamAnnotations() throws Exception { runTest("compiler/testData/diagnostics/tests/annotations/withUseSiteTarget/ParamAnnotations.kt"); diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 2dc2a4aa36c..ee9b0e662fd 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -98,6 +98,7 @@ enum class LanguageFeature( ProhibitJvmOverloadsOnConstructorsOfAnnotationClasses(KOTLIN_1_4, kind = BUG_FIX), ProhibitTypeParametersInAnonymousObjects(KOTLIN_1_4, kind = BUG_FIX), ProperInlineFromHigherPlatformDiagnostic(KOTLIN_1_4, kind = BUG_FIX), + ProhibitRepeatedUseSiteTargetAnnotations(KOTLIN_1_4, kind = BUG_FIX), ProperVisibilityForCompanionObjectInstanceField(sinceVersion = null, kind = BUG_FIX), // Temporarily disabled, see KT-27084/KT-22379