Skip to content

Commit c6b7d4d

Browse files
authored
[type-declaration] Add AddClosureParamArrayWhereDimFetchRector (#6815)
* [type-declaration] Add AddClosureParamArrayWhereDimFetchRector * skip teranry
1 parent f5edbf5 commit c6b7d4d

File tree

10 files changed

+261
-0
lines changed

10 files changed

+261
-0
lines changed
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\TypeDeclaration\Rector\FuncCall\AddArrowFunctionParamArrayWhereDimFetchRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class AddArrowFunctionParamArrayWhereDimFetchRectorTest 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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\FuncCall\AddArrowFunctionParamArrayWhereDimFetchRector\Fixture;
4+
5+
final class NestedDimFetches
6+
{
7+
public function run()
8+
{
9+
$array = [['name' => 'John']];
10+
11+
$result = array_map(fn ($item) => $item['name']['nested'], $array);
12+
}
13+
}
14+
15+
?>
16+
-----
17+
<?php
18+
19+
namespace Rector\Tests\TypeDeclaration\Rector\FuncCall\AddArrowFunctionParamArrayWhereDimFetchRector\Fixture;
20+
21+
final class NestedDimFetches
22+
{
23+
public function run()
24+
{
25+
$array = [['name' => 'John']];
26+
27+
$result = array_map(fn (array $item) => $item['name']['nested'], $array);
28+
}
29+
}
30+
31+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\FuncCall\AddArrowFunctionParamArrayWhereDimFetchRector\Fixture;
4+
5+
final class SkipDifferentName
6+
{
7+
public function run()
8+
{
9+
$array = [['name' => 'John']];
10+
11+
$result = array_map(fn ($item) => $hemen['name'], $array);
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\FuncCall\AddArrowFunctionParamArrayWhereDimFetchRector\Fixture;
4+
5+
final class SkipFilledType
6+
{
7+
public function run()
8+
{
9+
$array = [['name' => 'John']];
10+
11+
$result = array_map(fn (iterable $item) => $item['name'], $array);
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\TypeDeclaration\Rector\FuncCall\AddArrowFunctionParamArrayWhereDimFetchRector\Fixture;
6+
7+
final class SkipTernary
8+
{
9+
public function run()
10+
{
11+
$array = [['name' => 'John']];
12+
13+
$result = array_map(fn ($item) => is_array($item) ? $item['name'] : null, $array);
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\FuncCall\AddArrowFunctionParamArrayWhereDimFetchRector\Fixture;
4+
5+
final class FillParamArray
6+
{
7+
public function run()
8+
{
9+
$array = [['name' => 'John']];
10+
11+
$result = array_map(fn ($item) => $item['name'], $array);
12+
}
13+
}
14+
15+
?>
16+
-----
17+
<?php
18+
19+
namespace Rector\Tests\TypeDeclaration\Rector\FuncCall\AddArrowFunctionParamArrayWhereDimFetchRector\Fixture;
20+
21+
final class FillParamArray
22+
{
23+
public function run()
24+
{
25+
$array = [['name' => 'John']];
26+
27+
$result = array_map(fn (array $item) => $item['name'], $array);
28+
}
29+
}
30+
31+
?>
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\TypeDeclaration\Rector\FuncCall\AddArrowFunctionParamArrayWhereDimFetchRector;
7+
8+
return RectorConfig::configure()
9+
->withRules([AddArrowFunctionParamArrayWhereDimFetchRector::class]);

rules/Naming/Rector/Class_/RenamePropertyToMatchTypeRector.php

+1
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ private function skipDateTimeOrMockObjectPropertyType(Property $property): bool
148148
if ($this->isName($property->type, ClassName::MOCK_OBJECT)) {
149149
return true;
150150
}
151+
151152
return $this->isName($property->type, ClassName::DATE_TIME);
152153
}
153154
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\TypeDeclaration\Rector\FuncCall;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Expr\ArrayDimFetch;
9+
use PhpParser\Node\Expr\ArrowFunction;
10+
use PhpParser\Node\Expr\FuncCall;
11+
use PhpParser\Node\Expr\Ternary;
12+
use PhpParser\Node\Identifier;
13+
use PhpParser\NodeFinder;
14+
use Rector\Rector\AbstractRector;
15+
use Rector\ValueObject\PhpVersionFeature;
16+
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
17+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
18+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
19+
20+
/**
21+
* @see \Rector\Tests\TypeDeclaration\Rector\FuncCall\AddArrowFunctionParamArrayWhereDimFetchRector\AddArrowFunctionParamArrayWhereDimFetchRectorTest
22+
*/
23+
final class AddArrowFunctionParamArrayWhereDimFetchRector extends AbstractRector implements MinPhpVersionInterface
24+
{
25+
public function getRuleDefinition(): RuleDefinition
26+
{
27+
return new RuleDefinition('Add function/closure param array type, if dim fetch is inside', [
28+
new CodeSample(
29+
<<<'CODE_SAMPLE'
30+
$array = [['name' => 'John']];
31+
32+
$result = array_map(fn ($item) => $item['name'], $array);
33+
CODE_SAMPLE
34+
35+
,
36+
<<<'CODE_SAMPLE'
37+
$array = [['name' => 'John']];
38+
39+
$result = array_map(fn ($item) => $item['name'], $array);
40+
CODE_SAMPLE
41+
),
42+
]);
43+
}
44+
45+
/**
46+
* @return array<class-string<Node>>
47+
*/
48+
public function getNodeTypes(): array
49+
{
50+
return [FuncCall::class];
51+
}
52+
53+
/**
54+
* @param FuncCall $node
55+
*/
56+
public function refactor(Node $node): ?Node
57+
{
58+
if (! $this->isName($node, 'array_map')) {
59+
return null;
60+
}
61+
62+
if ($node->isFirstClassCallable()) {
63+
return null;
64+
}
65+
66+
$firstArgExpr = $node->getArgs()[0]
67+
->value;
68+
if (! $firstArgExpr instanceof ArrowFunction) {
69+
return null;
70+
}
71+
72+
$arrowFunction = $firstArgExpr;
73+
$arrowFunctionParam = $arrowFunction->getParams()[0];
74+
75+
// param is known already
76+
if ($arrowFunctionParam->type instanceof Node) {
77+
return null;
78+
}
79+
80+
if ($this->hasTernary($arrowFunction)) {
81+
return null;
82+
}
83+
84+
$paramName = $this->getName($arrowFunctionParam);
85+
if (! $this->isParamArrayDimFetched($arrowFunction, $paramName)) {
86+
return null;
87+
}
88+
89+
$arrowFunctionParam->type = new Identifier('array');
90+
91+
return $node;
92+
}
93+
94+
public function provideMinPhpVersion(): int
95+
{
96+
return PhpVersionFeature::SCALAR_TYPES;
97+
}
98+
99+
private function isParamArrayDimFetched(ArrowFunction $arrowFunction, string $paramName): bool
100+
{
101+
$nodeFinder = new NodeFinder();
102+
103+
$arrayDimFetches = $nodeFinder->findInstanceOf($arrowFunction->expr, ArrayDimFetch::class);
104+
foreach ($arrayDimFetches as $arrayDimFetch) {
105+
if ($this->isName($arrayDimFetch->var, $paramName)) {
106+
return true;
107+
}
108+
}
109+
110+
return false;
111+
}
112+
113+
private function hasTernary(ArrowFunction $arrowFunction): bool
114+
{
115+
$nodeFinder = new NodeFinder();
116+
return (bool) $nodeFinder->findFirstInstanceOf($arrowFunction->expr, Ternary::class);
117+
}
118+
}

src/Config/Level/TypeDeclarationLevel.php

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
use Rector\TypeDeclaration\Rector\Closure\AddClosureVoidReturnTypeWhereNoReturnRector;
4949
use Rector\TypeDeclaration\Rector\Closure\ClosureReturnTypeRector;
5050
use Rector\TypeDeclaration\Rector\Empty_\EmptyOnNullableObjectToInstanceOfRector;
51+
use Rector\TypeDeclaration\Rector\FuncCall\AddArrowFunctionParamArrayWhereDimFetchRector;
5152
use Rector\TypeDeclaration\Rector\Function_\AddFunctionVoidReturnTypeWhereNoReturnRector;
5253
use Rector\TypeDeclaration\Rector\FunctionLike\AddParamTypeSplFixedArrayRector;
5354
use Rector\TypeDeclaration\Rector\FunctionLike\AddReturnTypeDeclarationFromYieldsRector;
@@ -120,6 +121,7 @@ final class TypeDeclarationLevel
120121
// closures
121122
AddClosureNeverReturnTypeRector::class,
122123
ClosureReturnTypeRector::class,
124+
AddArrowFunctionParamArrayWhereDimFetchRector::class,
123125

124126
// more risky rules
125127
ReturnTypeFromStrictParamRector::class,

0 commit comments

Comments
 (0)