[DeadCode] Add RemoveUnusedNonEmptyArrayBeforeForeachRector (#3066)

This commit is contained in:
Tomas Votruba 2020-03-25 19:00:26 +01:00 committed by GitHub
parent 6cd9aa70cc
commit 5204ce1523
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 304 additions and 1 deletions

View File

@ -36,3 +36,4 @@ services:
Rector\DeadCode\Rector\Assign\RemoveUnusedVariableAssignRector: null
Rector\DeadCode\Rector\FunctionLike\RemoveDuplicatedIfReturnRector: null
Rector\DeadCode\Rector\Function_\RemoveUnusedFunctionRector: null
Rector\DeadCode\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector: null

View File

@ -1,4 +1,4 @@
# All 468 Rectors Overview
# All 469 Rectors Overview
- [Projects](#projects)
- [General](#general)
@ -3083,6 +3083,32 @@ Remove unused function
<br>
### `RemoveUnusedNonEmptyArrayBeforeForeachRector`
- class: [`Rector\DeadCode\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector`](/../master/rules/dead-code/src/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php)
- [test fixtures](/../master/rules/dead-code/tests/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector/Fixture)
Remove unused if check to non-empty array before foreach of the array
```diff
class SomeClass
{
public function run()
{
$values = [];
- if ($values !== []) {
- foreach ($values as $value) {
- echo $value;
- }
+ foreach ($values as $value) {
+ echo $value;
}
}
}
```
<br>
### `RemoveUnusedParameterRector`
- class: [`Rector\DeadCode\Rector\ClassMethod\RemoveUnusedParameterRector`](/../master/rules/dead-code/src/Rector/ClassMethod/RemoveUnusedParameterRector.php)

View File

@ -0,0 +1,135 @@
<?php
declare(strict_types=1);
namespace Rector\DeadCode\Rector\If_;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp\NotEqual;
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
use PhpParser\Node\Stmt\Foreach_;
use PhpParser\Node\Stmt\If_;
use Rector\Core\PhpParser\Node\Manipulator\IfManipulator;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
/**
* @see \Rector\DeadCode\Tests\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector\RemoveUnusedNonEmptyArrayBeforeForeachRectorTest
*/
final class RemoveUnusedNonEmptyArrayBeforeForeachRector extends AbstractRector
{
/**
* @var IfManipulator
*/
private $ifManipulator;
public function __construct(IfManipulator $ifManipulator)
{
$this->ifManipulator = $ifManipulator;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Remove unused if check to non-empty array before foreach of the array', [
new CodeSample(
<<<'PHP'
class SomeClass
{
public function run()
{
$values = [];
if ($values !== []) {
foreach ($values as $value) {
echo $value;
}
}
}
}
PHP
,
<<<'PHP'
class SomeClass
{
public function run()
{
$values = [];
foreach ($values as $value) {
echo $value;
}
}
}
PHP
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [If_::class];
}
/**
* @param If_ $node
*/
public function refactor(Node $node): ?Node
{
if (! $this->ifManipulator->isIfWithOnlyForeach($node)) {
return null;
}
/** @var Foreach_ $foreach */
$foreach = $node->stmts[0];
$foreachExpr = $foreach->expr;
if (! $node->cond instanceof NotIdentical && ! $node->cond instanceof NotEqual) {
return null;
}
/** @var NotIdentical|NotEqual $notIdentical */
$notIdentical = $node->cond;
if (! $this->isMatching($notIdentical, $foreachExpr)) {
return null;
}
return $foreach;
}
private function isEmptyArray(Expr $expr): bool
{
if (! $expr instanceof Array_) {
return false;
}
return $expr->items === [];
}
private function isEmptyArrayAndForeachedVariable(Expr $leftExpr, Expr $rightExpr, Expr $foreachExpr): bool
{
if (! $this->isEmptyArray($leftExpr)) {
return false;
}
return $this->areNodesWithoutCommentsEqual($foreachExpr, $rightExpr);
}
/**
* @param NotIdentical|NotEqual $binaryOp
*/
private function isMatching(BinaryOp $binaryOp, Expr $foreachExpr): bool
{
if ($this->isEmptyArrayAndForeachedVariable($binaryOp->left, $binaryOp->right, $foreachExpr)) {
return true;
}
return $this->isEmptyArrayAndForeachedVariable($binaryOp->right, $binaryOp->left, $foreachExpr);
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace Rector\DeadCode\Tests\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector\Fixture;
class SomeClass
{
public function run()
{
$values = [];
if ($values !== []) {
foreach ($values as $value) {
echo $value;
}
}
if ([] !== $values) {
foreach ($values as $value) {
echo $value;
}
}
}
}
?>
-----
<?php
namespace Rector\DeadCode\Tests\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector\Fixture;
class SomeClass
{
public function run()
{
$values = [];
foreach ($values as $value) {
echo $value;
}
foreach ($values as $value) {
echo $value;
}
}
}
?>

View File

@ -0,0 +1,35 @@
<?php
namespace Rector\DeadCode\Tests\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector\Fixture;
class NotEqual
{
public function run()
{
$values = [];
if ($values != []) {
foreach ($values as $value) {
echo $value;
}
}
}
}
?>
-----
<?php
namespace Rector\DeadCode\Tests\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector\Fixture;
class NotEqual
{
public function run()
{
$values = [];
foreach ($values as $value) {
echo $value;
}
}
}
?>

View File

@ -0,0 +1,18 @@
<?php
namespace Rector\DeadCode\Tests\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector\Fixture;
class SkipDifferentForeach
{
public function run()
{
$values = [];
$values2 = [];
if ($values != []) {
foreach ($values2 as $value) {
echo $value;
}
}
}
}

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Rector\DeadCode\Tests\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector;
use Iterator;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Rector\DeadCode\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector;
final class RemoveUnusedNonEmptyArrayBeforeForeachRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(string $file): void
{
$this->doTestFile($file);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return RemoveUnusedNonEmptyArrayBeforeForeachRector::class;
}
}

View File

@ -276,6 +276,19 @@ final class IfManipulator
return $this->hasOnlyStmtOfType($node, Return_::class);
}
public function isIfWithOnlyForeach(Node $node): bool
{
if (! $node instanceof If_) {
return false;
}
if (! $this->isIfWithoutElseAndElseIfs($node)) {
return false;
}
return $this->hasOnlyStmtOfType($node, Foreach_::class);
}
private function matchComparedAndReturnedNode(NotIdentical $notIdentical, Return_ $returnNode): ?Expr
{
if ($this->betterStandardPrinter->areNodesEqual(