213213import static org .codehaus .groovy .ast .tools .GeneralUtils .callX ;
214214import static org .codehaus .groovy .ast .tools .GeneralUtils .castX ;
215215import static org .codehaus .groovy .ast .tools .GeneralUtils .constX ;
216+ import static org .codehaus .groovy .ast .tools .GeneralUtils .elvisX ;
216217import static org .codehaus .groovy .ast .tools .GeneralUtils .getGetterName ;
217218import static org .codehaus .groovy .ast .tools .GeneralUtils .getSetterName ;
218219import 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