Report error if repeated annotation is used with JVM target 1.6
#KT-12794
This commit is contained in:
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+20
-5
@@ -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)
|
||||
}
|
||||
|
||||
+1
@@ -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");
|
||||
|
||||
@@ -70,6 +70,7 @@ public interface ErrorsJvm {
|
||||
DiagnosticFactory0<KtExpression> POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory1<KtAnnotationEntry, FqName> DEPRECATED_JAVA_ANNOTATION = DiagnosticFactory1.create(WARNING);
|
||||
DiagnosticFactory0<KtAnnotationEntry> NON_SOURCE_REPEATED_ANNOTATION = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtAnnotationEntry> REPEATED_ANNOTATION_TARGET6 = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory1<KtAnnotationEntry, FqName> ANNOTATION_IS_NOT_APPLICABLE_TO_MULTIFILE_CLASSES = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<KtAnnotationEntry> JVM_PACKAGE_NAME_CANNOT_BE_EMPTY = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
@@ -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")
|
||||
|
||||
+49
@@ -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
|
||||
+49
@@ -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 <!REPEATED_ANNOTATION_TARGET6!>@Runtime<!>
|
||||
class UseRuntime
|
||||
|
||||
@Clazz <!REPEATED_ANNOTATION_TARGET6!>@Clazz<!>
|
||||
class UseClazz
|
||||
|
||||
@Source @Source
|
||||
class UseSource
|
||||
+68
@@ -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<out Clazz>*/)
|
||||
public final val value: kotlin.Array<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
|
||||
}
|
||||
}
|
||||
|
||||
@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<out Runtime>*/)
|
||||
public final val value: kotlin.Array<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
|
||||
}
|
||||
}
|
||||
|
||||
@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<out Source>*/)
|
||||
public final val value: kotlin.Array<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
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
}
|
||||
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
@@ -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 =
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user