Skip to content

Commit 1d2a3b9

Browse files
committed
[tdd] Add AddReturnDocblockFromMethodCallDocblockRector
1 parent 8bd31f0 commit 1d2a3b9

File tree

9 files changed

+312
-0
lines changed

9 files changed

+312
-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\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class AddReturnDocblockFromMethodCallDocblockRectorTest 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,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Fixture;
6+
7+
use Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Source\SomeRepository;
8+
9+
final class SkipMissingArrayDeclaration
10+
{
11+
private SomeRepository $someRepository;
12+
13+
public function __construct(SomeRepository $someRepository)
14+
{
15+
$this->someRepository = $someRepository;
16+
}
17+
18+
public function getAll()
19+
{
20+
return $this->someRepository->findAll();
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Fixture;
6+
7+
use Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Source\SomeRepository;
8+
9+
final class SkipMissingArrayOnCall
10+
{
11+
private SomeRepository $someRepository;
12+
13+
public function __construct(SomeRepository $someRepository)
14+
{
15+
$this->someRepository = $someRepository;
16+
}
17+
18+
public function getAll(): array
19+
{
20+
return $this->someRepository->findAllWithoutArray();
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Fixture;
6+
7+
use Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Source\SomeRepository;
8+
9+
final class SomeClass
10+
{
11+
private SomeRepository $someRepository;
12+
13+
public function __construct(SomeRepository $someRepository)
14+
{
15+
$this->someRepository = $someRepository;
16+
}
17+
18+
public function getAll(): array
19+
{
20+
return $this->someRepository->findAll();
21+
}
22+
}
23+
24+
?>
25+
-----
26+
<?php
27+
28+
declare(strict_types=1);
29+
30+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Fixture;
31+
32+
use Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Source\SomeRepository;
33+
34+
final class SomeClass
35+
{
36+
private SomeRepository $someRepository;
37+
38+
public function __construct(SomeRepository $someRepository)
39+
{
40+
$this->someRepository = $someRepository;
41+
}
42+
43+
/**
44+
* @return SomeEntity[]
45+
*/
46+
public function getAll(): array
47+
{
48+
return $this->someRepository->findAll();
49+
}
50+
}
51+
52+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Source;
4+
5+
final class SomeEntity
6+
{
7+
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Source;
6+
7+
final class SomeRepository
8+
{
9+
/**
10+
* @return SomeEntity[]
11+
*/
12+
public function findAll(): array
13+
{
14+
return [];
15+
}
16+
17+
/**
18+
* @return SomeEntity[]
19+
*/
20+
public function findAllWithoutArray()
21+
{
22+
return [];
23+
}
24+
}
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\AddReturnDocblockFromMethodCallDocblockRector;
7+
8+
return RectorConfig::configure()
9+
->withRules([AddReturnDocblockFromMethodCallDocblockRector::class]);
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\TypeDeclarationDocblocks\Rector\ClassMethod;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Expr\MethodCall;
9+
use PhpParser\Node\Identifier;
10+
use PhpParser\Node\Stmt\ClassMethod;
11+
use PhpParser\Node\Stmt\Return_;
12+
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
13+
use PHPStan\Reflection\ReflectionProvider;
14+
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
15+
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
16+
use Rector\PhpParser\AstResolver;
17+
use Rector\Rector\AbstractRector;
18+
use Rector\TypeDeclarationDocblocks\NodeDocblockTypeDecorator;
19+
use Rector\TypeDeclarationDocblocks\NodeFinder\ReturnNodeFinder;
20+
use Rector\TypeDeclarationDocblocks\TagNodeAnalyzer\UsefulArrayTagNodeAnalyzer;
21+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
22+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
23+
24+
/**
25+
* @see \Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\AddReturnDocblockFromMethodCallDocblockRectorTest
26+
*/
27+
final class AddReturnDocblockFromMethodCallDocblockRector extends AbstractRector
28+
{
29+
public function __construct(
30+
private readonly PhpDocInfoFactory $phpDocInfoFactory,
31+
private readonly ReturnNodeFinder $returnNodeFinder,
32+
private readonly UsefulArrayTagNodeAnalyzer $usefulArrayTagNodeAnalyzer,
33+
private readonly AstResolver $astResolver,
34+
private readonly PhpDocTypeChanger $phpDocTypeChanger,
35+
) {
36+
}
37+
38+
public function getRuleDefinition(): RuleDefinition
39+
{
40+
return new RuleDefinition(
41+
'Add @return docblock based on detailed type of method call docblock',
42+
[
43+
new CodeSample(
44+
<<<'CODE_SAMPLE'
45+
final class SomeController
46+
{
47+
public function getAll(): array
48+
{
49+
return $this->repository->findAll();
50+
}
51+
}
52+
53+
final class Repository
54+
{
55+
/**
56+
* @return SomeEntity[]
57+
*/
58+
public function findAll(): array
59+
{
60+
// ...
61+
}
62+
}
63+
}
64+
CODE_SAMPLE
65+
,
66+
<<<'CODE_SAMPLE'
67+
final class SomeController
68+
{
69+
/**
70+
* @return SomeEntity[]
71+
*/
72+
public function getAll(): array
73+
{
74+
return $this->repository->findAll();
75+
}
76+
}
77+
78+
final class Repository
79+
{
80+
/**
81+
* @return SomeEntity[]
82+
*/
83+
public function findAll(): array
84+
{
85+
// ...
86+
}
87+
}
88+
CODE_SAMPLE
89+
),
90+
91+
]
92+
);
93+
}
94+
95+
/**
96+
* @return array<class-string<Node>>
97+
*/
98+
public function getNodeTypes(): array
99+
{
100+
return [ClassMethod::class];
101+
}
102+
103+
/**
104+
* @param ClassMethod $node
105+
*/
106+
public function refactor(Node $node): ?Node
107+
{
108+
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
109+
if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($phpDocInfo->getReturnTagValue())) {
110+
return null;
111+
}
112+
113+
// definitely not an array return
114+
if (! $node->returnType instanceof Node || ! $this->isName($node->returnType, 'array')) {
115+
return null;
116+
}
117+
118+
$onlyReturnWithExpr = $this->returnNodeFinder->findOnlyReturnWithExpr($node);
119+
if (! $onlyReturnWithExpr instanceof Return_ || ! $onlyReturnWithExpr->expr instanceof MethodCall) {
120+
return null;
121+
}
122+
123+
$returnedMethodCall = $onlyReturnWithExpr->expr;
124+
125+
$calledClassMethod = $this->astResolver->resolveClassMethodFromCall($returnedMethodCall);
126+
if (! $calledClassMethod->returnType instanceof Identifier) {
127+
return null;
128+
}
129+
130+
if (! $this->isName($calledClassMethod->returnType, 'array')) {
131+
return null;
132+
}
133+
$calledClassMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNode($calledClassMethod);
134+
135+
$calledReturnTagValue = $calledClassMethodPhpDocInfo->getReturnTagValue();
136+
if (! $calledReturnTagValue instanceof ReturnTagValueNode) {
137+
return null;
138+
}
139+
140+
$this->phpDocTypeChanger->changeReturnTypeNode($node, $phpDocInfo, $calledReturnTagValue->type);
141+
142+
return $node;
143+
}
144+
}

src/Config/Level/TypeDeclarationDocblocksLevel.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockForCommonObjectDenominatorRector;
2222
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockForDimFetchArrayFromAssignsRector;
2323
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockForJsonArrayRector;
24+
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector;
2425
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\DocblockGetterReturnArrayFromPropertyDocblockVarRector;
2526
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\DocblockReturnArrayFromDirectArrayInstanceRector;
2627

@@ -63,5 +64,7 @@ final class TypeDeclarationDocblocksLevel
6364

6465
// run latter after other rules, as more generic
6566
AddReturnDocblockForDimFetchArrayFromAssignsRector::class,
67+
68+
AddReturnDocblockFromMethodCallDocblockRector::class,
6669
];
6770
}

0 commit comments

Comments
 (0)