From 0433f40b4d5e6967c4f22a6da8f6c57335a22319 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 29 Jun 2026 12:28:10 +0200 Subject: [PATCH] [DeadCode] Keep type-assertion guard in BooleanAnd when right operand reuses the variable --- ...c => boolean_and_guard_not_reused.php.inc} | 12 ++-- ...p_type_guard_reused_in_boolean_and.php.inc | 15 +++++ .../If_/RemoveAlwaysTrueIfConditionRector.php | 67 +++++++++++++++++++ 3 files changed, 88 insertions(+), 6 deletions(-) rename rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/{boolean_and.php.inc => boolean_and_guard_not_reused.php.inc} (53%) create mode 100644 rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_type_guard_reused_in_boolean_and.php.inc diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/boolean_and.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/boolean_and_guard_not_reused.php.inc similarity index 53% rename from rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/boolean_and.php.inc rename to rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/boolean_and_guard_not_reused.php.inc index fe0dec6a1f6..06c11f215ca 100644 --- a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/boolean_and.php.inc +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/boolean_and_guard_not_reused.php.inc @@ -2,11 +2,11 @@ namespace Rector\Tests\DeadCode\Rector\If_\RemoveAlwaysTrueIfConditionRector\Fixture; -final class BooleanAndWithObject +final class BooleanAndGuardNotReused { - public function run(\stdClass $someObject) + public function run(\stdClass $someObject, bool $flag) { - if (is_object($someObject) && method_exists($someObject, 'some_method')) { + if (is_object($someObject) && $flag) { return 100; } @@ -20,11 +20,11 @@ final class BooleanAndWithObject namespace Rector\Tests\DeadCode\Rector\If_\RemoveAlwaysTrueIfConditionRector\Fixture; -final class BooleanAndWithObject +final class BooleanAndGuardNotReused { - public function run(\stdClass $someObject) + public function run(\stdClass $someObject, bool $flag) { - if (method_exists($someObject, 'some_method')) { + if ($flag) { return 100; } diff --git a/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_type_guard_reused_in_boolean_and.php.inc b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_type_guard_reused_in_boolean_and.php.inc new file mode 100644 index 00000000000..e364711d69a --- /dev/null +++ b/rules-tests/DeadCode/Rector/If_/RemoveAlwaysTrueIfConditionRector/Fixture/skip_type_guard_reused_in_boolean_and.php.inc @@ -0,0 +1,15 @@ +isReusedTypeGuard($booleanAnd)) { + return null; + } + $if->cond = $booleanAnd->right; return $if; } + + private function isReusedTypeGuard(BooleanAnd $booleanAnd): bool + { + /** @var FuncCall[] $funcCalls */ + $funcCalls = $this->betterNodeFinder->findInstancesOf($booleanAnd->left, [FuncCall::class]); + foreach ($funcCalls as $funcCall) { + if (! $this->isNames($funcCall, self::TYPE_ASSERTION_FUNCTIONS)) { + continue; + } + + $args = $funcCall->getArgs(); + if (! isset($args[0])) { + continue; + } + + $subject = $args[0]->value; + if (! $subject instanceof Variable) { + continue; + } + + $isReusedOnRight = (bool) $this->betterNodeFinder->findFirst( + $booleanAnd->right, + fn (Node $subNode): bool => $subNode instanceof Variable && $this->nodeComparator->areNodesEqual( + $subNode, + $subject + ) + ); + + if ($isReusedOnRight) { + return true; + } + } + + return false; + } }