/* * Copyright 2009-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.eclipse.jdt.groovy.core.tests.basic; import static org.eclipse.jdt.core.tests.util.GroovyUtils.isAtLeastGroovy; import static org.junit.Assume.assumeTrue; import org.junit.Ignore; import org.junit.Test; public final class AnnotationsTests extends GroovyCompilerTestSuite { public AnnotationsTests(long level) { super(level); } @Test public void testGroovyAnnotation() { String[] sources = { "Foo.groovy", "@interface A {}", "Bar.groovy", "@A class Bar {}" }; runConformTest(sources); } @Test // GRECLIPSE-697 public void testInlineDeclaration() { String[] sources = { "A.groovy", "@B\n"+ "class A { \n"+ " public static void main(String[]argv) {print 'abc';}\n"+ "}\n"+ "@interface B {\n"+ " String value() default \"\"\n"+ "}" }; runConformTest(sources, "abc"); } @Test public void testLongLiteral() { // ArrayIndexOutOfBoundsException in LongLiteral.computeConstant String[] sources = { "Min.java", "import java.lang.annotation.*;\n" + "@Target(ElementType.FIELD)\n" + "@interface Min {\n" + " long value();\n" + "}", "Main.groovy", "class Main {\n" + " @Min(0L)\n" + " Integer index\n" + "}" }; runConformTest(sources); } @Test public void testBigIntegerLiteral() { String[] sources = { "Min.java", "import java.lang.annotation.*;\n" + "@Target(ElementType.FIELD)\n" + "@interface Min {\n" + " long value();\n" + "}", "Main.groovy", "class Main {\n" + " @Min(0G)\n" + " Integer index\n" + "}" }; // there should not be an error from the Java model -- org.codehaus.jdt.groovy.internal.compiler.ast.GroovyCompilationUnitDeclaration.UnitPopulator.createConstantExpression(ConstantExpression) runNegativeTest(sources, "----------\n" + "1. ERROR in Main.groovy (at line 2)\n" + "\t@Min(0G)\n" + "\t ^" + (isAtLeastGroovy(20) ? "^" : "") + "\n" + "Groovy:Attribute 'value' should have type 'java.lang.Long'; but found type 'java.math.BigInteger' in @Min\n" + "----------\n"); } @Test public void testBigDecimalLiteral() { String[] sources = { "Min.java", "import java.lang.annotation.*;\n" + "@Target(ElementType.FIELD)\n" + "@interface Min {\n" + " double value();\n" + "}", "Main.groovy", "class Main {\n" + " @Min(1.1G)\n" + " BigDecimal index\n" + "}" }; // there should not be an error from the Java model -- org.codehaus.jdt.groovy.internal.compiler.ast.GroovyCompilationUnitDeclaration.UnitPopulator.createConstantExpression(ConstantExpression) runNegativeTest(sources, "----------\n" + "1. ERROR in Main.groovy (at line 2)\n" + "\t@Min(1.1G)\n" + "\t ^" + (isAtLeastGroovy(20) ? "^^^" : "") + "\n" + "Groovy:Attribute 'value' should have type 'java.lang.Double'; but found type 'java.math.BigDecimal' in @Min\n" + "----------\n"); } @Test public void testClassAnnotationValue() { String[] sources = { "Anno.java", "import java.lang.annotation.*;\n" + "@Retention(RetentionPolicy.RUNTIME)\n" + "@Target(ElementType.TYPE)\n" + "@interface Anno {\n" + " Class<?> value();\n" + "}", "Main.groovy", "@Anno(URL.class)\n" + "class Main {\n" + "}" }; runConformTest(sources); } @Test public void testClassAnnotationValue2() { String[] sources = { "Anno.java", "import java.lang.annotation.*;\n" + "@Retention(RetentionPolicy.RUNTIME)\n" + "@Target(ElementType.TYPE)\n" + "@interface Anno {\n" + " Class<?> value();\n" + "}", "Main.groovy", "@Anno(URL)\n" + "class Main {\n" + "}" }; runConformTest(sources); } @Test public void testClosureAnnotationValue() { String[] sources = { "Anno.java", "import java.lang.annotation.*;\n" + "@Retention(RetentionPolicy.RUNTIME)\n" + "@Target(ElementType.TYPE)\n" + "@interface Anno {\n" + " Class<?> value();\n" + "}", "Main.groovy", "@Anno(value={ println 'hello' })\n" + "class Main {\n" + "}" }; runConformTest(sources); } @Test // GRECLIPSE-629 public void testConstAnnotationValue() { String[] sources = { "Const.java", "public class Const {\n" + "static final String instance= \"abc\";\n" + " public static void main(String[] argv) {\n" + " System.out.println(XXX.class.getAnnotation(Anno.class));\n" + " }\n" + "}", "B.groovy", "import java.lang.annotation.*\n" + "@Anno(Const.instance)\n" + "class XXX {}\n" + "@Retention(RetentionPolicy.RUNTIME)\n" + "@interface Anno {\n" + " String value()\n" + "}" }; runConformTest(sources, "@Anno(value=abc)"); } @Test // GRECLIPSE-830 public void testDoubleAttributeWithBigDecimalValue() { String[] sources = { "AnnotationDouble.groovy", "import java.lang.annotation.*\n" + "@Target(ElementType.FIELD)\n" + "@Retention(RetentionPolicy.RUNTIME)\n" + "@interface AnnotationDouble {\n" + " String value()\n" + " double width() default 5.0d\n" + "}", "AnnotationDoubleTest.groovy", "class AnnotationDoubleTest {\n" + " class FooWithAnnotation {\n" + " @AnnotationDouble(value='test', width=1.0) double value\n" + " }\n" + " def test = new AnnotationDoubleTest()\n" + "}" }; runNegativeTest(sources, "----------\n" + "1. ERROR in AnnotationDoubleTest.groovy (at line 3)\n" + "\t@AnnotationDouble(value='test', width=1.0) double value\n" + "\t ^" + (isAtLeastGroovy(20) ? "^^" : "") + "\n" + "Groovy:Attribute 'width' should have type 'java.lang.Double'; but found type 'java.math.BigDecimal' in @AnnotationDouble\n" + "----------\n"); } @Test public void testLocalAnnotationConstant1() { // there was an error because the variable expression VALUE was not recognized as constant // see ResolveVisitor.transformInlineConstants(Expression) String[] sources = { "Main.groovy", "class Main {\n" + " public static final String VALUE = 'nls'\n" + " @SuppressWarnings(VALUE)\n" + " def method() {\n" + " }\n" + "}" }; runConformTest(sources); } @Test public void testLocalAnnotationConstant2() { // there was an error because the variable expression VALUE was not recognized as constant // see ResolveVisitor.transformInlineConstants(Expression) String[] sources = { "Main.groovy", "class Main {\n" + " public static final String VALUE = 'nls'\n" + " @SuppressWarnings(value = [VALUE])\n" + " def method() {\n" + " }\n" + "}" }; runConformTest(sources); } @Test public void testLocalAnnotationConstant3() { String[] sources = { "Main.groovy", "@SuppressWarnings(Main.VALUE)\n" + "class Main {\n" + " public static final String VALUE = 'nls'\n" + "}" }; runConformTest(sources); } @Test public void testLocalAnnotationConstant3a() { String[] sources = { "Main.groovy", "@SuppressWarnings(VALUE)\n" + "class Main {\n" + " public static final String VALUE = 'nls'\n" + "}" }; runNegativeTest(sources, "----------\n" + "1. ERROR in Main.groovy (at line 1)\r\n" + "\t@SuppressWarnings(VALUE)\r\n" + "\t ^^^^^\n" + "VALUE cannot be resolved\n" + "----------\n"); } @Test public void testLocalAnnotationClassLiteral() { String[] sources = { "Anno.java", "import java.lang.annotation.*;\n" + "@Target(ElementType.TYPE)\n" + "@interface Anno {\n" + " Class value();\n" + "}", "Main.groovy", "@Anno(Main.Inner)\n" + "class Main {\n" + " static class Inner {}\n" + "}" }; runConformTest(sources); } @Test public void testLocalAnnotationClassLiteral2() { String[] sources = { "Anno.java", "import java.lang.annotation.*;\n" + "@Target(ElementType.TYPE)\n" + "@interface Anno {\n" + " Class<?> value();\n" + "}", "Main.groovy", "@Anno(Inner)\n" + "class Main {\n" + " static class Inner {}\n" + "}" }; runNegativeTest(sources, "----------\n" + "1. ERROR in Main.groovy (at line 1)\r\n" + "\t@Anno(Inner)\r\n" + "\t ^^^^^\n" + "Inner cannot be resolved\n" + "----------\n" + "2. ERROR in Main.groovy (at line 1)\r\n" + "\t@Anno(Inner)\r\n" + "\t ^^^^^\n" + "Inner cannot be resolved or is not a field\n" + "----------\n"); } @Test public void testImportedAnnotationConstant1() { String[] sources = { "p/I.java", "package p;\n" + "public interface I {\n" + " static final String VALUE = \"nls\";\n" + "}", "Main.groovy", "import static p.I.VALUE\n" + "class Main {\n" + " @SuppressWarnings(VALUE)\n" + " def method() {\n" + " }\n" + "}" }; runConformTest(sources); } @Test public void testImportedAnnotationConstant2() { String[] sources = { "p/I.java", "package p;\n" + "public interface I {\n" + " static final String VALUE = \"nls\";\n" + "}", "Main.groovy", "import static p.I.VALUE\n" + "class Main {\n" + " @SuppressWarnings(value=[VALUE])\n" + " def method() {\n" + " }\n" + "}" }; runConformTest(sources); } @Test public void testAliasedAnnotationConstant1() { String[] sources = { "p/I.java", "package p;\n" + "public interface I {\n" + " static final String VALUE = \"xyz\";\n" + "}", "Main.groovy", "import static p.I.VALUE as FOO\n" + "class Main {\n" + " @SuppressWarnings(FOO)\n" + " def method() {\n" + " }\n" + "}" }; runConformTest(sources); } @Test public void testAliasedAnnotationConstant2() { String[] sources = { "p/I.java", "package p;\n" + "public interface I {\n" + " static final String VALUE = \"xyz\";\n" + "}", "Main.groovy", "import static p.I.VALUE as FOO\n" + "class Main {\n" + " @SuppressWarnings(value=[FOO])\n" + " def method() {\n" + " }\n" + "}" }; runConformTest(sources); } @Test public void testAliasedAnnotationClassLiteral() { String[] sources = { "Anno.java", "import java.lang.annotation.*;\n" + "@Target(ElementType.TYPE)\n" + "@interface Anno {\n" + " Class<?> value();\n" + "}", "Main.groovy", "import java.lang.Class as Trash\n" + "@Anno(Trash)\n" + "class Main {\n" + "}" }; runConformTest(sources); } @Test public void testAliasedAnnotationClassLiteral2() { String[] sources = { "Anno.java", "import java.lang.annotation.*;\n" + "@Target(ElementType.TYPE)\n" + "@interface Anno {\n" + " Class<?> value();\n" + "}", "Main.groovy", "import java.util.regex.Pattern\n" + "import java.util.regex.Pattern as Regex\n" + "@Anno(Regex)\n" + "class Main {\n" + "}" }; runConformTest(sources); } @Test public void testAliasedAnnotationClassLiteral3() { String[] sources = { "Anno.java", "import java.lang.annotation.*;\n" + "@Target(ElementType.TYPE)\n" + "@interface Anno {\n" + " Class<?> value();\n" + "}", "p/Outer.java", "package p;\n" + "public class Outer { public static class Inner { } }", "Main.groovy", "import p.Outer as Retou\n" + "@Anno(Retou.Inner)\n" + "class Main {\n" + "}" }; runConformTest(sources); } @Test public void testTargetMetaAnnotation() { String[] sources = { "Anno.java", "import java.lang.annotation.*;\n" + "@Target(ElementType.METHOD)\n" + "@interface Anno {\n" + "}", "Bar.groovy", "@Anno class Bar {}" }; runNegativeTest(sources, "----------\n" + "1. ERROR in Bar.groovy (at line 1)\n" + "\t@Anno class Bar {}\n" + "\t ^^^^\n" + "Groovy:Annotation @Anno is not allowed on element TYPE\n" + "----------\n"); } @Test public void testTypeLevelAnnotations01() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno\n"+ "public class X {\n" + " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno {}\n" }; runConformTest(sources, "success"); checkGCUDeclaration("X.groovy", "public @Anno class X {"); checkDisassemblyFor("p/X.class", "@p.Anno\n" + "public class p.X implements groovy.lang.GroovyObject {\n"); } @Test public void testMethodLevelAnnotations() { String[] sources = { "p/X.groovy", "package p;\n" + "public class X {\n" + " @Anno\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno {}\n" }; runConformTest(sources, "success"); String expectedOutput = "public static @Anno void main(public String... argv) {"; checkGCUDeclaration("X.groovy",expectedOutput); expectedOutput = //" // Method descriptor #46 ([Ljava/lang/String;)V\n" + " // Stack: 3, Locals: 2\n" + " @p.Anno\n" + " public static void main(java.lang.String... argv);\n"; checkDisassemblyFor("p/X.class", expectedOutput); } @Test public void testFieldLevelAnnotations01() { String[] sources = { "p/X.groovy", "package p;\n" + "public class X {\n" + " @Anno\n"+ " String s\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno {}\n" }; runConformTest(sources, "success"); String expectedOutput = "private @Anno String s;"; checkGCUDeclaration("X.groovy",expectedOutput); expectedOutput = //" // Field descriptor #11 Ljava/lang/String;\n" + " @p.Anno\n" + " private java.lang.String s;\n"; checkDisassemblyFor("p/X.class", expectedOutput); } @Test public void testFieldLevelAnnotations_classRetention() { String[] sources = { "p/X.groovy", "package p;\n" + "public class X {\n" + " @Anno\n"+ " String s\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno {}\n" }; runConformTest(sources, "success"); String expectedOutput = //" // Field descriptor #11 Ljava/lang/String;\n" + " @p.Anno\n" + " private java.lang.String s;\n"; checkDisassemblyFor("p/X.class", expectedOutput); } @Test public void testFieldLevelAnnotations_sourceRetention() { String[] sources = { "p/X.groovy", "package p;\n" + "public class X {\n" + " @Anno\n"+ " String s\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.SOURCE)\n"+ "@interface Anno {}\n" }; runConformTest(sources, "success"); String expectedOutput = //" // Field descriptor #9"+ descriptor number varies across compilers (1.6/1.7) "Ljava/lang/String;\n" + " private java.lang.String s;\n"; checkDisassemblyFor("p/X.class", expectedOutput); } @Test public void testFieldLevelAnnotations_defaultRetention() { String[] sources = { "p/X.groovy", "package p;\n" + "public class X {\n" + " @Anno\n"+ " String s\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "@interface Anno {}\n" }; runConformTest(sources, "success"); String expectedOutput = // " // Field descriptor #9 "+ // descriptor number varies across compiler versions "Ljava/lang/String;\n" + " private java.lang.String s;\n"; checkDisassemblyFor("p/X.class", expectedOutput); } @Test public void testFieldLevelAnnotations_delegate() { String[] sources = { "Bar.groovy", "class Bar {\n"+ " public void m() {\n"+ "Object o = new Other().me;\n"+ "}}", "Other.groovy", "public class Other {\n" + " public @Anno Date me\n"+ "}\n", "Anno.java", "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno {}\n" }; runConformTest(sources); checkGCUDeclaration("Other.groovy", "public class Other {\n" + " public @Anno Date me;\n" + " public Other() {\n" + " }\n" + "}\n"); } @Test public void testConstructorLevelAnnotations01() { String[] sources = { "p/X.groovy", "package p;\n" + "public class X {\n" + " @Anno\n"+ " X(String s) {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno {}\n" }; runConformTest(sources, "success"); String expectedOutput = "public @Anno X(public String s) {"; checkGCUDeclaration("X.groovy", expectedOutput); expectedOutput = " // Stack: 2, Locals: 4\n" + " @p.Anno\n" + " public X(java.lang.String s);\n"; checkDisassemblyFor("p/X.class", expectedOutput); } @Test public void testAnnotations04_defaultParamMethods() { String[] sources = { "p/X.groovy", "package p;\n" + "public class X {\n" + " @Anno\n"+ " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno {}\n" }; runConformTest(sources, "success"); String expectedOutput = "public @Anno void foo() {"; checkGCUDeclaration("X.groovy",expectedOutput); expectedOutput = "public @Anno void foo(public String s) {"; checkGCUDeclaration("X.groovy",expectedOutput); } @Test public void testTypeLevelAnnotations_SingleMember() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(Target.class)\n"+ "public class X {\n" + " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno { Class<?> value(); }\n", "p/Target.java", "package p;\n"+ "class Target { }" }; runConformTest(sources, "success"); String expectedOutput = "public @Anno(Target.class) class X"; checkGCUDeclaration("X.groovy",expectedOutput); } @Test public void testTypeLevelAnnotations_SingleMember02() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(p.Target.class)\n"+ "public class X {\n" + " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno { Class<?> value(); }\n", "p/Target.java", "package p;\n"+ "class Target { }" }; runConformTest(sources, "success"); String expectedOutput = "public @Anno(p.Target.class) class X"; checkGCUDeclaration("X.groovy", expectedOutput); } @Test public void testMethodLevelAnnotations_SingleMember() { String[] sources = { "p/X.groovy", "package p;\n" + "public class X {\n" + " @Anno(Target.class)\n"+ " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno { Class<?> value(); }\n", "p/Target.java", "package p;\n"+ "class Target { }" }; runConformTest(sources, "success"); String expectedOutput = "public @Anno(Target.class) void foo(public String s) {"; checkGCUDeclaration("X.groovy",expectedOutput); } @Test public void testMethodLevelAnnotations_SingleMember02() { String[] sources = { "p/X.groovy", "package p;\n" + "public class X {\n" + " @Anno(p.Target.class)\n"+ " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno { Class<?> value(); }\n", "p/Target.java", "package p;\n"+ "class Target { }" }; runConformTest(sources, "success"); String expectedOutput = "public @Anno(p.Target.class) void foo(public String s) {"; checkGCUDeclaration("X.groovy",expectedOutput); } @Test public void testFieldLevelAnnotations_SingleMember() { String[] sources = { "p/X.groovy", "package p;\n" + "public class X {\n" + " @Anno(Target.class)\n"+ " public int foo = 5\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno { Class<?> value(); }\n", "p/Target.java", "package p;\n"+ "class Target { }" }; runConformTest(sources, "success"); String expectedOutput = "public @Anno(Target.class) int foo"; checkGCUDeclaration("X.groovy",expectedOutput); } @Test public void testAnnotations_singleMemberAnnotationField() { String[] sources = { "p/X.groovy", "package p;\n" + "public class X {\n" + " @Anno(p.Target.class)\n"+ " public int foo = 5\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno { Class<?> value(); }\n", "p/Target.java", "package p;\n"+ "class Target { }" }; runConformTest(sources, "success"); String expectedOutput = "public @Anno(p.Target.class) int foo"; checkGCUDeclaration("X.groovy",expectedOutput); } @Test public void testAnnotations_singleMemberAnnotationFailure() { String[] sources = { "p/X.groovy", "package p;\n" + "public class X {\n" + " @Anno(IDontExist.class)\n"+ " public int foo = 5\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno { Class<?> value(); }\n" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.groovy (at line 3)\n" + "\t@Anno(IDontExist.class)\n" + "\t ^^^^^^^^^^\n" + "Groovy:unable to find class 'IDontExist.class' for annotation attribute constant\n" + "----------\n" + "2. ERROR in p\\X.groovy (at line 3)\n" + "\t@Anno(IDontExist.class)\n" + "\t ^"+(isAtLeastGroovy(20)?"^^^^^^^^^^^^^^^":"")+"\n" + "Groovy:Only classes and closures can be used for attribute 'value' in @p.Anno\n" + "----------\n"); } @Test public void testAnnotationCollector() { assumeTrue(isAtLeastGroovy(21)); String[] sources = { "Type.groovy", "@Alias(includes='id')\n"+ "class Type {\n" + " String id\n" + " String hidden = '456'\n" + " \n" + " static void main(String[] args) {\n" + " print(new Type(id:'123'))\n" + " }\n" + "}", "Alias.groovy", "import groovy.transform.*\n" + "@AnnotationCollector\n" + "@EqualsAndHashCode\n" + "@ToString\n" + "@interface Alias { }", }; runConformTest(sources); } @Test public void testAnnotationCollector2() { assumeTrue(isAtLeastGroovy(21)); String[] sources = { "Type.groovy", "@Alias(includes='id')\n"+ "class Type {\n" + " String id\n" + " String hidden = '456'\n" + " \n" + " static void main(String[] args) {\n" + " print(new Type(id:'123'))\n" + " }\n" + "}", "Alias.groovy", "import groovy.transform.*\n" + "@AnnotationCollector([EqualsAndHashCode, ToString])\n" + "@interface Alias { }", }; runConformTest(sources, "Type(123)"); } @Test // All types in groovy with TYPE specified for Target and obeyed public void testAnnotationsTargetType() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(p.Foo.class)\n"+ "public class X {\n" + " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.groovy", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@Target([ElementType.TYPE])\n"+ "@interface Anno { Class<?> value(); }\n", "p/Foo.groovy", "package p;\n"+ "class Foo { }" }; runConformTest(sources, "success"); } @Test // All groovy but annotation can only be put on METHOD - that is violated by class X public void testAnnotationsTargetType02() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(p.Foo.class)\n"+ "public class X {\n" + " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.groovy", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@Target([ElementType.METHOD])\n"+ "@interface Anno { Class<?> value(); }\n", "p/Foo.groovy", "package p;\n"+ "class Foo { }" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.groovy (at line 2)\n" + "\t@Anno(p.Foo.class)\n" + "\t ^^^^\n" + "Groovy:Annotation @p.Anno is not allowed on element TYPE\n" + "----------\n"); } @Test // All groovy but annotation can only be put on FIELD - that is violated by class X public void testAnnotationsTargetType03() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(p.Foo.class)\n"+ "public class X {\n" + " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.groovy", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@Target([ElementType.FIELD])\n"+ "@interface Anno { Class<?> value(); }\n", "p/Foo.groovy", "package p;\n"+ "class Foo { }" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.groovy (at line 2)\n" + "\t@Anno(p.Foo.class)\n" + "\t ^^^^\n" + "Groovy:Annotation @p.Anno is not allowed on element TYPE\n" + "----------\n"); } @Test // All groovy but annotation can only be put on FIELD or METHOD - that is violated by class X public void testAnnotationsTargetType04() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(p.Foo.class)\n"+ "public class X {\n" + " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.groovy", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@Target([ElementType.FIELD,ElementType.METHOD])\n"+ "@interface Anno { Class<?> value(); }\n", "p/Foo.groovy", "package p;\n"+ "class Foo { }" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.groovy (at line 2)\n" + "\t@Anno(p.Foo.class)\n" + "\t ^^^^\n" + "Groovy:Annotation @p.Anno is not allowed on element TYPE\n" + "----------\n"); } @Test // Two types in groovy, one in java with TYPE specified for Target and obeyed public void testAnnotationsTargetType05() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(p.Foo.class)\n"+ "public class X {\n" + " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.groovy", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@Target([ElementType.TYPE])\n"+ "@interface Anno { Class<?> value(); }\n", "p/Foo.java", "package p;\n"+ "class Foo { }" }; runConformTest(sources, "success"); } @Test // 2 groovy, 1 java but annotation can only be put on METHOD - that is violated by class X public void testAnnotationsTargetType06() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(p.Foo.class)\n"+ "public class X {\n" + " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.groovy", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@Target([ElementType.METHOD])\n"+ "@interface Anno { Class<?> value(); }\n", "p/Foo.java", "package p;\n"+ "class Foo { }" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.groovy (at line 2)\n" + "\t@Anno(p.Foo.class)\n" + "\t ^^^^\n" + "Groovy:Annotation @p.Anno is not allowed on element TYPE\n" + "----------\n"); } @Test // 2 groovy, 1 java but annotation can only be put on FIELD - that is violated by class X public void testAnnotationsTargetType07() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(p.Foo.class)\n"+ "public class X {\n" + " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.groovy", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@Target([ElementType.FIELD])\n"+ "@interface Anno { Class<?> value(); }\n", "p/Foo.groovy", "package p;\n"+ "class Foo { }" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.groovy (at line 2)\n" + "\t@Anno(p.Foo.class)\n" + "\t ^^^^\n" + "Groovy:Annotation @p.Anno is not allowed on element TYPE\n" + "----------\n"); } @Test // 2 groovy, 1 java but annotation can only be put on FIELD or METHOD - that is violated by class X public void testAnnotationsTargetType08() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(p.Foo.class)\n"+ "public class X {\n" + " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.groovy", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@Target([ElementType.FIELD,ElementType.METHOD])\n"+ "@interface Anno { Class<?> value(); }\n", "p/Foo.java", "package p;\n"+ "class Foo { }" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.groovy (at line 2)\n" + "\t@Anno(p.Foo.class)\n" + "\t ^^^^\n" + "Groovy:Annotation @p.Anno is not allowed on element TYPE\n" + "----------\n"); } @Test // 1 groovy, 2 java with TYPE specified for Target and obeyed public void testAnnotationsTargetType09() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(p.Foo.class)\n"+ "public class X {\n" + " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@Target({ElementType.TYPE})\n"+ "@interface Anno { Class<?> value(); }\n", "p/Foo.java", "package p;\n"+ "class Foo { }" }; runConformTest(sources, "success"); } @Test // 1 groovy, 2 java but annotation can only be put on METHOD - that is violated by class X public void testAnnotationsTargetType10() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(p.Foo.class)\n"+ "public class X {\n" + " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@Target({ElementType.METHOD})\n"+ "@interface Anno { Class<?> value(); }\n", "p/Foo.java", "package p;\n"+ "class Foo { }" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.groovy (at line 2)\n" + "\t@Anno(p.Foo.class)\n" + "\t ^^^^\n" + "Groovy:Annotation @p.Anno is not allowed on element TYPE\n" + "----------\n"); } @Test // 1 groovy, 2 java but annotation can only be put on FIELD - that is violated by class X public void testAnnotationsTargetType11() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(p.Foo.class)\n"+ "public class X {\n" + " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@Target({ElementType.FIELD})\n"+ "@interface Anno { Class<?> value(); }\n", "p/Foo.groovy", "package p;\n"+ "class Foo { }" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.groovy (at line 2)\n" + "\t@Anno(p.Foo.class)\n" + "\t ^^^^\n" + "Groovy:Annotation @p.Anno is not allowed on element TYPE\n" + "----------\n"); } @Test // 1 groovy, 2 java but annotation can only be put on FIELD or METHOD - that is violated by class X public void testAnnotationsTargetType12() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(p.Foo.class)\n"+ "public class X {\n" + " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@Target({ElementType.FIELD,ElementType.METHOD})\n"+ "@interface Anno { Class<?> value(); }\n", "p/Foo.java", "package p;\n"+ "class Foo { }" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.groovy (at line 2)\n" + "\t@Anno(p.Foo.class)\n" + "\t ^^^^\n" + "Groovy:Annotation @p.Anno is not allowed on element TYPE\n" + "----------\n"); } @Test // FIXASC groovy bug? Why didn't it complain that String doesn't meet the bound - at the moment letting JDT complain... public void testWildcards01() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(String.class)\n"+ "public class X {\n" + " public void foo(String s = \"abc\") {}\n"+ " public static void main(String[]argv) {\n"+ " print \"success\"\n"+ " }\n"+ "}\n", "p/Anno.groovy", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno { Class<? extends Number> value(); }\n" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.groovy (at line 2)\n" + "\t@Anno(String.class)\n" + "\t ^^^^^^^^^^^^^\n" + "Type mismatch: cannot convert from Class<String> to Class<? extends Number>\n" + "----------\n"); } @Test public void testWildcards02() { String[] sources = { "p/X.java", "package p;\n" + "@Anno(String.class)\n"+ "public class X {\n" + " public void foo(String s) {}\n"+ " public static void main(String[]argv) {\n"+ " System.out.println(\"success\");\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno { Class<? extends Number> value(); }\n" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.java (at line 2)\n" + "\t@Anno(String.class)\n" + "\t ^^^^^^^^^^^^\n" + "Type mismatch: cannot convert from Class<String> to Class<? extends Number>\n" + "----------\n"); } @Test public void testWildcards03() { String[] sources = { "p/X.java", "package p;\n" + "@Anno(String.class)\n"+ "public class X {\n" + " public void foo(String s) {}\n"+ " public static void main(String[]argv) {\n"+ " System.out.println(\"success\");\n"+ " }\n"+ "}\n", "p/Anno.groovy", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno { Class<? extends Number> value(); }\n" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.java (at line 2)\n" + "\t@Anno(String.class)\n" + "\t ^^^^^^^^^^^^\n" + "Type mismatch: cannot convert from Class<String> to Class<? extends Number>\n" + "----------\n"); } @Test public void testWildcards04() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(String.class)\n"+ "public class X {\n" + " public void foo(String s) {}\n"+ " public static void main(String[]argv) {\n"+ " System.out.println(\"success\");\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno { Class<? extends Number> value(); }\n" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.groovy (at line 2)\n" + "\t@Anno(String.class)\n" + "\t ^^^^^^^^^^^^^\n" + "Type mismatch: cannot convert from Class<String> to Class<? extends Number>\n" + "----------\n"); } @Test public void testWildcards05() { String[] sources = { "p/X.java", "package p;\n" + "@Anno(Integer.class)\n"+ "public class X {\n" + " public void foo(String s) {}\n"+ " public static void main(String[]argv) {\n"+ " System.out.println(\"success\");\n"+ " }\n"+ "}\n", "p/Anno.groovy", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno { Class<? extends Number> value(); }\n" }; runConformTest(sources, "success"); } @Test public void testWildcards06() { String[] sources = { "p/X.java", "package p;\n" + "@Anno(Number.class)\n"+ "public class X {\n" + " public void foo(String s) {}\n"+ " public static void main(String[]argv) {\n"+ " System.out.println(\"success\");\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno { Class<? super Integer> value(); }\n" }; runConformTest(sources, "success"); } @Test // bounds violation: String does not meet '? super Integer' public void testWildcards07() { String[] sources = { "p/X.groovy", "package p;\n" + "@Anno(String.class)\n"+ "public class X {\n" + " public static void main(String[]argv) {\n"+ " System.out.println(\"success\");\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "import java.lang.annotation.*;\n"+ "@Retention(RetentionPolicy.RUNTIME)\n"+ "@interface Anno { Class<? super Integer> value(); }\n" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.groovy (at line 2)\n" + "\t@Anno(String.class)\n" + "\t ^^^^^^^^^^^^^\n" + "Type mismatch: cannot convert from Class<String> to Class<? super Integer>\n" + "----------\n"); } @Test // double upper bounds public void testWildcards08() { String[] sources = { "p/X.java", "package p;\n" + "public class X {\n" + " public static void main(String[]argv) {\n"+ " Object o = new Wibble<Integer>().run();\n"+ " System.out.println(\"success\");\n"+ " }\n"+ "}\n", "p/Anno.java", "package p;\n"+ "class Wibble<T extends Number & I> { Class<T> run() { return null;} }\n", "p/I.java", "package p;\n"+ "interface I {}\n" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.java (at line 4)\n" + "\tObject o = new Wibble<Integer>().run();\n" + "\t ^^^^^^^\n" + "Bound mismatch: The type Integer is not a valid substitute for the bounded parameter <T extends Number & I> of the type Wibble<T>\n" + "----------\n"); } @Test // double upper bounds public void testWildcards09() { String[] sources = { "p/X.java", "package p;\n" + "public class X {\n" + " public static void main(String[]argv) {\n"+ " Object o = new Wibble<Integer>().run();\n"+ " System.out.println(\"success\");\n"+ " }\n"+ "}\n", "p/Anno.groovy", "package p;\n"+ "class Wibble<T extends Number & I> { Class<T> run() { return null;} }\n", "p/I.java", "package p;\n"+ "interface I {}\n" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.java (at line 4)\n" + "\tObject o = new Wibble<Integer>().run();\n" + "\t ^^^^^^^\n" + "Bound mismatch: The type Integer is not a valid substitute for the bounded parameter <T extends Number & I> of the type Wibble<T>\n" + "----------\n"); } @Test @Ignore("FIXASC groovy bug? Why does groovy not care about bounds violation?") public void testWildcards10() { String[] sources = { "p/X.groovy", "package p;\n" + "public class X {\n" + " public static void main(String[]argv) {\n"+ " Object o = new Wibble<Integer>().run();\n"+ " System.out.println(\"success\");\n"+ " }\n"+ "}\n", "p/Anno.groovy", "package p;\n"+ "class Wibble<T extends Number & I> { Class<T> run() { return null;} }\n", "p/I.java", "package p;\n"+ "interface I {}\n" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.java (at line 4)\n" + "\tObject o = new Wibble<Integer>().run();\n" + "\t ^^^^^^^\n" + "Bound mismatch: The type Integer is not a valid substitute for the bounded parameter <T extends Number & I> of the type Wibble<T>\n" + "----------\n"); } @Test public void testWildcards11() { String[] sources = { "p/X.groovy", "package p;\n" + "public class X extends Wibble<Foo> {\n" + " public static void main(String[]argv) {\n"+ " System.out.println(\"success\");\n"+ " }\n"+ "}\n", "p/Anno.groovy", "package p;\n"+ "class Wibble<T extends Number & I> { Class<T> run() { return null;} }\n", "p/I.java", "package p;\n"+ "interface I {}\n", "p/Foo.java", "package p;\n"+ "class Foo implements I {}\n" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.groovy (at line 2)\n" + "\tpublic class X extends Wibble<Foo> {\n" + "\t ^^^^^^\n" + "Groovy:The type Foo is not a valid substitute for the bounded parameter <T extends java.lang.Number & p.I>\n" + "----------\n"); } // FIXASC groovy bug? why doesn't it complain - the type parameter doesn't meet the secondary upper bound public void _testWildcards12() { String[] sources = { "p/X.groovy", "package p;\n" + "public class X extends Wibble<Integer> {\n" + " public static void main(String[]argv) {\n"+ " System.out.println(\"success\");\n"+ " }\n"+ "}\n", "p/Anno.groovy", "package p;\n"+ "class Wibble<T extends Number & I> { Class<T> run() { return null;} }\n", "p/I.java", "package p;\n"+ "interface I {}\n", "p/Foo.java", "package p;\n"+ "class Foo implements I {}\n" }; runNegativeTest(sources, "----------\n" + "1. ERROR in p\\X.groovy (at line 2)\n" + "\tpublic class X extends Wibble<Foo> {\n" + "\t ^^\n" + "Groovy:The type Foo is not a valid substitute for the bounded parameter <T extends java.lang.Number & p.I>\n" + "----------\n"); } }