Skip to content

Commit 3aa2d76

Browse files
committed
[dead-code] Add RemoveNextSameValueConditionRector
1 parent b52ae95 commit 3aa2d76

7 files changed

Lines changed: 255 additions & 0 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Rector\Tests\DeadCode\Rector\Stmt\RemoveNextSameValueConditionRector\Fixture;
4+
5+
final class SkipIfBodyUsed
6+
{
7+
public function __construct(array $items)
8+
{
9+
$count = 100;
10+
if ($items === []) {
11+
$items = ['a'];
12+
}
13+
14+
if ($items === []) {
15+
return $count;
16+
}
17+
}
18+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Rector\Tests\DeadCode\Rector\Stmt\RemoveNextSameValueConditionRector\Fixture;
4+
5+
final class SkipIfStmtsBetween
6+
{
7+
public function __construct(array $items)
8+
{
9+
$count = 100;
10+
if ($items === []) {
11+
$count = 0;
12+
}
13+
14+
$items = ['a'];
15+
16+
if ($items === []) {
17+
return $count;
18+
}
19+
}
20+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace Rector\Tests\DeadCode\Rector\Stmt\RemoveNextSameValueConditionRector\Fixture;
4+
5+
final class SomeClass
6+
{
7+
public function __construct(array $items)
8+
{
9+
$count = 100;
10+
if ($items === []) {
11+
$count = 0;
12+
}
13+
14+
if ($items === []) {
15+
return $count;
16+
}
17+
}
18+
}
19+
20+
?>
21+
-----
22+
<?php
23+
24+
namespace Rector\Tests\DeadCode\Rector\Stmt\RemoveNextSameValueConditionRector\Fixture;
25+
26+
final class SomeClass
27+
{
28+
public function __construct(array $items)
29+
{
30+
$count = 100;
31+
if ($items === []) {
32+
$count = 0;
33+
return $count;
34+
}
35+
}
36+
}
37+
38+
?>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\DeadCode\Rector\Stmt\RemoveNextSameValueConditionRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class RemoveNextSameValueConditionRectorTest extends AbstractRectorTestCase
12+
{
13+
#[DataProvider('provideData')]
14+
public function test(string $filePath): void
15+
{
16+
$this->doTestFile($filePath);
17+
}
18+
19+
public static function provideData(): Iterator
20+
{
21+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
22+
}
23+
24+
public function provideConfigFilePath(): string
25+
{
26+
return __DIR__ . '/config/configured_rule.php';
27+
}
28+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use Rector\DeadCode\Rector\Stmt\RemoveNextSameValueConditionRector;
7+
8+
return RectorConfig::configure()
9+
->withRules([RemoveNextSameValueConditionRector::class]);
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\DeadCode\Rector\Stmt;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Expr\Variable;
9+
use PhpParser\Node\Stmt\If_;
10+
use Rector\Contract\PhpParser\Node\StmtsAwareInterface;
11+
use Rector\DeadCode\SideEffect\SideEffectNodeDetector;
12+
use Rector\PhpParser\Node\BetterNodeFinder;
13+
use Rector\Rector\AbstractRector;
14+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
15+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
16+
17+
/**
18+
* @see \Rector\Tests\DeadCode\Rector\Stmt\RemoveNextSameValueConditionRector\RemoveNextSameValueConditionRectorTest
19+
*/
20+
final class RemoveNextSameValueConditionRector extends AbstractRector
21+
{
22+
public function __construct(
23+
private readonly SideEffectNodeDetector $sideEffectNodeDetector,
24+
private readonly BetterNodeFinder $betterNodeFinder,
25+
) {
26+
}
27+
28+
public function getRuleDefinition(): RuleDefinition
29+
{
30+
return new RuleDefinition(
31+
'Remove already checked if condition repeated in the very next stmt',
32+
[
33+
new CodeSample(
34+
<<<'CODE_SAMPLE'
35+
final class SomeClass
36+
{
37+
public function __construct(array $items)
38+
{
39+
$count = 100;
40+
if ($items === []) {
41+
$count = 0;
42+
}
43+
44+
if ($items === []) {
45+
return $count;
46+
}
47+
}
48+
}
49+
CODE_SAMPLE
50+
51+
,
52+
<<<'CODE_SAMPLE'
53+
final class SomeClass
54+
{
55+
public function __construct(array $items)
56+
{
57+
$count = 100;
58+
if ($items === []) {
59+
$count = 0;
60+
return $count;
61+
}
62+
}
63+
}
64+
CODE_SAMPLE
65+
),
66+
67+
]
68+
);
69+
}
70+
71+
/**
72+
* @return array<class-string<Node>>
73+
*/
74+
public function getNodeTypes(): array
75+
{
76+
return [StmtsAwareInterface::class];
77+
}
78+
79+
/**
80+
* @param StmtsAwareInterface $node
81+
*/
82+
public function refactor(Node $node): ?Node
83+
{
84+
if ($node->stmts === null) {
85+
return null;
86+
}
87+
88+
foreach ($node->stmts as $key => $stmt) {
89+
if (! $stmt instanceof If_) {
90+
continue;
91+
}
92+
93+
// first condition must be without side effect
94+
if ($this->sideEffectNodeDetector->detect($stmt->cond)) {
95+
continue;
96+
}
97+
98+
if ($this->isCondVariableUsedInIfBody($stmt)) {
99+
continue;
100+
}
101+
102+
$nextStmt = $node->stmts[$key + 1] ?? null;
103+
if (! $nextStmt instanceof If_) {
104+
continue;
105+
}
106+
107+
if (! $this->nodeComparator->areNodesEqual($stmt->cond, $nextStmt->cond)) {
108+
continue;
109+
}
110+
111+
$stmt->stmts = array_merge($stmt->stmts, $nextStmt->stmts);
112+
113+
// remove next node
114+
unset($node->stmts[$key + 1]);
115+
116+
return $node;
117+
}
118+
119+
return null;
120+
}
121+
122+
private function isCondVariableUsedInIfBody(If_ $if): bool
123+
{
124+
$condVariables = $this->betterNodeFinder->findInstancesOf($if->cond, [Variable::class]);
125+
126+
foreach ($condVariables as $condVariable) {
127+
$condVariableName = $this->getName($condVariable);
128+
if ($condVariableName === null) {
129+
continue;
130+
}
131+
132+
if ($this->betterNodeFinder->findVariableOfName($if->stmts, $condVariableName)) {
133+
return true;
134+
}
135+
}
136+
137+
return false;
138+
}
139+
}

src/Config/Level/DeadCodeLevel.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
use Rector\DeadCode\Rector\Return_\RemoveDeadConditionAboveReturnRector;
5555
use Rector\DeadCode\Rector\StaticCall\RemoveParentCallWithoutParentRector;
5656
use Rector\DeadCode\Rector\Stmt\RemoveConditionExactReturnRector;
57+
use Rector\DeadCode\Rector\Stmt\RemoveNextSameValueConditionRector;
5758
use Rector\DeadCode\Rector\Stmt\RemoveUnreachableStatementRector;
5859
use Rector\DeadCode\Rector\Switch_\RemoveDuplicatedCaseInSwitchRector;
5960
use Rector\DeadCode\Rector\Ternary\TernaryToBooleanOrFalseToBooleanAndRector;
@@ -99,6 +100,8 @@ final class DeadCodeLevel
99100
RemoveUselessAssignFromPropertyPromotionRector::class,
100101
RemoveConcatAutocastRector::class,
101102
SimplifyIfElseWithSameContentRector::class,
103+
104+
RemoveNextSameValueConditionRector::class,
102105
SimplifyUselessVariableRector::class,
103106
RemoveDeadZeroAndOneOperationRector::class,
104107

0 commit comments

Comments
 (0)