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 {
+}