mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-26 12:52:36 +00:00
[EarlyReturn] Add ReturnBinaryOrToEarlyReturnRector (#5661)
Co-authored-by: kaizen-ci <info@kaizen-ci.org>
This commit is contained in:
parent
5caae361e8
commit
be554a4a53
|
@ -7,15 +7,12 @@ 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_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Type\BooleanType;
|
||||
use Rector\Core\NodeAnalyzer\CallAnalyzer;
|
||||
use Rector\Core\NodeManipulator\IfManipulator;
|
||||
use Rector\Core\PhpParser\Node\AssignAndBinaryMap;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
||||
|
@ -29,21 +26,33 @@ final class ReturnBinaryAndToEarlyReturnRector extends AbstractRector
|
|||
*/
|
||||
private $ifManipulator;
|
||||
|
||||
public function __construct(IfManipulator $ifManipulator)
|
||||
/**
|
||||
* @var AssignAndBinaryMap
|
||||
*/
|
||||
private $assignAndBinaryMap;
|
||||
|
||||
/**
|
||||
* @var CallAnalyzer
|
||||
*/
|
||||
private $callAnalyzer;
|
||||
|
||||
public function __construct(IfManipulator $ifManipulator, AssignAndBinaryMap $assignAndBinaryMap, CallAnalyzer $callAnalyzer)
|
||||
{
|
||||
$this->ifManipulator = $ifManipulator;
|
||||
$this->assignAndBinaryMap = $assignAndBinaryMap;
|
||||
$this->callAnalyzer = $callAnalyzer;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Changes Single return of && && to early returns', [
|
||||
return new RuleDefinition('Changes Single return of && to early returns', [
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function accept($something, $somethingelse)
|
||||
public function accept()
|
||||
{
|
||||
return $something && $somethingelse;
|
||||
return $this->something() && $this->somethingelse();
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
|
@ -52,12 +61,12 @@ CODE_SAMPLE
|
|||
<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function accept($something, $somethingelse)
|
||||
public function accept()
|
||||
{
|
||||
if (!$something) {
|
||||
if (!$this->something()) {
|
||||
return false;
|
||||
}
|
||||
return (bool) $somethingelse;
|
||||
return (bool) $this->somethingelse();
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
|
@ -85,15 +94,16 @@ CODE_SAMPLE
|
|||
$left = $node->expr->left;
|
||||
$ifNegations = $this->createMultipleIfsNegation($left, $node, []);
|
||||
|
||||
foreach ($ifNegations as $key => $ifNegation) {
|
||||
if ($key === 0) {
|
||||
$this->mirrorComments($ifNegation, $node);
|
||||
$this->mirrorComments($ifNegations[0], $node);
|
||||
foreach ($ifNegations as $ifNegation) {
|
||||
if (! $this->callAnalyzer->isObjectCall($ifNegation->cond)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->addNodeBeforeNode($ifNegation, $node);
|
||||
}
|
||||
|
||||
$lastReturnExpr = $this->getLastReturnExpr($node->expr->right);
|
||||
$lastReturnExpr = $this->assignAndBinaryMap->getTruthyExpr($node->expr->right);
|
||||
$this->addNodeBeforeNode(new Return_($lastReturnExpr), $node);
|
||||
$this->removeNode($node);
|
||||
|
||||
|
@ -120,29 +130,6 @@ CODE_SAMPLE
|
|||
];
|
||||
}
|
||||
|
||||
private function getLastReturnExpr(Expr $expr): Expr
|
||||
{
|
||||
if ($expr instanceof Bool_) {
|
||||
return $expr;
|
||||
}
|
||||
|
||||
if ($expr instanceof BooleanNot) {
|
||||
return $expr;
|
||||
}
|
||||
|
||||
$scope = $expr->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return new Bool_($expr);
|
||||
}
|
||||
|
||||
$type = $scope->getType($expr);
|
||||
if ($type instanceof BooleanType) {
|
||||
return $expr;
|
||||
}
|
||||
|
||||
return new Bool_($expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param If_[] $ifNegations
|
||||
* @return If_[]
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
<?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\BinaryOp\BooleanOr;
|
||||
use PhpParser\Node\Stmt\If_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use Rector\Core\NodeAnalyzer\CallAnalyzer;
|
||||
use Rector\Core\NodeManipulator\IfManipulator;
|
||||
use Rector\Core\PhpParser\Node\AssignAndBinaryMap;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
||||
/**
|
||||
* @see \Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\ReturnBinaryOrToEarlyReturnRectorTest
|
||||
*/
|
||||
final class ReturnBinaryOrToEarlyReturnRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var IfManipulator
|
||||
*/
|
||||
private $ifManipulator;
|
||||
|
||||
/**
|
||||
* @var AssignAndBinaryMap
|
||||
*/
|
||||
private $assignAndBinaryMap;
|
||||
|
||||
/**
|
||||
* @var CallAnalyzer
|
||||
*/
|
||||
private $callAnalyzer;
|
||||
|
||||
public function __construct(
|
||||
IfManipulator $ifManipulator,
|
||||
AssignAndBinaryMap $assignAndBinaryMap,
|
||||
CallAnalyzer $callAnalyzer
|
||||
) {
|
||||
$this->ifManipulator = $ifManipulator;
|
||||
$this->assignAndBinaryMap = $assignAndBinaryMap;
|
||||
$this->callAnalyzer = $callAnalyzer;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Changes Single return of || to early returns', [
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
return $this->something() || $this->somethingElse();
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
if ($this->something()) {
|
||||
return true;
|
||||
}
|
||||
return (bool) $this->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 BooleanOr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$left = $node->expr->left;
|
||||
$ifs = $this->createMultipleIfs($left, $node, []);
|
||||
|
||||
if ($ifs === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->mirrorComments($ifs[0], $node);
|
||||
foreach ($ifs as $if) {
|
||||
if (! $this->callAnalyzer->isObjectCall($if->cond)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->addNodeBeforeNode($if, $node);
|
||||
}
|
||||
|
||||
$lastReturnExpr = $this->assignAndBinaryMap->getTruthyExpr($node->expr->right);
|
||||
$this->addNodeBeforeNode(new Return_($lastReturnExpr), $node);
|
||||
$this->removeNode($node);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param If_[] $ifs
|
||||
* @return If_[]
|
||||
*/
|
||||
private function createMultipleIfs(Expr $expr, Return_ $return, array $ifs): array
|
||||
{
|
||||
while ($expr instanceof BooleanOr) {
|
||||
$ifs = array_merge($ifs, $this->collectLeftBooleanOrToIfs($expr, $return, $ifs));
|
||||
$ifs[] = $this->ifManipulator->createIfExpr(
|
||||
$expr->right,
|
||||
new Return_($this->nodeFactory->createTrue())
|
||||
);
|
||||
|
||||
$expr = $expr->right;
|
||||
if ($expr instanceof BooleanAnd) {
|
||||
return [];
|
||||
}
|
||||
if (! $expr instanceof BooleanOr) {
|
||||
continue;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
return $ifs + [$this->ifManipulator->createIfExpr($expr, new Return_($this->nodeFactory->createTrue()))];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param If_[] $ifs
|
||||
* @return If_[]
|
||||
*/
|
||||
private function collectLeftBooleanOrToIfs(BooleanOr $BooleanOr, Return_ $return, array $ifs): array
|
||||
{
|
||||
$left = $BooleanOr->left;
|
||||
if (! $left instanceof BooleanOr) {
|
||||
return [$this->ifManipulator->createIfExpr($left, new Return_($this->nodeFactory->createTrue()))];
|
||||
}
|
||||
|
||||
return $this->createMultipleIfs($left, $return, $ifs);
|
||||
}
|
||||
}
|
|
@ -4,16 +4,16 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
class Comment
|
||||
{
|
||||
public function accept($something, $somethingelse, $anotherelse, $last)
|
||||
public function accept()
|
||||
{
|
||||
// a comment
|
||||
if ($a) {
|
||||
if (rand(0, 1)) {
|
||||
// another comment
|
||||
return $something && $somethingelse && $anotherelse && $last;
|
||||
return $this->something() && $this->somethingElse();
|
||||
}
|
||||
|
||||
// another next comment
|
||||
return $a;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,25 +25,19 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
class Comment
|
||||
{
|
||||
public function accept($something, $somethingelse, $anotherelse, $last)
|
||||
public function accept()
|
||||
{
|
||||
// a comment
|
||||
if ($a) {
|
||||
if (rand(0, 1)) {
|
||||
// another comment
|
||||
if (!$something) {
|
||||
if (!$this->something()) {
|
||||
return false;
|
||||
}
|
||||
if (!$somethingelse) {
|
||||
return false;
|
||||
}
|
||||
if (!$anotherelse) {
|
||||
return false;
|
||||
}
|
||||
return (bool) $last;
|
||||
return (bool) $this->somethingElse();
|
||||
}
|
||||
|
||||
// another next comment
|
||||
return $a;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
class DontChangeAlreadyCasted
|
||||
{
|
||||
public function accept($something, $somethingelse)
|
||||
public function accept()
|
||||
{
|
||||
return $something && (bool) $somethingelse;
|
||||
return $this->something() && (bool) $this->somethingelse();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,13 +18,13 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
class DontChangeAlreadyCasted
|
||||
{
|
||||
public function accept($something, $somethingelse)
|
||||
public function accept()
|
||||
{
|
||||
if (!$something) {
|
||||
if (!$this->something()) {
|
||||
return false;
|
||||
}
|
||||
return (bool) $somethingelse;
|
||||
return (bool) $this->somethingelse();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
?>
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRector\Fixture;
|
||||
|
||||
class DontChangeAlreadyReturnTyped
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
return $this->something() && $this->somethingElse();
|
||||
}
|
||||
|
||||
private function something(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function somethingElse(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRector\Fixture;
|
||||
|
||||
class DontChangeAlreadyReturnTyped
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
if (!$this->something()) {
|
||||
return false;
|
||||
}
|
||||
return $this->somethingElse();
|
||||
}
|
||||
|
||||
private function something(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function somethingElse(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -4,9 +4,9 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
class Fixture
|
||||
{
|
||||
public function accept($something, $somethingelse)
|
||||
public function accept()
|
||||
{
|
||||
return $something && $somethingelse;
|
||||
return $this->something() && $this->somethingElse();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,12 +18,12 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
class Fixture
|
||||
{
|
||||
public function accept($something, $somethingelse)
|
||||
public function accept()
|
||||
{
|
||||
if (!$something) {
|
||||
if (!$this->something()) {
|
||||
return false;
|
||||
}
|
||||
return (bool) $somethingelse;
|
||||
return (bool) $this->somethingElse();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
class Identical
|
||||
{
|
||||
public function accept($something, $somethingelse)
|
||||
public function accept()
|
||||
{
|
||||
return $something === 1 && !$somethingelse;
|
||||
return $this->something() === 1 && !$this->somethingElse();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,12 +18,12 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
class Identical
|
||||
{
|
||||
public function accept($something, $somethingelse)
|
||||
public function accept()
|
||||
{
|
||||
if ($something !== 1) {
|
||||
if ($this->something() !== 1) {
|
||||
return false;
|
||||
}
|
||||
return !$somethingelse;
|
||||
return !$this->somethingElse();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
class LastReturnBool
|
||||
{
|
||||
public function accept($something)
|
||||
public function accept()
|
||||
{
|
||||
return $something && $this->getSomethingElse();
|
||||
return $this->something() && $this->getSomethingElse();
|
||||
}
|
||||
|
||||
public function accept2($something, $somethingelse)
|
||||
public function accept2()
|
||||
{
|
||||
return $something && $somethingelse === 'something else';
|
||||
return $this->something() && $this->somethingelse() === 'something else';
|
||||
}
|
||||
|
||||
private function getSomethingElse(): bool
|
||||
|
@ -28,20 +28,20 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
class LastReturnBool
|
||||
{
|
||||
public function accept($something)
|
||||
public function accept()
|
||||
{
|
||||
if (!$something) {
|
||||
if (!$this->something()) {
|
||||
return false;
|
||||
}
|
||||
return $this->getSomethingElse();
|
||||
}
|
||||
|
||||
public function accept2($something, $somethingelse)
|
||||
public function accept2()
|
||||
{
|
||||
if (!$something) {
|
||||
if (!$this->something()) {
|
||||
return false;
|
||||
}
|
||||
return $somethingelse === 'something else';
|
||||
return $this->somethingelse() === 'something else';
|
||||
}
|
||||
|
||||
private function getSomethingElse(): bool
|
||||
|
|
|
@ -4,9 +4,9 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
class MultipleBinaryAnd
|
||||
{
|
||||
public function accept($something, $somethingelse, $anotherelse, $last)
|
||||
public function accept()
|
||||
{
|
||||
return $something && $somethingelse && $anotherelse && $last;
|
||||
return $this->something() && $this->somethingelse() && $this->anotherelse() && $this->last();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,18 +18,18 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
class MultipleBinaryAnd
|
||||
{
|
||||
public function accept($something, $somethingelse, $anotherelse, $last)
|
||||
public function accept()
|
||||
{
|
||||
if (!$something) {
|
||||
if (!$this->something()) {
|
||||
return false;
|
||||
}
|
||||
if (!$somethingelse) {
|
||||
if (!$this->somethingelse()) {
|
||||
return false;
|
||||
}
|
||||
if (!$anotherelse) {
|
||||
if (!$this->anotherelse()) {
|
||||
return false;
|
||||
}
|
||||
return (bool) $last;
|
||||
return (bool) $this->last();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRector\Fixture;
|
||||
|
||||
class NotObjectCallInLast
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
return $this->something() && true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRector\Fixture;
|
||||
|
||||
class NotObjectCallInLast
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
if (!$this->something()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRector\Fixture;
|
||||
|
||||
class SkipNotObjectCall
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
return $this->something || $this->somethingelse;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -4,9 +4,9 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
class SkipOrInNext
|
||||
{
|
||||
public function accept($something, $somethingelse, $anotherelse, $last)
|
||||
public function accept()
|
||||
{
|
||||
return $something && $somethingelse || $anotherelse && $last;
|
||||
return $this->something() && $this->somethingelse() || $this->anotherelse() && $this->last();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
final class SomeNotIdentical
|
||||
{
|
||||
public function accept($something, $somethingelse)
|
||||
public function accept()
|
||||
{
|
||||
return $something !== 1 && !$somethingelse;
|
||||
return $this->something() !== 1 && !$this->somethingelse();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,12 +18,12 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
final class SomeNotIdentical
|
||||
{
|
||||
public function accept($something, $somethingelse)
|
||||
public function accept()
|
||||
{
|
||||
if ($something === 1) {
|
||||
if ($this->something() === 1) {
|
||||
return false;
|
||||
}
|
||||
return !$somethingelse;
|
||||
return !$this->somethingelse();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
class TruthyNegation
|
||||
{
|
||||
public function accept($something, $somethingelse)
|
||||
public function accept()
|
||||
{
|
||||
return !$something && !$somethingelse;
|
||||
return !$this->something() && !$this->somethingelse();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,12 +18,12 @@ namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRe
|
|||
|
||||
class TruthyNegation
|
||||
{
|
||||
public function accept($something, $somethingelse)
|
||||
public function accept()
|
||||
{
|
||||
if ($something) {
|
||||
if ($this->something()) {
|
||||
return false;
|
||||
}
|
||||
return !$somethingelse;
|
||||
return !$this->somethingelse();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class Comment
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
// a comment
|
||||
if (rand(0, 1)) {
|
||||
// another comment
|
||||
return $this->something() || $this->somethingElse();
|
||||
}
|
||||
|
||||
// another next comment
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class Comment
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
// a comment
|
||||
if (rand(0, 1)) {
|
||||
// another comment
|
||||
if ($this->something()) {
|
||||
return true;
|
||||
}
|
||||
return (bool) $this->somethingElse();
|
||||
}
|
||||
|
||||
// another next comment
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class DontChangeAlreadyCasted
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
return $this->something() || (bool) $this->somethingelse();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class DontChangeAlreadyCasted
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
if ($this->something()) {
|
||||
return true;
|
||||
}
|
||||
return (bool) $this->somethingelse();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class DontChangeAlreadyReturnTyped
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
return $this->something() || $this->somethingElse();
|
||||
}
|
||||
|
||||
private function something(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function somethingElse(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class DontChangeAlreadyReturnTyped
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
if ($this->something()) {
|
||||
return true;
|
||||
}
|
||||
return $this->somethingElse();
|
||||
}
|
||||
|
||||
private function something(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function somethingElse(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class Fixture
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
return $this->something() || $this->somethingElse();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class Fixture
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
if ($this->something()) {
|
||||
return true;
|
||||
}
|
||||
return (bool) $this->somethingElse();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class Identical
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
return $this->something() === 1 || !$this->somethingElse();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class Identical
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
if ($this->something() === 1) {
|
||||
return true;
|
||||
}
|
||||
return !$this->somethingElse();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class LastReturnBool
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
return $this->something() || $this->getSomethingElse();
|
||||
}
|
||||
|
||||
public function accept2()
|
||||
{
|
||||
return $this->something() || $this->somethingelse() === 'something else';
|
||||
}
|
||||
|
||||
private function getSomethingElse(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class LastReturnBool
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
if ($this->something()) {
|
||||
return true;
|
||||
}
|
||||
return $this->getSomethingElse();
|
||||
}
|
||||
|
||||
public function accept2()
|
||||
{
|
||||
if ($this->something()) {
|
||||
return true;
|
||||
}
|
||||
return $this->somethingelse() === 'something else';
|
||||
}
|
||||
|
||||
private function getSomethingElse(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class MultipleBinaryOr
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
return $this->something() || $this->somethingelse() || $this->anotherelse() || $this->last();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class MultipleBinaryOr
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
if ($this->something()) {
|
||||
return true;
|
||||
}
|
||||
if ($this->somethingelse()) {
|
||||
return true;
|
||||
}
|
||||
if ($this->anotherelse()) {
|
||||
return true;
|
||||
}
|
||||
return (bool) $this->last();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class NotObjectCallInLast
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
return $this->something() || true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class NotObjectCallInLast
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
if ($this->something()) {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class SkipAndInNext
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
return $this->something() || $this->somethingelse() && $this->anotherelse() || $this->last();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class SkipNotObjectCall
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
return $this->something || $this->somethingelse;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
final class SomeNotIdentical
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
return $this->something() !== 1 || !$this->somethingelse();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
final class SomeNotIdentical
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
if ($this->something() !== 1) {
|
||||
return true;
|
||||
}
|
||||
return !$this->somethingelse();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class TruthyNegation
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
return !$this->something() || !$this->somethingelse();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
|
||||
|
||||
class TruthyNegation
|
||||
{
|
||||
public function accept()
|
||||
{
|
||||
if (!$this->something()) {
|
||||
return true;
|
||||
}
|
||||
return !$this->somethingelse();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\EarlyReturn\Rector\Return_\ReturnBinaryOrToEarlyReturnRector;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class ReturnBinaryOrToEarlyReturnRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
}
|
||||
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
{
|
||||
return ReturnBinaryOrToEarlyReturnRector::class;
|
||||
}
|
||||
}
|
47
src/NodeAnalyzer/CallAnalyzer.php
Normal file
47
src/NodeAnalyzer/CallAnalyzer.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Core\NodeAnalyzer;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\BinaryOp;
|
||||
use PhpParser\Node\Expr\BooleanNot;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\NullsafeMethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
|
||||
final class CallAnalyzer
|
||||
{
|
||||
/**
|
||||
* @var array<class-string<Expr>>
|
||||
*/
|
||||
private const OBJECT_CALLS = [
|
||||
MethodCall::class,
|
||||
NullsafeMethodCall::class,
|
||||
StaticCall::class
|
||||
];
|
||||
|
||||
public function isObjectCall(Node $node): bool
|
||||
{
|
||||
if ($node instanceof BooleanNot) {
|
||||
$node = $node->expr;
|
||||
}
|
||||
|
||||
if ($node instanceof BinaryOp) {
|
||||
$isObjectCallLeft = $this->isObjectCall($node->left);
|
||||
$isObjectCallRight = $this->isObjectCall($node->right);
|
||||
|
||||
return $isObjectCallLeft || $isObjectCallRight;
|
||||
}
|
||||
|
||||
foreach (self::OBJECT_CALLS as $objectCall) {
|
||||
if (is_a($node, $objectCall, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace Rector\Core\PhpParser\Node;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\AssignOp;
|
||||
use PhpParser\Node\Expr\AssignOp\BitwiseAnd as AssignBitwiseAnd;
|
||||
use PhpParser\Node\Expr\AssignOp\BitwiseOr as AssignBitwiseOr;
|
||||
|
@ -39,6 +40,11 @@ use PhpParser\Node\Expr\BinaryOp\ShiftLeft;
|
|||
use PhpParser\Node\Expr\BinaryOp\ShiftRight;
|
||||
use PhpParser\Node\Expr\BinaryOp\Smaller;
|
||||
use PhpParser\Node\Expr\BinaryOp\SmallerOrEqual;
|
||||
use PhpParser\Node\Expr\BooleanNot;
|
||||
use PhpParser\Node\Expr\Cast\Bool_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Type\BooleanType;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
final class AssignAndBinaryMap
|
||||
{
|
||||
|
@ -104,4 +110,27 @@ final class AssignAndBinaryMap
|
|||
$nodeClass = get_class($binaryOp);
|
||||
return self::BINARY_OP_TO_INVERSE_CLASSES[$nodeClass] ?? null;
|
||||
}
|
||||
|
||||
public function getTruthyExpr(Expr $expr): Expr
|
||||
{
|
||||
if ($expr instanceof Bool_) {
|
||||
return $expr;
|
||||
}
|
||||
|
||||
if ($expr instanceof BooleanNot) {
|
||||
return $expr;
|
||||
}
|
||||
|
||||
$scope = $expr->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return new Bool_($expr);
|
||||
}
|
||||
|
||||
$type = $scope->getType($expr);
|
||||
if ($type instanceof BooleanType) {
|
||||
return $expr;
|
||||
}
|
||||
|
||||
return new Bool_($expr);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user