@@ -479,7 +479,7 @@ private function wrapBinaryOpWithBrackets(Node $node): void
479479 // an assignment nested directly under a unary operator (e.g. "!$x = 1", "-$x += 1",
480480 // "(int) $x = 1") loses its required parentheses once "and"/"or" become "&&"/"||"; force
481481 // the assignment to reprint so the pretty-printer re-adds them based on precedence
482- if ($ this ->isUnaryExprWithAssign ($ node ) && $ this ->origTokens instanceof TokenStream) {
482+ if ($ node -> getStartTokenPos () >= 0 && $ this ->isUnaryExprWithAssign ($ node ) && $ this ->origTokens instanceof TokenStream) {
483483 $ node ->expr ->setAttribute (AttributeKey::ORIGINAL_NODE , null );
484484 }
485485
@@ -491,16 +491,29 @@ private function wrapBinaryOpWithBrackets(Node $node): void
491491 return ;
492492 }
493493
494- if ($ this -> isAssignExpr ( $ node ->left )
494+ if ($ node ->left instanceof Assign
495495 && $ this ->origTokens instanceof TokenStream
496496 && ! $ this ->origTokens ->haveParens ($ node ->left ->getStartTokenPos (), $ node ->left ->getEndTokenPos ())) {
497497 $ node ->left ->setAttribute (AttributeKey::ORIGINAL_NODE , null );
498498 }
499499
500- if ($ this -> isAssignExpr ( $ node ->right ) && $ this ->origTokens instanceof TokenStream) {
500+ if ($ node ->right instanceof Assign && $ this ->origTokens instanceof TokenStream) {
501501 $ node ->right ->setAttribute (AttributeKey::ORIGINAL_NODE , null );
502502 }
503503
504+ // a compound assignment ("+=", "-=", …) nested in an originally printed operand keeps its
505+ // verbatim, parenless tokens; force it to reprint so the required parentheses are re-added.
506+ // only for source operands: freshly created binary nodes already get them from precedence
507+ if ($ node ->getStartTokenPos () >= 0 ) {
508+ if ($ node ->left instanceof AssignOp || $ node ->left instanceof AssignRef) {
509+ $ node ->left ->setAttribute (AttributeKey::ORIGINAL_NODE , null );
510+ }
511+
512+ if ($ node ->right instanceof AssignOp || $ node ->right instanceof AssignRef) {
513+ $ node ->right ->setAttribute (AttributeKey::ORIGINAL_NODE , null );
514+ }
515+ }
516+
504517 // boolean operators (&&, ||) bind tighter than the logical ones (and, or); when
505518 // LogicalToBooleanRector swaps them, operands with lower precedence must be reprinted
506519 // so the pretty-printer re-adds the parentheses that keep the original semantics
0 commit comments