blob: d31d6f7ebdfd7318e9412d89253cf2c4f76b621b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2016 Jesper S Moller and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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"
},
"");
Runner runner = new Runner();
runner.testFiles =
new String[] {
"X.java",
"@Foo @Foo public class X { /* Problem */\n" +
"}\n"
};
runner.expectedCompilerLog =
"----------\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";
runner.shouldFlushOutputDirectory = false;
runner.javacTestOptions = JavacTestOptions.JavacHasABug.JavacBug8044196;
runner.runNegativeTest();
}
// 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.Annotation;\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) class Y1 {}\n" +
" @Attr(1) @Attr(2) class Y2 {} \n" +
" public static void main(String args[]) {\n" +
" System.out.print(\"Y1: \" + normalizeAnnotation(Y1.class.getAnnotation(Attr.class)) + \"\\n\");\n" +
" System.out.print(\"Y2: \" + normalizeAnnotation(Y2.class.getAnnotation(Attr.class)) + \"\\n\");\n" +
" System.out.print(\"Y1: \" + normalizeAnnotation(Y1.class.getAnnotation(AttrContainer.class)) + \"\\n\");\n" +
" System.out.print(\"Y2: \" + normalizeAnnotation(Y2.class.getAnnotation(AttrContainer.class)) + \"\\n\");\n" +
" }\n" +
" static String normalizeAnnotation(Annotation a) {\n" +
" if (a == null) return null;\n" +
" String str = a.toString();\n" +
" str = str.replace(\"value={@\", \"value=[@\");\n" +
" str = str.replace(\")}\", \")]\");\n" +
" return str;\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(normalizeAnnotation(a) + \" \");\n" +
" }\n" +
" System.out.print(\"\\n\");\n" +
" }\n" +
" }\n" +
" }\n" +
" static String normalizeAnnotation(Annotation a) {\n" +
" if (a == null) return null;\n" +
" String str = a.toString();\n" +
" str = str.replace(\"value={@\", \"value=[@\");\n" +
" str = str.replace(\")}\", \")]\");\n" +
" return str;\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(normalizeAnnotation(a) + \" \");\n" +
" }\n" +
" System.out.print(\"\\n\");\n" +
" }\n" +
" }\n" +
" }\n" +
" static String normalizeAnnotation(Annotation a) {\n" +
" if (a == null) return null;\n" +
" String str = a.toString();\n" +
" str = str.replace(\"value={@\", \"value=[@\");\n" +
" str = str.replace(\")}\", \")]\");\n" +
" return str;\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");
}
}