Report error on class named Container inside repeatable annotation

#KT-12794
 #KT-47971
This commit is contained in:
Alexander Udalov
2021-07-30 02:13:57 +02:00
parent 5f7803f2db
commit 847c58d574
13 changed files with 297 additions and 8 deletions
@@ -2177,6 +2177,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
public void testKotlinRepeatable_1_6() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/repeatable/kotlinRepeatable_1_6.kt");
}
@Test
@TestMetadata("nestedClassContainer_1_5.kt")
public void testNestedClassContainer_1_5() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_5.kt");
}
@Test
@TestMetadata("nestedClassContainer_1_6.kt")
public void testNestedClassContainer_1_6() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_6.kt");
}
}
@Nested
@@ -2177,6 +2177,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
public void testKotlinRepeatable_1_6() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/repeatable/kotlinRepeatable_1_6.kt");
}
@Test
@TestMetadata("nestedClassContainer_1_5.kt")
public void testNestedClassContainer_1_5() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_5.kt");
}
@Test
@TestMetadata("nestedClassContainer_1_6.kt")
public void testNestedClassContainer_1_6() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_6.kt");
}
}
@Nested
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.resolve.jvm.checkers
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
@@ -16,7 +17,9 @@ 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.Diagnostic
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@@ -63,19 +66,51 @@ class RepeatableAnnotationChecker(
if (annotated is KtClassOrObject && annotated.hasModifier(KtTokens.ANNOTATION_KEYWORD)) {
val annotationClass = trace.get(BindingContext.CLASS, annotated)
val repeatable = annotations.find { it.descriptor.fqName == JvmAnnotationNames.REPEATABLE_ANNOTATION }
if (annotationClass != null && repeatable != null) {
val containerKClassValue = repeatable.descriptor.allValueArguments[Name.identifier("value")]
if (containerKClassValue is KClassValue) {
val containerClass = TypeUtils.getClassDescriptor(containerKClassValue.getArgumentType(module))
if (containerClass != null) {
checkRepeatableAnnotationContainer(annotationClass, containerClass, trace, repeatable.entry)
}
if (annotationClass != null) {
val javaRepeatable = annotations.find { it.descriptor.fqName == JvmAnnotationNames.REPEATABLE_ANNOTATION }
val kotlinRepeatable = annotations.find { it.descriptor.fqName == StandardNames.FqNames.repeatable }
when {
javaRepeatable != null -> checkJavaRepeatableAnnotationDeclaration(javaRepeatable, annotationClass, trace)
kotlinRepeatable != null -> checkKotlinRepeatableAnnotationDeclaration(kotlinRepeatable, annotationClass, trace)
}
}
}
}
private fun checkJavaRepeatableAnnotationDeclaration(
javaRepeatable: ResolvedAnnotation,
annotationClass: ClassDescriptor,
trace: BindingTrace,
) {
val containerKClassValue = javaRepeatable.descriptor.allValueArguments[Name.identifier("value")]
if (containerKClassValue is KClassValue) {
val containerClass = TypeUtils.getClassDescriptor(containerKClassValue.getArgumentType(module))
if (containerClass != null) {
checkRepeatableAnnotationContainer(annotationClass, containerClass, trace, javaRepeatable.entry)
}
}
}
private fun checkKotlinRepeatableAnnotationDeclaration(
kotlinRepeatable: ResolvedAnnotation,
annotationClass: ClassDescriptor,
trace: BindingTrace,
) {
val nestedClassNamedContainer =
annotationClass.unsubstitutedInnerClassesScope.getContributedClassifier(
Name.identifier(JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_NAME),
NoLookupLocation.FOR_ALREADY_TRACKED
)
if (nestedClassNamedContainer != null) {
trace.report(
select(
ErrorsJvm.REPEATABLE_ANNOTATION_HAS_NESTED_CLASS_NAMED_CONTAINER,
ErrorsJvm.REPEATABLE_ANNOTATION_HAS_NESTED_CLASS_NAMED_CONTAINER_ERROR
).on(kotlinRepeatable.entry)
)
}
}
private fun checkRepeatedEntries(annotations: List<ResolvedAnnotation>, trace: BindingTrace) {
val entryTypesWithAnnotations = hashMapOf<FqName, MutableList<AnnotationUseSiteTarget?>>()
@@ -83,6 +83,8 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
MAP.put(REPEATABLE_CONTAINER_HAS_SHORTER_RETENTION_ERROR, "Container annotation ''{0}'' has shorter retention (''{1}'') than the repeatable annotation ''{2}'' (''{3}'')", TO_STRING, TO_STRING, TO_STRING, TO_STRING);
MAP.put(REPEATABLE_CONTAINER_TARGET_SET_NOT_A_SUBSET, "Target set of container annotation ''{0}'' must be a subset of the target set of contained annotation ''{1}''. This code will be prohibited in Kotlin 1.6", TO_STRING, TO_STRING);
MAP.put(REPEATABLE_CONTAINER_TARGET_SET_NOT_A_SUBSET_ERROR, "Target set of container annotation ''{0}'' must be a subset of the target set of contained annotation ''{1}''", TO_STRING, TO_STRING);
MAP.put(REPEATABLE_ANNOTATION_HAS_NESTED_CLASS_NAMED_CONTAINER, "Repeatable annotation cannot have a nested class named 'Container'. This name is reserved for auto-generated container class");
MAP.put(REPEATABLE_ANNOTATION_HAS_NESTED_CLASS_NAMED_CONTAINER_ERROR, "Repeatable annotation cannot have a nested class named 'Container'. This name is reserved for auto-generated container class");
MAP.put(JVM_PACKAGE_NAME_CANNOT_BE_EMPTY, "''@JvmPackageName'' annotation value cannot be empty");
MAP.put(JVM_PACKAGE_NAME_MUST_BE_VALID_NAME, "''@JvmPackageName'' annotation value must be a valid dot-qualified name of a package");
@@ -83,6 +83,8 @@ public interface ErrorsJvm {
DiagnosticFactory4<KtAnnotationEntry, FqName, String, FqName, String> REPEATABLE_CONTAINER_HAS_SHORTER_RETENTION_ERROR = DiagnosticFactory4.create(ERROR);
DiagnosticFactory2<KtAnnotationEntry, FqName, FqName> REPEATABLE_CONTAINER_TARGET_SET_NOT_A_SUBSET = DiagnosticFactory2.create(WARNING);
DiagnosticFactory2<KtAnnotationEntry, FqName, FqName> REPEATABLE_CONTAINER_TARGET_SET_NOT_A_SUBSET_ERROR = DiagnosticFactory2.create(ERROR);
DiagnosticFactory0<KtAnnotationEntry> REPEATABLE_ANNOTATION_HAS_NESTED_CLASS_NAMED_CONTAINER = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<KtAnnotationEntry> REPEATABLE_ANNOTATION_HAS_NESTED_CLASS_NAMED_CONTAINER_ERROR = DiagnosticFactory0.create(WARNING);
DiagnosticFactory1<KtAnnotationEntry, FqName> ANNOTATION_IS_NOT_APPLICABLE_TO_MULTIFILE_CLASSES = DiagnosticFactory1.create(ERROR);
@@ -0,0 +1,21 @@
// !LANGUAGE: -RepeatableAnnotations -RepeatableAnnotationContainerConstraints
// FULL_JDK
@Repeatable
annotation class A1 {
class Container
}
@java.lang.annotation.Repeatable(D1::class)
annotation class B1 {
class Container
}
annotation class D1(val value: Array<B1>)
@Repeatable
@java.lang.annotation.Repeatable(D2::class)
annotation class B2 {
class Container
}
annotation class D2(val value: Array<B2>)
@@ -0,0 +1,21 @@
// !LANGUAGE: -RepeatableAnnotations -RepeatableAnnotationContainerConstraints
// FULL_JDK
<!REPEATABLE_ANNOTATION_HAS_NESTED_CLASS_NAMED_CONTAINER!>@Repeatable<!>
annotation class A1 {
class Container
}
@java.lang.annotation.Repeatable(D1::class)
annotation class B1 {
class Container
}
annotation class D1(val value: Array<B1>)
@Repeatable
@java.lang.annotation.Repeatable(D2::class)
annotation class B2 {
class Container
}
annotation class D2(val value: Array<B2>)
@@ -0,0 +1,59 @@
package
@kotlin.annotation.Repeatable public final annotation class A1 : kotlin.Annotation {
public constructor A1()
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 class Container {
public constructor Container()
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 = D1::class) public final annotation class B1 : kotlin.Annotation {
public constructor B1()
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 class Container {
public constructor Container()
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.Repeatable @java.lang.annotation.Repeatable(value = D2::class) public final annotation class B2 : kotlin.Annotation {
public constructor B2()
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 class Container {
public constructor Container()
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 D1 : kotlin.Annotation {
public constructor D1(/*0*/ value: kotlin.Array<B1>)
public final val value: kotlin.Array<B1>
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 D2 : kotlin.Annotation {
public constructor D2(/*0*/ value: kotlin.Array<B2>)
public final val value: kotlin.Array<B2>
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
}
@@ -0,0 +1,21 @@
// !LANGUAGE: +RepeatableAnnotations +RepeatableAnnotationContainerConstraints
// FULL_JDK
@Repeatable
annotation class A1 {
class Container
}
@java.lang.annotation.Repeatable(D1::class)
annotation class B1 {
class Container
}
annotation class D1(val value: Array<B1>)
@Repeatable
@java.lang.annotation.Repeatable(D2::class)
annotation class B2 {
class Container
}
annotation class D2(val value: Array<B2>)
@@ -0,0 +1,21 @@
// !LANGUAGE: +RepeatableAnnotations +RepeatableAnnotationContainerConstraints
// FULL_JDK
<!REPEATABLE_ANNOTATION_HAS_NESTED_CLASS_NAMED_CONTAINER_ERROR!>@Repeatable<!>
annotation class A1 {
class Container
}
@java.lang.annotation.Repeatable(D1::class)
annotation class B1 {
class Container
}
annotation class D1(val value: Array<B1>)
@Repeatable
@java.lang.annotation.Repeatable(D2::class)
annotation class B2 {
class Container
}
annotation class D2(val value: Array<B2>)
@@ -0,0 +1,59 @@
package
@kotlin.annotation.Repeatable public final annotation class A1 : kotlin.Annotation {
public constructor A1()
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 class Container {
public constructor Container()
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 = D1::class) public final annotation class B1 : kotlin.Annotation {
public constructor B1()
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 class Container {
public constructor Container()
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.Repeatable @java.lang.annotation.Repeatable(value = D2::class) public final annotation class B2 : kotlin.Annotation {
public constructor B2()
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 class Container {
public constructor Container()
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 D1 : kotlin.Annotation {
public constructor D1(/*0*/ value: kotlin.Array<B1>)
public final val value: kotlin.Array<B1>
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 D2 : kotlin.Annotation {
public constructor D2(/*0*/ value: kotlin.Array<B2>)
public final val value: kotlin.Array<B2>
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
}
@@ -2183,6 +2183,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
public void testKotlinRepeatable_1_6() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/repeatable/kotlinRepeatable_1_6.kt");
}
@Test
@TestMetadata("nestedClassContainer_1_5.kt")
public void testNestedClassContainer_1_5() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_5.kt");
}
@Test
@TestMetadata("nestedClassContainer_1_6.kt")
public void testNestedClassContainer_1_6() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_6.kt");
}
}
@Nested
@@ -2177,6 +2177,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
public void testKotlinRepeatable_1_6() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/repeatable/kotlinRepeatable_1_6.kt");
}
@Test
@TestMetadata("nestedClassContainer_1_5.kt")
public void testNestedClassContainer_1_5() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_5.kt");
}
@Test
@TestMetadata("nestedClassContainer_1_6.kt")
public void testNestedClassContainer_1_6() throws Exception {
runTest("compiler/testData/diagnostics/tests/annotations/repeatable/nestedClassContainer_1_6.kt");
}
}
@Nested