diff --git a/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/assign_op.php.inc b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/assign_op.php.inc new file mode 100644 index 00000000000..39e35f69da0 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/assign_op.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/elvis.php.inc b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/elvis.php.inc new file mode 100644 index 00000000000..ea9b5b7b37e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/elvis.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/print.php.inc b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/print.php.inc new file mode 100644 index 00000000000..41518119307 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/print.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/ternary.php.inc b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/ternary.php.inc new file mode 100644 index 00000000000..57b13b302a1 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/ternary.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/yield.php.inc b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/yield.php.inc new file mode 100644 index 00000000000..a21ed6326c2 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/LogicalAnd/LogicalToBooleanRector/Fixture/yield.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/src/PhpParser/Printer/BetterStandardPrinter.php b/src/PhpParser/Printer/BetterStandardPrinter.php index 5dc11f7e3ed..b854b668ed9 100644 --- a/src/PhpParser/Printer/BetterStandardPrinter.php +++ b/src/PhpParser/Printer/BetterStandardPrinter.php @@ -14,13 +14,19 @@ use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\AssignOp; +use PhpParser\Node\Expr\AssignRef; use PhpParser\Node\Expr\BinaryOp; +use PhpParser\Node\Expr\BinaryOp\BooleanAnd; +use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Expr\BinaryOp\Pipe; use PhpParser\Node\Expr\CallLike; use PhpParser\Node\Expr\Instanceof_; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\Print_; use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Expr\Yield_; +use PhpParser\Node\Expr\YieldFrom; use PhpParser\Node\InterpolatedStringPart; use PhpParser\Node\Scalar\InterpolatedString; use PhpParser\Node\Scalar\String_; @@ -482,6 +488,14 @@ private function wrapBinaryOpWithBrackets(Node $node): void $node->right->setAttribute(AttributeKey::ORIGINAL_NODE, null); } + // boolean operators (&&, ||) bind tighter than the logical ones (and, or); when + // LogicalToBooleanRector swaps them, operands with lower precedence must be reprinted + // so the pretty-printer re-adds the parentheses that keep the original semantics + if ($node instanceof BooleanAnd || $node instanceof BooleanOr) { + $this->reprintLowerPrecedenceOperand($node->left); + $this->reprintLowerPrecedenceOperand($node->right); + } + if ($node->left instanceof BinaryOp && $node->left->getAttribute(AttributeKey::ORIGINAL_NODE) instanceof Node) { $node->left->setAttribute(AttributeKey::ORIGINAL_NODE, null); @@ -493,6 +507,29 @@ private function wrapBinaryOpWithBrackets(Node $node): void } } + /** + * @see https://github.com/rectorphp/rector/issues/9800 + */ + private function reprintLowerPrecedenceOperand(Node $node): void + { + if (! $node instanceof AssignRef + && ! $node instanceof AssignOp + && ! $node instanceof Ternary + && ! $node instanceof Print_ + && ! $node instanceof Yield_ + && ! $node instanceof YieldFrom) { + return; + } + + $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); + + // the format-preserving printer keeps yield's precedence parentheses only for the + // "yield from" and empty "yield" forms; "yield " needs them added explicitly + if ($node instanceof Yield_ && $node->value instanceof Expr) { + $node->setAttribute(AttributeKey::WRAPPED_IN_PARENTHESES, true); + } + } + /** * ensure left side is assign and right side is just created *