Skip to content

Commit b11f55f

Browse files
authored
[typed-collections] Add RemoveUselessIsEmptyAssignRector (#437)
1 parent 360ed2a commit b11f55f

7 files changed

Lines changed: 253 additions & 0 deletions

File tree

config/sets/typed-collections.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use Rector\Doctrine\TypedCollections\Rector\If_\RemoveIfCollectionIdenticalToNullRector;
3030
use Rector\Doctrine\TypedCollections\Rector\If_\RemoveIfInstanceofCollectionRector;
3131
use Rector\Doctrine\TypedCollections\Rector\If_\RemoveIsArrayOnCollectionRector;
32+
use Rector\Doctrine\TypedCollections\Rector\If_\RemoveUselessIsEmptyAssignRector;
3233
use Rector\Doctrine\TypedCollections\Rector\MethodCall\AssertNullOnCollectionToAssertEmptyRector;
3334
use Rector\Doctrine\TypedCollections\Rector\MethodCall\AssertSameCountOnCollectionToAssertCountRector;
3435
use Rector\Doctrine\TypedCollections\Rector\MethodCall\SetArrayToNewCollectionRector;
@@ -87,6 +88,7 @@
8788

8889
// cleanup
8990
RemoveNullsafeOnCollectionRector::class,
91+
RemoveUselessIsEmptyAssignRector::class,
9092

9193
// test assertions
9294
RemoveAssertNotNullOnCollectionRector::class,
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace Rector\Doctrine\Tests\TypedCollections\Rector\If_\RemoveUselessIsEmptyAssignRector\Fixture;
4+
5+
use Doctrine\Common\Collections\ArrayCollection;
6+
use Doctrine\Common\Collections\Collection;
7+
8+
final class RemoveUseless
9+
{
10+
public Collection $items;
11+
12+
public function someMethod()
13+
{
14+
if ($this->items->isEmpty()) {
15+
$this->items = new ArrayCollection();
16+
}
17+
18+
return $this->items;
19+
}
20+
}
21+
22+
?>
23+
-----
24+
<?php
25+
26+
namespace Rector\Doctrine\Tests\TypedCollections\Rector\If_\RemoveUselessIsEmptyAssignRector\Fixture;
27+
28+
use Doctrine\Common\Collections\ArrayCollection;
29+
use Doctrine\Common\Collections\Collection;
30+
31+
final class RemoveUseless
32+
{
33+
public Collection $items;
34+
35+
public function someMethod()
36+
{
37+
return $this->items;
38+
}
39+
}
40+
41+
?>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Rector\Doctrine\Tests\TypedCollections\Rector\If_\RemoveUselessIsEmptyAssignRector\Fixture;
4+
5+
use Doctrine\Common\Collections\ArrayCollection;
6+
use Doctrine\Common\Collections\Collection;
7+
8+
final class SkipDifferentNew
9+
{
10+
public Collection $items;
11+
12+
public function someMethod()
13+
{
14+
if ($this->items->isEmpty()) {
15+
$this->items = new \stdClass();
16+
}
17+
18+
return $this->items;
19+
}
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Rector\Doctrine\Tests\TypedCollections\Rector\If_\RemoveUselessIsEmptyAssignRector\Fixture;
4+
5+
use Doctrine\Common\Collections\ArrayCollection;
6+
use Doctrine\Common\Collections\Collection;
7+
8+
final class SkipDifferentVariable
9+
{
10+
public Collection $items;
11+
12+
public function someMethod()
13+
{
14+
if ($this->items->isEmpty()) {
15+
$this->another = new ArrayCollection();
16+
}
17+
18+
return $this->items;
19+
}
20+
}
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\Doctrine\Tests\TypedCollections\Rector\If_\RemoveUselessIsEmptyAssignRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class RemoveUselessIsEmptyAssignRectorTest 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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use Rector\Doctrine\TypedCollections\Rector\If_\RemoveUselessIsEmptyAssignRector;
7+
8+
return static function (RectorConfig $rectorConfig): void {
9+
$rectorConfig->rule(RemoveUselessIsEmptyAssignRector::class);
10+
};
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Doctrine\TypedCollections\Rector\If_;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Expr;
9+
use PhpParser\Node\Expr\Assign;
10+
use PhpParser\Node\Expr\MethodCall;
11+
use PhpParser\Node\Expr\New_;
12+
use PhpParser\Node\Stmt\Expression;
13+
use PhpParser\Node\Stmt\If_;
14+
use PhpParser\NodeVisitor;
15+
use Rector\Doctrine\Enum\DoctrineClass;
16+
use Rector\Doctrine\TypedCollections\TypeAnalyzer\CollectionTypeDetector;
17+
use Rector\Rector\AbstractRector;
18+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
19+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
20+
21+
/**
22+
* @see \Rector\Doctrine\Tests\TypedCollections\Rector\If_\RemoveUselessIsEmptyAssignRector\RemoveUselessIsEmptyAssignRectorTest
23+
*/
24+
final class RemoveUselessIsEmptyAssignRector extends AbstractRector
25+
{
26+
public function __construct(
27+
private readonly CollectionTypeDetector $collectionTypeDetector,
28+
) {
29+
30+
}
31+
32+
public function getNodeTypes(): array
33+
{
34+
return [If_::class];
35+
}
36+
37+
/**
38+
* @param If_ $node
39+
*/
40+
public function refactor(Node $node): ?int
41+
{
42+
if (! $node->cond instanceof MethodCall) {
43+
return null;
44+
}
45+
46+
$methodCall = $node->cond;
47+
if (! $this->isName($methodCall->name, 'isEmpty')) {
48+
return null;
49+
}
50+
51+
if (! $this->collectionTypeDetector->isCollectionType($methodCall->var)) {
52+
return null;
53+
}
54+
55+
if (count($node->stmts) !== 1) {
56+
return null;
57+
}
58+
59+
$soleStmts = $node->stmts[0];
60+
61+
if (! $soleStmts instanceof Expression) {
62+
return null;
63+
}
64+
65+
if (! $soleStmts->expr instanceof Assign) {
66+
return null;
67+
}
68+
69+
$assign = $soleStmts->expr;
70+
if (! $this->nodeNameResolver->areNamesEqual($assign->var, $methodCall->var)) {
71+
return null;
72+
}
73+
74+
if (! $this->isArrayCollectionNewInstance($assign->expr)) {
75+
return null;
76+
}
77+
78+
return NodeVisitor::REMOVE_NODE;
79+
}
80+
81+
public function getRuleDefinition(): RuleDefinition
82+
{
83+
return new RuleDefinition(
84+
'Remove useless isEmpty() check on collection with following new ArrayCollection() instance',
85+
[
86+
new CodeSample(
87+
<<<'CODE_SAMPLE'
88+
use Doctrine\Common\Collections\Collection;
89+
90+
final class SomeClass
91+
{
92+
private Collection $collection;
93+
94+
public function someMethod()
95+
{
96+
if ($this->collection->isEmpty()) {
97+
$this->collection = new ArrayCollection();
98+
}
99+
100+
return $this->collection;
101+
}
102+
}
103+
CODE_SAMPLE
104+
,
105+
<<<'CODE_SAMPLE'
106+
use Doctrine\Common\Collections\Collection;
107+
108+
final class SomeClass
109+
{
110+
private Collection $collection;
111+
112+
public function someMethod()
113+
{
114+
return $this->collection;
115+
}
116+
}
117+
CODE_SAMPLE
118+
),
119+
120+
]
121+
);
122+
}
123+
124+
private function isArrayCollectionNewInstance(Expr $expr): bool
125+
{
126+
if (! $expr instanceof New_) {
127+
return true;
128+
}
129+
130+
return $this->isName($expr->class, DoctrineClass::ARRAY_COLLECTION);
131+
}
132+
}

0 commit comments

Comments
 (0)