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 2021/12/17 00:15:35 UTC
[groovy] branch master updated: GROOVY-10419: STC: fix overflow for elvis assignment using setter method
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 2dd2144 GROOVY-10419: STC: fix overflow for elvis assignment using setter method
2dd2144 is described below
commit 2dd2144292d4d8b291700370d0f21b8379462bc9
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Dec 16 17:57:40 2021 -0600
GROOVY-10419: STC: fix overflow for elvis assignment using setter method
---
.../transform/stc/StaticTypeCheckingVisitor.java | 39 ++++++------
.../groovy/transform/stc/STCAssignmentTest.groovy | 73 +++++++++++++++-------
2 files changed, 70 insertions(+), 42 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 86bf51c..c0e5ea5 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -214,6 +214,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.binX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.castX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.elvisX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.getGetterName;
import static org.codehaus.groovy.ast.tools.GeneralUtils.getSetterName;
import static org.codehaus.groovy.ast.tools.GeneralUtils.isOrImplements;
@@ -999,9 +1000,16 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
// because we need to check if a setter uses @DelegatesTo
VariableExpression receiver = varX("%", setterInfo.receiverType);
// for "x op= y" expression, find type as if it was "x = x op y"
- Expression newRightExpression = isCompoundAssignment(expression)
- ? binX(leftExpression, getOpWithoutEqual(expression), rightExpression)
- : rightExpression;
+ Expression valueExpression = rightExpression;
+ if (isCompoundAssignment(expression)) {
+ Token op = ((BinaryExpression) expression).getOperation();
+ if (op.getType() == ELVIS_EQUAL) { // GROOVY-10419: "x ?= y"
+ valueExpression = elvisX(leftExpression, rightExpression);
+ } else {
+ op = Token.newSymbol(TokenUtil.removeAssignment(op.getType()), op.getStartLine(), op.getStartColumn());
+ valueExpression = binX(leftExpression, op, rightExpression);
+ }
+ }
Function<Expression, MethodNode> setterCall = right -> {
MethodCallExpression call = callX(receiver, setterInfo.name, right);
@@ -1018,14 +1026,14 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
return type;
};
- MethodNode methodTarget = setterCall.apply(newRightExpression);
+ MethodNode methodTarget = setterCall.apply(valueExpression);
if (methodTarget == null && !isCompoundAssignment(expression)) {
// if no direct match, try implicit conversion
for (MethodNode setter : setterInfo.setters) {
ClassNode lType = setterType.apply(setter);
- ClassNode rType = getDeclaredOrInferredType(newRightExpression);
- if (checkCompatibleAssignmentTypes(lType, rType, newRightExpression, false)) {
- methodTarget = setterCall.apply(castX(lType, newRightExpression));
+ ClassNode rType = getDeclaredOrInferredType(valueExpression);
+ if (checkCompatibleAssignmentTypes(lType, rType, valueExpression, false)) {
+ methodTarget = setterCall.apply(castX(lType, valueExpression));
if (methodTarget != null) {
break;
}
@@ -1045,7 +1053,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
return false;
} else {
ClassNode firstSetterType = setterType.apply(setterInfo.setters.get(0));
- addAssignmentError(firstSetterType, getType(newRightExpression), expression);
+ addAssignmentError(firstSetterType, getType(valueExpression), expression);
return true;
}
}
@@ -1055,16 +1063,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
private static boolean isCompoundAssignment(final Expression exp) {
- if (!(exp instanceof BinaryExpression)) return false;
- int type = ((BinaryExpression) exp).getOperation().getType();
- return isAssignment(type) && type != ASSIGN;
- }
-
- private static Token getOpWithoutEqual(final Expression exp) {
- if (!(exp instanceof BinaryExpression)) return null; // should never happen
- Token op = ((BinaryExpression) exp).getOperation();
- int typeWithoutEqual = TokenUtil.removeAssignment(op.getType());
- return new Token(typeWithoutEqual, op.getText() /* will do */, op.getStartLine(), op.getStartColumn());
+ if (exp instanceof BinaryExpression) {
+ Token op = ((BinaryExpression) exp).getOperation();
+ return isAssignment(op.getType()) && op.getType() != EQUAL;
+ }
+ return false;
}
protected ClassNode getOriginalDeclarationType(final Expression lhs) {
diff --git a/src/test/groovy/transform/stc/STCAssignmentTest.groovy b/src/test/groovy/transform/stc/STCAssignmentTest.groovy
index adc5d75..2af8030 100644
--- a/src/test/groovy/transform/stc/STCAssignmentTest.groovy
+++ b/src/test/groovy/transform/stc/STCAssignmentTest.groovy
@@ -1178,16 +1178,16 @@ class STCAssignmentTest extends StaticTypeCheckingTestCase {
void testMultiAssign() {
assertScript '''
- def m() {
- def row = ['', '', '']
- def (left, right) = [row[0], row[1]]
- left.toUpperCase()
- }
+ def m() {
+ def row = ['', '', '']
+ def (left, right) = [row[0], row[1]]
+ left.toUpperCase()
+ }
'''
}
// GROOVY-8220
- void testFlowTypingParameterTempTypeAssignmentTracking() {
+ void testFlowTypingParameterTempTypeAssignmentTracking1() {
assertScript '''
class Foo {
CharSequence makeEnv( env, StringBuilder result = new StringBuilder() ) {
@@ -1202,7 +1202,10 @@ class STCAssignmentTest extends StaticTypeCheckingTestCase {
}
assert new Foo().makeEnv('X=1') == 'export X=1;'
'''
- // GROOVY-8237
+ }
+
+ // GROOVY-8237
+ void testFlowTypingParameterTempTypeAssignmentTracking2() {
assertScript '''
class Foo {
String parse(Reader reader) {
@@ -1216,7 +1219,7 @@ class STCAssignmentTest extends StaticTypeCheckingTestCase {
'''
}
- void testFlowTypingParameterTempTypeAssignmentTrackingWithGenerics() {
+ void testFlowTypingParameterTempTypeAssignmentTracking3() {
assertScript '''
class M {
Map<String, List<Object>> mvm = new HashMap<String, List<Object>>()
@@ -1246,27 +1249,49 @@ class STCAssignmentTest extends StaticTypeCheckingTestCase {
void testNarrowingConversion() {
assertScript '''
- interface A1{}
- interface A2 extends A1{}
-
- class C1 implements A1{}
-
- def m(A2 a2) {
- C1 c1 = (C1) a2
- }
+ interface A {
+ }
+ interface B extends A {
+ }
+ class C implements B {
+ }
+ def m(B b) {
+ C c = (C) b
+ }
'''
}
void testFinalNarrowingConversion() {
shouldFailWithMessages '''
- interface A1{}
- interface A2 extends A1{}
-
- final class C1 implements A1{}
+ interface I {
+ }
+ interface B extends I {
+ }
+ final class C implements I {
+ }
+ def m(B b) {
+ C c = (C) b
+ }
+ ''',
+ 'Inconvertible types: cannot cast B to C'
+ }
- def m(A2 a2) {
- C1 c1 = (C1) a2
- }
- ''', 'Inconvertible types: cannot cast A2 to C1'
+ // GROOVY-10419
+ void testElvisAssignmentAndSetter() {
+ assertScript '''
+ class C {
+ def p
+ void setP(p) {
+ this.p = p
+ }
+ }
+ @groovy.transform.TypeChecked
+ void test(C c) {
+ c.p ?= 'x'
+ }
+ def c = new C()
+ test(c)
+ assert c.p == 'x'
+ '''
}
}