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 2022/01/29 19:49:16 UTC

[groovy] 01/02: GROOVY-6802, GROOVY-6803: STC: assign list/map expr to bool/class/string

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

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

commit c9f938178523bd4b298500c008c7719a8a73a339
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Oct 8 14:00:54 2021 -0500

    GROOVY-6802, GROOVY-6803: STC: assign list/map expr to bool/class/string
    
    Conflicts:
    	src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 28 ++++---
 .../groovy/transform/stc/CoercionSTCTest.groovy    | 88 +++++++++++++++++++++-
 .../transform/stc/ConstructorsSTCTest.groovy       |  4 +-
 .../asm/sc/CoercionStaticCompileTests.groovy       | 27 +++++++
 4 files changed, 132 insertions(+), 15 deletions(-)

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 3a2bb7b..4732da9 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1214,19 +1214,20 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     private void addListAssignmentConstructorErrors(final ClassNode leftRedirect, final ClassNode leftExpressionType, final ClassNode inferredRightExpressionType, final Expression rightExpression, final Expression assignmentExpression) {
+        if (isWildcardLeftHandSide(leftRedirect) && !CLASS_Type.equals(leftRedirect)) return; // GROOVY-6802, GROOVY-6803
         // if left type is not a list but right type is a list, then we're in the case of a groovy
         // constructor type : Dimension d = [100,200]
         // In that case, more checks can be performed
-        if (rightExpression instanceof ListExpression && !implementsInterfaceOrIsSubclassOf(LIST_TYPE, leftRedirect)) {
+        if (rightExpression instanceof ListExpression
+                && !implementsInterfaceOrIsSubclassOf(LIST_TYPE, leftRedirect)) {
             ArgumentListExpression argList = args(((ListExpression) rightExpression).getExpressions());
             ClassNode[] args = getArgumentTypes(argList);
             MethodNode methodNode = checkGroovyStyleConstructor(leftRedirect, args, assignmentExpression);
             if (methodNode != null) {
                 rightExpression.putNodeMetaData(DIRECT_METHOD_CALL_TARGET, methodNode);
             }
-        } else if (!implementsInterfaceOrIsSubclassOf(inferredRightExpressionType, leftRedirect)
-                && implementsInterfaceOrIsSubclassOf(inferredRightExpressionType, LIST_TYPE)
-                && !isWildcardLeftHandSide(leftExpressionType)) {
+        } else if (implementsInterfaceOrIsSubclassOf(inferredRightExpressionType, LIST_TYPE)
+                && !implementsInterfaceOrIsSubclassOf(inferredRightExpressionType, leftRedirect)) {
             if (!extension.handleIncompatibleAssignment(leftExpressionType, inferredRightExpressionType, assignmentExpression)) {
                 addAssignmentError(leftExpressionType, inferredRightExpressionType, assignmentExpression);
             }
@@ -1234,8 +1235,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     private void addMapAssignmentConstructorErrors(final ClassNode leftRedirect, final Expression leftExpression, final Expression rightExpression) {
-        if (!(rightExpression instanceof MapExpression) || (leftExpression instanceof VariableExpression && ((VariableExpression) leftExpression).isDynamicTyped())
-                || leftRedirect.equals(OBJECT_TYPE) || implementsInterfaceOrIsSubclassOf(leftRedirect, MAP_TYPE)) {
+        if ((!(rightExpression instanceof MapExpression)
+                || leftExpression instanceof VariableExpression && ((VariableExpression) leftExpression).isDynamicTyped())
+                || (isWildcardLeftHandSide(leftRedirect) && !CLASS_Type.equals(leftRedirect)) // GROOVY-6802, GROOVY-6803
+                || implementsInterfaceOrIsSubclassOf(leftRedirect, MAP_TYPE)) {
             return;
         }
 
@@ -1381,16 +1384,23 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 ConstructorNode cn = new ConstructorNode(Opcodes.ACC_PUBLIC, new Parameter[]{new Parameter(LINKEDHASHMAP_CLASSNODE, "args")}, ClassNode.EMPTY_ARRAY, EmptyStatement.INSTANCE);
                 return cn;
             } else {
-                addStaticTypeError("No matching constructor found: " + node + toMethodParametersString("<init>", arguments), source);
+                addStaticTypeError("No matching constructor found: " + prettyPrintTypeName(node) + toMethodParametersString("", arguments), source);
                 return null;
             }
         } else if (constructorList.size() > 1) {
-            addStaticTypeError("Ambiguous constructor call " + node + toMethodParametersString("<init>", arguments), source);
+            addStaticTypeError("Ambiguous constructor call " + prettyPrintTypeName(node) + toMethodParametersString("", arguments), source);
             return null;
         }
         return constructorList.get(0);
     }
 
+    private static String prettyPrintTypeName(final ClassNode node) {
+        if (node.isArray()) {
+            return prettyPrintTypeName(node.getComponentType()) + "[]";
+        }
+        return node.isGenericsPlaceHolder() ? node.getUnresolvedName() : node.getText();
+    }
+
     /**
      * When instanceof checks are found in the code, we store temporary type
      * information data in the {@link TypeCheckingContext#temporaryIfBranchTypeInformation}
@@ -4847,7 +4857,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             }
         }
 
-        if (receiver.equals(CLASS_Type) && receiver.getGenericsTypes() != null) {
+        if (isClassClassNodeWrappingConcreteType(receiver)) {
             List<MethodNode> result = findMethod(receiver.getGenericsTypes()[0].getType(), name, args);
             if (!result.isEmpty()) return result;
         }
diff --git a/src/test/groovy/transform/stc/CoercionSTCTest.groovy b/src/test/groovy/transform/stc/CoercionSTCTest.groovy
index 0bbad56..51b2549 100644
--- a/src/test/groovy/transform/stc/CoercionSTCTest.groovy
+++ b/src/test/groovy/transform/stc/CoercionSTCTest.groovy
@@ -22,9 +22,9 @@ package groovy.transform.stc
  * Unit tests for static type checking : coercions.
  */
 class CoercionSTCTest extends StaticTypeCheckingTestCase {
+
     void testCoerceToArray() {
         assertScript '''
-        def m() {
             try {
                 throw new Exception()
             } catch (Throwable t) {
@@ -38,10 +38,90 @@ class CoercionSTCTest extends StaticTypeCheckingTestCase {
                 // ...
                 clean = newTrace.toArray(newTrace as StackTraceElement[])
             }
-        }
+        '''
+    }
 
-        m()
+    // GROOVY-6802
+    void testCoerceToBool1() {
+        assertScript '''
+            boolean b = [new Object()]
+            assert b
+        '''
+        assertScript '''
+            boolean b = [false]
+            assert b
+        '''
+        assertScript '''
+            boolean b = [true]
+            assert b
+        '''
+        assertScript '''
+            boolean b = ['x']
+            assert b
+        '''
+        assertScript '''
+            boolean b = [:]
+            assert !b
+        '''
+        assertScript '''
+            boolean b = []
+            assert !b
         '''
     }
-}
 
+    void testCoerceToBool2() {
+        assertScript '''
+            Boolean b = [new Object()]
+            assert b
+        '''
+        assertScript '''
+            Boolean b = [false]
+            assert b
+        '''
+        assertScript '''
+            Boolean b = [true]
+            assert b
+        '''
+        assertScript '''
+            Boolean b = ['x']
+            assert b
+        '''
+        assertScript '''
+            Boolean b = [:]
+            assert !b
+        '''
+        assertScript '''
+            Boolean b = []
+            assert !b
+        '''
+    }
+
+    void testCoerceToClass() {
+        assertScript '''
+            Class c = 'java.lang.String'
+            assert String.class
+        '''
+        shouldFailWithMessages '''
+            Class c = []
+        ''', 'No matching constructor found: java.lang.Class()'
+        shouldFailWithMessages '''
+            Class c = [:]
+        ''', 'No matching constructor found: java.lang.Class(java.util.LinkedHashMap)'
+    }
+
+    // GROOVY-6803
+    void testCoerceToString() {
+        assertScript '''
+            String s = ['x']
+            assert s == '[x]'
+        '''
+        assertScript '''
+            String s = [:]
+            assert s == '{}'
+        '''
+        assertScript '''
+            String s = []
+            assert s == '[]'
+        '''
+    }
+}
diff --git a/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy b/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
index 08ba530..c00eaff 100644
--- a/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
@@ -37,7 +37,7 @@ class ConstructorsSTCTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             import java.awt.Dimension
             Dimension d = [100]
-        ''', 'No matching constructor found: java.awt.Dimension<init>(int)'
+        ''', 'No matching constructor found: java.awt.Dimension(int)'
     }
 
     void testWrongNumberOfArgumentsWithDefaultConstructor() {
@@ -62,7 +62,7 @@ class ConstructorsSTCTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             import java.awt.Dimension
             Dimension d = ['100','200']
-        ''', 'No matching constructor found: java.awt.Dimension<init>(java.lang.String, java.lang.String)'
+        ''', 'No matching constructor found: java.awt.Dimension(java.lang.String, java.lang.String)'
     }
 
     void testConstructFromListAndVariables() {
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/CoercionStaticCompileTests.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/CoercionStaticCompileTests.groovy
new file mode 100644
index 0000000..7506ceb
--- /dev/null
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/CoercionStaticCompileTests.groovy
@@ -0,0 +1,27 @@
+/*
+ *  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 org.codehaus.groovy.classgen.asm.sc
+
+import groovy.transform.stc.CoercionSTCTest
+
+/**
+ * Unit tests for static compilation : coercions.
+ */
+final class CoercionStaticCompileTests extends CoercionSTCTest implements StaticCompilationTestSupport {
+}