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 2021/11/16 11:00:49 UTC

[groovy] branch master updated: GROOVY-10361: TupleConstructor could be improved to have a smarter mode for handling default values (additional refactoring)

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

paulk 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 6ef7a11  GROOVY-10361: TupleConstructor could be improved to have a smarter mode for handling default values (additional refactoring)
6ef7a11 is described below

commit 6ef7a11a0a2c53f5b3d9668f8d2ffef7e4da5ef1
Author: Paul King <pa...@asert.com.au>
AuthorDate: Tue Nov 16 21:00:36 2021 +1000

    GROOVY-10361: TupleConstructor could be improved to have a smarter mode for handling default values (additional refactoring)
---
 src/main/java/groovy/transform/DefaultsMode.java    |  5 +++--
 .../java/groovy/transform/TupleConstructor.java     |  6 ++++--
 .../TupleConstructorASTTransformation.java          | 13 -------------
 src/spec/doc/core-object-orientation.adoc           | 15 ++++++++++++---
 src/spec/test/objectorientation/MethodsTest.groovy  | 10 ++++++++++
 .../transform/TupleConstructorTransformTest.groovy  | 21 ++++++++++++++++++++-
 6 files changed, 49 insertions(+), 21 deletions(-)

diff --git a/src/main/java/groovy/transform/DefaultsMode.java b/src/main/java/groovy/transform/DefaultsMode.java
index c75dfa2..b2a0926 100644
--- a/src/main/java/groovy/transform/DefaultsMode.java
+++ b/src/main/java/groovy/transform/DefaultsMode.java
@@ -31,8 +31,9 @@ public enum DefaultsMode {
     OFF,
 
     /**
-     * Produce multiple constructors as required to handle any parameters with explicit initial values.
-     * Stops at the first parameter from the right which has no such explicit value.
+     * Produce multiple constructors as required to handle any mandatory and optional arguments.
+     * An argument is optional if the respective property/field has an explicit initial value.
+     * A property/field without an initial value is deemed mandatory.
      */
     AUTO,
 
diff --git a/src/main/java/groovy/transform/TupleConstructor.java b/src/main/java/groovy/transform/TupleConstructor.java
index dfc3f65..1e6bf86 100644
--- a/src/main/java/groovy/transform/TupleConstructor.java
+++ b/src/main/java/groovy/transform/TupleConstructor.java
@@ -302,8 +302,10 @@ public @interface TupleConstructor {
      *
      * When set to {@code AUTO}, default values are catered for where explicit
      * default values are given for the respective property/field.
-     * Default values are processed from the right and processing stops when the
-     * first property/field is found without an explicit deault value.
+     * Additional positional constructors are generated as per Groovy's normal default value processing.
+     * Properties/fields with an explicit initial value are deemed {@em optional} and may be dropped.
+     * Properties/fields with no initial value are deemed {@em mandatory} and must be supplied as an argument to the respective constructor.
+     * Optional arguments to a positional constructor are dropped from the right.
      *
      * When set to {@code OFF}, default values are not allowed for properties and fields.
      * Only the constructor containing all arguments will be provided.
diff --git a/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
index c87922c..691261a 100644
--- a/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
@@ -254,19 +254,6 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation
             params.sort(includeComparator);
         }
 
-        if (defaultsMode == AUTO) {
-            boolean foundNoDefault = false;
-            for (int i = params.size() - 1; i >= 0; i--) {
-                Parameter p = params.get(i);
-                if (!p.hasInitialExpression()) {
-                    foundNoDefault = true;
-                }
-                if (foundNoDefault) {
-                    p.setInitialExpression(null);
-                }
-            }
-        }
-
         for (PropertyNode pNode : list) {
             String name = pNode.getName();
             if (shouldSkipUndefinedAware(name, excludes, includes, allNames)) continue;
diff --git a/src/spec/doc/core-object-orientation.adoc b/src/spec/doc/core-object-orientation.adoc
index 337bef0..8ff4535 100644
--- a/src/spec/doc/core-object-orientation.adoc
+++ b/src/spec/doc/core-object-orientation.adoc
@@ -419,14 +419,23 @@ Mix named and positional arguments with caution.
 
 ==== Default arguments
 
-Default arguments make parameters optional. If the argument is not supplied, the method assumes a default value.
+Default arguments make parameters optional.
+If the argument is not supplied, the method assumes a default value.
 
 [source,groovy]
 ----
-include::../test/objectorientation/MethodsTest.groovy[tags=default_arguments ,indent=0]
+include::../test/objectorientation/MethodsTest.groovy[tags=default_arguments,indent=0]
 ----
 
-Note that no mandatory parameter can be defined after a default parameter is present, only other default parameters.
+Parameters are dropped from the right, however mandatory parameters are never dropped.
+
+[source,groovy]
+----
+include::../test/objectorientation/MethodsTest.groovy[tags=default_arguments2,indent=0]
+----
+
+The same rule applies to constructors as well as methods.
+If using `@TupleConstructor`, additional configuration options apply.
 
 ==== Varargs
 
diff --git a/src/spec/test/objectorientation/MethodsTest.groovy b/src/spec/test/objectorientation/MethodsTest.groovy
index 4b8cb91..3f22294 100644
--- a/src/spec/test/objectorientation/MethodsTest.groovy
+++ b/src/spec/test/objectorientation/MethodsTest.groovy
@@ -77,6 +77,16 @@ class MethodsTest extends GroovyTestCase {
             assert foo('Marie').age == 1
             // end::default_arguments[]
         '''
+        assertScript '''
+            // tag::default_arguments2[]
+            def baz(a = 'a', int b, c = 'c', boolean d, e = 'e') { "$a $b $c $d $e" }
+
+            assert baz(42, true) == 'a 42 c true e'
+            assert baz('A', 42, true) == 'A 42 c true e'
+            assert baz('A', 42, 'C', true) == 'A 42 C true e'
+            assert baz('A', 42, 'C', true, 'E') == 'A 42 C true E'
+            // end::default_arguments2[]
+        '''
     }
 
     void testVarargs() {
diff --git a/src/test/org/codehaus/groovy/transform/TupleConstructorTransformTest.groovy b/src/test/org/codehaus/groovy/transform/TupleConstructorTransformTest.groovy
index ddfca66..9976e5b 100644
--- a/src/test/org/codehaus/groovy/transform/TupleConstructorTransformTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/TupleConstructorTransformTest.groovy
@@ -401,7 +401,9 @@ class TupleConstructorTransformTest extends GroovyShellTestCase {
 
             assert C.declaredConstructors*.toString().toSet() == [
                 'public C(java.lang.String,int,int,java.lang.String)',
-                'public C(java.lang.String,int,int)'
+                'public C(java.lang.String,int,int)',
+                'public C(java.lang.String,int)',
+                'public C(int)'
             ].toSet()
 
             @TupleConstructor(defaultsMode = DefaultsMode.ON, includeFields = true)
@@ -420,5 +422,22 @@ class TupleConstructorTransformTest extends GroovyShellTestCase {
                 'public D()'
             ].toSet()
         '''
+        assertScript '''
+        import groovy.transform.*
+        @Canonical(defaultsMode=DefaultsMode.AUTO)
+        class Bar {
+            String a = 'a'
+            long b
+            Integer c = 24
+            short d
+            String e = 'e'
+        }
+
+        short one = 1
+        assert new Bar(3L, one).toString() == 'Bar(a, 3, 24, 1, e)'
+        assert new Bar('A', 3L, one).toString() == 'Bar(A, 3, 24, 1, e)'
+        assert new Bar('A', 3L, 42, one).toString() == 'Bar(A, 3, 42, 1, e)'
+        assert new Bar('A', 3L, 42, one, 'E').toString() == 'Bar(A, 3, 42, 1, E)'
+        '''
     }
 }