From c5a8ceff480af59a8f6bde8a2c0ab8e7262677e9 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 2 Aug 2026 12:40:38 +0200 Subject: [PATCH] [DeadCode] Skip RemoveDefaultValueFromAssignedPropertyRector on early return in constructor-called method --- ...skip_early_return_in_called_method.php.inc | 23 ++++++++++ ...DefaultValueFromAssignedPropertyRector.php | 45 ++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 rules-tests/DeadCode/Rector/Property/RemoveDefaultValueFromAssignedPropertyRector/Fixture/skip_early_return_in_called_method.php.inc diff --git a/rules-tests/DeadCode/Rector/Property/RemoveDefaultValueFromAssignedPropertyRector/Fixture/skip_early_return_in_called_method.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveDefaultValueFromAssignedPropertyRector/Fixture/skip_early_return_in_called_method.php.inc new file mode 100644 index 00000000000..161fcb50323 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveDefaultValueFromAssignedPropertyRector/Fixture/skip_early_return_in_called_method.php.inc @@ -0,0 +1,23 @@ +fetchLeads(); + } + + private function fetchLeads(): void + { + if ($this->campaignMembers === []) { + return; + } + + $this->unknownLeadIds = $this->campaignMembers; + } +} diff --git a/rules/DeadCode/Rector/Property/RemoveDefaultValueFromAssignedPropertyRector.php b/rules/DeadCode/Rector/Property/RemoveDefaultValueFromAssignedPropertyRector.php index d6498647ad6..8751d62de99 100644 --- a/rules/DeadCode/Rector/Property/RemoveDefaultValueFromAssignedPropertyRector.php +++ b/rules/DeadCode/Rector/Property/RemoveDefaultValueFromAssignedPropertyRector.php @@ -8,8 +8,10 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Return_; use Rector\Configuration\Parameter\FeatureFlags; use Rector\NodeAnalyzer\PropertyFetchAnalyzer; @@ -85,7 +87,7 @@ public function refactor(Node $node): ?Node } // early return can skip the assign, so the default value is still needed - if ($this->betterNodeFinder->hasInstancesOfInFunctionLikeScoped($constructClassMethod, Return_::class)) { + if ($this->hasEarlyReturn($node, $constructClassMethod)) { return null; } @@ -138,6 +140,47 @@ public function refactor(Node $node): ?Node return null; } + /** + * The constructor itself, or any local method it calls, can skip the assign with an early return + */ + private function hasEarlyReturn(Class_ $class, ClassMethod $constructClassMethod): bool + { + if ($this->betterNodeFinder->hasInstancesOfInFunctionLikeScoped($constructClassMethod, Return_::class)) { + return true; + } + + foreach ((array) $constructClassMethod->stmts as $stmt) { + if (! $stmt instanceof Expression) { + continue; + } + + if (! $stmt->expr instanceof MethodCall) { + continue; + } + + $methodCall = $stmt->expr; + if (! $this->isName($methodCall->var, 'this')) { + continue; + } + + $methodName = $this->getName($methodCall->name); + if ($methodName === null) { + continue; + } + + $calledClassMethod = $class->getMethod($methodName); + if (! $calledClassMethod instanceof ClassMethod) { + continue; + } + + if ($this->betterNodeFinder->hasInstancesOfInFunctionLikeScoped($calledClassMethod, Return_::class)) { + return true; + } + } + + return false; + } + private function isAssignedViaArrayDimFetch(Class_ $class, string $propertyName): bool { return $this->betterNodeFinder->findFirst($class, function (Node $subNode) use ($propertyName): bool {