You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2026/09/17 07:16:42 UTC

(groovy) 02/02: GROOVY-12413: TYPE_USE annotations on primitive record components and tuple-constructor parameters are duplicated or misapplied

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

paulk-asert pushed a commit to branch GROOVY_6_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit b409e933c9529f96806663d0afe47986db3d8b05
Author: Paul King <pa...@asert.com.au>
AuthorDate: Wed Sep 16 21:57:09 2026 +1000

    GROOVY-12413: TYPE_USE annotations on primitive record components and tuple-constructor parameters are duplicated or misapplied
---
 .../transform/RecordTypeASTTransformation.java     |  2 +-
 .../TupleConstructorASTTransformation.java         |  2 +-
 .../org/codehaus/groovy/classgen/RecordTest.groovy | 32 ++++++++++++++++++++++
 3 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/transform/RecordTypeASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/RecordTypeASTTransformation.java
index fbe97906ae..48c2bc078b 100644
--- a/src/main/java/org/codehaus/groovy/transform/RecordTypeASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/RecordTypeASTTransformation.java
@@ -310,7 +310,7 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple
             }
             for (PropertyNode pNode : pList) {
                 ClassNode pType = pNode.getOriginType();
-                ClassNode type = pType.getPlainNodeReference();
+                ClassNode type = pType.getPlainNodeReference(false);
                 type.setGenericsPlaceHolder(pType.isGenericsPlaceHolder());
                 type.setGenericsTypes(pType.getGenericsTypes());
                 RecordComponentNode rec = new RecordComponentNode(cNode, pNode.getName(), type, pNode.getAnnotations());
diff --git a/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
index 4109f1f498..f59ee8e492 100644
--- a/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
@@ -392,7 +392,7 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation
 
     private static Parameter createParam(final FieldNode fNode, final String name, final DefaultsMode defaultsMode, final AbstractASTTransformation xform, final boolean makeImmutable) {
         ClassNode fType = fNode.getType();
-        ClassNode type = fType.getPlainNodeReference();
+        ClassNode type = fType.getPlainNodeReference(false);
         type.setGenericsTypes(fType.getGenericsTypes());
         type.setGenericsPlaceHolder(fType.isGenericsPlaceHolder());
 
diff --git a/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy b/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy
index 1c3025c66e..87667401d9 100644
--- a/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy
+++ b/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy
@@ -116,6 +116,38 @@ final class RecordTest {
         '''
     }
 
+    @Test
+    void testTypeUseAnnotationOnPrimitiveRecordComponent() {
+        assertScript shell, '''
+            import java.lang.annotation.*
+            import java.lang.reflect.AnnotatedElement
+
+            @Retention(RetentionPolicy.RUNTIME)
+            @Target([ElementType.TYPE_USE])
+            @interface NonNeg {}
+
+            @Retention(RetentionPolicy.RUNTIME)
+            @Target([ElementType.RECORD_COMPONENT, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE_USE])
+            @interface Both {}
+
+            record Point(@NonNeg int x, @Both long y) {}
+
+            def check = { AnnotatedElement ae, Class expected ->
+                def annos = ae.annotations
+                assert annos.size() == 1 && annos[0].annotationType() == expected
+            }
+            Point.recordComponents.each { rc ->
+                def expected = rc.name == 'x' ? NonNeg : Both
+                check(rc.annotatedType, expected)
+                check(rc.accessor.annotatedReturnType, expected)
+                check(Point.getDeclaredField(rc.name).annotatedType, expected)
+            }
+            def ctor = Point.getDeclaredConstructor(int, long)
+            check(ctor.annotatedParameterTypes[0], NonNeg)
+            check(ctor.annotatedParameterTypes[1], Both)
+        '''
+    }
+
     @Test
     void testNativeRecordOnJDK16_java() {
         assumeTrue(isAtLeastJdk('16.0'))