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;'
         ])
     }
 }