| /******************************************************************************* |
| * Copyright (c) 2013, 2016 Jesper S Moller and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Jesper S Moller - initial API and implementation |
| * Bug 412151 - [1.8][compiler] Check repeating annotation's collection type |
| * Bug 412149 - [1.8][compiler] Emit repeated annotations into the designated container |
| * Bug 419209 - [1.8] Repeating container annotations should be rejected in the presence of annotation it contains |
| * Stephan Herrmann - Contribution for |
| * Bug 419209 - [1.8] Repeating container annotations should be rejected in the presence of annotation it contains |
| *******************************************************************************/ |
| package org.eclipse.jdt.core.tests.compiler.regression; |
| |
| import java.io.File; |
| |
| import junit.framework.Test; |
| |
| import org.eclipse.jdt.core.util.ClassFileBytesDisassembler; |
| import org.eclipse.jdt.internal.compiler.ASTVisitor; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.jdt.internal.compiler.impl.IntConstant; |
| import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; |
| |
| @SuppressWarnings({ "rawtypes" }) |
| public class RepeatableAnnotationTest extends AbstractComparableTest { |
| |
| // Static initializer to specify tests subset using TESTS_* static variables |
| // All specified tests which do not belong to the class are skipped... |
| static { |
| // TESTS_NAMES = new String[] { "test006" }; |
| // TESTS_NUMBERS = new int[] { 297 }; |
| // TESTS_RANGE = new int[] { 294, -1 }; |
| } |
| |
| public RepeatableAnnotationTest(String name) { |
| super(name); |
| } |
| |
| public static Test suite() { |
| return buildMinimalComplianceTestSuite(testClass(), F_1_8); |
| } |
| |
| public static Class testClass() { |
| return RepeatableAnnotationTest.class; |
| } |
| |
| // check repeated occurrence of non-repeatable annotation |
| public void test001() { |
| this.runNegativeTest( |
| new String[] { |
| "X.java", |
| "public @Foo @Foo class X {\n" + |
| "}\n" + |
| "\n", |
| "Foo.java", |
| "public @interface Foo {\n" + |
| "}\n" |
| }, |
| "----------\n" + |
| "1. ERROR in X.java (at line 1)\n" + |
| " public @Foo @Foo class X {\n" + |
| " ^^^^\n" + |
| "Duplicate annotation of non-repeatable type @Foo. Only annotation types marked @Repeatable can be used multiple times at one target.\n" + |
| "----------\n" + |
| "2. ERROR in X.java (at line 1)\n" + |
| " public @Foo @Foo class X {\n" + |
| " ^^^^\n" + |
| "Duplicate annotation of non-repeatable type @Foo. Only annotation types marked @Repeatable can be used multiple times at one target.\n" + |
| "----------\n"); |
| } |
| |
| public void test002() { |
| this.runConformTest( |
| new String[] { |
| "X.java", |
| "@Foo @Foo public class X {\n" + |
| "}\n" + |
| "\n", |
| "Foo.java", |
| "@java.lang.annotation.Repeatable(FooContainer.class) public @interface Foo {\n" + |
| "}\n", |
| "FooContainer.java", |
| "public @interface FooContainer {\n" + |
| " Foo[] value();\n" + |
| "}\n" |
| }, |
| ""); |
| } |
| |
| // check repeated occurrence of annotation where annotation container is not valid for the target |
| public void test003() { |
| this.runNegativeTest( |
| new String[] { |
| "FooContainer.java", |
| "import java.lang.annotation.ElementType;\n" + |
| "import java.lang.annotation.Target;\n" + |
| "@Target({ElementType.METHOD, ElementType.FIELD}) public @interface FooContainer {\n" + |
| |
| " Foo[] value();\n" + |
| "}\n", |
| "Foo.java", |
| "@java.lang.annotation.Repeatable(FooContainer.class) public @interface Foo {\n" + |
| "}\n", |
| "X.java", |
| "@Foo @Foo public class X { /* Problem */\n" + |
| " @Foo @Foo void okHere() { /* No problem */\n" + |
| " @Foo @Foo int local = 0; /* Problem! */\n" + |
| " }\n" + |
| " @Foo @Foo int alsoFoo = 0; /* No problem */\n" + |
| " @Foo class Y {} /* No problem since not repeated */\n" + |
| "}\n" |
| }, |
| "----------\n" + |
| "1. ERROR in X.java (at line 1)\n" + |
| " @Foo @Foo public class X { /* Problem */\n" + |
| " ^^^^\n" + |
| "The annotation @Foo cannot be repeated at this location since its container annotation type @FooContainer is disallowed at this location\n" + |
| "----------\n" + |
| "2. ERROR in X.java (at line 3)\n" + |
| " @Foo @Foo int local = 0; /* Problem! */\n" + |
| " ^^^^\n" + |
| "The annotation @Foo cannot be repeated at this location since its container annotation type @FooContainer is disallowed at this location\n" + |
| "----------\n"); |
| } |
| |
| // This is the same test as test003, only where the annotation info for Foo is from a class file, not from the compiler |
| public void test004() { |
| this.runConformTest( |
| new String[] { |
| "FooContainer.java", |
| "import java.lang.annotation.ElementType;\n" + |
| "import java.lang.annotation.Target;\n" + |
| "@Target({ElementType.METHOD, ElementType.FIELD}) public @interface FooContainer {\n" + |
| " Foo[] value();\n" + |
| "}\n", |
| "Foo.java", |
| "@java.lang.annotation.Repeatable(FooContainer.class) public @interface Foo {\n" + |
| "}\n" |
| }, |
| ""); |
| this.runNegativeTest( |
| new String[] { |
| "X.java", |
| "@Foo @Foo public class X { /* Problem */\n" + |
| "}\n" |
| }, |
| "----------\n" + |
| "1. ERROR in X.java (at line 1)\n" + |
| " @Foo @Foo public class X { /* Problem */\n" + |
| " ^^^^\n" + |
| "The annotation @Foo cannot be repeated at this location since its container annotation type @FooContainer is disallowed at this location\n" + |
| "----------\n", |
| null, false /* don't flush*/); |
| } |
| |
| // Test that a single, repeatable annotation can exist just fine an occurrence of its container annotation |
| public void test005() { |
| this.runConformTest( |
| new String[] { |
| "X.java", |
| "@java.lang.annotation.Repeatable(FooContainer.class) @interface Foo {}\n" + |
| "@interface FooContainer { Foo[] value(); }\n" + |
| "@Foo @FooContainer({@Foo, @Foo}) public class X { /* Not a problem */ }\n" |
| }, |
| ""); |
| } |
| |
| // Test that an repeated annotation can't occur together with its container annotation |
| public void test006() { |
| this.runNegativeTest( |
| new String[] { |
| "X.java", |
| "@interface FooContainer { Foo[] value(); }\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class) @interface Foo {}\n" + |
| "@Foo @Foo @FooContainer({@Foo, @Foo}) public class X { /* A problem */ }\n" |
| }, |
| "----------\n" + |
| "1. ERROR in X.java (at line 3)\n" + |
| " @Foo @Foo @FooContainer({@Foo, @Foo}) public class X { /* A problem */ }\n" + |
| " ^^^^\n" + |
| "The repeatable annotation @Foo may not be repeated where its container annotation type @FooContainer is also used directly\n" + |
| "----------\n"); |
| } |
| |
| // Test that an repeated annotation can't occur together with its container annotation, even if it itself is repeatable. |
| public void test007() { |
| this.runNegativeTest( |
| new String[] { |
| "X.java", |
| "@interface FooContainerContainer { FooContainer[] value(); }\n" + |
| "@java.lang.annotation.Repeatable(FooContainerContainer.class) @interface FooContainer { Foo[] value(); }\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class) @interface Foo {}\n" + |
| "@Foo @Foo @FooContainer({@Foo, @Foo}) public class X { /* Still a problem */ }\n" |
| }, |
| "----------\n" + |
| "1. ERROR in X.java (at line 4)\n" + |
| " @Foo @Foo @FooContainer({@Foo, @Foo}) public class X { /* Still a problem */ }\n" + |
| " ^^^^\n" + |
| "The repeatable annotation @Foo may not be repeated where its container annotation type @FooContainer is also used directly\n" + |
| "----------\n"); |
| } |
| |
| // Test that an repeated annotation can't occur together with its container annotation, even if it itself is repeatable. |
| public void test007a() { |
| this.runNegativeTest( |
| new String[] { |
| "X.java", |
| "@interface FooContainerContainer { FooContainer[] value(); }\n" + |
| "@java.lang.annotation.Repeatable(FooContainerContainer.class) @interface FooContainer { Foo[] value(); }\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class) @interface Foo {}\n" + |
| "@interface Bar {}\n" + |
| "@Foo @Foo @Bar @Bar @FooContainer({@Foo, @Foo}) public class X { /* Still a problem */ }\n" |
| }, |
| "----------\n" + |
| "1. ERROR in X.java (at line 5)\n" + |
| " @Foo @Foo @Bar @Bar @FooContainer({@Foo, @Foo}) public class X { /* Still a problem */ }\n" + |
| " ^^^^\n" + |
| "The repeatable annotation @Foo may not be repeated where its container annotation type @FooContainer is also used directly\n" + |
| "----------\n" + |
| "2. ERROR in X.java (at line 5)\n" + |
| " @Foo @Foo @Bar @Bar @FooContainer({@Foo, @Foo}) public class X { /* Still a problem */ }\n" + |
| " ^^^^\n" + |
| "Duplicate annotation of non-repeatable type @Bar. Only annotation types marked @Repeatable can be used multiple times at one target.\n" + |
| "----------\n" + |
| "3. ERROR in X.java (at line 5)\n" + |
| " @Foo @Foo @Bar @Bar @FooContainer({@Foo, @Foo}) public class X { /* Still a problem */ }\n" + |
| " ^^^^\n" + |
| "Duplicate annotation of non-repeatable type @Bar. Only annotation types marked @Repeatable can be used multiple times at one target.\n" + |
| "----------\n"); |
| } |
| |
| // Test that repeated annotations should be contiguous (raises a warning if not) -- not yet in BETA_JAVA8 |
| public void _test008() { |
| this.runNegativeTest( |
| new String[] { |
| "X.java", |
| "@interface Bar {}\n" + |
| "@interface Baz {}\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class) @interface Foo {}\n" + |
| "@interface FooContainer { Foo[] value(); }\n" + |
| "@Foo @Bar @Foo /* just lexical */ @Foo public class X { /* Gives a warning */ }\n" |
| }, |
| "----------\n" + |
| "1. WARNING in X.java (at line 5)\n" + |
| " @Foo @Bar @Foo /* just lexical */ @Foo public class X { /* Gives a warning */ }\n" + |
| " ^^^^\n" + |
| "Repeated @Foo annotations are not grouped together\n" + |
| "----------\n"); |
| } |
| // Test that deprecation of container annotation is reflected in the repeated annotation (disabled until specification clarification is available) |
| public void _test009() { |
| this.runConformTest( |
| new String[] { |
| "Y.java", |
| "@java.lang.annotation.Repeatable(FooContainer.class) @interface Foo { int value(); }\n" + |
| "@Deprecated @interface FooContainer { Foo[] value(); }\n" + |
| "@Foo(0) class X { /* Gives a warning */ }\n" + |
| "@Foo(1) @Foo(2) public class Y { /* Gives a warning */ }\n" |
| }, |
| new ASTVisitor() { |
| public boolean visit( |
| TypeDeclaration typeDeclaration, |
| CompilationUnitScope scope) { |
| if (new String(typeDeclaration.name).equals("X")) { |
| assertFalse("Foo on X should NOT be deprecated!", typeDeclaration.annotations[0].getCompilerAnnotation().getAnnotationType().isDeprecated()); |
| } |
| if (new String(typeDeclaration.name).equals("Y")) { |
| assertEquals("Find Foo(1) on Y", IntConstant.fromValue(1), typeDeclaration.annotations[0].getCompilerAnnotation().getElementValuePairs()[0].value); |
| assertTrue("1st Foo on Y should be deprecated!", typeDeclaration.annotations[0].getCompilerAnnotation().getAnnotationType().isDeprecated()); |
| assertEquals("Find Foo(2) on Y", IntConstant.fromValue(2), typeDeclaration.annotations[1].getCompilerAnnotation().getElementValuePairs()[0].value); |
| assertTrue("2nd Foo on Y should be deprecated!", typeDeclaration.annotations[1].getCompilerAnnotation().getAnnotationType().isDeprecated()); |
| } |
| return true; // do nothing by default, keep traversing |
| } |
| }); |
| } |
| // Bug 412151: [1.8][compiler] Check repeating annotation's collection type |
| // 412151: The collections type's (TC) declaration must have a array of Ts as its value() - with Foo and FooContainer in same compilation round |
| public void test010() { |
| this.runNegativeTest( |
| new String[] { |
| "Foo.java", |
| "@interface FooContainer {\n" + |
| "}\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@interface Foo {}\n" |
| }, |
| "----------\n" + |
| "1. ERROR in Foo.java (at line 3)\n" + |
| " @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| " ^^^^^^^^^^^^^^^^^^\n" + |
| "The container annotation type @FooContainer must declare a member value()\n" + |
| "----------\n"); |
| } |
| // 412151: The collections type's (TC) declaration must have a array of Ts as its value() - with Foo and FooContainer in same compilation round |
| public void test011() { |
| this.runNegativeTest( |
| new String[] { |
| "Foo.java", |
| "@interface FooContainer {\n" + |
| " int[] value();\n" + |
| "}\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@interface Foo {}\n" |
| }, |
| "----------\n" + |
| "1. ERROR in Foo.java (at line 4)\n" + |
| " @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| " ^^^^^^^^^^^^^^^^^^\n" + |
| "The value method in the container annotation type @FooContainer must be of type Foo[] but is int[]\n" + |
| "----------\n"); |
| } |
| // 412151: The collections type's (TC) declaration must have a array of Ts as its value() - with Foo and FooContainer in same compilation round |
| public void test012() { |
| this.runNegativeTest( |
| new String[] { |
| "Foo.java", |
| "@interface FooContainer {\n" + |
| " Foo[][] value();\n" + |
| "}\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@interface Foo {}\n" |
| }, |
| "----------\n" + |
| "1. ERROR in Foo.java (at line 2)\n" + |
| " Foo[][] value();\n" + |
| " ^^^^^^^\n" + |
| "Invalid type Foo[][] for the annotation attribute FooContainer.value; only primitive type, String, Class, annotation, enumeration are permitted or 1-dimensional arrays thereof\n" + |
| "----------\n" + |
| "2. ERROR in Foo.java (at line 4)\n" + |
| " @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| " ^^^^^^^^^^^^^^^^^^\n" + |
| "The value method in the container annotation type @FooContainer must be of type Foo[] but is Foo[][]\n" + |
| "----------\n" |
| ); |
| } |
| // 412151: Any methods declared by TC other than value() have a default value (JLS 9.6.2). |
| public void test013() { |
| this.runNegativeTest( |
| new String[] { |
| "Foo.java", |
| "@interface FooContainer {\n" + |
| " Foo[] value();\n" + |
| " int hasDefaultValue() default 1337;\n" + |
| " int doesntHaveDefaultValue();\n" + |
| "}\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@interface Foo {}\n" |
| }, |
| "----------\n" + |
| "1. ERROR in Foo.java (at line 6)\n" + |
| " @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| " ^^^^^^^^^^^^^^^^^^\n" + |
| "The container annotation type @FooContainer must declare a default value for the annotation attribute \'doesntHaveDefaultValue\'\n" + |
| "----------\n"); |
| } |
| // 412151: The @Retention meta-annotation of TC must at least include the retention of T () |
| public void test014() { |
| this.runConformTest( |
| new String[] { |
| "Foo.java", |
| "import java.lang.annotation.Retention;\n" + |
| "import java.lang.annotation.RetentionPolicy;\n" + |
| "@Retention(RetentionPolicy.CLASS)\n" + |
| "@interface FooContainer {\n" + |
| " Foo[] value();\n" + |
| "}\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@Retention(RetentionPolicy.CLASS)\n" + |
| "@interface Foo {\n" + |
| "}\n" |
| }, |
| ""); |
| } |
| |
| // |
| public void test015() { |
| // These are fine: |
| this.runConformTest( |
| new String[] { |
| "FooContainer.java", |
| "public @interface FooContainer {\n" + |
| " Foo[] value();\n" + |
| "}\n", |
| "Foo.java", |
| "@java.lang.annotation.Repeatable(FooContainer.class) public @interface Foo {\n" + |
| "}\n" |
| }, |
| ""); |
| // This changes FooContainer without re-checking Foo |
| this.runConformTest( |
| new String[] { |
| "FooContainer.java", |
| "public @interface FooContainer {\n" + |
| " int[] value();\n" + |
| "}\n" |
| }, |
| "", |
| null, |
| false, |
| null); |
| this.runNegativeTest( |
| new String[] { |
| "X.java", |
| "@Foo @Foo public class X { /* Problem since Foo now uses FooContainer which doesn't work anymore*/\n" + |
| "}\n" |
| }, |
| "----------\n" + |
| "1. ERROR in X.java (at line 1)\n" + |
| " @Foo @Foo public class X { /* Problem since Foo now uses FooContainer which doesn\'t work anymore*/\n" + |
| " ^^^^\n" + |
| "The value method in the container annotation type @FooContainer must be of type Foo[] but is int[]\n" + |
| "----------\n", |
| null, false /* don't flush*/); |
| } |
| |
| // 412151: The @Retention meta-annotation of TC must at least include the retention of T () |
| // Base example, both targets are specified |
| public void test016() { |
| this.runNegativeTest( |
| new String[] { |
| "Foo.java", |
| "import java.lang.annotation.Retention;\n" + |
| "import java.lang.annotation.RetentionPolicy;\n" + |
| "@Retention(RetentionPolicy.SOURCE)\n" + |
| "@interface FooContainer { Foo[] value(); }\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@Retention(RetentionPolicy.RUNTIME)\n" + |
| "@interface Foo { }\n" |
| }, |
| "----------\n" + |
| "1. ERROR in Foo.java (at line 5)\n" + |
| " @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| " ^^^^^^^^^^^^^^^^^^\n" + |
| "Retention \'RUNTIME\' of @Foo is longer than the retention of its container annotation type @FooContainer, which is \'SOURCE\'\n" + |
| "----------\n"); |
| } |
| |
| // 412151: The @Retention meta-annotation of TC must at least include the retention of T () |
| // Only specified on FooContainer |
| public void test017() { |
| this.runNegativeTest( |
| new String[] { |
| "Foo.java", |
| "import java.lang.annotation.Retention;\n" + |
| "import java.lang.annotation.RetentionPolicy;\n" + |
| "@Retention(RetentionPolicy.SOURCE)\n" + |
| "@interface FooContainer { Foo[] value(); }\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@interface Foo { }\n" |
| }, |
| "----------\n" + |
| "1. ERROR in Foo.java (at line 5)\n" + |
| " @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| " ^^^^^^^^^^^^^^^^^^\n" + |
| "Retention \'CLASS\' of @Foo is longer than the retention of its container annotation type @FooContainer, which is \'SOURCE\'\n" + |
| "----------\n"); |
| } |
| |
| // 412151: The @Retention meta-annotation of TC must at least include the retention of T () |
| // Only specified on Foo |
| public void test018() { |
| this.runNegativeTest( |
| new String[] { |
| "Foo.java", |
| "import java.lang.annotation.Retention;\n" + |
| "import java.lang.annotation.RetentionPolicy;\n" + |
| "@interface FooContainer { Foo[] value(); }\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@Retention(RetentionPolicy.RUNTIME)\n" + |
| "@interface Foo { }\n" |
| }, |
| "----------\n" + |
| "1. ERROR in Foo.java (at line 4)\n" + |
| " @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| " ^^^^^^^^^^^^^^^^^^\n" + |
| "Retention \'RUNTIME\' of @Foo is longer than the retention of its container annotation type @FooContainer, which is \'CLASS\'\n" + |
| "----------\n"); |
| } |
| |
| // 412151: The @Retention meta-annotation of TC must at least include the retention of T () |
| // Only specified on Foo - but positive |
| public void test019() { |
| this.runConformTest( |
| new String[] { |
| "Foo.java", |
| "import java.lang.annotation.Retention;\n" + |
| "import java.lang.annotation.RetentionPolicy;\n" + |
| "@interface FooContainer { Foo[] value(); }\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@Retention(RetentionPolicy.SOURCE)\n" + |
| "@interface Foo { }\n" |
| }); |
| } |
| |
| // 412151: The @Retention meta-annotation of TC must at least include the retention of T |
| // Only specified on FooContainer, separate compilation |
| public void test020() { |
| this.runConformTest( |
| new String[] { |
| "FooContainer.java", |
| "import java.lang.annotation.Retention;\n" + |
| "import java.lang.annotation.RetentionPolicy;\n" + |
| "@Retention(RetentionPolicy.SOURCE)\n" + |
| "public @interface FooContainer { Foo[] value(); }\n", |
| "Foo.java", |
| "import java.lang.annotation.Retention;\n" + |
| "import java.lang.annotation.RetentionPolicy;\n" + |
| "@Retention(RetentionPolicy.SOURCE)\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "public @interface Foo { }\n" |
| }); |
| this.runNegativeTest( |
| new String[] { |
| "Foo.java", |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "public @interface Foo { } // If omitted, retention is class\n" |
| }, |
| "----------\n" + |
| "1. ERROR in Foo.java (at line 1)\n" + |
| " @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| " ^^^^^^^^^^^^^^^^^^\n" + |
| "Retention \'CLASS\' of @Foo is longer than the retention of its container annotation type @FooContainer, which is \'SOURCE\'\n" + |
| "----------\n", |
| null, false /* don't flush*/); |
| } |
| |
| // 412151: The @Retention meta-annotation of TC must at least include the retention of T () |
| // Only specified on Foo, separate compilation |
| public void test021() { |
| this.runConformTest( |
| new String[] { |
| "FooContainer.java", |
| "import java.lang.annotation.Retention;\n" + |
| "import java.lang.annotation.RetentionPolicy;\n" + |
| "public @interface FooContainer { Foo[] value(); }\n", |
| "Foo.java", |
| "import java.lang.annotation.Retention;\n" + |
| "import java.lang.annotation.RetentionPolicy;\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "public @interface Foo { }\n" |
| }); |
| this.runNegativeTest( |
| new String[] { |
| "Foo.java", |
| "import java.lang.annotation.Retention;\n" + |
| "import java.lang.annotation.RetentionPolicy;\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@Retention(RetentionPolicy.RUNTIME)\n" + |
| "@interface Foo { }\n" |
| }, |
| "----------\n" + |
| "1. ERROR in Foo.java (at line 3)\n" + |
| " @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| " ^^^^^^^^^^^^^^^^^^\n" + |
| "Retention \'RUNTIME\' of @Foo is longer than the retention of its container annotation type @FooContainer, which is \'CLASS\'\n" + |
| "----------\n", |
| null, false /* don't flush*/); |
| } |
| |
| // 412151: TC's @Targets, if specified, must be a subset or the same as T's @Targets |
| // TC's @Targets, if specified, must be a subset or the same as T's @Targets. Simple test |
| public void test022() { |
| this.runNegativeTest( |
| new String[] { |
| "FooContainer.java", |
| "import java.lang.annotation.Target;\n" + |
| "import java.lang.annotation.ElementType;\n" + |
| "public @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})\n" + |
| "@interface FooContainer { Foo[] value(); }\n", |
| "Foo.java", |
| "import java.lang.annotation.Target;\n" + |
| "import java.lang.annotation.ElementType;\n" + |
| "public @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@Target({ElementType.FIELD})\n" + |
| "@interface Foo { }\n" |
| }, |
| "----------\n" + |
| "1. ERROR in Foo.java (at line 3)\n" + |
| " public @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| " ^^^^^^^^^^^^^^^^^^\n" + |
| "The container annotation type @FooContainer is allowed at targets where the repeatable annotation type @Foo is not: TYPE, METHOD\n" + |
| "----------\n"); |
| } |
| |
| // 412151: TC's @Targets, if specified, must be a subset or the same as T's @Targets |
| // TC's @Targets, if specified, must be a subset or the same as T's @Targets. Test this as a separate pass, so that |
| // FooContainer is loaded from binary. |
| public void test023() { |
| this.runConformTest( |
| new String[] { |
| "FooContainer.java", |
| "import java.lang.annotation.Target;\n" + |
| "import java.lang.annotation.ElementType;\n" + |
| "public @Target({ElementType.METHOD})\n" + |
| "@interface FooContainer { Foo[] value(); }\n", |
| "Foo.java", |
| "import java.lang.annotation.Target;\n" + |
| "import java.lang.annotation.ElementType;\n" + |
| "public @Target({ElementType.METHOD})\n" + |
| "@interface Foo { }\n" |
| }); |
| this.runNegativeTest( |
| new String[] { |
| "Foo.java", |
| "import java.lang.annotation.Target;\n" + |
| "import java.lang.annotation.ElementType;\n" + |
| "public @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@java.lang.annotation.Target({ElementType.FIELD})\n" + |
| "@interface Foo { }\n" |
| }, |
| "----------\n" + |
| "1. ERROR in Foo.java (at line 3)\n" + |
| " public @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| " ^^^^^^^^^^^^^^^^^^\n" + |
| "The container annotation type @FooContainer is allowed at targets where the repeatable annotation type @Foo is not: METHOD\n" + |
| "----------\n", |
| null, false /* don't flush*/); |
| } |
| |
| // 412151: TC's @Targets, if specified, must be a subset or the same as T's @Targets |
| // TC's may target ANNOTATION_TYPE but that should match TYPE for T, since it's a superset |
| public void test024() { |
| this.runConformTest( |
| new String[] { |
| "FooContainer.java", |
| "import java.lang.annotation.ElementType;\n" + |
| "@java.lang.annotation.Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})\n" + |
| "@interface FooContainer { Foo[] value(); }\n", |
| "Foo.java", |
| "import java.lang.annotation.ElementType;\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@java.lang.annotation.Target({ElementType.METHOD, ElementType.TYPE})\n" + |
| "@interface Foo { }\n" |
| }); |
| } |
| |
| // 412151: TC's @Targets, if specified, must be a subset or the same as T's @Targets |
| // Test that all ElementTypes can be reported |
| public void test025() { |
| this.runNegativeTest( |
| new String[] { |
| "FooContainer.java", |
| "import java.lang.annotation.Target;\n" + |
| "import java.lang.annotation.ElementType;\n" + |
| "public @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE, ElementType.ANNOTATION_TYPE, ElementType.PACKAGE, ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})\n" + |
| "@interface FooContainer { Foo[] value(); }\n", |
| "Foo.java", |
| "import java.lang.annotation.Target;\n" + |
| "import java.lang.annotation.ElementType;\n" + |
| "public @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@Target({})\n" + |
| "@interface Foo { }\n" |
| }, |
| "----------\n" + |
| "1. ERROR in Foo.java (at line 3)\n" + |
| " public @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| " ^^^^^^^^^^^^^^^^^^\n" + |
| "The container annotation type @FooContainer is allowed at targets where the repeatable annotation type @Foo is not: TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE\n" + |
| "----------\n"); |
| } |
| |
| // 412151: TC's @Targets, if specified, must be a subset or the same as T's @Targets |
| // TC's has no @Targets (=every SE7 location), but @Foo has, then complain. |
| public void test026() { |
| this.runConformTest( |
| new String[] { |
| "FooContainer.java", |
| "@interface FooContainer { Foo[] value(); }\n", |
| "Foo.java", |
| "@interface Foo { }\n" |
| }); |
| this.runNegativeTest( |
| new String[] { |
| "Foo.java", |
| "import java.lang.annotation.Target;\n" + |
| "import java.lang.annotation.ElementType;\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@java.lang.annotation.Target({ElementType.FIELD})\n" + |
| "@interface Foo { }\n" |
| }, |
| "----------\n" + |
| "1. ERROR in Foo.java (at line 3)\n" + |
| " @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| " ^^^^^^^^^^^^^^^^^^\n" + |
| "The container annotation type @FooContainer is allowed at targets where the repeatable annotation type @Foo is not: TYPE, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE\n" + |
| "----------\n", |
| null, false /* don't flush*/); |
| } |
| |
| // 412151: If T is @Documented, then TC should also be Documented |
| public void test027() { |
| this.runConformTest( |
| new String[] { |
| "FooContainer.java", |
| "@java.lang.annotation.Documented @interface FooContainer { Foo[] value(); }\n", |
| "Foo.java", |
| "@java.lang.annotation.Documented @interface Foo { }\n"}); |
| } |
| |
| // 412151: If T is @Documented, then TC should also be Documented, OK for TC to be documented while T is not |
| public void test028() { |
| this.runConformTest( |
| new String[] { |
| "FooContainer.java", |
| "@java.lang.annotation.Documented @interface FooContainer { Foo[] value(); }\n", |
| "Foo.java", |
| "@interface Foo { }\n"}); |
| } |
| |
| // 412151: If T is @Documented, then TC should also be Documented |
| public void test029() { |
| this.runNegativeTest( |
| new String[] { |
| "FooContainer.java", |
| "@interface FooContainer { Foo[] value(); }\n", |
| "Foo.java", |
| "@java.lang.annotation.Repeatable(FooContainer.class) @java.lang.annotation.Documented\n" + |
| "@interface Foo { }\n" |
| }, |
| "----------\n" + |
| "1. ERROR in Foo.java (at line 1)\n" + |
| " @java.lang.annotation.Repeatable(FooContainer.class) @java.lang.annotation.Documented\n" + |
| " ^^^^^^^^^^^^^^^^^^\n" + |
| "The repeatable annotation type @Foo is marked @Documented, but its container annotation type @FooContainer is not\n" + |
| "----------\n"); |
| } |
| |
| // 412151: If T is @Documented, then TC should also be Documented - check from previous compilation |
| public void test030() { |
| this.runConformTest( |
| new String[] { |
| "FooContainer.java", |
| "@java.lang.annotation.Documented @interface FooContainer { Foo[] value(); }\n", |
| "Foo.java", |
| "@java.lang.annotation.Documented @interface Foo { }\n" |
| }); |
| this.runConformTest( |
| new String[] { |
| "Foo.java", |
| "public @java.lang.annotation.Documented @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@interface Foo { }\n" |
| }, |
| "", |
| null, |
| false, |
| null); |
| } |
| |
| // 412151: If T is @Inherited, then TC should also be Inherited |
| public void test031() { |
| this.runConformTest( |
| new String[] { |
| "FooContainer.java", |
| "@java.lang.annotation.Inherited @interface FooContainer { Foo[] value(); }\n", |
| "Foo.java", |
| "@java.lang.annotation.Inherited @interface Foo { }\n"}); |
| } |
| |
| // 412151: If T is @Inherited, then TC should also be Inherited, OK for TC to be inherited while T is not. |
| public void test032() { |
| this.runConformTest( |
| new String[] { |
| "FooContainer.java", |
| "@java.lang.annotation.Inherited @interface FooContainer { Foo[] value(); }\n", |
| "Foo.java", |
| "@interface Foo { }\n"}); |
| } |
| // 412151: If T is @Inherited, then TC should also be Inherited |
| public void test033() { |
| this.runNegativeTest( |
| new String[] { |
| "FooContainer.java", |
| "@interface FooContainer { Foo[] value(); }\n", |
| "Foo.java", |
| "@java.lang.annotation.Repeatable(FooContainer.class) @java.lang.annotation.Inherited\n" + |
| "@interface Foo { }\n" |
| }, |
| "----------\n" + |
| "1. ERROR in Foo.java (at line 1)\n" + |
| " @java.lang.annotation.Repeatable(FooContainer.class) @java.lang.annotation.Inherited\n" + |
| " ^^^^^^^^^^^^^^^^^^\n" + |
| "The repeatable annotation type @Foo is marked @Inherited, but its container annotation type @FooContainer is not\n" + |
| "----------\n"); |
| } |
| |
| // 412151: If T is @Inherited, then TC should also be Inherited - check from previous compilation |
| public void test034() { |
| this.runConformTest( |
| new String[] { |
| "FooContainer.java", |
| "@java.lang.annotation.Inherited @interface FooContainer { Foo[] value(); }\n", |
| "Foo.java", |
| "@java.lang.annotation.Inherited @interface Foo { }\n" |
| }); |
| this.runConformTest( |
| new String[] { |
| "Foo.java", |
| "public @java.lang.annotation.Inherited @java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@interface Foo { }\n" |
| }, |
| "", |
| null, |
| false, |
| null); |
| } |
| // 412151: Ensure no double reporting for bad target. |
| public void test035() { |
| this.runNegativeTest( |
| new String[] { |
| "X.java", |
| "import java.lang.annotation.ElementType;\n" + |
| "import java.lang.annotation.Repeatable;\n" + |
| "import java.lang.annotation.Target;\n" + |
| "@Target(ElementType.FIELD)\n" + |
| "@interface TC {\n" + |
| " T [] value();\n" + |
| "}\n" + |
| "@Target(ElementType.TYPE)\n" + |
| "@Repeatable(TC.class)\n" + |
| "@interface T {\n" + |
| "}\n" + |
| "@T @T // we used to double report here.\n" + |
| "public class X { \n" + |
| " X f;\n" + |
| "}\n" |
| }, |
| "----------\n" + |
| "1. ERROR in X.java (at line 9)\n" + |
| " @Repeatable(TC.class)\n" + |
| " ^^^^^^^^\n" + |
| "The container annotation type @TC is allowed at targets where the repeatable annotation type @T is not: FIELD\n" + |
| "----------\n" + |
| "2. ERROR in X.java (at line 12)\n" + |
| " @T @T // we used to double report here.\n" + |
| " ^^\n" + |
| "The annotation @T cannot be repeated at this location since its container annotation type @TC is disallowed at this location\n" + |
| "----------\n"); |
| } |
| // 412149: [1.8][compiler] Emit repeated annotations into the designated container |
| public void test036() { |
| this.runConformTest( |
| new String[] { |
| "X.java", |
| "import java.lang.annotation.Repeatable;\n" + |
| "import java.lang.annotation.Retention;\n" + |
| "import static java.lang.annotation.RetentionPolicy.*;\n" + |
| "\n" + |
| "@Retention(RUNTIME)\n" + |
| "@interface AttrContainer {\n" + |
| " public Attr[] value();\n" + |
| "}\n" + |
| "@Retention(RUNTIME)\n" + |
| "@Repeatable(AttrContainer.class)\n" + |
| "@interface Attr {\n" + |
| " public int value() default -1;\n" + |
| "}\n" + |
| "\n" + |
| "@Attr(1) @Attr(2)\n" + |
| "public class X {\n" + |
| " public static void main(String args[]) {\n" + |
| " Object e[] = X.class.getAnnotationsByType(Attr.class);\n" + |
| " for (int i=0; i<e.length;++i) System.out.print(e[i] + \" \");\n" + |
| " }\n" + |
| "}" |
| }, |
| "@Attr(value=1) @Attr(value=2)"); |
| |
| } |
| // 412149: [1.8][compiler] Emit repeated annotations into the designated container |
| // Test that only repetitions go into the container |
| public void test037() { |
| this.runConformTest( |
| new String[] { |
| "X.java", |
| "import java.lang.annotation.Repeatable;\n" + |
| "import java.lang.annotation.Retention;\n" + |
| "import static java.lang.annotation.RetentionPolicy.*;\n" + |
| "\n" + |
| "@Retention(RUNTIME)\n" + |
| "@interface AttrContainer {\n" + |
| " public Attr[] value();\n" + |
| "}\n" + |
| "@Retention(RUNTIME)\n" + |
| "@Repeatable(AttrContainer.class)\n" + |
| "@interface Attr {\n" + |
| " public int value() default -1;\n" + |
| "}\n" + |
| "\n" + |
| "public class X {\n" + |
| " @Attr(1) class Y1 {}\n" + |
| " @Attr(1) @Attr(2) class Y2 {} \n" + |
| " public static void main(String args[]) {\n" + |
| " System.out.println(\"Y1: \" + Y1.class.getAnnotation(Attr.class));\n" + |
| " System.out.println(\"Y2: \" + Y2.class.getAnnotation(Attr.class));\n" + |
| " System.out.println(\"Y1: \" + Y1.class.getAnnotation(AttrContainer.class));\n" + |
| " System.out.println(\"Y2: \" + Y2.class.getAnnotation(AttrContainer.class));\n" + |
| " }\n" + |
| "}" |
| }, |
| "Y1: @Attr(value=1)\n" + |
| "Y2: null\n" + |
| "Y1: null\n" + |
| "Y2: @AttrContainer(value=[@Attr(value=1), @Attr(value=2)])"); |
| |
| } |
| // 412149: [1.8][compiler] Emit repeated annotations into the designated container |
| // Test that the retention from the containing annotation is used |
| public void test038() { |
| this.runConformTest( |
| new String[] { |
| "X.java", |
| "import java.lang.annotation.Repeatable;\n" + |
| "import java.lang.annotation.Retention;\n" + |
| "import static java.lang.annotation.RetentionPolicy.*;\n" + |
| "\n" + |
| "@Retention(RUNTIME)\n" + |
| "@interface AttrContainer {\n" + |
| " public Attr[] value();\n" + |
| "}\n" + |
| "@Retention(SOURCE)\n" + |
| "@Repeatable(AttrContainer.class)\n" + |
| "@interface Attr {\n" + |
| " public int value() default -1;\n" + |
| "}\n" + |
| "\n" + |
| "public class X {\n" + |
| " @Attr(1) class Y1 {}\n" + |
| " @Attr(1) @Attr(2) class Y2 {} \n" + |
| " public static void main(String args[]) {\n" + |
| " System.out.println(\"Y1 has \" + Y1.class.getAnnotationsByType(Attr.class).length);\n" + |
| " System.out.println(\"Y2 has \" + Y2.class.getAnnotationsByType(Attr.class).length);\n" + |
| " }\n" + |
| "}" |
| }, |
| "Y1 has 0\n" + |
| "Y2 has 2"); |
| |
| } |
| // 412149: [1.8][compiler] Emit repeated annotations into the designated container |
| // Test that repeated annotations can appear at package targets |
| public void test039() throws Exception { |
| String[] testFiles = { |
| "repeatable/Main.java", |
| "package repeatable;\n" + |
| "public class Main {\n" + |
| " public static void main (String[] argv) {\n" + |
| " };\n" + |
| "}", |
| |
| "repeatable/FooContainer.java", |
| "package repeatable;\n" + |
| "@java.lang.annotation.Target(java.lang.annotation.ElementType.PACKAGE)\n" + |
| "@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)\n" + |
| "public @interface FooContainer {\n" + |
| " Foo[] value();\n" + |
| "}\n", |
| |
| "repeatable/Foo.java", |
| "package repeatable;\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "public @interface Foo {}\n", |
| |
| "repeatable/package-info.java", |
| "@Foo @Foo\n" + |
| "package repeatable;\n" + |
| "import repeatable.Foo;", |
| }; |
| runConformTest(testFiles, ""); |
| String expectedOutout = |
| " RuntimeVisibleAnnotations: \n" + |
| " #8 @repeatable.FooContainer(\n" + |
| " #9 value=[\n" + |
| " annotation value =\n" + |
| " #10 @repeatable.Foo(\n" + |
| " )\n" + |
| " annotation value =\n" + |
| " #10 @repeatable.Foo(\n" + |
| " )\n" + |
| " ]\n" + |
| " )\n"; |
| checkDisassembledClassFile(OUTPUT_DIR + File.separator + "repeatable" + File.separator + "package-info.class", "package-info", expectedOutout, ClassFileBytesDisassembler.SYSTEM); |
| } |
| // 412149: [1.8][compiler] Emit repeated annotations into the designated container |
| // Test that repeated annotations show up on fields, methods, and parameters |
| public void test040() { |
| this.runConformTest( |
| new String[] { |
| "X.java", |
| "import java.lang.reflect.Field;\n" + |
| "import java.lang.reflect.Method;\n" + |
| "import java.lang.reflect.Parameter;\n" + |
| "import java.lang.annotation.Repeatable;\n" + |
| "import java.lang.annotation.Retention;\n" + |
| "import static java.lang.annotation.RetentionPolicy.*;\n" + |
| "\n" + |
| "@Retention(RUNTIME)\n" + |
| "@interface AttrContainer {\n" + |
| " public Attr[] value();\n" + |
| "}\n" + |
| "@Retention(RUNTIME)\n" + |
| "@Repeatable(AttrContainer.class)\n" + |
| "@interface Attr {\n" + |
| " public int value() default -1;\n" + |
| "}\n" + |
| "\n" + |
| "public class X {\n" + |
| " @Attr(1) @Attr(2) public int field;\n" + |
| "\n" + |
| " @Attr(3) @Attr(4)\n" + |
| " public static void main(@Attr(5) @Attr(6) String args[]) throws Exception {\n" + |
| " Field fieldField = X.class.getField(\"field\");\n" + |
| " dump(fieldField.getAnnotationsByType(Attr.class));\n" + |
| " Method mainMethod = X.class.getMethod(\"main\", (new String[0]).getClass());\n" + |
| " dump(mainMethod.getAnnotationsByType(Attr.class));\n" + |
| " Parameter argvParameter = mainMethod.getParameters()[0];\n" + |
| " dump(argvParameter.getAnnotationsByType(Attr.class));\n" + |
| " }\n" + |
| " static void dump(Attr[] attrs) {\n" + |
| " for (int i=0; i<attrs.length;++i) System.out.print(attrs[i] + \" \");\n" + |
| " }\n" + |
| "}" |
| }, |
| "@Attr(value=1) @Attr(value=2) @Attr(value=3) @Attr(value=4) @Attr(value=5) @Attr(value=6)"); |
| } |
| // Test that repeated annotations show up type parameters properly. |
| public void testTypeParameters() { |
| this.runConformTest( |
| new String[] { |
| "X.java", |
| "import java.lang.annotation.Annotation;\n" + |
| "import java.lang.annotation.ElementType;\n" + |
| "import java.lang.annotation.Repeatable;\n" + |
| "import java.lang.annotation.Retention;\n" + |
| "import java.lang.annotation.Target;\n" + |
| "import java.lang.reflect.AnnotatedElement;\n" + |
| "import java.lang.reflect.AnnotatedType;\n" + |
| "import java.lang.reflect.Field;\n" + |
| "import java.lang.reflect.Method;\n" + |
| "import java.lang.reflect.Type;\n" + |
| "import java.lang.reflect.TypeVariable;\n" + |
| "\n" + |
| "import static java.lang.annotation.RetentionPolicy.*;\n" + |
| "\n" + |
| "@Retention(RUNTIME)\n" + |
| "@Target({ElementType.TYPE_USE, ElementType.TYPE, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER,})\n" + |
| "@interface TC {\n" + |
| " public T[] value();\n" + |
| "}\n" + |
| "@Retention(RUNTIME)\n" + |
| "@Repeatable(TC.class)\n" + |
| "@Target({ElementType.TYPE_USE, ElementType.TYPE, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.METHOD})\n" + |
| "@interface T {\n" + |
| " public int value() default -1;\n" + |
| "}\n" + |
| "\n" + |
| "interface I<@T(1) @T(2) K extends @T(3) @T(4) Object & java.lang.@T(5) @T(6) Comparable<?>> {\n" + |
| "}\n" + |
| "\n" + |
| "\n" + |
| "public class X {\n" + |
| " public static void main(String args[]) {\n" + |
| " Class<I> ci = I.class; \n" + |
| " printAnnotations(\"I.class\", ci);\n" + |
| " TypeVariable<Class<I>>[] typeParameters = ci.getTypeParameters();\n" + |
| " for (TypeVariable<?> t: typeParameters) {\n" + |
| " printAnnotations(t.getName(), t);\n" + |
| " AnnotatedType[] bounds = t.getAnnotatedBounds();\n" + |
| " for (AnnotatedType bound : bounds) {\n" + |
| " printAnnotations(bound.getType().getTypeName(), bound);\n" + |
| " }\n" + |
| " }\n" + |
| " }\n" + |
| " \n" + |
| " static void printAnnotations(String name, AnnotatedElement element) {\n" + |
| " int [] iterations = { 0, 1 };\n" + |
| " for (int i : iterations) {\n" + |
| " Class<? extends Annotation> annotation = i == 0 ? T.class : TC.class;\n" + |
| " for (int j: iterations) {\n" + |
| " Annotation [] annotations = j == 0 ? new Annotation [] { element.getAnnotation(annotation) } : element.getAnnotationsByType(annotation);\n" + |
| " if (annotations.length == 0 || (annotations.length == 1 && annotations[0] == null)) continue;\n" + |
| " System.out.print(name + (j == 0 ? \".getAnnotation(\" : \".getAnnotationByType(\") + annotation.getName() + \".class): \");\n" + |
| " for (Annotation a : annotations) {\n" + |
| " System.out.print(a + \" \");\n" + |
| " }\n" + |
| " System.out.println();\n" + |
| " }\n" + |
| " }\n" + |
| " }\n" + |
| "}\n" |
| |
| }, |
| "K.getAnnotationByType(T.class): @T(value=1) @T(value=2) \n" + |
| "K.getAnnotation(TC.class): @TC(value=[@T(value=1), @T(value=2)]) \n" + |
| "K.getAnnotationByType(TC.class): @TC(value=[@T(value=1), @T(value=2)]) \n" + |
| "java.lang.Object.getAnnotationByType(T.class): @T(value=3) @T(value=4) \n" + |
| "java.lang.Object.getAnnotation(TC.class): @TC(value=[@T(value=3), @T(value=4)]) \n" + |
| "java.lang.Object.getAnnotationByType(TC.class): @TC(value=[@T(value=3), @T(value=4)]) \n" + |
| "java.lang.Comparable<?>.getAnnotationByType(T.class): @T(value=5) @T(value=6) \n" + |
| "java.lang.Comparable<?>.getAnnotation(TC.class): @TC(value=[@T(value=5), @T(value=6)]) \n" + |
| "java.lang.Comparable<?>.getAnnotationByType(TC.class): @TC(value=[@T(value=5), @T(value=6)])", |
| null, |
| true, |
| new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution. |
| } |
| // Test that repeated annotations show up at various sites, both type use and declaration. |
| public void testVariousSites() { |
| this.runConformTest( |
| new String[] { |
| "X.java", |
| "import java.lang.annotation.Annotation;\n" + |
| "import java.lang.annotation.ElementType;\n" + |
| "import java.lang.annotation.Repeatable;\n" + |
| "import java.lang.annotation.Retention;\n" + |
| "import java.lang.annotation.Target;\n" + |
| "import java.lang.reflect.AnnotatedArrayType;\n" + |
| "import java.lang.reflect.AnnotatedElement;\n" + |
| "import java.lang.reflect.AnnotatedParameterizedType;\n" + |
| "import java.lang.reflect.AnnotatedType;\n" + |
| "import java.lang.reflect.Constructor;\n" + |
| "import java.lang.reflect.Field;\n" + |
| "import java.lang.reflect.Method;\n" + |
| "import java.lang.reflect.TypeVariable;\n" + |
| "\n" + |
| "import static java.lang.annotation.RetentionPolicy.*;\n" + |
| "\n" + |
| "@Retention(RUNTIME)\n" + |
| "@Target({ElementType.TYPE_USE, ElementType.TYPE, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.METHOD})\n" + |
| "@interface TC {\n" + |
| " public T[] value();\n" + |
| "}\n" + |
| "@Retention(RUNTIME)\n" + |
| "@Repeatable(TC.class)\n" + |
| "@Target({ElementType.TYPE_USE, ElementType.TYPE, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.METHOD})\n" + |
| "@interface T {\n" + |
| " public int value() default -1;\n" + |
| "}\n" + |
| "\n" + |
| "interface I {\n" + |
| "}\n" + |
| "\n" + |
| "@T(1) @T(2)\n" + |
| "public class X<@T(3) @T(4) K extends @T(5) @T(6) Object & java.lang.@T(7) @T(8) Comparable<?>, @T(9) @T(10) V> extends @T(11) @T(12) Object implements @T(13) @T(14) I {\n" + |
| " public @T(15) @T(16) X<@T(17) @T(18) String, @T(19) @T(20) Integer> field;\n" + |
| " @T(21) @T(22)\n" + |
| " public <@T(23) @T(24) Q> X @T(25) @T(26) [] method(@T(27) @T(28) X<K, V> this, \n" + |
| " @T(29) @T(30) X<@T(31) @T(32) String, String> that) throws @T(33) @T(34) NullPointerException {\n" + |
| " return null;\n" + |
| " }\n" + |
| " @T(35) @T(36)\n" + |
| " public X() {\n" + |
| " \n" + |
| " }\n" + |
| " @T(37) @T(48)\n" + |
| " public class MemberType {\n" + |
| " \n" + |
| " }\n" + |
| " \n" + |
| " public static void main(String args[]) {\n" + |
| " Class<X> xc = X.class; \n" + |
| " printAnnotations(\"Class: \" + \"X.class\", xc);\n" + |
| " TypeVariable<Class<X>>[] typeParameters = xc.getTypeParameters();\n" + |
| " for (TypeVariable<?> t: typeParameters) {\n" + |
| " printAnnotations(\"Type Parameter: \" + t.getName(), t);\n" + |
| " AnnotatedType[] bounds = t.getAnnotatedBounds();\n" + |
| " for (AnnotatedType bound : bounds) {\n" + |
| " printAnnotations(\"Type parameter bound: \" + bound.getType().getTypeName(), bound);\n" + |
| " }\n" + |
| " }\n" + |
| " AnnotatedType annotatedSuperclass = xc.getAnnotatedSuperclass();\n" + |
| " printAnnotations(\"Superclass: \" + annotatedSuperclass.getType().getTypeName(), annotatedSuperclass);\n" + |
| " \n" + |
| " AnnotatedType [] annotatedSuperInterfaces = xc.getAnnotatedInterfaces();\n" + |
| " printAnnotations(\"Superinterface: \" + annotatedSuperInterfaces[0].getType().getTypeName(), annotatedSuperInterfaces[0]);\n" + |
| " \n" + |
| " for (Field field: xc.getFields()) {\n" + |
| " printAnnotations(\"Field: \" + field.getName(), field);\n" + |
| " AnnotatedParameterizedType fType = (AnnotatedParameterizedType) field.getAnnotatedType();\n" + |
| " for (AnnotatedType typeArgumentType : fType.getAnnotatedActualTypeArguments())\n" + |
| " printAnnotations(\"Field Type argument: \" + typeArgumentType.getType().getTypeName(), typeArgumentType);\n" + |
| " \n" + |
| " }\n" + |
| " for (Method method: xc.getMethods()) {\n" + |
| " switch (method.getName()) {\n" + |
| " case \"method\" :\n" + |
| " printAnnotations(method.getName(), method);\n" + |
| " AnnotatedArrayType mType = (AnnotatedArrayType) method.getAnnotatedReturnType();\n" + |
| " printAnnotations(\"Method return type: \" + mType.getType().getTypeName(), mType);\n" + |
| " AnnotatedType mTypeEtype = mType.getAnnotatedGenericComponentType();\n" + |
| " printAnnotations(\"Method return type, element type: \" + mTypeEtype.getType().getTypeName(), mTypeEtype);\n" + |
| " TypeVariable<Method>[] typeParameters2 = method.getTypeParameters();\n" + |
| " for (TypeVariable<?> t: typeParameters2) {\n" + |
| " printAnnotations(\"Method Type Parameter: \" + t.getName(), t);\n" + |
| " }\n" + |
| " AnnotatedType annotatedReceiverType = method.getAnnotatedReceiverType();\n" + |
| " printAnnotations(\"Receiver: \", annotatedReceiverType);\n" + |
| " AnnotatedType[] annotatedParameterTypes = method.getAnnotatedParameterTypes();\n" + |
| " for (AnnotatedType annotatedParameterType : annotatedParameterTypes) {\n" + |
| " printAnnotations(\"Parameter: \", annotatedParameterType);\n" + |
| " }\n" + |
| " AnnotatedType[] annotatedExceptionTypes = method.getAnnotatedExceptionTypes();\n" + |
| " for (AnnotatedType annotatedType : annotatedExceptionTypes) {\n" + |
| " printAnnotations(\"Exception type: \", annotatedType);\n" + |
| " }\n" + |
| " break;\n" + |
| " }\n" + |
| " }\n" + |
| " for (Constructor<?> constructor : xc.getConstructors()) {\n" + |
| " printAnnotations(\"Constructor: \", constructor);\n" + |
| " }\n" + |
| " // don't know how to get member classes.\n" + |
| " }\n" + |
| " \n" + |
| " static void printAnnotations(String name, AnnotatedElement element) {\n" + |
| " int [] iterations = { 0, 1 };\n" + |
| " for (int i : iterations) {\n" + |
| " Class<? extends Annotation> annotation = i == 0 ? T.class : TC.class;\n" + |
| " for (int j: iterations) {\n" + |
| " Annotation [] annotations = j == 0 ? new Annotation [] { element.getAnnotation(annotation) } : element.getAnnotationsByType(annotation);\n" + |
| " if (annotations.length == 0 || (annotations.length == 1 && annotations[0] == null)) continue;\n" + |
| " System.out.print(name + (j == 0 ? \".getAnnotation(\" : \".getAnnotationByType(\") + annotation.getName() + \".class): \");\n" + |
| " for (Annotation a : annotations) {\n" + |
| " System.out.print(a + \" \");\n" + |
| " }\n" + |
| " System.out.println();\n" + |
| " }\n" + |
| " }\n" + |
| " }\n" + |
| "}\n" |
| |
| }, |
| "Class: X.class.getAnnotationByType(T.class): @T(value=1) @T(value=2) \n" + |
| "Class: X.class.getAnnotation(TC.class): @TC(value=[@T(value=1), @T(value=2)]) \n" + |
| "Class: X.class.getAnnotationByType(TC.class): @TC(value=[@T(value=1), @T(value=2)]) \n" + |
| "Type Parameter: K.getAnnotationByType(T.class): @T(value=3) @T(value=4) \n" + |
| "Type Parameter: K.getAnnotation(TC.class): @TC(value=[@T(value=3), @T(value=4)]) \n" + |
| "Type Parameter: K.getAnnotationByType(TC.class): @TC(value=[@T(value=3), @T(value=4)]) \n" + |
| "Type parameter bound: java.lang.Object.getAnnotationByType(T.class): @T(value=5) @T(value=6) \n" + |
| "Type parameter bound: java.lang.Object.getAnnotation(TC.class): @TC(value=[@T(value=5), @T(value=6)]) \n" + |
| "Type parameter bound: java.lang.Object.getAnnotationByType(TC.class): @TC(value=[@T(value=5), @T(value=6)]) \n" + |
| "Type parameter bound: java.lang.Comparable<?>.getAnnotationByType(T.class): @T(value=7) @T(value=8) \n" + |
| "Type parameter bound: java.lang.Comparable<?>.getAnnotation(TC.class): @TC(value=[@T(value=7), @T(value=8)]) \n" + |
| "Type parameter bound: java.lang.Comparable<?>.getAnnotationByType(TC.class): @TC(value=[@T(value=7), @T(value=8)]) \n" + |
| "Type Parameter: V.getAnnotationByType(T.class): @T(value=9) @T(value=10) \n" + |
| "Type Parameter: V.getAnnotation(TC.class): @TC(value=[@T(value=9), @T(value=10)]) \n" + |
| "Type Parameter: V.getAnnotationByType(TC.class): @TC(value=[@T(value=9), @T(value=10)]) \n" + |
| "Superclass: java.lang.Object.getAnnotationByType(T.class): @T(value=11) @T(value=12) \n" + |
| "Superclass: java.lang.Object.getAnnotation(TC.class): @TC(value=[@T(value=11), @T(value=12)]) \n" + |
| "Superclass: java.lang.Object.getAnnotationByType(TC.class): @TC(value=[@T(value=11), @T(value=12)]) \n" + |
| "Superinterface: I.getAnnotationByType(T.class): @T(value=13) @T(value=14) \n" + |
| "Superinterface: I.getAnnotation(TC.class): @TC(value=[@T(value=13), @T(value=14)]) \n" + |
| "Superinterface: I.getAnnotationByType(TC.class): @TC(value=[@T(value=13), @T(value=14)]) \n" + |
| "Field: field.getAnnotationByType(T.class): @T(value=15) @T(value=16) \n" + |
| "Field: field.getAnnotation(TC.class): @TC(value=[@T(value=15), @T(value=16)]) \n" + |
| "Field: field.getAnnotationByType(TC.class): @TC(value=[@T(value=15), @T(value=16)]) \n" + |
| "Field Type argument: java.lang.String.getAnnotationByType(T.class): @T(value=17) @T(value=18) \n" + |
| "Field Type argument: java.lang.String.getAnnotation(TC.class): @TC(value=[@T(value=17), @T(value=18)]) \n" + |
| "Field Type argument: java.lang.String.getAnnotationByType(TC.class): @TC(value=[@T(value=17), @T(value=18)]) \n" + |
| "Field Type argument: java.lang.Integer.getAnnotationByType(T.class): @T(value=19) @T(value=20) \n" + |
| "Field Type argument: java.lang.Integer.getAnnotation(TC.class): @TC(value=[@T(value=19), @T(value=20)]) \n" + |
| "Field Type argument: java.lang.Integer.getAnnotationByType(TC.class): @TC(value=[@T(value=19), @T(value=20)]) \n" + |
| "method.getAnnotationByType(T.class): @T(value=21) @T(value=22) \n" + |
| "method.getAnnotation(TC.class): @TC(value=[@T(value=21), @T(value=22)]) \n" + |
| "method.getAnnotationByType(TC.class): @TC(value=[@T(value=21), @T(value=22)]) \n" + |
| "Method return type: X[].getAnnotationByType(T.class): @T(value=25) @T(value=26) \n" + |
| "Method return type: X[].getAnnotation(TC.class): @TC(value=[@T(value=25), @T(value=26)]) \n" + |
| "Method return type: X[].getAnnotationByType(TC.class): @TC(value=[@T(value=25), @T(value=26)]) \n" + |
| "Method return type, element type: X.getAnnotationByType(T.class): @T(value=21) @T(value=22) \n" + |
| "Method return type, element type: X.getAnnotation(TC.class): @TC(value=[@T(value=21), @T(value=22)]) \n" + |
| "Method return type, element type: X.getAnnotationByType(TC.class): @TC(value=[@T(value=21), @T(value=22)]) \n" + |
| "Method Type Parameter: Q.getAnnotationByType(T.class): @T(value=23) @T(value=24) \n" + |
| "Method Type Parameter: Q.getAnnotation(TC.class): @TC(value=[@T(value=23), @T(value=24)]) \n" + |
| "Method Type Parameter: Q.getAnnotationByType(TC.class): @TC(value=[@T(value=23), @T(value=24)]) \n" + |
| "Receiver: .getAnnotationByType(T.class): @T(value=27) @T(value=28) \n" + |
| "Receiver: .getAnnotation(TC.class): @TC(value=[@T(value=27), @T(value=28)]) \n" + |
| "Receiver: .getAnnotationByType(TC.class): @TC(value=[@T(value=27), @T(value=28)]) \n" + |
| "Parameter: .getAnnotationByType(T.class): @T(value=29) @T(value=30) \n" + |
| "Parameter: .getAnnotation(TC.class): @TC(value=[@T(value=29), @T(value=30)]) \n" + |
| "Parameter: .getAnnotationByType(TC.class): @TC(value=[@T(value=29), @T(value=30)]) \n" + |
| "Exception type: .getAnnotationByType(T.class): @T(value=33) @T(value=34) \n" + |
| "Exception type: .getAnnotation(TC.class): @TC(value=[@T(value=33), @T(value=34)]) \n" + |
| "Exception type: .getAnnotationByType(TC.class): @TC(value=[@T(value=33), @T(value=34)]) \n" + |
| "Constructor: .getAnnotationByType(T.class): @T(value=35) @T(value=36) \n" + |
| "Constructor: .getAnnotation(TC.class): @TC(value=[@T(value=35), @T(value=36)]) \n" + |
| "Constructor: .getAnnotationByType(TC.class): @TC(value=[@T(value=35), @T(value=36)])", |
| null, |
| true, |
| new String [] { "-Ddummy" }); // Not sure, unless we force the VM to not be reused by passing dummy vm argument, the generated program aborts midway through its execution. |
| } |
| |
| // Test that bad container specifications are handled properly. |
| public void testBadContainerType() { |
| this.runNegativeTest( |
| new String[] { |
| "X.java", |
| "import java.lang.annotation.Repeatable;\n" + |
| "@Repeatable(X.class)\n" + |
| "@interface T {\n" + |
| " public int value() default -1;\n" + |
| "}\n" + |
| "public class X {\n" + |
| "}\n" |
| }, |
| "----------\n" + |
| "1. ERROR in X.java (at line 2)\n" + |
| " @Repeatable(X.class)\n" + |
| " ^^^^^^^\n" + |
| "Type mismatch: cannot convert from Class<X> to Class<? extends Annotation>\n" + |
| "----------\n"); |
| } |
| // Test unspecified target. |
| public void testUnspecifiedTarget() { |
| this.runNegativeTest( |
| new String[] { |
| "X.java", |
| "import java.lang.annotation.ElementType;\n" + |
| "import java.lang.annotation.Repeatable;\n" + |
| "import java.lang.annotation.Target;\n" + |
| "\n" + |
| "@Target(ElementType.TYPE_USE)\n" + |
| "@interface TC {\n" + |
| " T [] value();\n" + |
| "}\n" + |
| "\n" + |
| "@Repeatable(TC.class)\n" + |
| "@interface T {\n" + |
| "}\n" + |
| "\n" + |
| "@T @T\n" + |
| "public class X { \n" + |
| " X f;\n" + |
| "}\n" |
| }, |
| "----------\n" + |
| "1. ERROR in X.java (at line 10)\n" + |
| " @Repeatable(TC.class)\n" + |
| " ^^^^^^^^\n" + |
| "The container annotation type @TC is allowed at targets where the repeatable annotation type @T is not: TYPE_USE\n" + |
| "----------\n"); |
| } |
| // Test unspecified target. |
| public void testUnspecifiedTarget2() { |
| this.runNegativeTest( |
| new String[] { |
| "X.java", |
| "import java.lang.annotation.ElementType;\n" + |
| "import java.lang.annotation.Repeatable;\n" + |
| "import java.lang.annotation.Target;\n" + |
| "\n" + |
| "@Target(ElementType.TYPE_PARAMETER)\n" + |
| "@interface TC {\n" + |
| " T [] value();\n" + |
| "}\n" + |
| "\n" + |
| "@Repeatable(TC.class)\n" + |
| "@interface T {\n" + |
| "}\n" + |
| "\n" + |
| "@T @T\n" + |
| "public class X { \n" + |
| " X f;\n" + |
| "}\n" |
| }, |
| "----------\n" + |
| "1. ERROR in X.java (at line 10)\n" + |
| " @Repeatable(TC.class)\n" + |
| " ^^^^^^^^\n" + |
| "The container annotation type @TC is allowed at targets where the repeatable annotation type @T is not: TYPE_PARAMETER\n" + |
| "----------\n" + |
| "2. ERROR in X.java (at line 14)\n" + |
| " @T @T\n" + |
| " ^^\n" + |
| "The annotation @T cannot be repeated at this location since its container annotation type @TC is disallowed at this location\n" + |
| "----------\n"); |
| } |
| public void testDeprecation() { |
| this.runNegativeTest( |
| new String[] { |
| "TC.java", |
| "@Deprecated\n" + |
| "public @interface TC {\n" + |
| " public T[] value();\n" + |
| "}\n", |
| "T.java", |
| "@java.lang.annotation.Repeatable(TC.class)\n" + |
| "@interface T {\n" + |
| " public int value() default -1;\n" + |
| "}\n" + |
| "interface I<@T(1) @T(2) K> {\n" + |
| "}\n" |
| }, |
| "----------\n" + |
| "1. WARNING in T.java (at line 1)\n" + |
| " @java.lang.annotation.Repeatable(TC.class)\n" + |
| " ^^\n" + |
| "The type TC is deprecated\n" + |
| "----------\n" + |
| "2. ERROR in T.java (at line 5)\n" + |
| " interface I<@T(1) @T(2) K> {\n" + |
| " ^^\n" + |
| "Annotation types that do not specify explicit target element types cannot be applied here\n" + |
| "----------\n" + |
| "3. WARNING in T.java (at line 5)\n" + |
| " interface I<@T(1) @T(2) K> {\n" + |
| " ^^\n" + |
| "The type TC is deprecated\n" + |
| "----------\n" + |
| "4. ERROR in T.java (at line 5)\n" + |
| " interface I<@T(1) @T(2) K> {\n" + |
| " ^^\n" + |
| "Annotation types that do not specify explicit target element types cannot be applied here\n" + |
| "----------\n" + |
| "5. ERROR in T.java (at line 5)\n" + |
| " interface I<@T(1) @T(2) K> {\n" + |
| " ^^\n" + |
| "Annotation types that do not specify explicit target element types cannot be applied here\n" + |
| "----------\n"); |
| } |
| public void testDeprecation2() { // verify that deprecation warning does not show up when the deprecated element is used in the same file defining it. |
| this.runNegativeTest( |
| new String[] { |
| "T.java", |
| "@Deprecated\n" + |
| "@interface TC {\n" + |
| " public T[] value();\n" + |
| "}\n" + |
| "@java.lang.annotation.Repeatable(TC.class)\n" + |
| "@interface T {\n" + |
| " public int value() default -1;\n" + |
| "}\n" + |
| "interface I<@T(1) @T(2) K> {\n" + |
| "}\n" |
| }, |
| "----------\n" + |
| "1. ERROR in T.java (at line 9)\n" + |
| " interface I<@T(1) @T(2) K> {\n" + |
| " ^^\n" + |
| "Annotation types that do not specify explicit target element types cannot be applied here\n" + |
| "----------\n" + |
| "2. ERROR in T.java (at line 9)\n" + |
| " interface I<@T(1) @T(2) K> {\n" + |
| " ^^\n" + |
| "Annotation types that do not specify explicit target element types cannot be applied here\n" + |
| "----------\n" + |
| "3. ERROR in T.java (at line 9)\n" + |
| " interface I<@T(1) @T(2) K> {\n" + |
| " ^^\n" + |
| "Annotation types that do not specify explicit target element types cannot be applied here\n" + |
| "----------\n"); |
| } |
| |
| // 419209: [1.8] Repeating container annotations should be rejected in the presence of annotation it contains |
| public void testRepeatableWithContaining1() { |
| this.runNegativeTest( |
| false /* skipJavac */, |
| JavacTestOptions.Excuse.EclipseHasSomeMoreWarnings, |
| new String[] { |
| "A.java", |
| "@interface FooContainerContainer {\n" + |
| " public FooContainer[] value();\n" + |
| "}\n" + |
| "@java.lang.annotation.Repeatable(FooContainerContainer.class)\n" + |
| "@interface FooContainer {\n" + |
| " public Foo[] value();\n" + |
| "}\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@interface Foo {\n" + |
| " public int value() default -1;\n" + |
| "}\n" + |
| "@FooContainer({@Foo(1)}) @FooContainer({@Foo(2)}) @Foo(3) class A {}\n" |
| }, |
| "----------\n" + |
| "1. WARNING in A.java (at line 12)\n" + |
| " @FooContainer({@Foo(1)}) @FooContainer({@Foo(2)}) @Foo(3) class A {}\n" + |
| " ^^^^\n" + |
| "The repeatable annotation @Foo may not be present where its container annotation type @FooContainer is repeated\n" + |
| "----------\n"); |
| } |
| // 419209: [1.8] Repeating container annotations should be rejected in the presence of annotation it contains |
| public void testRepeatableWithContaining2() { |
| this.runNegativeTest( |
| false /* skipJavac */, |
| JavacTestOptions.Excuse.EclipseHasSomeMoreWarnings, |
| new String[] { |
| "A.java", |
| "@interface FooContainerContainer {\n" + |
| " public FooContainer[] value();\n" + |
| "}\n" + |
| "@java.lang.annotation.Repeatable(FooContainerContainer.class)\n" + |
| "@interface FooContainer {\n" + |
| " public Foo[] value();\n" + |
| "}\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@interface Foo {\n" + |
| " public int value() default -1;\n" + |
| "}\n" + |
| "@Foo(1) @FooContainer({@Foo(2)}) @FooContainer({@Foo(3)}) class A {}\n" |
| }, |
| "----------\n" + |
| "1. WARNING in A.java (at line 12)\n" + |
| " @Foo(1) @FooContainer({@Foo(2)}) @FooContainer({@Foo(3)}) class A {}\n" + |
| " ^^^^\n" + |
| "The repeatable annotation @Foo may not be present where its container annotation type @FooContainer is repeated\n" + |
| "----------\n"); |
| } |
| // 419209: [1.8] Repeating container annotations should be rejected in the presence of annotation it contains |
| public void testRepeatableWithContaining3() { |
| this.runNegativeTest( |
| false /* skipJavac */, |
| JavacTestOptions.Excuse.EclipseHasSomeMoreWarnings, |
| new String[] { |
| "A.java", |
| "@interface FooContainerContainer {\n" + |
| " public FooContainer[] value();\n" + |
| "}\n" + |
| "@java.lang.annotation.Repeatable(FooContainerContainer.class)\n" + |
| "@interface FooContainer {\n" + |
| " public Foo[] value();\n" + |
| "}\n" + |
| "@java.lang.annotation.Repeatable(FooContainer.class)\n" + |
| "@interface Foo {\n" + |
| " public int value() default -1;\n" + |
| "}\n" + |
| "@FooContainer({@Foo(2)}) @Foo(1) @FooContainer({@Foo(3)}) class A {}\n" |
| }, |
| "----------\n" + |
| "1. WARNING in A.java (at line 12)\n" + |
| " @FooContainer({@Foo(2)}) @Foo(1) @FooContainer({@Foo(3)}) class A {}\n" + |
| " ^^^^\n" + |
| "The repeatable annotation @Foo may not be present where its container annotation type @FooContainer is repeated\n" + |
| "----------\n"); |
| } |
| } |