2020-12-19 08:56:24 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
namespace Rector\EarlyReturn\Rector\Return_;
|
|
|
|
|
|
|
|
use PhpParser\Node;
|
|
|
|
use PhpParser\Node\Expr;
|
|
|
|
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
|
|
|
|
use PhpParser\Node\Expr\BooleanNot;
|
|
|
|
use PhpParser\Node\Expr\Cast\Bool_;
|
|
|
|
use PhpParser\Node\Stmt\If_;
|
|
|
|
use PhpParser\Node\Stmt\Return_;
|
2020-12-19 15:24:53 +00:00
|
|
|
use PHPStan\Analyser\Scope;
|
|
|
|
use PHPStan\Type\BooleanType;
|
2021-02-09 22:43:04 +00:00
|
|
|
use Rector\Core\NodeManipulator\IfManipulator;
|
2020-12-19 08:56:24 +00:00
|
|
|
use Rector\Core\Rector\AbstractRector;
|
2020-12-19 15:24:53 +00:00
|
|
|
use Rector\NodeTypeResolver\Node\AttributeKey;
|
2020-12-19 08:56:24 +00:00
|
|
|
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
|
|
|
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @see \Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRector\ReturnBinaryAndToEarlyReturnRectorTest
|
|
|
|
*/
|
|
|
|
final class ReturnBinaryAndToEarlyReturnRector extends AbstractRector
|
|
|
|
{
|
2021-02-09 19:07:20 +00:00
|
|
|
/**
|
|
|
|
* @var IfManipulator
|
|
|
|
*/
|
|
|
|
private $ifManipulator;
|
|
|
|
|
|
|
|
public function __construct(IfManipulator $ifManipulator)
|
|
|
|
{
|
|
|
|
$this->ifManipulator = $ifManipulator;
|
|
|
|
}
|
|
|
|
|
2020-12-19 08:56:24 +00:00
|
|
|
public function getRuleDefinition(): RuleDefinition
|
|
|
|
{
|
|
|
|
return new RuleDefinition('Changes Single return of && && to early returns', [
|
|
|
|
new CodeSample(
|
|
|
|
<<<'CODE_SAMPLE'
|
|
|
|
class SomeClass
|
|
|
|
{
|
|
|
|
public function accept($something, $somethingelse)
|
|
|
|
{
|
|
|
|
return $something && $somethingelse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CODE_SAMPLE
|
|
|
|
|
|
|
|
,
|
|
|
|
<<<'CODE_SAMPLE'
|
|
|
|
class SomeClass
|
|
|
|
{
|
|
|
|
public function accept($something, $somethingelse)
|
|
|
|
{
|
|
|
|
if (!$something) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return (bool) $somethingelse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CODE_SAMPLE
|
|
|
|
),
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return string[]
|
|
|
|
*/
|
|
|
|
public function getNodeTypes(): array
|
|
|
|
{
|
|
|
|
return [Return_::class];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param Return_ $node
|
|
|
|
*/
|
|
|
|
public function refactor(Node $node): ?Node
|
|
|
|
{
|
|
|
|
if (! $node->expr instanceof BooleanAnd) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$left = $node->expr->left;
|
|
|
|
$ifNegations = $this->createMultipleIfsNegation($left, $node, []);
|
2020-12-19 15:24:53 +00:00
|
|
|
|
|
|
|
foreach ($ifNegations as $key => $ifNegation) {
|
|
|
|
if ($key === 0) {
|
2020-12-20 12:56:43 +00:00
|
|
|
$this->mirrorComments($ifNegation, $node);
|
2020-12-19 15:24:53 +00:00
|
|
|
}
|
2020-12-19 08:56:24 +00:00
|
|
|
|
|
|
|
$this->addNodeBeforeNode($ifNegation, $node);
|
|
|
|
}
|
|
|
|
|
|
|
|
$lastReturnExpr = $this->getLastReturnExpr($node->expr->right);
|
|
|
|
$this->addNodeBeforeNode(new Return_($lastReturnExpr), $node);
|
|
|
|
$this->removeNode($node);
|
|
|
|
|
|
|
|
return $node;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param If_[] $ifNegations
|
|
|
|
* @return If_[]
|
|
|
|
*/
|
|
|
|
private function createMultipleIfsNegation(Expr $expr, Return_ $return, array $ifNegations): array
|
|
|
|
{
|
|
|
|
while ($expr instanceof BooleanAnd) {
|
|
|
|
$ifNegations = array_merge($ifNegations, $this->collectLeftBooleanAndToIfs($expr, $return, $ifNegations));
|
2021-02-09 22:43:04 +00:00
|
|
|
$ifNegations[] = $this->ifManipulator->createIfNegation(
|
|
|
|
$expr->right,
|
|
|
|
new Return_($this->nodeFactory->createFalse())
|
|
|
|
);
|
2020-12-19 08:56:24 +00:00
|
|
|
|
|
|
|
$expr = $expr->right;
|
|
|
|
}
|
2021-02-09 22:43:04 +00:00
|
|
|
return $ifNegations + [
|
|
|
|
$this->ifManipulator->createIfNegation($expr, new Return_($this->nodeFactory->createFalse())),
|
|
|
|
];
|
2020-12-19 08:56:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private function getLastReturnExpr(Expr $expr): Expr
|
|
|
|
{
|
|
|
|
if ($expr instanceof Bool_) {
|
|
|
|
return $expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($expr instanceof BooleanNot) {
|
|
|
|
return $expr;
|
|
|
|
}
|
|
|
|
|
2020-12-19 15:24:53 +00:00
|
|
|
$scope = $expr->getAttribute(AttributeKey::SCOPE);
|
|
|
|
if (! $scope instanceof Scope) {
|
|
|
|
return new Bool_($expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
$type = $scope->getType($expr);
|
|
|
|
if ($type instanceof BooleanType) {
|
|
|
|
return $expr;
|
|
|
|
}
|
|
|
|
|
2020-12-19 08:56:24 +00:00
|
|
|
return new Bool_($expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param If_[] $ifNegations
|
|
|
|
* @return If_[]
|
|
|
|
*/
|
|
|
|
private function collectLeftBooleanAndToIfs(BooleanAnd $booleanAnd, Return_ $return, array $ifNegations): array
|
|
|
|
{
|
|
|
|
$left = $booleanAnd->left;
|
|
|
|
if (! $left instanceof BooleanAnd) {
|
2021-02-09 19:07:20 +00:00
|
|
|
return [$this->ifManipulator->createIfNegation($left, new Return_($this->nodeFactory->createFalse()))];
|
2020-12-19 08:56:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return $this->createMultipleIfsNegation($left, $return, $ifNegations);
|
|
|
|
}
|
|
|
|
}
|