Skip to content

Commit 94924c8

Browse files
committed
[code-quality] Improve InlineArrayReturnAssignRector to include implicit variable init and nested keys
1 parent 87b1e0d commit 94924c8

File tree

8 files changed

+187
-50
lines changed

8 files changed

+187
-50
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector\Fixture;
4+
5+
final class IncludeNestedVariable
6+
{
7+
public function create()
8+
{
9+
$items[0]['name'] = 'John';
10+
$items[0]['surname'] = 'Doe';
11+
$items[0]['age'] = 50;
12+
return $items;
13+
}
14+
}
15+
16+
?>
17+
-----
18+
<?php
19+
20+
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector\Fixture;
21+
22+
final class IncludeNestedVariable
23+
{
24+
public function create()
25+
{
26+
return [
27+
['name' => 'John', 'surname' => 'Doe', 'age' => 50]
28+
];
29+
}
30+
}
31+
32+
?>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector\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+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector\Fixture;
4+
5+
final 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\InlineArrayReturnAssignRector\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\InlineArrayReturnAssignRector\Fixture;
21+
22+
class SomeClass
23+
{
24+
public function create()
25+
{
26+
return ['name' => 'John', 'surname' => 'Doe', 'age' => 50];
27+
}
28+
}
29+
30+
?>

rules/CodeQuality/NodeAnalyzer/VariableDimFetchAssignResolver.php

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,36 @@
1111
use PhpParser\Node\Expr\Variable;
1212
use PhpParser\Node\Stmt;
1313
use PhpParser\Node\Stmt\Expression;
14+
use PhpParser\Node\Stmt\Return_;
1415
use Rector\CodeQuality\ValueObject\KeyAndExpr;
15-
use Rector\PhpParser\Comparing\NodeComparator;
16+
use Rector\NodeNameResolver\NodeNameResolver;
1617
use Rector\PhpParser\Node\BetterNodeFinder;
1718

1819
final readonly class VariableDimFetchAssignResolver
1920
{
2021
public function __construct(
21-
private NodeComparator $nodeComparator,
22-
private BetterNodeFinder $betterNodeFinder
22+
private BetterNodeFinder $betterNodeFinder,
23+
private NodeNameResolver $nodeNameResolver
2324
) {
2425
}
2526

2627
/**
2728
* @param Stmt[] $stmts
2829
* @return KeyAndExpr[]
2930
*/
30-
public function resolveFromStmtsAndVariable(array $stmts, Variable $variable): array
31+
public function resolveFromStmtsAndVariable(array $stmts, ?Assign $emptyArrayAssign, string $variableName): array
3132
{
3233
$keysAndExprs = [];
3334

3435
foreach ($stmts as $stmt) {
36+
if ($stmt instanceof Expression && $stmt->expr === $emptyArrayAssign) {
37+
continue;
38+
}
39+
40+
if ($stmt instanceof Return_) {
41+
continue;
42+
}
43+
3544
if (! $stmt instanceof Expression) {
3645
return [];
3746
}
@@ -43,7 +52,7 @@ public function resolveFromStmtsAndVariable(array $stmts, Variable $variable): a
4352

4453
$assign = $stmtExpr;
4554

46-
$keyExpr = $this->matchKeyOnArrayDimFetchOfVariable($assign, $variable);
55+
$keyExpr = $this->matchKeyOnArrayDimFetchOfVariable($assign, $variableName);
4756
if ($assign->var instanceof ArrayDimFetch && $assign->var->var instanceof ArrayDimFetch) {
4857
return [];
4958
}
@@ -60,20 +69,20 @@ public function resolveFromStmtsAndVariable(array $stmts, Variable $variable): a
6069
return $keysAndExprs;
6170
}
6271

63-
private function matchKeyOnArrayDimFetchOfVariable(Assign $assign, Variable $variable): ?Expr
72+
private function matchKeyOnArrayDimFetchOfVariable(Assign $assign, string $variableName): ?Expr
6473
{
6574
if (! $assign->var instanceof ArrayDimFetch) {
6675
return null;
6776
}
6877

6978
$arrayDimFetch = $assign->var;
70-
if (! $this->nodeComparator->areNodesEqual($arrayDimFetch->var, $variable)) {
79+
if (! $this->nodeNameResolver->isName($arrayDimFetch->var, $variableName)) {
7180
return null;
7281
}
7382

7483
$isFoundInExpr = (bool) $this->betterNodeFinder->findFirst(
7584
$assign->expr,
76-
fn (Node $subNode): bool => $this->nodeComparator->areNodesEqual($subNode, $variable)
85+
fn (Node $subNode): bool => $this->nodeNameResolver->isName($subNode, $variableName)
7786
);
7887

7988
if ($isFoundInExpr) {

rules/CodeQuality/Rector/ClassMethod/InlineArrayReturnAssignRector.php

Lines changed: 68 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
use Rector\CodeQuality\ValueObject\KeyAndExpr;
1919
use Rector\Contract\PhpParser\Node\StmtsAwareInterface;
2020
use Rector\NodeTypeResolver\Node\AttributeKey;
21-
use Rector\PhpParser\Node\Value\ValueResolver;
2221
use Rector\Rector\AbstractRector;
2322
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
2423
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@@ -30,7 +29,6 @@ final class InlineArrayReturnAssignRector extends AbstractRector
3029
{
3130
public function __construct(
3231
private readonly VariableDimFetchAssignResolver $variableDimFetchAssignResolver,
33-
private readonly ValueResolver $valueResolver
3432
) {
3533
}
3634

@@ -81,60 +79,46 @@ public function refactor(Node $node): ?Node
8179
return null;
8280
}
8381

84-
$firstStmt = array_shift($stmts);
85-
$variable = $this->matchVariableAssignOfEmptyArray($firstStmt);
86-
if (! $variable instanceof Variable) {
82+
$lastStmt = $node->stmts[count($stmts) - 1] ?? null;
83+
if (! $lastStmt instanceof Return_) {
8784
return null;
8885
}
8986

90-
if (! $this->areAssignExclusiveToDimFetch($stmts)) {
87+
if (! $lastStmt->expr instanceof Variable) {
9188
return null;
9289
}
9390

94-
$lastStmt = array_pop($stmts);
95-
if (! $lastStmt instanceof Stmt) {
91+
$returnedVariableName = $this->getName($lastStmt->expr);
92+
if (! is_string($returnedVariableName)) {
9693
return null;
9794
}
9895

99-
if (! $this->isReturnOfVariable($lastStmt, $variable)) {
100-
return null;
101-
}
102-
103-
$keysAndExprs = $this->variableDimFetchAssignResolver->resolveFromStmtsAndVariable($stmts, $variable);
104-
if ($keysAndExprs === []) {
105-
return null;
106-
}
107-
108-
$array = $this->createArray($keysAndExprs);
109-
$node->stmts = [new Return_($array)];
96+
$emptyArrayAssign = $this->resolveDefaultEmptyArrayAssign($stmts, $returnedVariableName);
11097

111-
return $node;
112-
}
113-
114-
private function matchVariableAssignOfEmptyArray(Stmt $stmt): ?Variable
115-
{
116-
if (! $stmt instanceof Expression) {
98+
if (! $this->areAssignExclusiveToDimFetchVariable($stmts, $emptyArrayAssign)) {
11799
return null;
118100
}
119101

120-
if (! $stmt->expr instanceof Assign) {
102+
if (! $this->isReturnOfVariable($lastStmt, $returnedVariableName)) {
121103
return null;
122104
}
123105

124-
$assign = $stmt->expr;
125-
126-
if (! $this->valueResolver->isValue($assign->expr, [])) {
106+
$keysAndExprs = $this->variableDimFetchAssignResolver->resolveFromStmtsAndVariable(
107+
$stmts,
108+
$emptyArrayAssign,
109+
$returnedVariableName
110+
);
111+
if ($keysAndExprs === []) {
127112
return null;
128113
}
129114

130-
if (! $assign->var instanceof Variable) {
131-
return null;
132-
}
115+
$array = $this->createArray($keysAndExprs);
116+
$node->stmts = [new Return_($array)];
133117

134-
return $assign->var;
118+
return $node;
135119
}
136120

137-
private function isReturnOfVariable(Stmt $stmt, Variable $variable): bool
121+
private function isReturnOfVariable(Stmt $stmt, string $variableName): bool
138122
{
139123
if (! $stmt instanceof Return_) {
140124
return false;
@@ -144,7 +128,7 @@ private function isReturnOfVariable(Stmt $stmt, Variable $variable): bool
144128
return false;
145129
}
146130

147-
return $this->nodeComparator->areNodesEqual($stmt->expr, $variable);
131+
return $this->isName($stmt->expr, $variableName);
148132
}
149133

150134
/**
@@ -169,7 +153,7 @@ private function createArray(array $keysAndExprs): Array_
169153
*
170154
* @param Stmt[] $stmts
171155
*/
172-
private function areAssignExclusiveToDimFetch(array $stmts): bool
156+
private function areAssignExclusiveToDimFetchVariable(array $stmts, ?Assign $emptyArrayAssign): bool
173157
{
174158
$lastKey = array_key_last($stmts);
175159

@@ -189,6 +173,16 @@ private function areAssignExclusiveToDimFetch(array $stmts): bool
189173

190174
$assign = $stmt->expr;
191175

176+
// skip initial assign
177+
if ($assign === $emptyArrayAssign) {
178+
continue;
179+
}
180+
181+
// instance of empty array in first line is ok
182+
if ($key === 0 && $assign->expr instanceof Array_) {
183+
continue;
184+
}
185+
192186
// skip new X instance with args to keep complex assign readable
193187
if ($assign->expr instanceof New_ && ! $assign->expr->isFirstClassCallable() && $assign->expr->getArgs() !== []) {
194188
return false;
@@ -197,8 +191,46 @@ private function areAssignExclusiveToDimFetch(array $stmts): bool
197191
if (! $assign->var instanceof ArrayDimFetch) {
198192
return false;
199193
}
194+
195+
$arrayDimFetch = $assign->var;
196+
if (! $arrayDimFetch->var instanceof Variable) {
197+
return false;
198+
}
200199
}
201200

202201
return true;
203202
}
203+
204+
/**
205+
* @param Expression[] $stmts
206+
*/
207+
private function resolveDefaultEmptyArrayAssign(array $stmts, string $returnedVariableName): ?Assign
208+
{
209+
foreach ($stmts as $stmt) {
210+
if (! $stmt instanceof Expression) {
211+
continue;
212+
}
213+
214+
if (! $stmt->expr instanceof Assign) {
215+
continue;
216+
}
217+
218+
$assign = $stmt->expr;
219+
if (! $assign->var instanceof Variable) {
220+
continue;
221+
}
222+
223+
if (! $this->isName($assign->var, $returnedVariableName)) {
224+
continue;
225+
}
226+
227+
if (! $assign->expr instanceof Array_) {
228+
continue;
229+
}
230+
231+
return $assign;
232+
}
233+
234+
return null;
235+
}
204236
}

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)