Skip to content

Commit 8826594

Browse files
committed
[code-quality] Kick off SoleArrayDimFetchAssignsToDirectNewArrayRector
1 parent 87b1e0d commit 8826594

8 files changed

Lines changed: 301 additions & 6 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\SoleArrayDimFetchAssignsToDirectNewArrayRector\Fixture;
4+
5+
final class SkipDifferentVariable
6+
{
7+
public function create()
8+
{
9+
$item['sub_key'] = 500;
10+
11+
$items['name'] = 'John';
12+
$items['surname'] = 'Doe';
13+
$items['age'] = $item;
14+
15+
return $items;
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\SoleArrayDimFetchAssignsToDirectNewArrayRector\Fixture;
4+
5+
class SkipMultipleReturns
6+
{
7+
public function create()
8+
{
9+
$items['name'] = 'John';
10+
$items['surname'] = 'Doe';
11+
$items['age'] = 50;
12+
13+
if (mt_rand(1, 2)) {
14+
return [];
15+
}
16+
17+
return $items;
18+
}
19+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\SoleArrayDimFetchAssignsToDirectNewArrayRector\Fixture;
4+
5+
class SomeClass
6+
{
7+
public function create()
8+
{
9+
$items['name'] = 'John';
10+
$items['surname'] = 'Doe';
11+
$items['age'] = 50;
12+
return $items;
13+
}
14+
}
15+
16+
?>
17+
-----
18+
<?php
19+
20+
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\SoleArrayDimFetchAssignsToDirectNewArrayRector\Fixture;
21+
22+
class SomeClass
23+
{
24+
public function create()
25+
{
26+
return ['name' => 'John', 'surname' => 'Doe', 'age' => 50];
27+
}
28+
}
29+
30+
?>
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\CodeQuality\Rector\ClassMethod\SoleArrayDimFetchAssignsToDirectNewArrayRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class SoleArrayDimFetchAssignsToDirectNewArrayRectorTest 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\CodeQuality\Rector\ClassMethod\SoleArrayDimFetchAssignsToDirectNewArrayRector;
6+
use Rector\Config\RectorConfig;
7+
8+
return RectorConfig::configure()
9+
->withRules([SoleArrayDimFetchAssignsToDirectNewArrayRector::class]);
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\CodeQuality\Rector\ClassMethod;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\ArrayItem;
9+
use PhpParser\Node\Expr;
10+
use PhpParser\Node\Expr\Array_;
11+
use PhpParser\Node\Expr\ArrayDimFetch;
12+
use PhpParser\Node\Expr\Assign;
13+
use PhpParser\Node\Expr\Variable;
14+
use PhpParser\Node\Stmt\ClassMethod;
15+
use PhpParser\Node\Stmt\Expression;
16+
use PhpParser\Node\Stmt\Function_;
17+
use PhpParser\Node\Stmt\Return_;
18+
use PHPStan\Type\Constant\ConstantArrayType;
19+
use Rector\Rector\AbstractRector;
20+
use Rector\TypeDeclarationDocblocks\NodeFinder\ReturnNodeFinder;
21+
use Rector\ValueObject\MethodName;
22+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
23+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
24+
25+
/**
26+
* @see \Rector\Tests\CodeQuality\Rector\ClassMethod\SoleArrayDimFetchAssignsToDirectNewArrayRector\SoleArrayDimFetchAssignsToDirectNewArrayRectorTest
27+
*/
28+
final class SoleArrayDimFetchAssignsToDirectNewArrayRector extends AbstractRector
29+
{
30+
public function __construct(
31+
private readonly ReturnNodeFinder $returnNodeFinder
32+
) {
33+
}
34+
35+
public function getRuleDefinition(): RuleDefinition
36+
{
37+
return new RuleDefinition(
38+
'Change repeated array assigns to direct new array instance',
39+
[
40+
new CodeSample(
41+
<<<'CODE_SAMPLE'
42+
class SomeClass
43+
{
44+
public function create()
45+
{
46+
$items[0]['name'] = 'John';
47+
$items[0]['surname'] = 'Doe';
48+
$items[0]['age'] = 50;
49+
return $items;
50+
}
51+
}
52+
CODE_SAMPLE
53+
54+
,
55+
<<<'CODE_SAMPLE'
56+
class SomeClass
57+
{
58+
public function create()
59+
{
60+
return [
61+
[
62+
'name' => 'John',
63+
'surname' => 'Doe',
64+
'age' => 50,
65+
]
66+
];
67+
}
68+
}
69+
CODE_SAMPLE
70+
),
71+
]
72+
);
73+
}
74+
75+
/**
76+
* @return array<class-string<Node>>
77+
*/
78+
public function getNodeTypes(): array
79+
{
80+
return [ClassMethod::class, Function_::class];
81+
}
82+
83+
/**
84+
* @param ClassMethod|Function_ $node
85+
*/
86+
public function refactor(Node $node): ?Node
87+
{
88+
if ($node instanceof ClassMethod && $this->isName($node, MethodName::CONSTRUCT)) {
89+
return null;
90+
}
91+
92+
if ($node->stmts === null) {
93+
return null;
94+
}
95+
96+
// find repeated assigns
97+
98+
$return = $this->returnNodeFinder->findOnlyReturnWithExpr($node);
99+
if (! $return instanceof Return_) {
100+
return null;
101+
}
102+
103+
if (! $return->expr instanceof Variable) {
104+
return null;
105+
}
106+
107+
$returnedVariableName = $this->getName($return->expr);
108+
if (! is_string($returnedVariableName)) {
109+
return null;
110+
}
111+
112+
// has only array dim assigns
113+
if (! $this->hasOnlyArrayDimAssigns($node)) {
114+
return null;
115+
}
116+
117+
$variable = $return->expr;
118+
119+
$variableType = $this->getType($variable);
120+
// must be constant array
121+
if (! $variableType instanceof ConstantArrayType) {
122+
return null;
123+
}
124+
125+
$arrayItems = [];
126+
127+
// convert dim fetch on no-level
128+
foreach ($node->stmts as $stmt) {
129+
if ($stmt instanceof Return_) {
130+
continue;
131+
}
132+
133+
if ($stmt instanceof Expression && $stmt->expr instanceof Assign) {
134+
$assign = $stmt->expr;
135+
/** @var ArrayDimFetch $dimFetch */
136+
$dimFetch = $assign->var;
137+
if ($dimFetch->var instanceof Variable) {
138+
if ($this->isName($dimFetch->var, $returnedVariableName)) {
139+
$arrayItems[] = new ArrayItem($assign->expr, $dimFetch->dim);
140+
141+
} else {
142+
return null;
143+
}
144+
}
145+
}
146+
}
147+
148+
if ($arrayItems === []) {
149+
return null;
150+
}
151+
152+
$node->stmts = [new Return_(new Array_($arrayItems))];
153+
154+
return $node;
155+
}
156+
157+
private function hasOnlyArrayDimAssigns(ClassMethod|Function_ $functionLike): bool
158+
{
159+
if ($functionLike->stmts === null) {
160+
return false;
161+
}
162+
163+
$itemCount = count($functionLike->stmts);
164+
165+
foreach ($functionLike->stmts as $key => $stmt) {
166+
// only accepted return
167+
if ($itemCount === $key + 1 && $stmt instanceof Return_) {
168+
continue;
169+
}
170+
171+
if (! $stmt instanceof Expression) {
172+
continue;
173+
}
174+
175+
if (! $stmt->expr instanceof Assign) {
176+
continue;
177+
}
178+
179+
$assign = $stmt->expr;
180+
if (! $assign->var instanceof ArrayDimFetch) {
181+
continue;
182+
}
183+
184+
$assignDimFetch = $assign->var;
185+
if (! $assignDimFetch->dim instanceof Expr) {
186+
continue;
187+
}
188+
189+
return true;
190+
}
191+
192+
return false;
193+
}
194+
}

rules/Privatization/TypeManipulator/TypeNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ final class TypeNormalizer
2525
* @deprecated This method is deprecated and will be removed in the next major release.
2626
* Use @see generalizeConstantTypes() instead.
2727
*/
28-
public function generalizeConstantBoolTypes(\PHPStan\Type\Type $type): Type
28+
public function generalizeConstantBoolTypes(Type $type): Type
2929
{
3030
return $this->generalizeConstantTypes($type);
3131
}

rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,9 @@ public function refactor(Node $node): ?Node
154154
if ($yields !== []) {
155155
$yieldType = $this->yieldTypeResolver->resolveFromYieldNodes($yields, $dataProviderClassMethod);
156156

157-
if ($yieldType instanceof FullyQualifiedGenericObjectType) {
158-
if ($yieldType->getClassName() === 'Generator') {
159-
// most likely, a static iterator is used in data test fixtures
160-
$yieldType = new FullyQualifiedGenericObjectType('Iterator', $yieldType->getTypes());
161-
}
157+
if ($yieldType instanceof FullyQualifiedGenericObjectType && $yieldType->getClassName() === 'Generator') {
158+
// most likely, a static iterator is used in data test fixtures
159+
$yieldType = new FullyQualifiedGenericObjectType('Iterator', $yieldType->getTypes());
162160
}
163161

164162
$this->addGeneratedTypeReturnDocblockType($yieldType, $classMethodPhpDocInfo, $dataProviderClassMethod);

0 commit comments

Comments
 (0)