Skip to content

Commit 93eea14

Browse files
committed
GROOVY-10419
#1320
1 parent c869670 commit 93eea14

File tree

3 files changed

+60
-18
lines changed

3 files changed

+60
-18
lines changed

base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java

+27
Original file line numberDiff line numberDiff line change
@@ -4937,4 +4937,31 @@ public void testTypeChecked10414() {
49374937

49384938
runConformTest(sources, "barbaz");
49394939
}
4940+
4941+
@Test
4942+
public void testTypeChecked10419() {
4943+
assumeTrue(isParrotParser());
4944+
4945+
//@formatter:off
4946+
String[] sources = {
4947+
"Main.groovy",
4948+
"@groovy.transform.ToString\n" +
4949+
"class C {\n" +
4950+
" def p\n" +
4951+
" void setP(p) {\n" +
4952+
" this.p = p\n" +
4953+
" }\n" +
4954+
"}\n" +
4955+
"@groovy.transform.TypeChecked\n" +
4956+
"void test(C c) {\n" +
4957+
" c.p ?= 'x'\n" +
4958+
"}\n" +
4959+
"def c = new C()\n" +
4960+
"test(c)\n" +
4961+
"print c\n",
4962+
};
4963+
//@formatter:on
4964+
4965+
runConformTest(sources, "C(x)");
4966+
}
49404967
}

base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+12
Original file line numberDiff line numberDiff line change
@@ -1042,9 +1042,21 @@ private boolean ensureValidSetter(final Expression expression, final Expression
10421042
};
10431043
// GRECLIPSE end
10441044
// for compound assignment "x op= y" find type as if it was "x = (x op y)"
1045+
/* GRECLIPSE edit -- GROOVY-10419
10451046
Expression newRightExpression = isCompoundAssignment(expression)
10461047
? binX(leftExpression, getOpWithoutEqual(expression), rightExpression)
10471048
: rightExpression;
1049+
*/
1050+
Expression newRightExpression = rightExpression;
1051+
if (isCompoundAssignment(expression)) {
1052+
Token op = ((BinaryExpression) expression).getOperation();
1053+
if (op.getType() == ELVIS_EQUAL) { // GROOVY-10419: "x ?= y"
1054+
newRightExpression = new ElvisOperatorExpression(leftExpression, rightExpression);
1055+
} else {
1056+
newRightExpression = binX(leftExpression, getOpWithoutEqual(expression), rightExpression);
1057+
}
1058+
}
1059+
// GRECLIPSE end
10481060
MethodCallExpression call = callX(ve, setterInfo.name, newRightExpression);
10491061
call.setImplicitThis(false);
10501062
visitMethodCallExpression(call);

base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+21-18
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@
213213
import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
214214
import static org.codehaus.groovy.ast.tools.GeneralUtils.castX;
215215
import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
216+
import static org.codehaus.groovy.ast.tools.GeneralUtils.elvisX;
216217
import static org.codehaus.groovy.ast.tools.GeneralUtils.getGetterName;
217218
import static org.codehaus.groovy.ast.tools.GeneralUtils.getSetterName;
218219
import static org.codehaus.groovy.ast.tools.GeneralUtils.isOrImplements;
@@ -1014,9 +1015,16 @@ private boolean ensureValidSetter(final Expression expression, final Expression
10141015
// because we need to check if a setter uses @DelegatesTo
10151016
VariableExpression receiver = varX("%", setterInfo.receiverType);
10161017
// for "x op= y" expression, find type as if it was "x = x op y"
1017-
Expression newRightExpression = isCompoundAssignment(expression)
1018-
? binX(leftExpression, getOpWithoutEqual(expression), rightExpression)
1019-
: rightExpression;
1018+
Expression valueExpression = rightExpression;
1019+
if (isCompoundAssignment(expression)) {
1020+
Token op = ((BinaryExpression) expression).getOperation();
1021+
if (op.getType() == ELVIS_EQUAL) { // GROOVY-10419: "x ?= y"
1022+
valueExpression = elvisX(leftExpression, rightExpression);
1023+
} else {
1024+
op = Token.newSymbol(TokenUtil.removeAssignment(op.getType()), op.getStartLine(), op.getStartColumn());
1025+
valueExpression = binX(leftExpression, op, rightExpression);
1026+
}
1027+
}
10201028

10211029
Function<Expression, MethodNode> setterCall = right -> {
10221030
MethodCallExpression call = callX(receiver, setterInfo.name, right);
@@ -1033,14 +1041,14 @@ private boolean ensureValidSetter(final Expression expression, final Expression
10331041
return type;
10341042
};
10351043

1036-
MethodNode methodTarget = setterCall.apply(newRightExpression);
1044+
MethodNode methodTarget = setterCall.apply(valueExpression);
10371045
if (methodTarget == null && !isCompoundAssignment(expression)) {
10381046
// if no direct match, try implicit conversion
10391047
for (MethodNode setter : setterInfo.setters) {
10401048
ClassNode lType = setterType.apply(setter);
1041-
ClassNode rType = getDeclaredOrInferredType(newRightExpression);
1042-
if (checkCompatibleAssignmentTypes(lType, rType, newRightExpression, false)) {
1043-
methodTarget = setterCall.apply(castX(lType, newRightExpression));
1049+
ClassNode rType = getDeclaredOrInferredType(valueExpression);
1050+
if (checkCompatibleAssignmentTypes(lType, rType, valueExpression, false)) {
1051+
methodTarget = setterCall.apply(castX(lType, valueExpression));
10441052
if (methodTarget != null) {
10451053
break;
10461054
}
@@ -1060,7 +1068,7 @@ private boolean ensureValidSetter(final Expression expression, final Expression
10601068
return false;
10611069
} else {
10621070
ClassNode firstSetterType = setterType.apply(setterInfo.setters.get(0));
1063-
addAssignmentError(firstSetterType, getType(newRightExpression), expression);
1071+
addAssignmentError(firstSetterType, getType(valueExpression), expression);
10641072
return true;
10651073
}
10661074
}
@@ -1070,16 +1078,11 @@ private static boolean isClosureWithType(final ClassNode type) {
10701078
}
10711079

10721080
private static boolean isCompoundAssignment(final Expression exp) {
1073-
if (!(exp instanceof BinaryExpression)) return false;
1074-
int type = ((BinaryExpression) exp).getOperation().getType();
1075-
return isAssignment(type) && type != ASSIGN;
1076-
}
1077-
1078-
private static Token getOpWithoutEqual(final Expression exp) {
1079-
if (!(exp instanceof BinaryExpression)) return null; // should never happen
1080-
Token op = ((BinaryExpression) exp).getOperation();
1081-
int typeWithoutEqual = TokenUtil.removeAssignment(op.getType());
1082-
return new Token(typeWithoutEqual, op.getText() /* will do */, op.getStartLine(), op.getStartColumn());
1081+
if (exp instanceof BinaryExpression) {
1082+
Token op = ((BinaryExpression) exp).getOperation();
1083+
return isAssignment(op.getType()) && op.getType() != EQUAL;
1084+
}
1085+
return false;
10831086
}
10841087

10851088
protected ClassNode getOriginalDeclarationType(final Expression lhs) {

0 commit comments

Comments
 (0)