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
commit 5bd68c5bb61197f2535abb9918c406894556f6a3 Author: Eric Milles <eric.mil...@thomsonreuters.com> AuthorDate: Tue Jul 15 12:19:43 2025 -0500 minor items --- .../transform/RecordTypeASTTransformation.java | 24 ++-- .../TupleConstructorASTTransformation.java | 9 +- .../org/codehaus/groovy/classgen/RecordTest.groovy | 122 +++++++++++++-------- 3 files changed, 90 insertions(+), 65 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/transform/RecordTypeASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/RecordTypeASTTransformation.java index 09e7d579a0..2a344463ce 100644 --- a/src/main/java/org/codehaus/groovy/transform/RecordTypeASTTransformation.java +++ b/src/main/java/org/codehaus/groovy/transform/RecordTypeASTTransformation.java @@ -212,17 +212,17 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple isAtLeastJDK16 = isAtLeast(targetBytecode, CompilerConfiguration.JDK16); message = "Expecting JDK16+ but found " + targetBytecode; } + List<PropertyNode> pList = Collections.unmodifiableList(getInstanceProperties(cNode)); boolean isNative = (isAtLeastJDK16 && mode != RecordTypeMode.EMULATE); if (isNative) { - String sName = cNode.getUnresolvedSuperClass().getName(); + String scName = cNode.getUnresolvedSuperClass().getName(); // don't expect any parent to be set at this point but we only check at grammar // level when using the record keyword so do a few more sanity checks here - if (!sName.equals(OBJECT) && !sName.equals(RECORD_CLASS_NAME)) { - addError("Invalid superclass for native record found: " + sName, cNode); + if (!scName.equals(OBJECT) && !scName.equals(RECORD_CLASS_NAME)) { + addError("Invalid superclass for native record found: " + scName, cNode); } cNode.setSuperClass(compilationUnit.getClassNodeResolver().resolveName(RECORD_CLASS_NAME, compilationUnit).getClassNode()); cNode.setModifiers(cNode.getModifiers() | Opcodes.ACC_RECORD); - final List<PropertyNode> pList = getInstanceProperties(cNode); if (!pList.isEmpty()) { cNode.setRecordComponents(new ArrayList<>()); } @@ -246,18 +246,18 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple makeClassFinal(this, cNode); makeInnerRecordStatic(cNode); - final List<PropertyNode> pList = getInstanceProperties(cNode); for (PropertyNode pNode : pList) { adjustPropertyForShallowImmutability(cNode, pNode, handler); pNode.setModifiers(pNode.getModifiers() | ACC_FINAL); } - final List<FieldNode> fList = cNode.getFields(); + List<FieldNode> fList = cNode.getFields(); for (FieldNode fNode : fList) { ensureNotPublic(this, cName, fNode); } + // 0L serialVersionUID by default if (cNode.getDeclaredField("serialVersionUID") == null) { - cNode.addField("serialVersionUID", ACC_PRIVATE | ACC_STATIC | ACC_FINAL, long_TYPE, constX(0L)); + cNode.addField("serialVersionUID", ACC_PRIVATE | ACC_STATIC | ACC_FINAL, long_TYPE, constX(0L, true)); } if (!hasAnnotation(cNode, ToStringASTTransformation.MY_TYPE)) { @@ -282,11 +282,11 @@ public class RecordTypeASTTransformation extends AbstractASTTransformation imple } if (hasAnnotation(cNode, TupleConstructorASTTransformation.MY_TYPE)) { - AnnotationNode tupleCons = cNode.getAnnotations(TupleConstructorASTTransformation.MY_TYPE).get(0); - if (unsupportedTupleAttribute(tupleCons, "excludes")) return; - if (unsupportedTupleAttribute(tupleCons, "includes")) return; - if (unsupportedTupleAttribute(tupleCons, "includeProperties")) return; - if (unsupportedTupleAttribute(tupleCons, "includeSuperFields")) return; + AnnotationNode tupleConstructor = cNode.getAnnotations(TupleConstructorASTTransformation.MY_TYPE).get(0); + if (unsupportedTupleAttribute(tupleConstructor, "excludes")) return; + if (unsupportedTupleAttribute(tupleConstructor, "includes")) return; + if (unsupportedTupleAttribute(tupleConstructor, "includeProperties")) return; + if (unsupportedTupleAttribute(tupleConstructor, "includeSuperFields")) return; } if (options != null && memberHasValue(options, COPY_WITH, Boolean.TRUE) && !hasDeclaredMethod(cNode, COPY_WITH, 1)) { diff --git a/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java index e6d6fc4746..e3b226167e 100644 --- a/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java +++ b/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java @@ -208,8 +208,7 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation if (pre != null) { superInPre = copyStatementsWithSuperAdjustment(pre, preBody); if (superInPre && callSuper) { - xform.addError("Error during " + MY_TYPE_NAME + " processing, can't have a super call in 'pre' " + - "closure and also 'callSuper' enabled", cNode); + xform.addError("Error during " + MY_TYPE_NAME + " processing, can't have a super call in 'pre' closure and also 'callSuper' enabled", cNode); } } @@ -268,7 +267,7 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation } int modifiers = getVisibility(anno, cNode, ConstructorNode.class, ACC_PUBLIC); - Parameter[] signature = params.toArray(Parameter.EMPTY_ARRAY); + Parameter[] signature = params.toArray(Parameter[]::new); if (cNode.getDeclaredConstructor(signature) != null) { if (sourceUnit != null) { String warning = String.format( @@ -287,15 +286,13 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation if (namedVariant) { BlockStatement inner = new BlockStatement(); Parameter mapParam = param(ClassHelper.MAP_TYPE.getPlainNodeReference(), NAMED_ARGS); - List<Parameter> genParams = new ArrayList<>(); - genParams.add(mapParam); ArgumentListExpression args = new ArgumentListExpression(); List<String> propNames = new ArrayList<>(); Map<Parameter, Expression> seen = new HashMap<>(); for (Parameter p : params) { if (!processImplicitNamedParam(xform, tupleCtor, mapParam, inner, args, propNames, p, false, seen)) return; } - NamedVariantASTTransformation.createMapVariant(xform, tupleCtor, anno, mapParam, genParams, cNode, inner, args, propNames); + NamedVariantASTTransformation.createMapVariant(xform, tupleCtor, anno, mapParam, List.of(mapParam), cNode, inner, args, propNames); } if (sourceUnit != null && !body.isEmpty()) { diff --git a/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy b/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy index 2ebdc3c1ea..13a7dfb48e 100644 --- a/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy +++ b/src/test/groovy/org/codehaus/groovy/classgen/RecordTest.groovy @@ -28,7 +28,9 @@ import org.codehaus.groovy.control.ClassNodeResolver import org.codehaus.groovy.control.CompilationUnit import org.codehaus.groovy.control.CompilerConfiguration import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit -import org.junit.Test +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.CsvSource import static groovy.test.GroovyAssert.assertScript import static groovy.test.GroovyAssert.isAtLeastJdk @@ -37,6 +39,15 @@ import static org.junit.Assume.assumeTrue final class RecordTest { + private final GroovyShell shell = GroovyShell.withConfig { + imports { + star 'groovy.transform' + staticStar 'java.lang.reflect.Modifier' + staticMember 'groovy.test.GroovyAssert', 'shouldFail' + staticMember 'org.codehaus.groovy.ast.ClassHelper', 'make' + } + } + @Test void testNativeRecordOnJDK16_groovy() { assumeTrue(isAtLeastJdk('16.0')) @@ -104,7 +115,7 @@ final class RecordTest { import java.lang.annotation.*; import java.util.*; - public record Person(@NotNull @NotNull2 @NotNull3 String name, int age, @NotNull2 @NotNull3 List<String> locations, String[] titles) {} + record Person(@NotNull @NotNull2 @NotNull3 String name, int age, @NotNull2 @NotNull3 List<String> locations, String[] titles) {} @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.RECORD_COMPONENT}) @@ -213,17 +224,6 @@ final class RecordTest { } } - //-------------------------------------------------------------------------- - - private final GroovyShell shell = GroovyShell.withConfig { - imports { - star 'groovy.transform' - staticStar 'java.lang.reflect.Modifier' - staticMember 'groovy.test.GroovyAssert', 'shouldFail' - staticMember 'org.codehaus.groovy.ast.ClassHelper', 'make' - } - } - @Test void testNativeRecordOnJDK16ByDefault() { assumeTrue(isAtLeastJdk('16.0')) @@ -524,45 +524,73 @@ final class RecordTest { ''' } - @Test - void testTupleConstructor() { - for (pair in [['RecordType', 'TupleConstructor'], ['defaults=false', 'defaultsMode=DefaultsMode.OFF']].combinations()) { - assertScript shell, """ - @${pair[0]}(${pair[1]}, namedVariant=false) - record Person(String name, Date dob) { - //Person(String,Date) - //Person(String) no! - //Person(Map) no! - //Person() no! - - public Person { // implies @TupleConstructor(pre={...}) - assert name.length() > 1 - } - - Person(Person that) { - this(that.name(), that.dob()) - } + @ParameterizedTest @CsvSource([ + 'RecordType, defaults=false', + 'TupleConstructor, defaults=false', + 'RecordType, defaultsMode=DefaultsMode.OFF', + 'TupleConstructor, defaultsMode=DefaultsMode.OFF' + ]) + void testTupleConstructor(String type, String mode) { + assertScript shell, """ + @$type($mode, namedVariant=false) + record Person(String name, Date dob) { + //Person(String,Date) + //Person(String) no! + //Person(Map) no! + //Person() no! + + public Person { // implies @TupleConstructor(pre={...}) + assert name.length() > 1 + } - //getAt(int i) - //toList() - //toMap() + Person(Person that) { + this(that.name(), that.dob()) } - assert Person.declaredConstructors.length == 2 // copy and tuple + //getAt(int i) + //toList() + //toMap() + } + + assert Person.declaredConstructors.length == 2 // copy and tuple - def person = new Person('Frank Grimes', new Date()) - def doppel = new Person(person) - shouldFail { - new Person(name:'Frank Grimes', dob:null) - } - shouldFail { - new Person('Frank Grimes') - } - shouldFail { - new Person() + def person = new Person('Frank Grimes', new Date()) + def doppel = new Person(person) + shouldFail { + new Person(name:'Frank Grimes', dob:null) + } + shouldFail { + new Person('Frank Grimes') + } + shouldFail { + new Person() + } + """ + } + + @Test + void testMapConstructor() { + assertScript shell, '''import static java.time.Month.NOVEMBER as NOV + + record Person(String name, LocalDate born) { + Person { + assert name.length() > 1 } - """ - } + } + + def person = new Person(name: 'Frank Grimes', born: LocalDate.of(1955,11,5)) + assert person.name() == 'Frank Grimes' + assert person.born().dayOfMonth == 5 + assert person.born().month == NOV + assert person.born().year == 1955 + + shouldFail { + new Person(name:'', born:LocalDate.now()) + } + shouldFail { + new Person(name:'John Doe') + } + ''' } @Test