You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2023/09/18 16:51:39 UTC
[groovy] branch master updated: GROOVY-11179: JSR 308: inline constants and variable scope for type anno
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new aae0d08b0b GROOVY-11179: JSR 308: inline constants and variable scope for type anno
aae0d08b0b is described below
commit aae0d08b0b58e347aaff64d6a2f0c5e32f27a0b2
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Sep 18 09:36:47 2023 -0500
GROOVY-11179: JSR 308: inline constants and variable scope for type anno
---
.../java/org/codehaus/groovy/ast/ClassNode.java | 4 +-
.../codehaus/groovy/classgen/ExtendedVerifier.java | 13 ++--
.../groovy/classgen/VariableScopeVisitor.java | 90 +++++++++++++++++++++-
.../codehaus/groovy/control/ResolveVisitor.java | 37 +++++----
src/test-resources/bugs/GROOVY-8228.groovy | 51 ++++++------
.../org/codehaus/groovy/ast/ClassNodeTest.java | 14 ++++
.../groovy/classgen/asm/TypeAnnotationsTest.groovy | 82 +++++++++++---------
7 files changed, 208 insertions(+), 83 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/ClassNode.java b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
index 187779bf5c..0b726ef719 100644
--- a/src/main/java/org/codehaus/groovy/ast/ClassNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
@@ -1616,8 +1616,8 @@ public class ClassNode extends AnnotatedNode {
}
public void addTypeAnnotation(final AnnotationNode annotation) {
- if (!isPrimaryClassNode() && !isRedirectNode() && isResolved()) {
- throw new GroovyBugError("Adding type annotation @" + annotation.getClassNode().getNameWithoutPackage() + " to non-primary, non-redirect node: " + getName());
+ if (!isRedirectNode() && (isResolved() || isPrimaryClassNode())) {
+ throw new GroovyBugError("Adding type annotation @" + annotation.getClassNode().getNameWithoutPackage() + " to non-redirect node: " + getName());
}
if (typeAnnotations == Collections.EMPTY_LIST) {
typeAnnotations = new ArrayList<>(3);
diff --git a/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java b/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
index 0389c1a4ed..a153b64233 100644
--- a/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java
@@ -103,19 +103,18 @@ public class ExtendedVerifier extends ClassCodeVisitorSupport {
AnnotationConstantsVisitor acv = new AnnotationConstantsVisitor();
acv.visitClass(node, this.source);
this.currentClass = node;
+ PackageNode packageNode = node.getPackage();
+ if (packageNode != null) {
+ visitAnnotations(packageNode, PACKAGE_TARGET);
+ }
if (node.isAnnotationDefinition()) {
visitAnnotations(node, ANNOTATION_TARGET);
} else {
visitAnnotations(node, TYPE_TARGET);
- visitTypeAnnotations(node);
- }
- PackageNode packageNode = node.getPackage();
- if (packageNode != null) {
- visitAnnotations(packageNode, PACKAGE_TARGET);
+ visitGenericsTypeAnnotations(node);
}
visitTypeAnnotations(node.getUnresolvedSuperClass());
- ClassNode[] interfaces = node.getInterfaces();
- for (ClassNode anInterface : interfaces) {
+ for (ClassNode anInterface : node.getInterfaces()) {
visitTypeAnnotations(anInterface);
}
if (node.isRecord()) {
diff --git a/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java b/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java
index 67338d8809..51d26ada89 100644
--- a/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/VariableScopeVisitor.java
@@ -20,18 +20,24 @@ package org.codehaus.groovy.classgen;
import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.DynamicVariable;
import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.GenericsType;
import org.codehaus.groovy.ast.InnerClassNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.PropertyNode;
import org.codehaus.groovy.ast.Variable;
import org.codehaus.groovy.ast.VariableScope;
+import org.codehaus.groovy.ast.expr.AnnotationConstantExpression;
+import org.codehaus.groovy.ast.expr.ArrayExpression;
import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.CastExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
@@ -134,6 +140,7 @@ public class VariableScopeVisitor extends ClassCodeVisitorSupport {
if (PlaceholderVisitor.isPlaceholder((ASTNode) variable)) {
return;
}
+ visitTypeReference(variable.getOriginType());
String scopeType = "scope";
String variableType = "variable";
@@ -283,7 +290,33 @@ public class VariableScopeVisitor extends ClassCodeVisitorSupport {
return variable;
}
- private static boolean isAnonymous(final ClassNode node) {
+ private void visitTypeVariables(final GenericsType[] types) {
+ for (GenericsType type : types) {
+ visitTypeReference(type.getType());
+ if (type.getLowerBound() != null) {
+ visitTypeReference(type.getLowerBound());
+ }
+ if (type.getUpperBounds() != null) {
+ for (ClassNode bound : type.getUpperBounds()) {
+ if (bound.getLineNumber() > 0) {
+ visitTypeReference(bound);
+ }
+ }
+ }
+ }
+ }
+
+ private void visitTypeReference(final ClassNode node) {
+ visitAnnotations(node.getTypeAnnotations());
+ if (node.isArray()) {
+ visitTypeReference(node.getComponentType());
+ } else if (node.getGenericsTypes() != null && !node.isGenericsPlaceHolder()
+ && (node.isRedirectNode() || (!node.isResolved() && !node.isPrimaryClassNode()))) {
+ visitTypeVariables(node.getGenericsTypes()); // "String" from "List<String> -> List<E>"
+ }
+ }
+
+ private boolean isAnonymous(final ClassNode node) {
return (node instanceof InnerClassNode && ((InnerClassNode) node).isAnonymous() && !node.isEnum());
}
@@ -373,6 +406,18 @@ public class VariableScopeVisitor extends ClassCodeVisitorSupport {
currentClass = node;
currentScope.setClassScope(node);
+ if (node.getGenericsTypes() != null) {
+ visitTypeVariables(node.getGenericsTypes());
+ }
+ ClassNode sc = node.getUnresolvedSuperClass();
+ if (sc != null && sc != ClassHelper.OBJECT_TYPE) {
+ visitTypeReference(sc);
+ }
+ for (ClassNode i : node.getUnresolvedInterfaces()) {
+ visitTypeReference(i);
+ }
+ // permitted subclasses exist in @Sealed annotations
+
super.visitClass(node);
if (recurseInnerClasses) {
for (Iterator<InnerClassNode> innerClasses = node.getInnerClasses(); innerClasses.hasNext(); ) {
@@ -385,6 +430,7 @@ public class VariableScopeVisitor extends ClassCodeVisitorSupport {
@Override
public void visitField(final FieldNode node) {
pushState(node.isStatic());
+ visitTypeReference(node.getOriginType());
super.visitField(node);
popState();
}
@@ -396,6 +442,12 @@ public class VariableScopeVisitor extends ClassCodeVisitorSupport {
popState();
}
+ @Override
+ protected void visitAnnotation(final AnnotationNode node) {
+ visitTypeReference(node.getClassNode());
+ super.visitAnnotation(node);
+ }
+
@Override
protected void visitConstructorOrMethod(final MethodNode node, final boolean isConstructor) {
pushState(node.isStatic());
@@ -403,6 +455,10 @@ public class VariableScopeVisitor extends ClassCodeVisitorSupport {
node.setVariableScope(currentScope);
visitAnnotations(node);
+ if (node.getGenericsTypes() != null) {
+ visitTypeVariables(node.getGenericsTypes());
+ }
+ visitTypeReference(node.getReturnType());
for (Parameter parameter : node.getParameters()) {
visitAnnotations(parameter);
}
@@ -414,6 +470,9 @@ public class VariableScopeVisitor extends ClassCodeVisitorSupport {
}
declare(parameter, node);
}
+ for (ClassNode e : node.getExceptions()) {
+ visitTypeReference(e);
+ }
visitClassCodeContainer(node.getCode());
popState();
@@ -463,6 +522,12 @@ public class VariableScopeVisitor extends ClassCodeVisitorSupport {
// expressions:
+ @Override
+ public void visitArrayExpression(final ArrayExpression expression) {
+ visitTypeReference(expression.getType());
+ super.visitArrayExpression(expression);
+ }
+
@Override
public void visitBinaryExpression(final BinaryExpression expression) {
super.visitBinaryExpression(expression);
@@ -472,6 +537,18 @@ public class VariableScopeVisitor extends ClassCodeVisitorSupport {
}
}
+ @Override
+ public void visitCastExpression(final CastExpression expression) {
+ visitTypeReference(expression.getType());
+ super.visitCastExpression(expression);
+ }
+
+ @Override
+ public void visitClassExpression(final ClassExpression expression) {
+ visitTypeReference(expression.getType());
+ super.visitClassExpression(expression);
+ }
+
@Override
public void visitClosureExpression(final ClosureExpression expression) {
pushState();
@@ -494,8 +571,17 @@ public class VariableScopeVisitor extends ClassCodeVisitorSupport {
popState();
}
+ @Override
+ public void visitConstantExpression(final ConstantExpression expression) {
+ if (expression instanceof AnnotationConstantExpression) {
+ visitTypeReference(expression.getType());
+ }
+ super.visitConstantExpression(expression);
+ }
+
@Override
public void visitConstructorCallExpression(final ConstructorCallExpression expression) {
+ if (!expression.isSpecialCall()) visitTypeReference(expression.getType());
boolean oldInSpecialCtorFlag = inSpecialConstructorCall;
inSpecialConstructorCall |= expression.isSpecialCall();
super.visitConstructorCallExpression(expression);
@@ -587,6 +673,8 @@ public class VariableScopeVisitor extends ClassCodeVisitorSupport {
expression.setImplicitThis(false);
expression.setMethod(method);
}
+ } else if (expression.getGenericsTypes() != null) {
+ visitTypeVariables(expression.getGenericsTypes());
}
super.visitMethodCallExpression(expression);
}
diff --git a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
index c2267323f7..8031113512 100644
--- a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
@@ -66,10 +66,12 @@ import org.codehaus.groovy.vmplugin.VMPluginFactory;
import org.objectweb.asm.Opcodes;
import java.lang.reflect.Modifier;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
@@ -1188,15 +1190,20 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
@Override
protected void visitAnnotation(final AnnotationNode node) {
- resolveOrFail(node.getClassNode(), " for annotation", node, true);
- // duplicates part of AnnotationVisitor because we cannot wait until later
- for (Map.Entry<String, Expression> entry : node.getMembers().entrySet()) {
- // resolve constant-looking expressions statically
- // do it now since they get transformed away later
- Expression value = transform(entry.getValue());
- value = transformInlineConstants(value);
- checkAnnotationMemberValue(value);
- entry.setValue(value);
+ Collection<AnnotationNode> collector = currentClass.getNodeMetaData(AnnotationNode[].class);
+ if (collector != null) {
+ collector.add(node); // GROOVY-11179: defer resolve and inlining
+ } else {
+ resolveOrFail(node.getClassNode(), " for annotation", node, true);
+ // duplicates part of AnnotationVisitor because we cannot wait until later
+ for (Map.Entry<String, Expression> entry : node.getMembers().entrySet()) {
+ // resolve constant-looking expressions statically
+ // do it now since they get transformed away later
+ Expression value = transform(entry.getValue());
+ value = transformInlineConstants(value);
+ checkAnnotationMemberValue(value);
+ entry.setValue(value);
+ }
}
}
@@ -1259,6 +1266,8 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
//
+ if (phase < 2) node.putNodeMetaData(AnnotationNode[].class, new LinkedHashSet<>());
+
if (!(node instanceof InnerClassNode) || Modifier.isStatic(node.getModifiers())) {
genericParameterNames = new HashMap<>();
}
@@ -1306,13 +1315,15 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
visitPackage(node.getPackage());
visitImports(node.getModule());
+ visitAnnotations(node);
+
+ @SuppressWarnings("unchecked") // grab the collected annotations and stop collecting
+ var headerAnnotations = (Set<AnnotationNode>) node.putNodeMetaData(AnnotationNode[].class, null);
node.visitContents(this);
visitObjectInitializerStatements(node);
-
- // GROOVY-10750: do last for inlining
- visitTypeAnnotations(node);
- visitAnnotations(node);
+ // GROOVY-10750, GROOVY-11179: resolve and inline
+ headerAnnotations.forEach(this::visitAnnotation);
}
currentClass = oldNode;
}
diff --git a/src/test-resources/bugs/GROOVY-8228.groovy b/src/test-resources/bugs/GROOVY-8228.groovy
index 9944a753bb..0c0031220e 100644
--- a/src/test-resources/bugs/GROOVY-8228.groovy
+++ b/src/test-resources/bugs/GROOVY-8228.groovy
@@ -31,12 +31,16 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME
@Retention(RUNTIME)
@interface JSR308 { }
-class JSR308BaseClass<T> {}
+abstract class JSR308Super<T> {}
interface JSR308Interface1<T> {}
-interface JSR308Interface2<T extends @JSR308 CharSequence> {}
+interface JSR308Interface2<T extends @JSR308 CharSequence> {}
+ class JSR308Permitted1 extends JSR308Class {}
+ class JSR308Permitted2<T> extends JSR308Class {}
-class JSR308Class extends @JSR308 JSR308BaseClass<@JSR308 List> implements @JSR308 JSR308Interface1<@JSR308 String>, @JSR308 JSR308Interface2<@JSR308 String> {
- @JSR308 private String name;
+sealed class JSR308Class extends @JSR308 JSR308Super<@JSR308 List> implements @JSR308 JSR308Interface1<@JSR308 String>, @JSR308 JSR308Interface2<@JSR308 String>
+ permits @JSR308 JSR308Permitted1, @JSR308 JSR308Permitted2
+{
+ @JSR308 private String name;
@JSR308 List<@JSR308 String> test(@JSR308 List<@JSR308 ? extends @JSR308 Object> list) throws @JSR308 IOException, @JSR308 java.sql.SQLException {
@JSR308 List<@JSR308 String> localVar = new @JSR308 ArrayList<@JSR308 String>();
@@ -65,37 +69,38 @@ class JSR308Class extends @JSR308 JSR308BaseClass<@JSR308 List> implements @JSR3
void test2(@JSR308 JSR308Class this) {}
}
-def jsr308Class = new JSR308Class();
-def list = new ArrayList<@JSR308 String>();
-list.addAll(["1", "2"]);
-def result = jsr308Class.test(list)
-assert ['1', '2', 'a', 'b'] == result
+def jsr308 = new JSR308Class()
+def result = jsr308.test(new ArrayList<@JSR308 String>(['1', '2']))
+assert result == ['1', '2', 'a', 'b']
-assert 'JSR308BaseClass<java.util.List>' == JSR308Class.class.getAnnotatedSuperclass().type.typeName
-assert ['JSR308Interface1<java.lang.String>', 'JSR308Interface2<java.lang.String>'] == JSR308Class.class.getAnnotatedInterfaces().collect(e -> e.type.typeName)
-Method testMethod = JSR308Class.class.getDeclaredMethods().find(e -> e.name == 'test')
-assert [IOException, SQLException] == testMethod.getAnnotatedExceptionTypes().collect(e -> e.type)
-assert 'java.util.List<java.lang.String>' == testMethod.getAnnotatedReturnType().type.typeName
+assert JSR308Class.annotatedSuperclass.type.typeName == 'JSR308Super<java.util.List>'
+assert JSR308Class.permittedSubclasses*.typeName == ['JSR308Permitted1', 'JSR308Permitted2']
+assert JSR308Class.annotatedInterfaces*.type*.typeName == ['JSR308Interface1<java.lang.String>', 'JSR308Interface2<java.lang.String>']
+
+Method testMethod = JSR308Class.declaredMethods.find(m -> m.name == 'test')
+assert testMethod.annotatedExceptionTypes*.type == [IOException, SQLException]
+assert testMethod.annotatedReturnType.type.typeName == 'java.util.List<java.lang.String>'
+
// 1)
-assert ['java.util.List<?>', 'java.util.List'].contains(testMethod.getAnnotatedParameterTypes().collect(e -> e.type.typeName).get(0))
+assert testMethod.annotatedParameterTypes.collect(t -> t.type.typeName)[0] in ['java.util.List', 'java.util.List<?>']
-Method test2Method = JSR308Class.class.getDeclaredMethods().find(e -> e.name == 'test2')
-assert JSR308Class.class == test2Method.getAnnotatedReceiverType().type
+Method test2Method = JSR308Class.declaredMethods.find(m -> m.name == 'test2')
+assert test2Method.annotatedReceiverType.type == JSR308Class
// 2)
-Parameter listParameter = testMethod.getParameters()[0]
-assert ['java.util.List<?>', 'java.util.List'].contains(listParameter.getAnnotatedType().type.typeName)
+Parameter listParameter = testMethod.parameters[0]
+assert listParameter.annotatedType.type.typeName in ['java.util.List', 'java.util.List<?>']
-Field nameField = JSR308Class.class.getDeclaredField('name');
-assert String.class == nameField.getAnnotatedType().type
+Field nameField = JSR308Class.getDeclaredField('name')
+assert nameField.annotatedType.type == String
// 3)
-TypeVariable tv = JSR308Interface2.class.getTypeParameters()[0]
-assert [CharSequence.class, null].contains(tv.getAnnotatedBounds().collect(e -> e.type).get(0))
+TypeVariable tv = JSR308Interface2.typeParameters[0]
+assert tv.getAnnotatedBounds().collect(e -> e.type)[0] in [CharSequence, null]
// the above 3 tests get different result when running in the different CI(travis-ci and teamcity)
// travis-ci succeeds: https://travis-ci.org/apache/groovy/builds/262506189
diff --git a/src/test/org/codehaus/groovy/ast/ClassNodeTest.java b/src/test/org/codehaus/groovy/ast/ClassNodeTest.java
index 51b9c7dd70..2091033a4b 100644
--- a/src/test/org/codehaus/groovy/ast/ClassNodeTest.java
+++ b/src/test/org/codehaus/groovy/ast/ClassNodeTest.java
@@ -18,6 +18,7 @@
*/
package org.codehaus.groovy.ast;
+import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.tools.GenericsUtils;
import org.junit.Before;
import org.junit.Test;
@@ -26,6 +27,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
@@ -82,6 +84,18 @@ public final class ClassNodeTest {
assertEquals("Package", "com.acme", packageNode.getPackageName());
}
+ @Test
+ public void testTypeAnnotations() {
+ var annotation = new AnnotationNode(ClassHelper.make(Deprecated.class));
+ // TYPE_USE annotations are recoreded as class annotations, not type annotations
+ assertThrows(GroovyBugError.class, () -> classNode.addTypeAnnotation(annotation));
+
+ ClassNode reference = classNode.getPlainNodeReference();
+ reference.addTypeAnnotation(annotation);
+ assertEquals(1, reference.getTypeAnnotations().size());
+ assertEquals(0, classNode.getTypeAnnotations().size());
+ }
+
@Test
public void testPermittedSubclasses() throws Exception {
assumeTrue(groovy.test.GroovyAssert.isAtLeastJdk("17.0"));
diff --git a/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
index bd54188368..4fe9a8b1c8 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
@@ -185,24 +185,28 @@ final class TypeAnnotationsTest extends AbstractBytecodeTestCase {
}
void testTypeAnnotationsForField1() {
- def bytecode = compile(classNamePattern: 'Foo', field: 'documents', imports + '''
- @Retention(RUNTIME) @Target(FIELD) @interface FieldAnno { }
- @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno0 { }
- @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno1 { }
- @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno2 { }
- @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno3 { }
+ def bytecode = compile(classNamePattern: 'Foo', field: 'foo', imports + '''
+ @Retention(RUNTIME) @Target(FIELD) @interface FieldAnno { String value() }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno0 { String value() }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno1 { String value() }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno2 { String value() }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno3 { String value() }
class Foo {
- public @FieldAnno Map<@TypeAnno0 ? extends @TypeAnno1 CharSequence, @TypeAnno2 List<@TypeAnno3 ?>> documents
+ public static final String FOO = "foo"
+ public @FieldAnno(value=Foo.FOO) Map<
+ @TypeAnno0(value=Foo.FOO) ? extends @TypeAnno1(value=Foo.FOO) CharSequence,
+ @TypeAnno2(value=Foo.FOO) List<@TypeAnno3(value=Foo.FOO) ?>
+ > foo
}
''')
assert bytecode.hasSequence([
- 'public Ljava/util/Map; documents',
- '@LFieldAnno;()',
- '@LTypeAnno0;() : FIELD, 0;',
- '@LTypeAnno1;() : FIELD, 0;*',
- '@LTypeAnno2;() : FIELD, 1;',
- '@LTypeAnno3;() : FIELD, 1;0;'
+ 'public Ljava/util/Map; foo',
+ '@LFieldAnno;(value="foo")',
+ '@LTypeAnno0;(value="foo") : FIELD, 0;',
+ '@LTypeAnno1;(value="foo") : FIELD, 0;*',
+ '@LTypeAnno2;(value="foo") : FIELD, 1;',
+ '@LTypeAnno3;(value="foo") : FIELD, 1;0;'
])
}
@@ -226,39 +230,43 @@ final class TypeAnnotationsTest extends AbstractBytecodeTestCase {
])
}
+ // GROOVY-11179
void testTypeAnnotationsForClass() {
- def bytecode = compile(classNamePattern: 'MyClass', imports + '''import java.rmi.Remote
- @Retention(RUNTIME) @Target(TYPE) @interface TypeAnno { }
+ def bytecode = compile(classNamePattern: 'Baz', imports + '''
+ @Retention(RUNTIME) @Target(TYPE) @interface TypeAnno { String value() }
@Retention(RUNTIME) @Target(TYPE_PARAMETER) @interface TypeParameterAnno1 { }
@Retention(RUNTIME) @Target(TYPE_PARAMETER) @interface TypeParameterAnno2 { }
- @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno0 { }
- @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno1 { }
- @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno2 { }
- @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno3 { }
- @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno4 { }
- @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno5 { }
- @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno6 { }
- @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno7 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno0 { String value() }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno1 { String value() }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno2 { String value() }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno3 { String value() }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno4 { String value() }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno5 { String value() }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno6 { String value() }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno7 { String value() }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeUseAnno8 { String value() }
- @TypeAnno @TypeUseAnno0 @TypeUseAnno1
- class MyClass<@TypeParameterAnno1 @TypeParameterAnno2 X, @TypeParameterAnno2 Y extends @TypeUseAnno2 File>
- extends @TypeUseAnno3 ArrayList<@TypeUseAnno4 X>
- implements @TypeUseAnno5 Remote, @TypeUseAnno6 List<@TypeUseAnno7 X> { }
+ @TypeAnno(value=Baz.VALUE) @TypeUseAnno0(value=Baz.VALUE) @TypeUseAnno1(value=Baz.VALUE)
+ class Baz<@TypeParameterAnno1 @TypeParameterAnno2 X, @TypeParameterAnno2 Y extends @TypeUseAnno2(value=Baz.VALUE) File>
+ extends @TypeUseAnno3(value=Baz.VALUE) ArrayList<@TypeUseAnno4(value=Baz.VALUE) X>
+ implements @TypeUseAnno5(value=Baz.VALUE) Serializable, @TypeUseAnno6(value=Baz.VALUE) List<@TypeUseAnno7(value=Baz.VALUE) X> {
+ public static final String VALUE = "foo"
+ }
''')
assert bytecode.hasSequence([
- 'public class MyClass extends java/util/ArrayList implements java/rmi/Remote java/util/List groovy/lang/GroovyObject {',
- '@LTypeAnno;()',
- '@LTypeUseAnno0;()',
- '@LTypeUseAnno1;()',
+ 'public class Baz extends java/util/ArrayList implements java/io/Serializable java/util/List groovy/lang/GroovyObject {',
+ '@LTypeAnno;(value="foo")',
+ '@LTypeUseAnno0;(value="foo")',
+ '@LTypeUseAnno1;(value="foo")',
'@LTypeParameterAnno1;() : CLASS_TYPE_PARAMETER 0, null',
'@LTypeParameterAnno2;() : CLASS_TYPE_PARAMETER 0, null',
'@LTypeParameterAnno2;() : CLASS_TYPE_PARAMETER 1, null',
- '@LTypeUseAnno2;() : CLASS_TYPE_PARAMETER_BOUND 1, 0, null',
- '@LTypeUseAnno3;() : CLASS_EXTENDS -1, null',
- '@LTypeUseAnno4;() : CLASS_EXTENDS -1, 0;',
- '@LTypeUseAnno5;() : CLASS_EXTENDS 0, null',
- '@LTypeUseAnno6;() : CLASS_EXTENDS 1, null',
- '@LTypeUseAnno7;() : CLASS_EXTENDS 1, 0;'
+ '@LTypeUseAnno2;(value="foo") : CLASS_TYPE_PARAMETER_BOUND 1, 0, null',
+ '@LTypeUseAnno3;(value="foo") : CLASS_EXTENDS -1, null',
+ '@LTypeUseAnno4;(value="foo") : CLASS_EXTENDS -1, 0;',
+ '@LTypeUseAnno5;(value="foo") : CLASS_EXTENDS 0, null',
+ '@LTypeUseAnno6;(value="foo") : CLASS_EXTENDS 1, null',
+ '@LTypeUseAnno7;(value="foo") : CLASS_EXTENDS 1, 0;'
])
}
}