Skip to content

Commit 86b724e

Browse files
authored
[type-declaration-docblocks] Add AddParamArrayDocblockFromAssignsParamToParamReferenceRector (#7323)
1 parent 8bd3c47 commit 86b724e

File tree

8 files changed

+263
-4
lines changed

8 files changed

+263
-4
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\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockFromAssignsParamToParamReferenceRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class AddParamArrayDocblockFromAssignsParamToParamReferenceRectorTest 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,11 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockFromAssignsParamToParamReferenceRector\Fixture;
4+
5+
final class SkipDimFetchAssingDeep
6+
{
7+
public function run(array &$items)
8+
{
9+
$items[][] = 'John';
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockFromAssignsParamToParamReferenceRector\Fixture;
4+
5+
final class SkipMultipleAssigns
6+
{
7+
public function run(array &$items)
8+
{
9+
$items[] = 'John';
10+
$items[] = 1000;
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockFromAssignsParamToParamReferenceRector\Fixture;
4+
5+
final class SomeClass
6+
{
7+
public function run(array &$items)
8+
{
9+
$items[] = 'John';
10+
}
11+
}
12+
13+
?>
14+
-----
15+
<?php
16+
17+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockFromAssignsParamToParamReferenceRector\Fixture;
18+
19+
final class SomeClass
20+
{
21+
/**
22+
* @param string[] $items
23+
*/
24+
public function run(array &$items)
25+
{
26+
$items[] = 'John';
27+
}
28+
}
29+
30+
?>
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\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockFromAssignsParamToParamReferenceRector;
7+
8+
return RectorConfig::configure()
9+
->withRules([AddParamArrayDocblockFromAssignsParamToParamReferenceRector::class]);

rules/TypeDeclarationDocblocks/NodeFinder/DimFetchFinder.php renamed to rules/TypeDeclarationDocblocks/NodeFinder/ArrayDimFetchFinder.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,50 @@
55
namespace Rector\TypeDeclarationDocblocks\NodeFinder;
66

77
use PhpParser\Node;
8+
use PhpParser\Node\Expr;
89
use PhpParser\Node\Expr\ArrayDimFetch;
10+
use PhpParser\Node\Expr\Assign;
911
use PhpParser\Node\Expr\Variable;
12+
use PhpParser\Node\Stmt\ClassMethod;
1013
use Rector\NodeNameResolver\NodeNameResolver;
1114
use Rector\PhpParser\Node\BetterNodeFinder;
1215

13-
final readonly class DimFetchFinder
16+
final readonly class ArrayDimFetchFinder
1417
{
1518
public function __construct(
1619
private BetterNodeFinder $betterNodeFinder,
1720
private NodeNameResolver $nodeNameResolver
1821
) {
1922
}
2023

24+
/**
25+
* @return Expr[]
26+
*/
27+
public function findDimFetchAssignToVariableName(ClassMethod $classMethod, string $variableName): array
28+
{
29+
$assigns = $this->betterNodeFinder->findInstancesOfScoped((array) $classMethod->stmts, Assign::class);
30+
31+
$exprs = [];
32+
foreach ($assigns as $assign) {
33+
if (! $assign->var instanceof ArrayDimFetch) {
34+
continue;
35+
}
36+
37+
$arrayDimFetch = $assign->var;
38+
if (! $arrayDimFetch->var instanceof Variable) {
39+
continue;
40+
}
41+
42+
if (! $this->nodeNameResolver->isName($arrayDimFetch->var, $variableName)) {
43+
continue;
44+
}
45+
46+
$exprs[] = $assign->expr;
47+
}
48+
49+
return $exprs;
50+
}
51+
2152
/**
2253
* @return ArrayDimFetch[]
2354
*/
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\TypeDeclarationDocblocks\Rector\ClassMethod;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Identifier;
9+
use PhpParser\Node\Stmt\ClassMethod;
10+
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
11+
use PHPStan\Type\ArrayType;
12+
use PHPStan\Type\MixedType;
13+
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
14+
use Rector\Rector\AbstractRector;
15+
use Rector\TypeDeclarationDocblocks\NodeDocblockTypeDecorator;
16+
use Rector\TypeDeclarationDocblocks\NodeFinder\ArrayDimFetchFinder;
17+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
18+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
19+
20+
/**
21+
* @see \Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockFromAssignsParamToParamReferenceRector\AddParamArrayDocblockFromAssignsParamToParamReferenceRectorTest
22+
*/
23+
final class AddParamArrayDocblockFromAssignsParamToParamReferenceRector extends AbstractRector
24+
{
25+
public function __construct(
26+
private readonly PhpDocInfoFactory $phpDocInfoFactory,
27+
private readonly ArrayDimFetchFinder $arrayDimFetchFinder,
28+
private readonly NodeDocblockTypeDecorator $nodeDocblockTypeDecorator,
29+
) {
30+
}
31+
32+
public function getRuleDefinition(): RuleDefinition
33+
{
34+
return new RuleDefinition(
35+
'Add @param docblock array type, based on type to assigned parameter reference',
36+
[
37+
new CodeSample(
38+
<<<'CODE_SAMPLE'
39+
final class SomeClass
40+
{
41+
public function run(array &$names): void
42+
{
43+
$names[] = 'John';
44+
}
45+
}
46+
CODE_SAMPLE
47+
,
48+
<<<'CODE_SAMPLE'
49+
final class SomeClass
50+
{
51+
/**
52+
* @param string[] $names
53+
*/
54+
public function run(array &$names): void
55+
{
56+
$names[] = 'John';
57+
}
58+
}
59+
CODE_SAMPLE
60+
),
61+
62+
]
63+
);
64+
}
65+
66+
/**
67+
* @return array<class-string<Node>>
68+
*/
69+
public function getNodeTypes(): array
70+
{
71+
return [ClassMethod::class];
72+
}
73+
74+
/**
75+
* @param ClassMethod $node
76+
*/
77+
public function refactor(Node $node): ?Node
78+
{
79+
$hasChanged = false;
80+
81+
if ($node->getParams() === []) {
82+
return null;
83+
}
84+
85+
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
86+
87+
foreach ($node->getParams() as $param) {
88+
if (! $param->byRef) {
89+
continue;
90+
}
91+
92+
if (! $param->type instanceof Identifier) {
93+
continue;
94+
}
95+
96+
if (! $this->isName($param->type, 'array')) {
97+
continue;
98+
}
99+
100+
$paramName = $this->getName($param);
101+
$paramTagValueNode = $phpDocInfo->getParamTagValueByName($paramName);
102+
103+
// already defined, lets skip it
104+
if ($paramTagValueNode instanceof ParamTagValueNode) {
105+
continue;
106+
}
107+
108+
$exprs = $this->arrayDimFetchFinder->findDimFetchAssignToVariableName($node, $paramName);
109+
110+
// to kick off with one
111+
if (count($exprs) !== 1) {
112+
continue;
113+
}
114+
115+
$assignedExprType = $this->getType($exprs[0]);
116+
$iterableType = new ArrayType(new MixedType(), $assignedExprType);
117+
118+
$hasParamTypeChanged = $this->nodeDocblockTypeDecorator->decorateGenericIterableParamType(
119+
$iterableType,
120+
$phpDocInfo,
121+
$node,
122+
$paramName
123+
);
124+
125+
if (! $hasParamTypeChanged) {
126+
continue;
127+
}
128+
129+
$hasChanged = true;
130+
}
131+
132+
if (! $hasChanged) {
133+
return null;
134+
}
135+
136+
return $node;
137+
}
138+
}

rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDimFetchAccessRector.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
1616
use Rector\Comments\NodeDocBlock\DocBlockUpdater;
1717
use Rector\Rector\AbstractRector;
18-
use Rector\TypeDeclarationDocblocks\NodeFinder\DimFetchFinder;
18+
use Rector\TypeDeclarationDocblocks\NodeFinder\ArrayDimFetchFinder;
1919
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
2020
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
2121

@@ -26,7 +26,7 @@ final class AddParamArrayDocblockFromDimFetchAccessRector extends AbstractRector
2626
{
2727
public function __construct(
2828
private readonly PhpDocInfoFactory $phpDocInfoFactory,
29-
private readonly DimFetchFinder $dimFetchFinder,
29+
private readonly ArrayDimFetchFinder $arrayDimFetchFinder,
3030
private readonly DocBlockUpdater $docBlockUpdater
3131
) {
3232
}
@@ -109,7 +109,7 @@ public function refactor(Node $node): ?Node
109109
continue;
110110
}
111111

112-
$dimFetches = $this->dimFetchFinder->findByVariableName($node, $paramName);
112+
$dimFetches = $this->arrayDimFetchFinder->findByVariableName($node, $paramName);
113113
if ($dimFetches === []) {
114114
continue;
115115
}

0 commit comments

Comments
 (0)