diff --git a/config/set/code-quality.php b/config/set/code-quality.php index 38654364e45..88f69ddc388 100644 --- a/config/set/code-quality.php +++ b/config/set/code-quality.php @@ -71,6 +71,7 @@ use Rector\CodeQuality\Rector\LogicalAnd\AndAssignsToSeparateLinesRector; use Rector\CodeQuality\Rector\LogicalAnd\LogicalToBooleanRector; use Rector\CodeQuality\Rector\New_\NewStaticToNewSelfRector; use Rector\CodeQuality\Rector\NotEqual\CommonNotEqualRector; +use Rector\CodeQuality\Rector\NullsafeMethodCall\CleanupUnneededNullsafeOperatorRector; use Rector\CodeQuality\Rector\PropertyFetch\ExplicitMethodCallOverMagicGetSetRector; use Rector\CodeQuality\Rector\Switch_\SingularSwitchToIfRector; use Rector\CodeQuality\Rector\Switch_\SwitchTrueToIfRector; @@ -82,7 +83,6 @@ use Rector\CodeQuality\Rector\Ternary\UnnecessaryTernaryExpressionRector; use Rector\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector; use Rector\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector; use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector; -use Rector\CodeQuality\Rector\NullsafeMethodCall\CleanupUnneededNullsafeOperatorRector; use Rector\Config\RectorConfig; use Rector\Php52\Rector\Property\VarToPublicPropertyRector; use Rector\Php71\Rector\FuncCall\RemoveExtraParametersRector; diff --git a/rules/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector.php b/rules/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector.php index 3b4ea3b41fa..1f191035eb2 100644 --- a/rules/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector.php +++ b/rules/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector.php @@ -6,13 +6,15 @@ namespace Rector\EarlyReturn\Rector\Return_; use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\Assign; -use PhpParser\Node\FunctionLike; +use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; +use Rector\Core\Contract\PhpParser\Node\StmtsAwareInterface; use Rector\Core\NodeManipulator\IfManipulator; use Rector\Core\Rector\AbstractRector; -use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\EarlyReturn\ValueObject\BareSingleAssignIf; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** @@ -38,11 +40,11 @@ class SomeClass { $var = null; - if (rand(0,1)) { + if (rand(0, 1)) { $var = 1; } - if (rand(0,1)) { + if (rand(0, 1)) { $var = 2; } @@ -55,11 +57,11 @@ class SomeClass { public function run() { - if (rand(0,1)) { + if (rand(0, 1)) { return 1; } - if (rand(0,1)) { + if (rand(0, 1)) { return 2; } @@ -74,155 +76,109 @@ CODE_SAMPLE */ public function getNodeTypes() : array { - return [Return_::class]; + return [StmtsAwareInterface::class]; } /** - * @param Return_ $node + * @param StmtsAwareInterface $node */ - public function refactor(Node $node) : ?Node + public function refactor(Node $node) { - $ifsBefore = $this->getIfsBefore($node); - if ($this->shouldSkip($ifsBefore, $node->expr)) { - return null; - } - if ($this->isAssignVarUsedInIfCond($ifsBefore, $node->expr)) { - return null; - } - /** @var Expr $returnExpr */ - $returnExpr = $node->expr; - /** @var Expression $previousFirstExpression */ - $previousFirstExpression = $this->getPreviousIfLinearEquals($ifsBefore[0], $returnExpr); - /** @var Assign $previousAssign */ - $previousAssign = $previousFirstExpression->expr; - if ($this->isPreviousVarUsedInAssignExpr($ifsBefore, $previousAssign->var)) { - return null; - } - foreach ($ifsBefore as $ifBefore) { - /** @var Expression $expressionIf */ - $expressionIf = $ifBefore->stmts[0]; - /** @var Assign $assignIf */ - $assignIf = $expressionIf->expr; - $ifBefore->stmts[0] = new Return_($assignIf->expr); - } - /** @var Assign $assignPrevious */ - $assignPrevious = $previousFirstExpression->expr; - $node->expr = $assignPrevious->expr; - $this->removeNode($previousFirstExpression); - return $node; - } - /** - * @param If_[] $ifsBefore - */ - private function isAssignVarUsedInIfCond(array $ifsBefore, ?Expr $expr) : bool - { - foreach ($ifsBefore as $ifBefore) { - $isUsedInIfCond = (bool) $this->betterNodeFinder->findFirst($ifBefore->cond, function (Node $node) use($expr) : bool { - return $this->nodeComparator->areNodesEqual($node, $expr); - }); - if ($isUsedInIfCond) { - return \true; + /** @var BareSingleAssignIf[] $bareSingleAssignIfs */ + $bareSingleAssignIfs = []; + $initialAssign = null; + $initialAssignPosition = null; + foreach ((array) $node->stmts as $key => $stmt) { + $bareSingleAssignIf = $this->matchBareSingleAssignIf($stmt); + if ($bareSingleAssignIf instanceof BareSingleAssignIf) { + $bareSingleAssignIfs[] = $bareSingleAssignIf; + continue; } - } - return \false; - } - /** - * @param If_[] $ifsBefore - */ - private function isPreviousVarUsedInAssignExpr(array $ifsBefore, Expr $expr) : bool - { - foreach ($ifsBefore as $ifBefore) { - /** @var Expression $expression */ - $expression = $ifBefore->stmts[0]; - /** @var Assign $assign */ - $assign = $expression->expr; - $isUsedInAssignExpr = (bool) $this->betterNodeFinder->findFirst($assign->expr, function (Node $node) use($expr) : bool { - return $this->nodeComparator->areNodesEqual($node, $expr); - }); - if ($isUsedInAssignExpr) { - return \true; + if ($stmt instanceof Expression && $stmt->expr instanceof Assign) { + $initialAssign = $stmt->expr; + $initialAssignPosition = $key; } - } - return \false; - } - /** - * @param If_[] $ifsBefore - */ - private function shouldSkip(array $ifsBefore, ?Expr $returnExpr) : bool - { - if ($ifsBefore === []) { - return \true; - } - return !(bool) $this->getPreviousIfLinearEquals($ifsBefore[0], $returnExpr); - } - private function getPreviousIfLinearEquals(?Node $node, ?Expr $expr) : ?Expression - { - if (!$node instanceof Node) { - return null; - } - if (!$expr instanceof Expr) { - return null; - } - $previous = $node->getAttribute(AttributeKey::PREVIOUS_NODE); - if (!$previous instanceof Expression) { - return $this->getPreviousIfLinearEquals($previous, $expr); - } - if (!$previous->expr instanceof Assign) { - return null; - } - if ($this->nodeComparator->areNodesEqual($previous->expr->var, $expr)) { - return $previous; + if (!$stmt instanceof Return_) { + continue; + } + $return = $stmt; + // match exact variable + if (!$return->expr instanceof Variable) { + return null; + } + if (!\is_int($initialAssignPosition)) { + return null; + } + if (!$initialAssign instanceof Assign) { + return null; + } + if ($bareSingleAssignIfs === []) { + return null; + } + if (!$this->isVariableSharedInAssignIfsAndReturn($bareSingleAssignIfs, $return->expr, $initialAssign)) { + return null; + } + return $this->refactorToDirectReturns($node, $initialAssignPosition, $bareSingleAssignIfs, $initialAssign, $return); } return null; } /** - * @return If_[] + * @param BareSingleAssignIf[] $bareSingleAssignIfs */ - private function getIfsBefore(Return_ $return) : array + private function isVariableSharedInAssignIfsAndReturn(array $bareSingleAssignIfs, Expr $returnedExpr, Assign $initialAssign) : bool { - $parentNode = $return->getAttribute(AttributeKey::PARENT_NODE); - if (!$parentNode instanceof FunctionLike && !$parentNode instanceof If_) { - return []; + if (!$this->nodeComparator->areNodesEqual($returnedExpr, $initialAssign->var)) { + return \false; } - if ($parentNode->stmts === []) { - return []; + foreach ($bareSingleAssignIfs as $bareSingleAssignIf) { + $assign = $bareSingleAssignIf->getAssign(); + $isVariableUsed = (bool) $this->betterNodeFinder->findFirst([$bareSingleAssignIf->getIfCondExpr(), $assign->expr], function (Node $node) use($returnedExpr) : bool { + return $this->nodeComparator->areNodesEqual($node, $returnedExpr); + }); + if ($isVariableUsed) { + return \false; + } + if (!$this->nodeComparator->areNodesEqual($assign->var, $returnedExpr)) { + return \false; + } } - \end($parentNode->stmts); - $firstItemPosition = \key($parentNode->stmts); - if ($parentNode->stmts[$firstItemPosition] !== $return) { - return []; + return \true; + } + private function matchBareSingleAssignIf(Stmt $stmt) : ?BareSingleAssignIf + { + if (!$stmt instanceof If_) { + return null; } - return $this->collectIfs($parentNode->stmts, $return); + // is exactly single stmt + if (\count($stmt->stmts) !== 1) { + return null; + } + $onlyStmt = $stmt->stmts[0]; + if (!$onlyStmt instanceof Expression) { + return null; + } + $expression = $onlyStmt; + if (!$expression->expr instanceof Assign) { + return null; + } + if (!$this->ifManipulator->isIfWithoutElseAndElseIfs($stmt)) { + return null; + } + return new BareSingleAssignIf($stmt, $expression->expr); } /** - * @param If_[] $stmts - * @return If_[] + * @param BareSingleAssignIf[] $bareSingleAssignIfs */ - private function collectIfs(array $stmts, Return_ $return) : array + private function refactorToDirectReturns(StmtsAwareInterface $stmtsAware, int $initialAssignPosition, array $bareSingleAssignIfs, Assign $initialAssign, Return_ $return) : StmtsAwareInterface { - /** @va If_[] $ifs */ - $ifs = $this->betterNodeFinder->findInstanceOf($stmts, If_::class); - /** Skip entirely if found skipped ifs */ - foreach ($ifs as $if) { - /** @var If_ $if */ - if (!$this->ifManipulator->isIfWithoutElseAndElseIfs($if)) { - return []; - } - $stmts = $if->stmts; - if (\count($stmts) !== 1) { - return []; - } - $expression = $stmts[0]; - if (!$expression instanceof Expression) { - return []; - } - if (!$expression->expr instanceof Assign) { - return []; - } - $assign = $expression->expr; - if (!$this->nodeComparator->areNodesEqual($assign->var, $return->expr)) { - return []; - } + // 1. remove initial assign + unset($stmtsAware->stmts[$initialAssignPosition]); + // 2. make ifs early return + foreach ($bareSingleAssignIfs as $bareSingleAssignIf) { + $if = $bareSingleAssignIf->getIf(); + $if->stmts[0] = new Return_($bareSingleAssignIf->getAssign()->expr); } - return $ifs; + // 3. make return default value + $return->expr = $initialAssign->expr; + return $stmtsAware; } } diff --git a/rules/EarlyReturn/ValueObject/BareSingleAssignIf.php b/rules/EarlyReturn/ValueObject/BareSingleAssignIf.php new file mode 100644 index 00000000000..7f142d58d62 --- /dev/null +++ b/rules/EarlyReturn/ValueObject/BareSingleAssignIf.php @@ -0,0 +1,38 @@ +if = $if; + $this->assign = $assign; + } + public function getIfCondExpr() : Expr + { + return $this->if->cond; + } + public function getIf() : If_ + { + return $this->if; + } + public function getAssign() : Assign + { + return $this->assign; + } +} diff --git a/src/Application/VersionResolver.php b/src/Application/VersionResolver.php index 7c20db1705d..6d51681cfb1 100644 --- a/src/Application/VersionResolver.php +++ b/src/Application/VersionResolver.php @@ -19,12 +19,12 @@ final class VersionResolver * @api * @var string */ - public const PACKAGE_VERSION = '1e052f588331717f87a8d610db6eb7f89f8690d3'; + public const PACKAGE_VERSION = '9a2fe2126433ad285790cd81ec8c05594de5b396'; /** * @api * @var string */ - public const RELEASE_DATE = '2023-05-08 12:50:25'; + public const RELEASE_DATE = '2023-05-08 14:02:52'; /** * @var int */ diff --git a/vendor/autoload.php b/vendor/autoload.php index 1eed058b2f2..6e23fee9cd4 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) { require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInit1f0ff480be11c176eb74aef185ba5426::getLoader(); +return ComposerAutoloaderInit024b5dda55aedfbd918b3d4a5a23a784::getLoader(); diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 4839934afc7..f742d6fec0b 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -1866,6 +1866,7 @@ return array( 'Rector\\EarlyReturn\\Rector\\Return_\\ReturnBinaryAndToEarlyReturnRector' => $baseDir . '/rules/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector.php', 'Rector\\EarlyReturn\\Rector\\Return_\\ReturnBinaryOrToEarlyReturnRector' => $baseDir . '/rules/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector.php', 'Rector\\EarlyReturn\\Rector\\StmtsAwareInterface\\ReturnEarlyIfVariableRector' => $baseDir . '/rules/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector.php', + 'Rector\\EarlyReturn\\ValueObject\\BareSingleAssignIf' => $baseDir . '/rules/EarlyReturn/ValueObject/BareSingleAssignIf.php', 'Rector\\FamilyTree\\NodeAnalyzer\\ClassChildAnalyzer' => $baseDir . '/packages/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php', 'Rector\\FamilyTree\\Reflection\\FamilyRelationsAnalyzer' => $baseDir . '/packages/FamilyTree/Reflection/FamilyRelationsAnalyzer.php', 'Rector\\FileSystemRector\\Contract\\AddedFileInterface' => $baseDir . '/packages/FileSystemRector/Contract/AddedFileInterface.php', diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index 9e1e314da69..2d7af1950a3 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit1f0ff480be11c176eb74aef185ba5426 +class ComposerAutoloaderInit024b5dda55aedfbd918b3d4a5a23a784 { private static $loader; @@ -22,17 +22,17 @@ class ComposerAutoloaderInit1f0ff480be11c176eb74aef185ba5426 return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit1f0ff480be11c176eb74aef185ba5426', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInit024b5dda55aedfbd918b3d4a5a23a784', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); - spl_autoload_unregister(array('ComposerAutoloaderInit1f0ff480be11c176eb74aef185ba5426', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit024b5dda55aedfbd918b3d4a5a23a784', 'loadClassLoader')); require __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInit1f0ff480be11c176eb74aef185ba5426::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInit024b5dda55aedfbd918b3d4a5a23a784::getInitializer($loader)); $loader->setClassMapAuthoritative(true); $loader->register(true); - $filesToLoad = \Composer\Autoload\ComposerStaticInit1f0ff480be11c176eb74aef185ba5426::$files; + $filesToLoad = \Composer\Autoload\ComposerStaticInit024b5dda55aedfbd918b3d4a5a23a784::$files; $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index a330e46e266..45d9ca8d3be 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -4,7 +4,7 @@ namespace Composer\Autoload; -class ComposerStaticInit1f0ff480be11c176eb74aef185ba5426 +class ComposerStaticInit024b5dda55aedfbd918b3d4a5a23a784 { public static $files = array ( 'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php', @@ -2108,6 +2108,7 @@ class ComposerStaticInit1f0ff480be11c176eb74aef185ba5426 'Rector\\EarlyReturn\\Rector\\Return_\\ReturnBinaryAndToEarlyReturnRector' => __DIR__ . '/../..' . '/rules/EarlyReturn/Rector/Return_/ReturnBinaryAndToEarlyReturnRector.php', 'Rector\\EarlyReturn\\Rector\\Return_\\ReturnBinaryOrToEarlyReturnRector' => __DIR__ . '/../..' . '/rules/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector.php', 'Rector\\EarlyReturn\\Rector\\StmtsAwareInterface\\ReturnEarlyIfVariableRector' => __DIR__ . '/../..' . '/rules/EarlyReturn/Rector/StmtsAwareInterface/ReturnEarlyIfVariableRector.php', + 'Rector\\EarlyReturn\\ValueObject\\BareSingleAssignIf' => __DIR__ . '/../..' . '/rules/EarlyReturn/ValueObject/BareSingleAssignIf.php', 'Rector\\FamilyTree\\NodeAnalyzer\\ClassChildAnalyzer' => __DIR__ . '/../..' . '/packages/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php', 'Rector\\FamilyTree\\Reflection\\FamilyRelationsAnalyzer' => __DIR__ . '/../..' . '/packages/FamilyTree/Reflection/FamilyRelationsAnalyzer.php', 'Rector\\FileSystemRector\\Contract\\AddedFileInterface' => __DIR__ . '/../..' . '/packages/FileSystemRector/Contract/AddedFileInterface.php', @@ -3122,9 +3123,9 @@ class ComposerStaticInit1f0ff480be11c176eb74aef185ba5426 public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInit1f0ff480be11c176eb74aef185ba5426::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInit1f0ff480be11c176eb74aef185ba5426::$prefixDirsPsr4; - $loader->classMap = ComposerStaticInit1f0ff480be11c176eb74aef185ba5426::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInit024b5dda55aedfbd918b3d4a5a23a784::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit024b5dda55aedfbd918b3d4a5a23a784::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit024b5dda55aedfbd918b3d4a5a23a784::$classMap; }, null, ClassLoader::class); }