Skip to content

Commit 13d6685

Browse files
authored
[typed-collections] Add ArrayOffsetSetToSetCollectionCallRector (#429)
1 parent cbe2a4c commit 13d6685

7 files changed

Lines changed: 247 additions & 0 deletions

File tree

config/sets/typed-collections.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Rector\Config\RectorConfig;
66
use Rector\Doctrine\TypedCollections\Rector\Assign\ArrayDimFetchAssignToAddCollectionCallRector;
7+
use Rector\Doctrine\TypedCollections\Rector\Assign\ArrayOffsetSetToSetCollectionCallRector;
78
use Rector\Doctrine\TypedCollections\Rector\Class_\CompleteParamDocblockFromSetterToCollectionRector;
89
use Rector\Doctrine\TypedCollections\Rector\Class_\CompletePropertyDocblockFromToManyRector;
910
use Rector\Doctrine\TypedCollections\Rector\Class_\CompleteReturnDocblockFromToManyRector;
@@ -47,6 +48,7 @@
4748

4849
// collection method calls
4950
ArrayDimFetchAssignToAddCollectionCallRector::class,
51+
ArrayOffsetSetToSetCollectionCallRector::class,
5052
ArrayMapOnCollectionToArrayRector::class,
5153
ArrayMergeOnCollectionToArrayRector::class,
5254
EmptyOnCollectionToIsEmptyCallRector::class,
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\Assign\ArrayOffsetSetToSetCollectionCallRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class ArrayOffsetSetToSetCollectionCallRectorTest 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: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Doctrine\Tests\TypedCollections\Rector\Assign\ArrayOffsetSetToSetCollectionCallRector\Fixture;
6+
7+
final class SkipNonCollection
8+
{
9+
/**
10+
* @var array<int, string>
11+
*/
12+
public $items;
13+
14+
public function setItems()
15+
{
16+
$items = $this->getItems();
17+
$items[5] = 10;
18+
}
19+
20+
private function getItems()
21+
{
22+
return $this->items;
23+
}
24+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Doctrine\Tests\TypedCollections\Rector\Assign\ArrayOffsetSetToSetCollectionCallRector\Fixture;
6+
7+
use Doctrine\Common\Collections\Collection;
8+
9+
final class SkipWithoutDim
10+
{
11+
/**
12+
* @var Collection<int, string>
13+
*/
14+
public $items;
15+
16+
public function setItems()
17+
{
18+
$items = $this->getItems();
19+
$items[] = 10;
20+
}
21+
22+
private function getItems(): Collection
23+
{
24+
return $this->items;
25+
}
26+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Doctrine\Tests\TypedCollections\Rector\Assign\ArrayOffsetSetToSetCollectionCallRector\Fixture;
6+
7+
use Doctrine\Common\Collections\Collection;
8+
9+
final class SomeClass
10+
{
11+
/**
12+
* @var Collection<int, string>
13+
*/
14+
public $items;
15+
16+
public function setItems()
17+
{
18+
$items = $this->getItems();
19+
$items[5] = 10;
20+
}
21+
22+
private function getItems(): Collection
23+
{
24+
return $this->items;
25+
}
26+
}
27+
28+
?>
29+
-----
30+
<?php
31+
32+
declare(strict_types=1);
33+
34+
namespace Rector\Doctrine\Tests\TypedCollections\Rector\Assign\ArrayOffsetSetToSetCollectionCallRector\Fixture;
35+
36+
use Doctrine\Common\Collections\Collection;
37+
38+
final class SomeClass
39+
{
40+
/**
41+
* @var Collection<int, string>
42+
*/
43+
public $items;
44+
45+
public function setItems()
46+
{
47+
$items = $this->getItems();
48+
$items->set(5, 10);
49+
}
50+
51+
private function getItems(): Collection
52+
{
53+
return $this->items;
54+
}
55+
}
56+
57+
?>
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\Assign\ArrayOffsetSetToSetCollectionCallRector;
7+
8+
return static function (RectorConfig $rectorConfig): void {
9+
$rectorConfig->rule(ArrayOffsetSetToSetCollectionCallRector::class);
10+
};
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Doctrine\TypedCollections\Rector\Assign;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Arg;
9+
use PhpParser\Node\Expr;
10+
use PhpParser\Node\Expr\ArrayDimFetch;
11+
use PhpParser\Node\Expr\Assign;
12+
use PhpParser\Node\Expr\MethodCall;
13+
use Rector\Doctrine\TypedCollections\TypeAnalyzer\CollectionTypeDetector;
14+
use Rector\Rector\AbstractRector;
15+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
16+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
17+
18+
/**
19+
* @see \Rector\Doctrine\Tests\TypedCollections\Rector\Assign\ArrayOffsetSetToSetCollectionCallRector\ArrayOffsetSetToSetCollectionCallRectorTest
20+
*/
21+
final class ArrayOffsetSetToSetCollectionCallRector extends AbstractRector
22+
{
23+
public function __construct(
24+
private readonly CollectionTypeDetector $collectionTypeDetector
25+
) {
26+
27+
}
28+
29+
public function getRuleDefinition(): RuleDefinition
30+
{
31+
return new RuleDefinition(
32+
'Change dim assign on a Collection to clear ->set() call',
33+
[
34+
new CodeSample(
35+
<<<'CODE_SAMPLE'
36+
use Doctrine\Common\Collections\Collection;
37+
38+
final class SomeClass
39+
{
40+
/**
41+
* @var Collection<int, string>
42+
*/
43+
public $items;
44+
45+
public function setItem()
46+
{
47+
$this->items['key'] = 'value';
48+
}
49+
}
50+
CODE_SAMPLE
51+
,
52+
<<<'CODE_SAMPLE'
53+
use Doctrine\Common\Collections\Collection;
54+
55+
final class SomeClass
56+
{
57+
/**
58+
* @var Collection<int, string>
59+
*/
60+
public $items;
61+
62+
public function setItem()
63+
{
64+
$this->items->set('key', 'value');
65+
}
66+
}
67+
CODE_SAMPLE
68+
)]
69+
);
70+
}
71+
72+
public function getNodeTypes(): array
73+
{
74+
return [Assign::class];
75+
}
76+
77+
/**
78+
* @param Assign $node
79+
*/
80+
public function refactor(Node $node): ?MethodCall
81+
{
82+
if (! $node->var instanceof ArrayDimFetch) {
83+
return null;
84+
}
85+
86+
$arrayDimFetch = $node->var;
87+
if (! $arrayDimFetch->dim instanceof Expr) {
88+
return null;
89+
}
90+
91+
$assignedExpr = $arrayDimFetch->var;
92+
if (! $this->collectionTypeDetector->isCollectionType($assignedExpr)) {
93+
return null;
94+
}
95+
96+
$args = [new Arg($arrayDimFetch->dim), new Arg($node->expr)];
97+
98+
return new MethodCall($assignedExpr, 'set', $args);
99+
}
100+
}

0 commit comments

Comments
 (0)