From b2550f69bccc269c0105d85f0309242643e7d867 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 23 Jul 2021 22:30:27 +0200 Subject: [PATCH] Report error if repeated annotation is used with JVM target 1.6 #KT-12794 --- ...irOldFrontendDiagnosticsTestGenerated.java | 6 ++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 ++ .../checkers/RepeatableAnnotationChecker.kt | 25 +++++-- .../diagnostics/DefaultErrorMessagesJvm.java | 1 + .../resolve/jvm/diagnostics/ErrorsJvm.java | 1 + .../kotlin/resolve/AnnotationChecker.kt | 4 +- .../javaRepeatableJvmTarget6.fir.kt | 49 +++++++++++++ .../repeatable/javaRepeatableJvmTarget6.kt | 49 +++++++++++++ .../repeatable/javaRepeatableJvmTarget6.txt | 68 +++++++++++++++++++ .../test/runners/DiagnosticTestGenerated.java | 6 ++ .../kotlin/resolve/DescriptorUtils.kt | 4 +- ...CompilerTestFE10TestdataTestGenerated.java | 6 ++ 12 files changed, 217 insertions(+), 8 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.fir.kt create mode 100644 compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.kt create mode 100644 compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.txt 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 d81d3a6a162..bb185908039 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 @@ -2094,6 +2094,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/annotations/repeatable"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("javaRepeatableJvmTarget6.kt") + public void testJavaRepeatableJvmTarget6() throws Exception { + runTest("compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.kt"); + } + @Test @TestMetadata("javaRepeatable_1_5.kt") public void testJavaRepeatable_1_5() 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 b3e8ae30734..74f2d8cab24 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 @@ -2094,6 +2094,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/annotations/repeatable"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("javaRepeatableJvmTarget6.kt") + public void testJavaRepeatableJvmTarget6() throws Exception { + runTest("compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.kt"); + } + @Test @TestMetadata("javaRepeatable_1_5.kt") public void testJavaRepeatable_1_5() throws Exception { 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 c53cf8ffd72..db2366244b4 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 @@ -5,8 +5,10 @@ package org.jetbrains.kotlin.resolve.jvm.checkers +import org.jetbrains.kotlin.config.JvmTarget import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget @@ -17,10 +19,15 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass import org.jetbrains.kotlin.resolve.descriptorUtil.getAnnotationRetention -import org.jetbrains.kotlin.resolve.descriptorUtil.isRepeatableAnnotation +import org.jetbrains.kotlin.resolve.descriptorUtil.isAnnotatedWithKotlinRepeatable +import org.jetbrains.kotlin.resolve.jvm.JvmPlatformAnnotationFeaturesSupport import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm -class RepeatableAnnotationChecker(languageVersionSettings: LanguageVersionSettings) : AdditionalAnnotationChecker { +class RepeatableAnnotationChecker( + languageVersionSettings: LanguageVersionSettings, + private val jvmTarget: JvmTarget, + private val platformAnnotationFeaturesSupport: JvmPlatformAnnotationFeaturesSupport, +) : AdditionalAnnotationChecker { private val nonSourceDisallowed = !languageVersionSettings.supportsFeature(LanguageFeature.RepeatableAnnotations) override fun checkEntries( @@ -44,15 +51,23 @@ class RepeatableAnnotationChecker(languageVersionSettings: LanguageVersionSettin || (existingTargetsForAnnotation.any { (it == null) != (useSiteTarget == null) }) if (duplicateAnnotation - && classDescriptor.isRepeatableAnnotation() + && isRepeatableAnnotation(classDescriptor) && classDescriptor.getAnnotationRetention() != KotlinRetention.SOURCE ) { - if (nonSourceDisallowed) { - trace.report(ErrorsJvm.NON_SOURCE_REPEATED_ANNOTATION.on(entry)) + val error = when { + jvmTarget == JvmTarget.JVM_1_6 -> ErrorsJvm.REPEATED_ANNOTATION_TARGET6 + nonSourceDisallowed -> ErrorsJvm.NON_SOURCE_REPEATED_ANNOTATION + else -> null + } + if (error != null) { + trace.report(error.on(entry)) } } existingTargetsForAnnotation.add(useSiteTarget) } } + + private fun isRepeatableAnnotation(classDescriptor: ClassDescriptor): Boolean = + classDescriptor.isAnnotatedWithKotlinRepeatable() || platformAnnotationFeaturesSupport.isRepeatableAnnotationClass(classDescriptor) } 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 784f48f2fdd..eac72d699a0 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 @@ -72,6 +72,7 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { MAP.put(POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION, "Only named arguments are available for Java annotations"); MAP.put(DEPRECATED_JAVA_ANNOTATION, "This annotation is deprecated in Kotlin. Use ''@{0}'' instead", TO_STRING); MAP.put(NON_SOURCE_REPEATED_ANNOTATION, "Repeatable annotations with non-SOURCE retention are only supported starting from Kotlin 1.6"); + MAP.put(REPEATED_ANNOTATION_TARGET6, "Repeatable annotations with non-SOURCE retention are not supported with JVM target 1.6. Use -jvm-target 1.8"); MAP.put(ANNOTATION_IS_NOT_APPLICABLE_TO_MULTIFILE_CLASSES, "Annotation ''@{0}'' is not applicable to the multi-file classes", TO_STRING); MAP.put(JVM_PACKAGE_NAME_CANNOT_BE_EMPTY, "''@JvmPackageName'' annotation value cannot be empty"); 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 438b580aa2d..c9966fe71ff 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 @@ -70,6 +70,7 @@ public interface ErrorsJvm { DiagnosticFactory0 POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 DEPRECATED_JAVA_ANNOTATION = DiagnosticFactory1.create(WARNING); DiagnosticFactory0 NON_SOURCE_REPEATED_ANNOTATION = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 REPEATED_ANNOTATION_TARGET6 = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 ANNOTATION_IS_NOT_APPLICABLE_TO_MULTIFILE_CLASSES = DiagnosticFactory1.create(ERROR); DiagnosticFactory0 JVM_PACKAGE_NAME_CANNOT_BE_EMPTY = DiagnosticFactory0.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt index deece389004..f863eef52aa 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt @@ -24,7 +24,7 @@ import org.jetbrains.kotlin.resolve.constants.ArrayValue import org.jetbrains.kotlin.resolve.constants.EnumValue import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass import org.jetbrains.kotlin.resolve.descriptorUtil.getAnnotationRetention -import org.jetbrains.kotlin.resolve.descriptorUtil.isRepeatableAnnotation +import org.jetbrains.kotlin.resolve.descriptorUtil.isAnnotatedWithKotlinRepeatable import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor @@ -294,7 +294,7 @@ class AnnotationChecker( } private fun isRepeatableAnnotation(descriptor: ClassDescriptor): Boolean = - descriptor.isRepeatableAnnotation() || platformAnnotationFeaturesSupport.isRepeatableAnnotationClass(descriptor) + descriptor.isAnnotatedWithKotlinRepeatable() || platformAnnotationFeaturesSupport.isRepeatableAnnotationClass(descriptor) companion object { private val TARGET_ALLOWED_TARGETS = Name.identifier("allowedTargets") diff --git a/compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.fir.kt b/compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.fir.kt new file mode 100644 index 00000000000..0a58e2974dd --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.fir.kt @@ -0,0 +1,49 @@ +// !LANGUAGE: +RepeatableAnnotations +// !JVM_TARGET: 1.6 +// FULL_JDK +// FILE: Runtime.java + +import java.lang.annotation.*; + +@Repeatable(Runtime.Container.class) +@Retention(RetentionPolicy.RUNTIME) +public @interface Runtime { + public @interface Container { + Runtime[] value(); + } +} + +// FILE: Clazz.java + +import java.lang.annotation.*; + +@Repeatable(Clazz.Container.class) +@Retention(RetentionPolicy.CLASS) +public @interface Clazz { + public @interface Container { + Clazz[] value(); + } +} + +// FILE: Source.java + +import java.lang.annotation.*; + +@Repeatable(Source.Container.class) +@Retention(RetentionPolicy.SOURCE) +public @interface Source { + public @interface Container { + Source[] value(); + } +} + +// FILE: usage.kt + +@Runtime @Runtime +class UseRuntime + +@Clazz @Clazz +class UseClazz + +@Source @Source +class UseSource diff --git a/compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.kt b/compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.kt new file mode 100644 index 00000000000..205dfbefab2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.kt @@ -0,0 +1,49 @@ +// !LANGUAGE: +RepeatableAnnotations +// !JVM_TARGET: 1.6 +// FULL_JDK +// FILE: Runtime.java + +import java.lang.annotation.*; + +@Repeatable(Runtime.Container.class) +@Retention(RetentionPolicy.RUNTIME) +public @interface Runtime { + public @interface Container { + Runtime[] value(); + } +} + +// FILE: Clazz.java + +import java.lang.annotation.*; + +@Repeatable(Clazz.Container.class) +@Retention(RetentionPolicy.CLASS) +public @interface Clazz { + public @interface Container { + Clazz[] value(); + } +} + +// FILE: Source.java + +import java.lang.annotation.*; + +@Repeatable(Source.Container.class) +@Retention(RetentionPolicy.SOURCE) +public @interface Source { + public @interface Container { + Source[] value(); + } +} + +// FILE: usage.kt + +@Runtime @Runtime +class UseRuntime + +@Clazz @Clazz +class UseClazz + +@Source @Source +class UseSource diff --git a/compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.txt b/compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.txt new file mode 100644 index 00000000000..7c295b4f6cc --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.txt @@ -0,0 +1,68 @@ +package + +@java.lang.annotation.Repeatable(value = Clazz.Container::class) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class Clazz : kotlin.Annotation { + public constructor Clazz() + 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 Container : kotlin.Annotation { + public constructor Container(/*0*/ vararg value: Clazz /*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 + } +} + +@java.lang.annotation.Repeatable(value = Runtime.Container::class) @kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) public final annotation class Runtime : kotlin.Annotation { + public constructor Runtime() + 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 Container : kotlin.Annotation { + public constructor Container(/*0*/ vararg value: Runtime /*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 + } +} + +@java.lang.annotation.Repeatable(value = Source.Container::class) @kotlin.annotation.Retention(value = AnnotationRetention.SOURCE) public final annotation class Source : kotlin.Annotation { + public constructor Source() + 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 Container : kotlin.Annotation { + public constructor Container(/*0*/ vararg value: Source /*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 + } +} + +@Clazz @Clazz public final class UseClazz { + public constructor UseClazz() + 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 +} + +@Runtime @Runtime public final class UseRuntime { + public constructor UseRuntime() + 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 +} + +@Source @Source public final class UseSource { + public constructor UseSource() + 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-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 f384eaa1ba3..499f0d433e4 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 @@ -2100,6 +2100,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/annotations/repeatable"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("javaRepeatableJvmTarget6.kt") + public void testJavaRepeatableJvmTarget6() throws Exception { + runTest("compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.kt"); + } + @Test @TestMetadata("javaRepeatable_1_5.kt") public void testJavaRepeatable_1_5() throws Exception { diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt index 2f94f6b9034..7b276e76a5a 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt @@ -199,7 +199,9 @@ fun ValueParameterDescriptor.declaresOrInheritsDefaultValue(): Boolean { ) } -fun Annotated.isRepeatableAnnotation(): Boolean = +// Note that on JVM, an annotation class is also considered repeatable if it's annotated with java.lang.annotation.Repeatable. +// See JvmPlatformAnnotationFeaturesSupport. +fun Annotated.isAnnotatedWithKotlinRepeatable(): Boolean = annotations.findAnnotation(StandardNames.FqNames.repeatable) != null fun Annotated.isDocumentedAnnotation(): Boolean = diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index a88f26e1438..d89cbcbccdf 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -2094,6 +2094,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/annotations/repeatable"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("javaRepeatableJvmTarget6.kt") + public void testJavaRepeatableJvmTarget6() throws Exception { + runTest("compiler/testData/diagnostics/tests/annotations/repeatable/javaRepeatableJvmTarget6.kt"); + } + @Test @TestMetadata("javaRepeatable_1_5.kt") public void testJavaRepeatable_1_5() throws Exception {