Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d52149d

Browse files
authored
[type-declaration] Add AddParamFromDimFetchKeyUseRector (#7424)
1 parent bbd09ab commit d52149d

File tree

10 files changed

+292
-1
lines changed

10 files changed

+292
-1
lines changed
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\Tests\TypeDeclaration\Rector\ClassMethod\AddParamFromDimFetchKeyUseRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class AddParamFromDimFetchKeyUseRectorTest 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: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddParamFromDimFetchKeyUseRector\Fixture;
4+
5+
final class Fixture
6+
{
7+
public function process($key)
8+
{
9+
$items = [
10+
'first' => 'Firstitem',
11+
'second' => 'Seconditem',
12+
];
13+
14+
return $items[$key];
15+
}
16+
}
17+
18+
?>
19+
-----
20+
<?php
21+
22+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddParamFromDimFetchKeyUseRector\Fixture;
23+
24+
final class Fixture
25+
{
26+
public function process(string $key)
27+
{
28+
$items = [
29+
'first' => 'Firstitem',
30+
'second' => 'Seconditem',
31+
];
32+
33+
return $items[$key];
34+
}
35+
}
36+
37+
?>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddParamFromDimFetchKeyUseRector\Fixture;
4+
5+
final class IntOrString
6+
{
7+
public function process($key)
8+
{
9+
$items = [
10+
'first' => 'Firstitem',
11+
111 => 'Seconditem',
12+
'second' => 'Seconditem',
13+
];
14+
15+
return $items[$key];
16+
}
17+
}
18+
19+
?>
20+
-----
21+
<?php
22+
23+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddParamFromDimFetchKeyUseRector\Fixture;
24+
25+
final class IntOrString
26+
{
27+
public function process(string|int $key)
28+
{
29+
$items = [
30+
'first' => 'Firstitem',
31+
111 => 'Seconditem',
32+
'second' => 'Seconditem',
33+
];
34+
35+
return $items[$key];
36+
}
37+
}
38+
39+
?>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddParamFromDimFetchKeyUseRector\Fixture;
4+
5+
final class SkipExistingType
6+
{
7+
public function process(int $key): void
8+
{
9+
$items = [
10+
'first' => 'Firstitem',
11+
'second' => 'Seconditem',
12+
];
13+
14+
return $items[$key];
15+
}
16+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddParamFromDimFetchKeyUseRector\Fixture;
4+
5+
final class SkipUnclearType
6+
{
7+
public function process($key, $items)
8+
{
9+
return $items[$key];
10+
}
11+
}
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\TypeDeclaration\Rector\ClassMethod\AddParamFromDimFetchKeyUseRector;
7+
8+
return static function (RectorConfig $rectorConfig): void {
9+
$rectorConfig->rule(AddParamFromDimFetchKeyUseRector::class);
10+
};

rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/config/configured_rule.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
declare(strict_types=1);
44

5-
use Rector\ValueObject\PhpVersionFeature;
65
use Rector\Config\RectorConfig;
76
use Rector\TypeDeclaration\Rector\ClassMethod\KnownMagicClassMethodTypeRector;
7+
use Rector\ValueObject\PhpVersionFeature;
88

99
return RectorConfig::configure()
1010
->withRules([KnownMagicClassMethodTypeRector::class])
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\TypeDeclaration\Rector\ClassMethod;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Stmt\Class_;
9+
use PHPStan\Type\ArrayType;
10+
use PHPStan\Type\Constant\ConstantArrayType;
11+
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
12+
use Rector\Rector\AbstractRector;
13+
use Rector\StaticTypeMapper\StaticTypeMapper;
14+
use Rector\TypeDeclarationDocblocks\NodeFinder\ArrayDimFetchFinder;
15+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
16+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
17+
18+
/**
19+
* @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddParamFromDimFetchKeyUseRector\AddParamFromDimFetchKeyUseRectorTest
20+
*/
21+
final class AddParamFromDimFetchKeyUseRector extends AbstractRector
22+
{
23+
public function __construct(
24+
private readonly ArrayDimFetchFinder $arrayDimFetchFinder,
25+
private readonly StaticTypeMapper $staticTypeMapper
26+
) {
27+
}
28+
29+
public function getRuleDefinition(): RuleDefinition
30+
{
31+
return new RuleDefinition(
32+
'Add method param type based on use in array dim fetch of known keys',
33+
[
34+
new CodeSample(
35+
<<<'CODE_SAMPLE'
36+
final class SomeClass
37+
{
38+
public function get($key)
39+
{
40+
$data = [
41+
'name' => 'John',
42+
'age' => 30,
43+
];
44+
45+
return $data[$key];
46+
}
47+
}
48+
CODE_SAMPLE
49+
,
50+
<<<'CODE_SAMPLE'
51+
final class SomeClass
52+
{
53+
public function get(string $key)
54+
{
55+
$data = [
56+
'name' => 'John',
57+
'age' => 30,
58+
];
59+
60+
return $data[$key];
61+
}
62+
}
63+
CODE_SAMPLE
64+
),
65+
66+
]
67+
);
68+
}
69+
70+
/**
71+
* @return array<class-string<Node>>
72+
*/
73+
public function getNodeTypes(): array
74+
{
75+
return [Class_::class];
76+
}
77+
78+
/**
79+
* @param Class_ $node
80+
*/
81+
public function refactor(Node $node): ?Node
82+
{
83+
$hasChanged = false;
84+
85+
foreach ($node->getMethods() as $classMethod) {
86+
if ($classMethod->params === []) {
87+
continue;
88+
}
89+
90+
foreach ($classMethod->getParams() as $param) {
91+
if ($param->type instanceof Node) {
92+
continue;
93+
}
94+
95+
/** @var string $paramName */
96+
$paramName = $this->getName($param->var);
97+
98+
$dimFetches = $this->arrayDimFetchFinder->findByDimName($classMethod, $paramName);
99+
if ($dimFetches === []) {
100+
continue;
101+
}
102+
103+
foreach ($dimFetches as $dimFetch) {
104+
$dimFetchType = $this->getType($dimFetch->var);
105+
106+
if (! $dimFetchType instanceof ArrayType && ! $dimFetchType instanceof ConstantArrayType) {
107+
continue;
108+
}
109+
110+
$paramTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode(
111+
$dimFetchType->getKeyType(),
112+
TypeKind::PARAM
113+
);
114+
115+
if (! $paramTypeNode instanceof Node) {
116+
continue;
117+
}
118+
119+
$param->type = $paramTypeNode;
120+
$hasChanged = true;
121+
}
122+
}
123+
}
124+
125+
if ($hasChanged) {
126+
return $node;
127+
}
128+
129+
return null;
130+
}
131+
}

rules/TypeDeclarationDocblocks/NodeFinder/ArrayDimFetchFinder.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,20 @@ public function findByVariableName(Node $node, string $variableName): array
6464
return $this->nodeNameResolver->isName($arrayDimFetch->var, $variableName);
6565
});
6666
}
67+
68+
/**
69+
* @return ArrayDimFetch[]
70+
*/
71+
public function findByDimName(ClassMethod $classMethod, string $dimName): array
72+
{
73+
$dimFetches = $this->betterNodeFinder->findInstancesOfScoped([$classMethod], ArrayDimFetch::class);
74+
75+
return array_filter($dimFetches, function (ArrayDimFetch $arrayDimFetch) use ($dimName): bool {
76+
if (! $arrayDimFetch->dim instanceof Variable) {
77+
return false;
78+
}
79+
80+
return $this->nodeNameResolver->isName($arrayDimFetch->dim, $dimName);
81+
});
82+
}
6783
}

src/Config/Level/TypeDeclarationLevel.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Rector\TypeDeclaration\Rector\Class_\TypedPropertyFromDocblockSetUpDefinedRector;
1717
use Rector\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector;
1818
use Rector\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector;
19+
use Rector\TypeDeclaration\Rector\ClassMethod\AddParamFromDimFetchKeyUseRector;
1920
use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeBasedOnPHPUnitDataProviderRector;
2021
use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeFromPropertyTypeRector;
2122
use Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationBasedOnParentClassMethodRector;
@@ -146,9 +147,11 @@ final class TypeDeclarationLevel
146147

147148
// array parameter from dim fetch assign inside
148149
StrictArrayParamDimFetchRector::class,
150+
AddParamFromDimFetchKeyUseRector::class,
149151

150152
// possibly based on docblocks, but also helpful, intentionally last
151153
AddArrayFunctionClosureParamTypeRector::class,
152154
TypedPropertyFromDocblockSetUpDefinedRector::class,
155+
153156
];
154157
}

0 commit comments

Comments
 (0)