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 2023/01/26 18:07:05 UTC
[groovy] 04/04: GROOVY-8136: STC: map literal assignment to interface that extends `Map`
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 b8a6b01e575433dafcb5563caa1b84f447499d83
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Sep 7 15:17:55 2022 -0500
GROOVY-8136: STC: map literal assignment to interface that extends `Map`
3_0_X backport
---
.../transform/stc/StaticTypeCheckingSupport.java | 2 ++
.../transform/stc/StaticTypeCheckingVisitor.java | 32 ++++++++++++----------
.../stc/ArraysAndCollectionsSTCTest.groovy | 9 ++++++
3 files changed, 28 insertions(+), 15 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index 795bf4e5c4..5a9c842fe7 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -59,6 +59,7 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
@@ -163,6 +164,7 @@ public abstract class StaticTypeCheckingSupport {
protected static final ClassNode ArrayList_TYPE = makeWithoutCaching(ArrayList.class);
protected static final ClassNode Collection_TYPE = makeWithoutCaching(Collection.class);
protected static final ClassNode Deprecated_TYPE = makeWithoutCaching(Deprecated.class);
+ protected static final ClassNode LinkedHashMap_TYPE = makeWithoutCaching(LinkedHashMap.class);
protected static final ClassNode LinkedHashSet_TYPE = makeWithoutCaching(LinkedHashSet.class);
protected static final Map<ClassNode, Integer> NUMBER_TYPES = Maps.of(
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 e4945662e5..598eff6c5b 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -236,6 +236,7 @@ import static org.codehaus.groovy.syntax.Types.MOD_EQUAL;
import static org.codehaus.groovy.syntax.Types.PLUS_PLUS;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.ArrayList_TYPE;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.Collection_TYPE;
+import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.LinkedHashMap_TYPE;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.LinkedHashSet_TYPE;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.Matcher_TYPE;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.NUMBER_OPS;
@@ -325,7 +326,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
protected static final MethodNode GET_THISOBJECT = CLOSURE_TYPE.getGetterMethod("getThisObject");
protected static final ClassNode DELEGATES_TO = ClassHelper.make(DelegatesTo.class);
protected static final ClassNode DELEGATES_TO_TARGET = ClassHelper.make(DelegatesTo.Target.class);
- protected static final ClassNode LINKEDHASHMAP_CLASSNODE = ClassHelper.make(LinkedHashMap.class);
+ protected static final ClassNode LINKEDHASHMAP_CLASSNODE = LinkedHashMap_TYPE; //TODO @Deprecated
protected static final ClassNode CLOSUREPARAMS_CLASSNODE = ClassHelper.make(ClosureParams.class);
protected static final ClassNode NAMED_PARAMS_CLASSNODE = ClassHelper.make(NamedParams.class);
protected static final ClassNode NAMED_PARAM_CLASSNODE = ClassHelper.make(NamedParam.class);
@@ -1290,19 +1291,18 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
- private void addMapAssignmentConstructorErrors(final ClassNode leftRedirect, final Expression leftExpression, final Expression rightExpression) {
- if ((leftExpression instanceof VariableExpression && ((VariableExpression) leftExpression).isDynamicTyped())
- || (isWildcardLeftHandSide(leftRedirect) && !leftRedirect.equals(CLASS_Type)) // GROOVY-6802, GROOVY-6803
- || implementsInterfaceOrIsSubclassOf(leftRedirect, MAP_TYPE)) {
+ private void addMapAssignmentConstructorErrors(final ClassNode leftRedirect, final Expression leftExpression, final MapExpression rightExpression) {
+ if (!isConstructorAbbreviation(leftRedirect, rightExpression)
+ // GROOVY-6802, GROOVY-6803: Object, String or [Bb]oolean target
+ || (isWildcardLeftHandSide(leftRedirect) && !leftRedirect.equals(CLASS_Type))) {
return;
}
// groovy constructor shorthand: A a = [x:2, y:3]
- ClassNode[] argTypes = getArgumentTypes(args(rightExpression));
+ ClassNode[] argTypes = {getType(rightExpression)};
checkGroovyStyleConstructor(leftRedirect, argTypes, rightExpression);
// perform additional type checking on arguments
- MapExpression mapExpression = (MapExpression) rightExpression;
- checkGroovyConstructorMap(leftExpression, leftRedirect, mapExpression);
+ checkGroovyConstructorMap(leftExpression, leftRedirect, rightExpression);
}
private void checkTypeGenerics(final ClassNode leftExpressionType, final ClassNode rightExpressionType, final Expression rightExpression) {
@@ -1327,6 +1327,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
return !(ArrayList_TYPE.isDerivedFrom(leftType) || ArrayList_TYPE.implementsInterface(leftType)
|| LinkedHashSet_TYPE.isDerivedFrom(leftType) || LinkedHashSet_TYPE.implementsInterface(leftType));
}
+ if (rightExpression instanceof MapExpression) {
+ return !(LinkedHashMap_TYPE.isDerivedFrom(leftType) || LinkedHashMap_TYPE.implementsInterface(leftType));
+ }
return false;
}
@@ -1354,7 +1357,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (rightExpression instanceof ListExpression) {
addListAssignmentConstructorErrors(lTypeRedirect, leftExpressionType, rTypeInferred, rightExpression, assignmentExpression);
} else if (rightExpression instanceof MapExpression) {
- addMapAssignmentConstructorErrors(lTypeRedirect, leftExpression, rightExpression);
+ addMapAssignmentConstructorErrors(lTypeRedirect, leftExpression, (MapExpression)rightExpression);
}
if (!hasGStringStringError(leftExpressionType, rTypeAdjusted, rightExpression)
&& !isConstructorAbbreviation(leftExpressionType, rightExpression)) {
@@ -1438,10 +1441,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
constructors = findMethod(node, "<init>", arguments);
if (constructors.isEmpty()) {
- if (isBeingCompiled(node) && !node.isInterface() && arguments.length == 1 && arguments[0].equals(LINKEDHASHMAP_CLASSNODE)) {
+ if (isBeingCompiled(node) && !node.isInterface() && arguments.length == 1 && arguments[0].equals(LinkedHashMap_TYPE)) {
// there will be a default hash map constructor added later
- ConstructorNode cn = new ConstructorNode(Opcodes.ACC_PUBLIC, new Parameter[]{new Parameter(LINKEDHASHMAP_CLASSNODE, "args")}, ClassNode.EMPTY_ARRAY, EmptyStatement.INSTANCE);
- return cn;
+ return new ConstructorNode(Opcodes.ACC_PUBLIC, new Parameter[]{new Parameter(LinkedHashMap_TYPE, "args")}, ClassNode.EMPTY_ARRAY, EmptyStatement.INSTANCE);
} else {
addStaticTypeError("No matching constructor found: " + prettyPrintTypeName(node) + toMethodParametersString("", arguments), source);
return null;
@@ -4592,7 +4594,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
* the literal may be composed of sub-types of {@code Type}. In these cases,
* {@code ArrayList<Type>} is an appropriate result type for the expression.
*/
- private ClassNode getLiteralResultType(final ClassNode targetType, final ClassNode sourceType, final ClassNode baseType) {
+ private static ClassNode getLiteralResultType(final ClassNode targetType, final ClassNode sourceType, final ClassNode baseType) {
ClassNode resultType = sourceType.equals(baseType) ? sourceType
: GenericsUtils.parameterizeType(sourceType, baseType.getPlainNodeReference());
@@ -4608,7 +4610,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
return resultType;
}
- private ClassNode getMathResultType(final int op, final ClassNode leftRedirect, final ClassNode rightRedirect, final String operationName) {
+ private static ClassNode getMathResultType(final int op, final ClassNode leftRedirect, final ClassNode rightRedirect, final String operationName) {
if (isNumberType(leftRedirect) && isNumberType(rightRedirect)) {
if (isOperationInGroup(op)) {
if (isIntCategory(leftRedirect) && isIntCategory(rightRedirect)) return int_TYPE;
@@ -5311,7 +5313,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
protected ClassNode inferMapExpressionType(final MapExpression map) {
- ClassNode mapType = LINKEDHASHMAP_CLASSNODE.getPlainNodeReference();
+ ClassNode mapType = LinkedHashMap_TYPE.getPlainNodeReference();
List<MapEntryExpression> entryExpressions = map.getMapEntryExpressions();
int nExpressions = entryExpressions.size();
if (nExpressions == 0) return mapType;
diff --git a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
index 970d5d63ec..f4e0ab4f4b 100644
--- a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
@@ -908,4 +908,13 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
assert set.last() == 3
'''
}
+
+ // GROOVY-8136
+ void testInterfaceThatExtendsMapInitializedByMapLiteral() {
+ shouldFailWithMessages '''
+ interface MVM<K, V> extends Map<K, List<V>> { }
+ MVM map = [:] // no STC error; fails at runtime
+ ''',
+ 'No matching constructor found'
+ }
}