You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2018/11/17 16:36:43 UTC

groovy git commit: GROOVY-8887: Support multi-assignment of tuples in STC(closes #824)

Repository: groovy
Updated Branches:
  refs/heads/master 55fd0ddaa -> f364a0c4b


GROOVY-8887: Support multi-assignment of tuples in STC(closes #824)


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/f364a0c4
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/f364a0c4
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/f364a0c4

Branch: refs/heads/master
Commit: f364a0c4bff71a3f9eb688285ef58c314a61ae8e
Parents: 55fd0dd
Author: Daniel Sun <su...@apache.org>
Authored: Sun Nov 18 00:36:19 2018 +0800
Committer: Daniel Sun <su...@apache.org>
Committed: Sun Nov 18 00:36:34 2018 +0800

----------------------------------------------------------------------
 .../org/codehaus/groovy/ast/ClassHelper.java    | 25 +++++++++
 .../stc/StaticTypeCheckingVisitor.java          | 51 ++++++++++++++++++-
 src/test/groovy/bugs/Groovy8887.groovy          | 53 ++++++++++++++++++++
 3 files changed, 128 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/f364a0c4/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
index 2e034fb..0bdf585 100644
--- a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
+++ b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
@@ -28,6 +28,24 @@ import groovy.lang.MetaClass;
 import groovy.lang.Range;
 import groovy.lang.Reference;
 import groovy.lang.Script;
+import groovy.lang.Tuple;
+import groovy.lang.Tuple0;
+import groovy.lang.Tuple1;
+import groovy.lang.Tuple10;
+import groovy.lang.Tuple11;
+import groovy.lang.Tuple12;
+import groovy.lang.Tuple13;
+import groovy.lang.Tuple14;
+import groovy.lang.Tuple15;
+import groovy.lang.Tuple16;
+import groovy.lang.Tuple2;
+import groovy.lang.Tuple3;
+import groovy.lang.Tuple4;
+import groovy.lang.Tuple5;
+import groovy.lang.Tuple6;
+import groovy.lang.Tuple7;
+import groovy.lang.Tuple8;
+import groovy.lang.Tuple9;
 import org.apache.groovy.util.Maps;
 import org.codehaus.groovy.classgen.asm.util.TypeUtil;
 import org.codehaus.groovy.runtime.GeneratedClosure;
@@ -69,6 +87,12 @@ public class ClassHelper {
             Iterator.class, GeneratedClosure.class, GeneratedLambda.class, GroovyObjectSupport.class
     };
 
+    public static final Class[] TUPLE_CLASSES = new Class[] {
+            Tuple0.class, Tuple1.class, Tuple2.class, Tuple3.class, Tuple4.class, Tuple5.class, Tuple6.class,
+            Tuple7.class, Tuple8.class, Tuple9.class, Tuple10.class, Tuple11.class, Tuple12.class, Tuple13.class,
+            Tuple14.class, Tuple15.class, Tuple16.class
+    };
+
     private static final String[] primitiveClassNames = new String[]{
             "", "boolean", "char", "byte", "short",
             "int", "long", "double", "float", "void"
@@ -79,6 +103,7 @@ public class ClassHelper {
             VOID_TYPE = makeCached(Void.TYPE),
             CLOSURE_TYPE = makeCached(Closure.class),
             GSTRING_TYPE = makeCached(GString.class), LIST_TYPE = makeWithoutCaching(List.class),
+            TUPLE_TYPE = makeWithoutCaching(Tuple.class),
             MAP_TYPE = makeWithoutCaching(Map.class), RANGE_TYPE = makeCached(Range.class),
             PATTERN_TYPE = makeCached(Pattern.class), STRING_TYPE = makeCached(String.class),
             SCRIPT_TYPE = makeCached(Script.class), REFERENCE_TYPE = makeWithoutCaching(Reference.class),

http://git-wip-us.apache.org/repos/asf/groovy/blob/f364a0c4/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 72ea3bc..f9b0a2a 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -153,6 +153,7 @@ import static org.codehaus.groovy.ast.ClassHelper.PATTERN_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.RANGE_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.STRING_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.Short_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.TUPLE_CLASSES;
 import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.byte_TYPE;
@@ -1085,11 +1086,14 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         // multiple assignment check
         if (!(leftExpression instanceof TupleExpression)) return true;
 
-        if (!(rightExpression instanceof ListExpression)) {
+        Expression transformedRightExpression = transformRightExpressionToSupportMultipleAssignment(rightExpression);
+        if (null == transformedRightExpression) {
             addStaticTypeError("Multiple assignments without list expressions on the right hand side are unsupported in static type checking mode", rightExpression);
             return false;
         }
 
+        rightExpression = transformedRightExpression;
+
         TupleExpression tuple = (TupleExpression) leftExpression;
         ListExpression list = (ListExpression) rightExpression;
         List<Expression> listExpressions = list.getExpressions();
@@ -1114,6 +1118,51 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         return true;
     }
 
+    private Expression transformRightExpressionToSupportMultipleAssignment(Expression rightExpression) {
+        if (rightExpression instanceof ListExpression) {
+            return rightExpression;
+        }
+
+        ClassNode cn = null;
+        if (rightExpression instanceof MethodCallExpression || rightExpression instanceof VariableExpression) {
+            cn = rightExpression.getType();
+        }
+
+        if (null == cn) {
+            return null;
+        }
+
+        Expression listExpression = transformToListExpression(rightExpression, cn);
+        if (listExpression != null) return listExpression;
+
+        return null;
+    }
+
+    private Expression transformToListExpression(Expression expression, ClassNode cn) {
+        if (null != cn && cn.isDerivedFrom(ClassHelper.TUPLE_TYPE)) { // just for performance to check
+            for (int i = 0, n = TUPLE_CLASSES.length; i < n; i++) {
+                Class tcn = TUPLE_CLASSES[i];
+                if (tcn.equals(cn.getTypeClass())) {
+                    ListExpression listExpression = new ListExpression();
+                    GenericsType[] genericsTypes = cn.getGenericsTypes();
+                    for (int j = 0; j < i; j++) {
+                        // the index of element in tuple starts with 1
+                        MethodCallExpression mce = new MethodCallExpression(expression, "v" + (j + 1), ArgumentListExpression.EMPTY_ARGUMENTS);
+                        ClassNode elementType = null != genericsTypes ? genericsTypes[j].getType() : ClassHelper.OBJECT_TYPE;
+                        mce.setType(elementType);
+                        storeType(mce, elementType);
+                        listExpression.addExpression(mce);
+                    }
+
+                    listExpression.setSourcePosition(expression);
+
+                    return listExpression;
+                }
+            }
+        }
+        return null;
+    }
+
     private static ClassNode adjustTypeForSpreading(ClassNode inferredRightExpressionType, Expression leftExpression) {
         // imagine we have: list*.foo = 100
         // then the assignment must be checked against [100], not 100

http://git-wip-us.apache.org/repos/asf/groovy/blob/f364a0c4/src/test/groovy/bugs/Groovy8887.groovy
----------------------------------------------------------------------
diff --git a/src/test/groovy/bugs/Groovy8887.groovy b/src/test/groovy/bugs/Groovy8887.groovy
new file mode 100644
index 0000000..2c4e80c
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy8887.groovy
@@ -0,0 +1,53 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.bugs
+
+class Groovy8887 extends GroovyTestCase {
+    void testMultiAssignment() {
+        assertScript '''
+            @groovy.transform.CompileStatic
+            class TtcTuple {
+                void multiAssignedByMethodCall() {
+                    def (String name, Integer age) = findPersonInfo()
+                    
+                    assert 'Daniel' == name
+                    assert 35 == age
+                }
+                
+                void multiAssignedByVariableAccess() {
+                    Tuple2<String, Integer> personInfo = findPersonInfo()
+                    def (String name, Integer age) = personInfo
+                    
+                    assert 'Daniel' == name
+                    assert 35 == age
+                }
+                
+                Tuple2<String, Integer> findPersonInfo() {
+                    Tuple2<String, Integer> t = new Tuple2<>('Daniel', 35)
+                    
+                    return t
+                }
+            }
+            
+            def tt = new TtcTuple()
+            tt.multiAssignedByMethodCall()
+            tt.multiAssignedByVariableAccess()
+        '''
+    }
+}