Skip to content

Commit

Permalink
GROOVY-10419
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Dec 17, 2021
1 parent c869670 commit 93eea14
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4937,4 +4937,31 @@ public void testTypeChecked10414() {

runConformTest(sources, "barbaz");
}

@Test
public void testTypeChecked10419() {
assumeTrue(isParrotParser());

//@formatter:off
String[] sources = {
"Main.groovy",
"@groovy.transform.ToString\n" +
"class C {\n" +
" def p\n" +
" void setP(p) {\n" +
" this.p = p\n" +
" }\n" +
"}\n" +
"@groovy.transform.TypeChecked\n" +
"void test(C c) {\n" +
" c.p ?= 'x'\n" +
"}\n" +
"def c = new C()\n" +
"test(c)\n" +
"print c\n",
};
//@formatter:on

runConformTest(sources, "C(x)");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1042,9 +1042,21 @@ private boolean ensureValidSetter(final Expression expression, final Expression
};
// GRECLIPSE end
// for compound assignment "x op= y" find type as if it was "x = (x op y)"
/* GRECLIPSE edit -- GROOVY-10419
Expression newRightExpression = isCompoundAssignment(expression)
? binX(leftExpression, getOpWithoutEqual(expression), rightExpression)
: rightExpression;
*/
Expression newRightExpression = rightExpression;
if (isCompoundAssignment(expression)) {
Token op = ((BinaryExpression) expression).getOperation();
if (op.getType() == ELVIS_EQUAL) { // GROOVY-10419: "x ?= y"
newRightExpression = new ElvisOperatorExpression(leftExpression, rightExpression);
} else {
newRightExpression = binX(leftExpression, getOpWithoutEqual(expression), rightExpression);
}
}
// GRECLIPSE end
MethodCallExpression call = callX(ve, setterInfo.name, newRightExpression);
call.setImplicitThis(false);
visitMethodCallExpression(call);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@
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;
Expand Down Expand Up @@ -1014,9 +1015,16 @@ private boolean ensureValidSetter(final Expression expression, final Expression
// 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);
Expand All @@ -1033,14 +1041,14 @@ private boolean ensureValidSetter(final Expression expression, final Expression
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;
}
Expand All @@ -1060,7 +1068,7 @@ private boolean ensureValidSetter(final Expression expression, final Expression
return false;
} else {
ClassNode firstSetterType = setterType.apply(setterInfo.setters.get(0));
addAssignmentError(firstSetterType, getType(newRightExpression), expression);
addAssignmentError(firstSetterType, getType(valueExpression), expression);
return true;
}
}
Expand All @@ -1070,16 +1078,11 @@ private static boolean isClosureWithType(final ClassNode type) {
}

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) {
Expand Down

0 comments on commit 93eea14

Please sign in to comment.