[EarlyReturn] Add ReturnBinaryOrToEarlyReturnRector (#5661)

Co-authored-by: kaizen-ci <info@kaizen-ci.org>
This commit is contained in:
Abdul Malik Ikhsan 2021-02-25 15:41:43 +07:00 committed by GitHub
parent 5caae361e8
commit be554a4a53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 828 additions and 98 deletions

View File

@ -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_[]

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}
?>
?>

View File

@ -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;
}
}
?>

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -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();
}
}

View File

@ -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;
}
}
?>

View File

@ -0,0 +1,13 @@
<?php
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryAndToEarlyReturnRector\Fixture;
class SkipNotObjectCall
{
public function accept()
{
return $this->something || $this->somethingelse;
}
}
?>

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}
?>

View File

@ -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();
}
}
?>

View File

@ -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;
}
}
?>

View File

@ -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();
}
}
?>

View File

@ -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();
}
}
?>

View File

@ -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;
}
}
?>

View File

@ -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();
}
}
?>

View File

@ -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;
}
}
?>

View File

@ -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();
}
}
?>

View File

@ -0,0 +1,13 @@
<?php
namespace Rector\EarlyReturn\Tests\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture;
class SkipNotObjectCall
{
public function accept()
{
return $this->something || $this->somethingelse;
}
}
?>

View File

@ -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();
}
}
?>

View File

@ -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();
}
}
?>

View File

@ -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;
}
}

View 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;
}
}

View File

@ -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);
}
}