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/10/25 16:00:12 UTC

[groovy] branch GROOVY_4_0_X updated: GROOVY-10750, GROOVY-11206: class header constant inlining

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
     new 5c7be2ff9b GROOVY-10750, GROOVY-11206: class header constant inlining
5c7be2ff9b is described below

commit 5c7be2ff9b42e2d3c6492e0a3db7506e77d70ed1
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Oct 25 09:51:29 2023 -0500

    GROOVY-10750, GROOVY-11206: class header constant inlining
    
    4_0_X backport
---
 .../codehaus/groovy/control/ResolveVisitor.java    |  58 ++--
 src/test/gls/annotations/AnnotationTest.groovy     |  61 +++-
 .../groovy/classgen/asm/TypeAnnotationsTest.groovy | 327 ++++++++++-----------
 3 files changed, 240 insertions(+), 206 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
index 70c1c35a09..2cbeeb3731 100644
--- a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
@@ -263,13 +263,6 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
         return source;
     }
 
-    @Override
-    public void visitMethod(final MethodNode node) {
-        super.visitMethod(node);
-        visitGenericsTypeAnnotations(node);
-        visitTypeAnnotations(node.getReturnType());
-    }
-
     @Override
     protected void visitConstructorOrMethod(final MethodNode node, final boolean isConstructor) {
         VariableScope oldScope = currentScope;
@@ -334,7 +327,9 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
     }
 
     private void resolveOrFail(final ClassNode type, final String msg, final ASTNode node, final boolean preferImports) {
-        visitTypeAnnotations(type);
+        if (type.isRedirectNode() || !type.isPrimaryClassNode()) {
+            visitTypeAnnotations(type); // JSR 308 support
+        }
         if (preferImports) {
             resolveGenericsTypes(type.getGenericsTypes());
             if (resolveAliasFromModule(type)) return;
@@ -997,13 +992,6 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
         visitAnnotations(ve);
         Variable v = ve.getAccessedVariable();
 
-        if(!(v instanceof DynamicVariable) && !checkingVariableTypeInDeclaration) {
-            /*
-             *  GROOVY-4009: when a normal variable is simply being used, there is no need to try to
-             *  resolve its type. Variable type resolve should proceed only if the variable is being declared.
-             */
-            return ve;
-        }
         if (v instanceof DynamicVariable) {
             String name = ve.getName();
             ClassNode t = ClassHelper.make(name);
@@ -1029,8 +1017,14 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
                 for (VariableScope scope = currentScope; scope != null && !scope.isRoot(); scope = scope.getParent()) {
                     if (scope.removeReferencedClassVariable(ve.getName()) == null) break;
                 }
-                return new ClassExpression(t);
+                ClassExpression ce = new ClassExpression(t);
+                ce.setSourcePosition(ve);
+                return ce;
             }
+        } else if (!checkingVariableTypeInDeclaration) {
+            // GROOVY-4009: When a normal variable is simply being used, there is no need to try to
+            // resolve its type. Variable type resolve should proceed only if the variable is being declared.
+            return ve;
         }
         resolveOrFail(ve.getType(), ve);
         ClassNode origin = ve.getOriginType();
@@ -1189,27 +1183,6 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
 
     private void visitTypeAnnotations(final ClassNode node) {
         visitAnnotations(node.getTypeAnnotations());
-        visitGenericsTypeAnnotations(node);
-    }
-
-    private void visitGenericsTypeAnnotations(final ClassNode node) {
-        GenericsType[] genericsTypes = node.getGenericsTypes();
-        if (node.isUsingGenerics() && genericsTypes != null) {
-            visitGenericsTypeAnnotations(genericsTypes);
-        }
-    }
-
-    private void visitGenericsTypeAnnotations(final MethodNode node) {
-        GenericsType[] genericsTypes = node.getGenericsTypes();
-        if (genericsTypes != null) {
-            visitGenericsTypeAnnotations(genericsTypes);
-        }
-    }
-
-    private void visitGenericsTypeAnnotations(final GenericsType[] genericsTypes) {
-        for (GenericsType gt : genericsTypes) {
-            visitTypeAnnotations(gt.getType());
-        }
     }
 
     @Override
@@ -1330,8 +1303,12 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
             // initialize scopes/variables now that imports and super types are resolved
             new VariableScopeVisitor(source).visitClass(node);
 
-            visitTypeAnnotations(node);
-            super.visitClass(node);
+            visitPackage(node.getPackage());
+            visitImports(node.getModule());
+
+            node.visitContents(this);
+            visitObjectInitializerStatements(node);
+            visitAnnotations(node); // GROOVY-10750, GROOVY-11206
         }
         currentClass = oldNode;
     }
@@ -1416,6 +1393,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
 
             String name = type.getName();
             ClassNode typeType = type.getType();
+            visitTypeAnnotations(typeType); // JSR 308 support
             GenericsTypeName gtn = new GenericsTypeName(name);
             boolean isWildcardGT = QUESTION_MARK.equals(name);
             boolean dealWithGenerics = (level == 0 || (level > 0 && genericParameterNames.get(gtn) != null));
@@ -1466,7 +1444,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
         if (genericsType.isResolved()) return true;
         currentClass.setUsingGenerics(true);
         ClassNode type = genericsType.getType();
-        visitTypeAnnotations(type); // JSR-308 support
+        visitTypeAnnotations(type); // JSR 308 support
         GenericsType tp = genericParameterNames.get(new GenericsTypeName(type.getName()));
         if (tp != null) {
             ClassNode[] bounds = tp.getUpperBounds();
diff --git a/src/test/gls/annotations/AnnotationTest.groovy b/src/test/gls/annotations/AnnotationTest.groovy
index cd4e49a871..a23e1da172 100644
--- a/src/test/gls/annotations/AnnotationTest.groovy
+++ b/src/test/gls/annotations/AnnotationTest.groovy
@@ -664,7 +664,7 @@ final class AnnotationTest {
     }
 
     // GROOVY-10750
-    @NotYetImplemented @Test
+    @Test
     void testAttributeValueConstants10() {
         assertScript shell, '''
             @Retention(RUNTIME)
@@ -687,9 +687,29 @@ final class AnnotationTest {
         '''
     }
 
-    // GROOVY-11207
+    // GROOVY-11206
     @Test
     void testAttributeValueConstants11() {
+        assertScript shell, '''
+            @Retention(RUNTIME)
+            @interface Foo {
+                String value()
+            }
+            @groovy.transform.CompileStatic
+            @Foo(BarController.SOME_URL)
+            class BarController {
+                public static final String BASE_URL = '/bars'
+                public static final String SOME_URL = BASE_URL + '/{barId}/bazs'
+            }
+
+            def foo = BarController.getAnnotation(Foo)
+            assert foo.value() == '/bars/{barId}/bazs'
+        '''
+    }
+
+    // GROOVY-11207
+    @Test
+    void testAttributeValueConstants12() {
         assertScript shell, '''
             @Retention(RUNTIME)
             @interface Foo {
@@ -998,6 +1018,43 @@ final class AnnotationTest {
         '''
     }
 
+    // GROOVY-11178
+    @NotYetImplemented @Test
+    void testAnnotationOnConstructorType() {
+        assertScript shell, '''package p
+            @Target(TYPE_USE)
+            @interface Tag {}
+
+            @groovy.transform.ASTTest(phase=CLASS_GENERATION, value={
+                def cce = node.rightExpression
+                assert cce.type.typeAnnotations.size() == 1
+                assert cce.type.typeAnnotations[0].classNode.name == 'p.Tag'
+            })
+            Object o = new @Tag Object()
+        '''
+
+        def err = shouldFail shell, '''\
+            @Target(PARAMETER)
+            @interface Tag {}
+
+            Object o = new @Tag Object()
+        '''
+        assert err =~ /Annotation @Tag is not allowed on element TYPE/
+    }
+
+    // GROOVY-9155
+    @Test
+    void testAnnotationOnTypeArgumentType() {
+        def err = shouldFail shell, '''package p
+            @Target(TYPE_USE)
+            @interface Tag {}
+
+            def m(List<@Tag(foo="") String> strings) {
+            }
+        '''
+        assert err =~ /'foo' is not part of the annotation Tag in @p.Tag/
+    }
+
     // GROOVY-8234
     @Test
     void testAnnotationWithRepeatableSupported() {
diff --git a/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
index 6e806ae38b..4d3ff7fe2b 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
@@ -18,28 +18,72 @@
  */
 package org.codehaus.groovy.classgen.asm
 
-class TypeAnnotationsTest extends AbstractBytecodeTestCase {
+final class TypeAnnotationsTest extends AbstractBytecodeTestCase {
+
+    private final String imports = '''\
+        |import java.lang.annotation.*
+        |import static java.lang.annotation.ElementType.*
+        |import static java.lang.annotation.RetentionPolicy.*
+        |'''.stripMargin()
+
+    void testTypeAnnotationsForConstructor1() {
+        def bytecode = compile(classNamePattern: 'HasConstructor', method: '<init>', imports + '''
+            @Retention(RUNTIME) @Target(CONSTRUCTOR) @interface CtorAnno  { }
+            @Retention(RUNTIME) @Target(TYPE_USE)    @interface TypeAnno0 { }
+            @Retention(RUNTIME) @Target(TYPE_USE)    @interface TypeAnno1 { }
+
+            class HasConstructor {
+                @CtorAnno @TypeAnno0 @TypeAnno1 HasConstructor() { }
+            }
+        ''')
+        assert bytecode.hasSequence([
+                'public <init>()V',
+                '@LCtorAnno;()',
+                '@LTypeAnno0;() : METHOD_RETURN',
+                '@LTypeAnno1;() : METHOD_RETURN'
+        ])
+    }
+
+    void testTypeAnnotationsForConstructor2() {
+        def bytecode = compile(classNamePattern: 'Foo.Bar', method: '<init>', imports + '''
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno0 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno1 { }
+
+            class Foo {
+                class Bar {
+                    @TypeAnno0
+                    Bar(@TypeAnno1 that) {
+                    }
+                }
+            }
+        ''')
+        assert bytecode.hasStrictSequence([
+                'public <init>(LFoo;Ljava/lang/Object;)V',
+              //'@LTypeAnno0;() : METHOD_RETURN', TODO
+                '@LTypeAnno1;() : METHOD_FORMAL_PARAMETER 1, null', // TODO: 0
+                'L0'
+        ])
+    }
+
     void testTypeAnnotationsForMethod1() {
-        assert compile(method: 'foo','''
-        import java.lang.annotation.*
-        import static java.lang.annotation.RetentionPolicy.RUNTIME
-        import static java.lang.annotation.ElementType.*
-
-        @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 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno4 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno5 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno6 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno7 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno8 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno9 { }
-
-        <@TypeAnno0 X extends @TypeAnno1 Number> @TypeAnno2 List<@TypeAnno3 X> foo(
-            @TypeAnno4 List<@TypeAnno5 ? super @TypeAnno6 Map<@TypeAnno7 X, @TypeAnno8 ?>> arg
-        ) throws @TypeAnno9 Exception {}
-        ''').hasSequence([
+        def bytecode = compile(method: 'foo', imports + '''
+            @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 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno4 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno5 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno6 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno7 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno8 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno9 { }
+
+            <@TypeAnno0 X extends @TypeAnno1 Number> @TypeAnno2 List<@TypeAnno3 X> foo(
+                @TypeAnno4 List<@TypeAnno5 ? super @TypeAnno6 Map<@TypeAnno7 X, @TypeAnno8 ?>> arg
+            ) throws @TypeAnno9 Exception {
+            }
+        ''')
+        assert bytecode.hasSequence([
                 'public foo(Ljava/util/List;)Ljava/util/List;',
                 '@LTypeAnno0;() : METHOD_TYPE_PARAMETER 0, null',
                 '@LTypeAnno1;() : METHOD_TYPE_PARAMETER_BOUND 0, 0, null',
@@ -55,30 +99,27 @@ class TypeAnnotationsTest extends AbstractBytecodeTestCase {
     }
 
     void testTypeAnnotationsForMethod2() {
-        assert compile(method: 'foo','''
-        import java.lang.annotation.*
-        import static java.lang.annotation.RetentionPolicy.RUNTIME
-        import static java.lang.annotation.ElementType.*
-
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeParameterAnno { }
-        @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 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno4 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno5 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno6 { }
-
-        <@TypeAnno0 @TypeParameterAnno E extends @TypeAnno1 Number & @TypeAnno2 List<@TypeAnno3 E>> void foo(
-            @TypeAnno4 E arg1, @TypeAnno5 int arg2, @TypeAnno6 int arg3
-        ) {
-            // TODO support in code examples below
-            try {
-                numbers.addAll(new @TypeAnno5 ArrayList<@TypeAnno6 Integer>());
-            } catch (@TypeAnno7 Throwable ignore) {
+        def bytecode = compile(method: 'foo', imports + '''
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeParameterAnno { }
+            @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 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno4 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno5 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno6 { }
+
+            <@TypeAnno0 @TypeParameterAnno E extends @TypeAnno1 Number & @TypeAnno2 List<@TypeAnno3 E>> void foo(
+                @TypeAnno4 E arg1, @TypeAnno5 int arg2, @TypeAnno6 int arg3
+            ) {
+                // TODO support in code examples below
+                try {
+                    numbers.addAll(new @TypeAnno5 ArrayList<@TypeAnno6 Integer>());
+                } catch (@TypeAnno7 Throwable ignore) {
+                }
             }
-        }
-        ''').hasSequence([
+        ''')
+        assert bytecode.hasSequence([
                 'public foo(Ljava/lang/Number;II)V',
                 '@LTypeAnno0;() : METHOD_TYPE_PARAMETER 0, null',
                 '@LTypeParameterAnno;() : METHOD_TYPE_PARAMETER 0, null',
@@ -92,39 +133,36 @@ class TypeAnnotationsTest extends AbstractBytecodeTestCase {
     }
 
     void testTypeAnnotationsForMethod3() {
-        assert compile(method: 'get', classNamePattern: 'Foo','''
-        import java.lang.annotation.*
-        import static java.lang.annotation.RetentionPolicy.RUNTIME
-        import static java.lang.annotation.ElementType.*
-
-        @Retention(RUNTIME) @Target([PARAMETER]) @interface ParameterAnno { }
-        @Retention(RUNTIME) @Target([TYPE_PARAMETER, TYPE_USE]) @interface TypeUseAndParameterAnno { }
-        @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 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno4 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno5 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno6 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno7 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno8 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno9 { }
-
-        class Foo<X, Y> {
-            @TypeAnno0 Map<@TypeAnno1 ? extends X, ? super @TypeAnno2 Y>
-            get(@TypeAnno3 @ParameterAnno @TypeUseAndParameterAnno List<@TypeAnno4 List<@TypeAnno5 X>> arg1,
-                Map<@TypeAnno6 ? extends @TypeAnno7 X, @TypeAnno8 ? super @TypeAnno9 Y> arg2
-            ) throws @TypeAnno0 RuntimeException, @TypeAnno1 IOException {
-                // TODO support in code example below
-                @TypeAnno0 List<@TypeAnno1 Integer> numbers = Arrays.<@TypeAnno3 Integer>asList(5, 3, 50, 24, 40, 2, 9, 18);
-                if (arg2 instanceof @TypeAnno2 Number) {
-                    return (@TypeAnno3 Map<@TypeAnno4 ? extends X, ? super @TypeAnno5 Y>) null;
+        def bytecode = compile(classNamePattern: 'Foo', method: 'get', imports + '''
+            @Retention(RUNTIME) @Target(PARAMETER) @interface ParameterAnno { }
+            @Retention(RUNTIME) @Target([TYPE_PARAMETER, TYPE_USE]) @interface TypeUseAndParameterAnno { }
+            @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 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno4 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno5 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno6 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno7 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno8 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno9 { }
+
+            class Foo<X, Y> {
+                @TypeAnno0 Map<@TypeAnno1 ? extends X, ? super @TypeAnno2 Y>
+                get(@TypeAnno3 @ParameterAnno @TypeUseAndParameterAnno List<@TypeAnno4 List<@TypeAnno5 X>> arg1,
+                    Map<@TypeAnno6 ? extends @TypeAnno7 X, @TypeAnno8 ? super @TypeAnno9 Y> arg2
+                ) throws @TypeAnno0 RuntimeException, @TypeAnno1 IOException {
+                    // TODO support in code example below
+                    @TypeAnno0 List<@TypeAnno1 Integer> numbers = Arrays.<@TypeAnno3 Integer>asList(5, 3, 50, 24, 40, 2, 9, 18);
+                    if (arg2 instanceof @TypeAnno2 Number) {
+                        return (@TypeAnno3 Map<@TypeAnno4 ? extends X, ? super @TypeAnno5 Y>) null;
+                    }
+                    //if (numbers.stream().sorted(@TypeAnno6 Integer::compareTo).count() > 0) return null; // needs grammar tweak
+                    return arg2;
                 }
-                //if (numbers.stream().sorted(@TypeAnno6 Integer::compareTo).count() > 0) return null; // needs grammar tweak
-                return arg2;
             }
-        }
-        ''').hasSequence([
+        ''')
+        assert bytecode.hasSequence([
                 'public get(Ljava/util/List;Ljava/util/Map;)Ljava/util/Map; throws java/lang/RuntimeException',
                 '@LTypeAnno0;() : METHOD_RETURN, null',
                 '@LTypeAnno1;() : METHOD_RETURN, 0;',
@@ -142,44 +180,48 @@ class TypeAnnotationsTest extends AbstractBytecodeTestCase {
         ])
     }
 
-    void testTypeAnnotationsForConstructor() {
-        assert compile(method: '<init>', classNamePattern: 'HasCons', '''
-        import java.lang.annotation.*
-        import static java.lang.annotation.RetentionPolicy.RUNTIME
-        import static java.lang.annotation.ElementType.*
-
-        @Retention(RUNTIME) @Target([CONSTRUCTOR]) @interface ConstructorAnno { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno0 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno1 { }
-
-        class HasCons {
-            @ConstructorAnno @TypeAnno0 @TypeAnno1 HasCons() { }
-        }
-        ''').hasSequence([
-                'public <init>()V',
-                '@LConstructorAnno;()',
-                '@LTypeAnno0;() : METHOD_RETURN',
-                '@LTypeAnno1;() : METHOD_RETURN'
+    // GROOVY-9154
+    void testTypeAnnotationsForMethod4() {
+        def bytecode = compile(classNamePattern: 'Foo', method: 'sizeZeroOrPositive', '''\
+            @Grab("net.jqwik:jqwik:1.1.4")
+            import net.jqwik.api.ForAll
+            import net.jqwik.api.Property
+            import net.jqwik.api.constraints.IntRange
+
+            @groovy.transform.CompileStatic
+            class Foo {
+                @Property
+                boolean sizeZeroOrPositive(@ForAll List<@IntRange(min=0, max=10) Integer> items) {
+                    items.size() >= 0
+                }
+            }
+        ''')
+        assert bytecode.hasSequence([
+            'public sizeZeroOrPositive(Ljava/util/List;)Z',
+            '@Lnet/jqwik/api/Property;()',
+            '@Lnet/jqwik/api/constraints/IntRange;(min=0, max=10) : METHOD_FORMAL_PARAMETER 0, 0;',
+            '// annotable parameter count: 1 (visible)',
+            '@Lnet/jqwik/api/ForAll;() // parameter 0'
         ])
     }
 
     void testTypeAnnotationsForField1() {
-        assert compile(field: 'documents', classNamePattern: 'Foo', '''
-        import java.lang.annotation.*
-        import static java.lang.annotation.RetentionPolicy.RUNTIME
-        import static java.lang.annotation.ElementType.*
-
-        @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 { }
-
-        class Foo {
-            public @FieldAnno Map<@TypeAnno0 ? extends @TypeAnno1 CharSequence, @TypeAnno2 List<@TypeAnno3 ?>> documents = null
-        }
-        ''').hasSequence([
-                'public Ljava/util/Map; documents',
+        def bytecode = compile(classNamePattern: 'Foo', field: 'foo', 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 { }
+
+            class Foo {
+                public @FieldAnno Map<
+                    @TypeAnno0 ? extends @TypeAnno1 CharSequence,
+                    @TypeAnno2 List<@TypeAnno3 ?>
+                > foo
+            }
+        ''')
+        assert bytecode.hasSequence([
+                'public Ljava/util/Map; foo',
                 '@LFieldAnno;()',
                 '@LTypeAnno0;() : FIELD, 0;',
                 '@LTypeAnno1;() : FIELD, 0;*',
@@ -189,65 +231,22 @@ class TypeAnnotationsTest extends AbstractBytecodeTestCase {
     }
 
     void testTypeAnnotationsForField2() {
-        assert compile(field: 'numbers', classNamePattern: 'Bar','''
-        import java.lang.annotation.*
-        import static java.lang.annotation.RetentionPolicy.RUNTIME
-        import static java.lang.annotation.ElementType.*
-
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno4 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno5 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno6 { }
-        @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno7 { }
-
-        class Bar {
-            // TODO support type annotations for code (TypeAnno7 below)
-            public @TypeAnno4 List<@TypeAnno5 ? super @TypeAnno6 Integer> numbers = Arrays.<@TypeAnno7 Integer>asList(5, 3, 50, 24, 40, 2, 9, 18)
-        }
-        ''').hasSequence([
+        def bytecode = compile(classNamePattern: 'Bar', field: 'numbers', imports + '''
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno4 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno5 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno6 { }
+            @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno7 { }
+
+            class Bar {
+                // TODO support type annotations for code (TypeAnno7 below)
+                public @TypeAnno4 List<@TypeAnno5 ? super @TypeAnno6 Integer> numbers = Arrays.<@TypeAnno7 Integer>asList(5, 3, 50, 24, 40, 2, 9, 18)
+            }
+        ''')
+        assert bytecode.hasSequence([
                 'public Ljava/util/List; numbers',
                 '@LTypeAnno4;() : FIELD, null',
                 '@LTypeAnno5;() : FIELD, 0;',
                 '@LTypeAnno6;() : FIELD, 0;*'
         ])
     }
-
-    void testTypeAnnotationsForClass() {
-        assert compile(classNamePattern: 'MyClass','''
-        import java.lang.annotation.*
-        import java.rmi.Remote
-        import static java.lang.annotation.RetentionPolicy.RUNTIME
-        import static java.lang.annotation.ElementType.*
-
-        @Retention(RUNTIME) @Target([TYPE]) @interface TypeAnno { }
-        @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 { }
-
-        @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> { }
-        ''').hasSequence([
-                'public class MyClass extends java/util/ArrayList implements java/rmi/Remote java/util/List groovy/lang/GroovyObject {',
-                '@LTypeAnno;()',
-                '@LTypeUseAnno0;()',
-                '@LTypeUseAnno1;()',
-                '@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;'
-        ])
-    }
 }