Report error if repeated annotation is used with JVM target 1.6

#KT-12794
This commit is contained in:
Alexander Udalov
2021-07-23 22:30:27 +02:00
parent 26043f3968
commit b2550f69bc
12 changed files with 217 additions and 8 deletions
@@ -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 {
@@ -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 {
@@ -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)
}
@@ -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")
@@ -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
@@ -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
@@ -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
}
@@ -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 =
@@ -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 {