This is an automated email from the ASF dual-hosted git repository.
emilles 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 868dabf GROOVY-10325: STC: no error for covariant assignment
`map*.value = x`
868dabf is described below
commit 868dabf9fadbddd13c7e8859556e4ed2796f7e1b
Author: Eric Milles <[email protected]>
AuthorDate: Fri Oct 22 14:29:06 2021 -0500
GROOVY-10325: STC: no error for covariant assignment `map*.value = x`
Map<String,Object> map = ...
map.entrySet()[0].value = '' // already works without typecast
---
.../transform/stc/StaticTypeCheckingVisitor.java | 25 ++++++++--------
.../transform/stc/TypeInferenceSTCTest.groovy | 33 ++++++++++++++++++++++
2 files changed, 46 insertions(+), 12 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 9c2ce3d..4ab3b0f 100644
---
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -224,6 +224,7 @@ import static
org.codehaus.groovy.ast.tools.GeneralUtils.propX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
import static org.codehaus.groovy.ast.tools.GeneralUtils.thisPropX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.makeClassSafe0;
import static
org.codehaus.groovy.ast.tools.WideningCategories.isBigDecCategory;
import static
org.codehaus.groovy.ast.tools.WideningCategories.isBigIntCategory;
import static org.codehaus.groovy.ast.tools.WideningCategories.isDouble;
@@ -1193,14 +1194,12 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
return true;
}
- private ClassNode adjustTypeForSpreading(final ClassNode
inferredRightExpressionType, final Expression leftExpression) {
- // imagine we have: list*.foo = 100
- // then the assignment must be checked against [100], not 100
- ClassNode wrappedRHS = inferredRightExpressionType;
+ private ClassNode adjustTypeForSpreading(final ClassNode
rightExpressionType, final Expression leftExpression) {
+ // given "list*.foo = 100" or "map*.value = 100", then the assignment
must be checked against [100], not 100
if (leftExpression instanceof PropertyExpression &&
((PropertyExpression) leftExpression).isSpreadSafe()) {
- wrappedRHS = extension.buildListType(inferredRightExpressionType);
+ return extension.buildListType(rightExpressionType);
}
- return wrappedRHS;
+ return rightExpressionType;
}
private boolean addedReadOnlyPropertyError(final Expression expr) {
@@ -1776,13 +1775,15 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
// map*.property syntax acts on Entry
switch (pexp.getPropertyAsString()) {
case "key":
- ClassNode keyList = LIST_TYPE.getPlainNodeReference();
- keyList.setGenericsTypes(new GenericsType[]{gts[0]});
- return keyList;
+ return makeClassSafe0(LIST_TYPE, gts[0]);
case "value":
- ClassNode valueList = LIST_TYPE.getPlainNodeReference();
- valueList.setGenericsTypes(new GenericsType[]{gts[1]});
- return valueList;
+ GenericsType v = gts[1];
+ if (!v.isWildcard()
+ && !Modifier.isFinal(v.getType().getModifiers())
+ &&
typeCheckingContext.isTargetOfEnclosingAssignment(pexp)) {
+ v = GenericsUtils.buildWildcardType(v.getType()); //
GROOVY-10325
+ }
+ return makeClassSafe0(LIST_TYPE, v);
default:
addStaticTypeError("Spread operator on map only allows one
of [key,value]", pexp);
}
diff --git a/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
b/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
index b5645e0..6d1adfb 100644
--- a/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
+++ b/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
@@ -740,6 +740,39 @@ class TypeInferenceSTCTest extends
StaticTypeCheckingTestCase {
''', 'Cannot find matching method java.lang.Integer#toUpperCase()'
}
+ void testStarOperatorOnMap4() {
+ assertScript '''
+ def map = [x:1,y:2,z:3]
+ map*.value = 0
+
+ assert map*.value == [0,0,0]
+ '''
+
+ assertScript '''
+ Map<String,? extends Object> map = [x:1,y:2,z:3]
+ map*.value = 0
+
+ assert map*.value == [0,0,0]
+ '''
+
+ // GROOVY-10325
+ assertScript '''
+ Map<String,Object> map = [x:1,y:2,z:3]
+ map*.value = 0 // was: Cannot assign List<Integer> to List<Object>
+
+ assert map*.value == [0,0,0]
+ '''
+
+ shouldFailWithMessages '''
+ [x:1,y:2,z:3]*.value = ""
+ ''', 'Cannot assign java.util.List<java.lang.String> to:
java.util.List<java.lang.Integer>'
+
+ def fail = shouldFail '''
+ [x:1,y:2,z:3]*.key = ""
+ '''
+ assert fail == 'Cannot set readonly property: key for class:
java.util.LinkedHashMap$Entry'
+ }
+
void testFlowTypingWithStringVariable() {
// as anything can be assigned to a string, flow typing engine
// could "erase" the type of the original variable although is must not