Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

$x = 0;
2 * $x += 42 and true;

?>
-----
<?php

$x = 0;
2 * ($x += 42) && true;

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

$t = 0;
(int) $t = 42 and true;

?>
-----
<?php

$t = 0;
(int) ($t = 42) && true;

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

$z = 0;
-$z += 42 and true;

?>
-----
<?php

$z = 0;
-($z += 42) && true;

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

$y = 0;
!$y = 42 and true;

?>
-----
<?php

$y = 0;
!($y = 42) && true;

?>
39 changes: 37 additions & 2 deletions src/PhpParser/Printer/BetterStandardPrinter.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,17 @@
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
use PhpParser\Node\Expr\BinaryOp\BooleanOr;
use PhpParser\Node\Expr\BinaryOp\Pipe;
use PhpParser\Node\Expr\BitwiseNot;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Expr\CallLike;
use PhpParser\Node\Expr\Cast;
use PhpParser\Node\Expr\ErrorSuppress;
use PhpParser\Node\Expr\Instanceof_;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\Print_;
use PhpParser\Node\Expr\Ternary;
use PhpParser\Node\Expr\UnaryMinus;
use PhpParser\Node\Expr\UnaryPlus;
use PhpParser\Node\Expr\Yield_;
use PhpParser\Node\Expr\YieldFrom;
use PhpParser\Node\InterpolatedStringPart;
Expand Down Expand Up @@ -470,6 +476,13 @@ private function wrapBinaryOpWithBrackets(Node $node): void
$node->expr->setAttribute(AttributeKey::ORIGINAL_NODE, null);
}

// an assignment nested directly under a unary operator (e.g. "!$x = 1", "-$x += 1",
// "(int) $x = 1") loses its required parentheses once "and"/"or" become "&&"/"||"; force
// the assignment to reprint so the pretty-printer re-adds them based on precedence
if ($this->isUnaryExprWithAssign($node) && $this->origTokens instanceof TokenStream) {
$node->expr->setAttribute(AttributeKey::ORIGINAL_NODE, null);
}

if (! $node instanceof BinaryOp) {
return;
}
Expand All @@ -478,13 +491,13 @@ private function wrapBinaryOpWithBrackets(Node $node): void
return;
}

if ($node->left instanceof Assign
if ($this->isAssignExpr($node->left)
&& $this->origTokens instanceof TokenStream
&& ! $this->origTokens->haveParens($node->left->getStartTokenPos(), $node->left->getEndTokenPos())) {
$node->left->setAttribute(AttributeKey::ORIGINAL_NODE, null);
}

if ($node->right instanceof Assign && $this->origTokens instanceof TokenStream) {
if ($this->isAssignExpr($node->right) && $this->origTokens instanceof TokenStream) {
$node->right->setAttribute(AttributeKey::ORIGINAL_NODE, null);
}

Expand All @@ -507,6 +520,28 @@ private function wrapBinaryOpWithBrackets(Node $node): void
}
}

/**
* @see https://github.com/rectorphp/rector/issues/9807
*/
private function isUnaryExprWithAssign(Node $node): bool
{
if (! $node instanceof BooleanNot
&& ! $node instanceof BitwiseNot
&& ! $node instanceof UnaryMinus
&& ! $node instanceof UnaryPlus
&& ! $node instanceof ErrorSuppress
&& ! $node instanceof Cast) {
return false;
}

return $this->isAssignExpr($node->expr);
}

private function isAssignExpr(Node $node): bool
{
return $node instanceof Assign || $node instanceof AssignOp || $node instanceof AssignRef;
}

/**
* @see https://github.com/rectorphp/rector/issues/9800
*/
Expand Down
Loading