diff --git a/config/set/early-return.php b/config/set/early-return.php index a8a766f5281..19ac6d20bf8 100644 --- a/config/set/early-return.php +++ b/config/set/early-return.php @@ -14,6 +14,7 @@ use Rector\EarlyReturn\Rector\Return_\PreparedValueToEarlyReturnRector; use Rector\EarlyReturn\Rector\Return_\ReturnBinaryAndToEarlyReturnRector; use Rector\EarlyReturn\Rector\Return_\ReturnBinaryOrToEarlyReturnRector; +use Rector\EarlyReturn\Rector\StmtsAwareInterface\ReturnEarlyIfVariableRector; return static function (RectorConfig $rectorConfig): void { $rectorConfig->rule(ChangeNestedForeachIfsToEarlyContinueRector::class); @@ -27,4 +28,5 @@ $rectorConfig->rule(ReturnAfterToEarlyOnBreakRector::class); $rectorConfig->rule(PreparedValueToEarlyReturnRector::class); $rectorConfig->rule(ReturnBinaryOrToEarlyReturnRector::class); + $rectorConfig->rule(ReturnEarlyIfVariableRector::class); }; diff --git a/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/Fixture/some_class.php.inc b/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..9c28ce6f1b3 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/Fixture/some_class.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/ReturnEarlyIfVariableRectorTest.php b/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/ReturnEarlyIfVariableRectorTest.php new file mode 100644 index 00000000000..f094f324d99 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/ReturnEarlyIfVariableRectorTest.php @@ -0,0 +1,33 @@ +doTestFileInfo($fileInfo); + } + + /** + * @return Iterator + */ + public function provideData(): Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/config/configured_rule.php b/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/config/configured_rule.php new file mode 100644 index 00000000000..371aa848c20 --- /dev/null +++ b/rules-tests/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(ReturnEarlyIfVariableRector::class); +}; diff --git a/rules/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector.php b/rules/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector.php new file mode 100644 index 00000000000..84fe76439ba --- /dev/null +++ b/rules/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector.php @@ -0,0 +1,148 @@ +> + */ + public function getNodeTypes(): array + { + return [StmtsAwareInterface::class]; + } + + /** + * @param StmtsAwareInterface $node + */ + public function refactor(Node $node): ?Node + { + $stmts = (array) $node->stmts; + + foreach ($stmts as $key => $stmt) { + $returnVariable = $this->matchNextStmtReturnVariable($node, $key); + if (! $returnVariable instanceof Variable) { + continue; + } + + if ($stmt instanceof If_ && $stmt->else === null && $stmt->elseifs === []) { + // is single condition if + $if = $stmt; + if (count($if->stmts) !== 1) { + continue; + } + + $onlyIfStmt = $if->stmts[0]; + $assignedExpr = $this->matchOnlyIfStmtReturnExpr($onlyIfStmt, $returnVariable); + if (! $assignedExpr instanceof Expr) { + continue; + } + + $if->stmts[0] = new Return_($assignedExpr); + return $node; + } + } + + return null; + } + + private function matchOnlyIfStmtReturnExpr(Stmt $onlyIfStmt, Variable $returnVariable): Expr|null + { + if (! $onlyIfStmt instanceof Expression) { + return null; + } + + if (! $onlyIfStmt->expr instanceof Assign) { + return null; + } + + $assign = $onlyIfStmt->expr; + + // assign to same variable that is returned + if (! $assign->var instanceof Variable) { + return null; + } + + if (! $this->nodeComparator->areNodesEqual($assign->var, $returnVariable)) { + return null; + } + + // return directly + return $assign->expr; + } + + private function matchNextStmtReturnVariable(StmtsAwareInterface $stmtsAware, int $key): Variable|null + { + $nextStmt = $stmtsAware->stmts[$key + 1] ?? null; + + // last item → stop + if ($nextStmt === null) { + return null; + } + + if (! $nextStmt instanceof Return_) { + return null; + } + + // next return must be variable + if (! $nextStmt->expr instanceof Variable) { + return null; + } + + return $nextStmt->expr; + } +}