Skip to content

Commit b31955b

Browse files
committed
fix promoted property
1 parent cfab7c9 commit b31955b

5 files changed

Lines changed: 84 additions & 64 deletions

File tree

rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector/Fixture/add_prefix_backslash.php.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class AddPrefixBackslash extends LogicException
4343
}
4444

4545
/**
46-
* @return array<string, array<int, class-string<\App\Support\Benchmarking\Contracts\Metric>>>
46+
* @return array<string, array<int, class-string<Metric>>>
4747
*/
4848
public function getDuplicateNames(): array
4949
{

rules/TypeDeclarationDocblocks/NodeDocblockTypeDecorator.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ public function decorateGenericIterableParamType(
5555

5656
public function decorateGenericIterableReturnType(
5757
Type|TypeNode $typeOrTypeNode,
58-
PhpDocInfo $classMethodPhpDocInfo,
59-
FunctionLike $functionLike
58+
PhpDocInfo $classMethodPhpDocInfo,
59+
FunctionLike $functionLike
6060
): bool {
6161
if ($typeOrTypeNode instanceof TypeNode) {
6262
$type = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($typeOrTypeNode, $functionLike);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\TypeDeclarationDocblocks\NodeFinder;
6+
7+
use PhpParser\Node\Expr\PropertyFetch;
8+
use PhpParser\Node\Param;
9+
use PhpParser\Node\Stmt\Class_;
10+
use PhpParser\Node\Stmt\ClassMethod;
11+
use PhpParser\Node\Stmt\Property;
12+
use PhpParser\Node\Stmt\Return_;
13+
use Rector\NodeNameResolver\NodeNameResolver;
14+
use Rector\ValueObject\MethodName;
15+
16+
final readonly class GetterClassMethodPropertyFinder
17+
{
18+
public function __construct(
19+
private NodeNameResolver $nodeNameResolver,
20+
) {
21+
}
22+
23+
public function find(ClassMethod $classMethod, Class_ $class): Property|Param|null
24+
{
25+
// we need exactly one statement of return
26+
if ($classMethod->stmts === null || count($classMethod->stmts) !== 1) {
27+
return null;
28+
}
29+
30+
$onlyStmt = $classMethod->stmts[0];
31+
if (! $onlyStmt instanceof Return_) {
32+
return null;
33+
}
34+
35+
if (! $onlyStmt->expr instanceof PropertyFetch) {
36+
return null;
37+
}
38+
39+
$propertyFetch = $onlyStmt->expr;
40+
if (! $this->nodeNameResolver->isName($propertyFetch->var, 'this')) {
41+
return null;
42+
}
43+
44+
$propertyName = $this->nodeNameResolver->getName($propertyFetch->name);
45+
if (! is_string($propertyName)) {
46+
return null;
47+
}
48+
49+
$property = $class->getProperty($propertyName);
50+
if ($property instanceof Property) {
51+
return $property;
52+
}
53+
54+
// try also promoted property in constructor
55+
$constructClassMethod = $class->getMethod(MethodName::CONSTRUCT);
56+
if (! $constructClassMethod instanceof ClassMethod) {
57+
return null;
58+
}
59+
60+
foreach ($constructClassMethod->getParams() as $param) {
61+
if (! $param->isPromoted()) {
62+
continue;
63+
}
64+
65+
if (! $this->nodeNameResolver->isName($param, $propertyName)) {
66+
continue;
67+
}
68+
69+
return $param;
70+
}
71+
72+
return null;
73+
}
74+
}

rules/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockGetterReturnArrayFromPropertyDocblockVarRector.php

Lines changed: 7 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,15 @@
44

55
namespace Rector\TypeDeclarationDocblocks\Rector\ClassMethod;
66

7+
use PhpParser\Node;
78
use PhpParser\Node\Stmt\Class_;
8-
use PhpParser\Node\Stmt\Property;
99
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
1010
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
1111
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
12-
use PHPStan\Type\Type;
13-
use PhpParser\Node;
14-
use PhpParser\Node\Expr\PropertyFetch;
15-
use PhpParser\Node\Stmt\ClassMethod;
16-
use PhpParser\Node\Stmt\Return_;
17-
use PHPStan\Type\ArrayType;
18-
use PHPStan\Type\MixedType;
19-
use PHPStan\Type\UnionType;
2012
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
2113
use Rector\Rector\AbstractRector;
2214
use Rector\TypeDeclarationDocblocks\NodeDocblockTypeDecorator;
15+
use Rector\TypeDeclarationDocblocks\NodeFinder\GetterClassMethodPropertyFinder;
2316
use Rector\TypeDeclarationDocblocks\TagNodeAnalyzer\UsefulArrayTagNodeAnalyzer;
2417
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
2518
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@@ -32,7 +25,8 @@ final class DocblockGetterReturnArrayFromPropertyDocblockVarRector extends Abstr
3225
public function __construct(
3326
private readonly PhpDocInfoFactory $phpDocInfoFactory,
3427
private readonly UsefulArrayTagNodeAnalyzer $usefulArrayTagNodeAnalyzer,
35-
private readonly NodeDocblockTypeDecorator $nodeDocblockTypeDecorator
28+
private readonly NodeDocblockTypeDecorator $nodeDocblockTypeDecorator,
29+
private readonly GetterClassMethodPropertyFinder $getterClassMethodPropertyFinder,
3630
) {
3731
}
3832

@@ -108,13 +102,12 @@ public function refactor(Node $node): ?Node
108102
continue;
109103
}
110104

111-
// @todo add promoted proeprty
112-
$property = $this->matchReturnLocalPropertyFetch($classMethod, $node);
113-
if (! $property instanceof Property) {
105+
$propertyOrParam = $this->getterClassMethodPropertyFinder->find($classMethod, $node);
106+
if (! $propertyOrParam instanceof Node) {
114107
continue;
115108
}
116109

117-
$propertyDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
110+
$propertyDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($propertyOrParam);
118111

119112
$varTagValueNode = $propertyDocInfo->getVarTagValueNode();
120113

@@ -144,50 +137,4 @@ public function refactor(Node $node): ?Node
144137

145138
return $node;
146139
}
147-
148-
private function matchReturnLocalPropertyFetch(ClassMethod $classMethod, Class_ $class): ?Property
149-
{
150-
// we need exactly one statement of return
151-
if ($classMethod->stmts === null || count($classMethod->stmts) !== 1) {
152-
return null;
153-
}
154-
155-
$onlyStmt = $classMethod->stmts[0];
156-
if (! $onlyStmt instanceof Return_) {
157-
return null;
158-
}
159-
160-
if (! $onlyStmt->expr instanceof PropertyFetch) {
161-
return null;
162-
}
163-
164-
$propertyFetch = $onlyStmt->expr;
165-
if (! $this->isName($propertyFetch->var, 'this')) {
166-
return null;
167-
}
168-
169-
$propertyName = $this->getName($propertyFetch->name);
170-
if (! is_string($propertyName)) {
171-
return null;
172-
}
173-
174-
return $class->getProperty($propertyName);
175-
}
176-
177-
private function isUsefulType(Type $type): bool
178-
{
179-
if ($type instanceof UnionType) {
180-
return false;
181-
}
182-
183-
if (! $type instanceof ArrayType) {
184-
return true;
185-
}
186-
187-
if (! $type->getKeyType() instanceof MixedType) {
188-
return true;
189-
}
190-
191-
return ! $type->getItemType() instanceof MixedType;
192-
}
193140
}

src/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
use Rector\BetterPhpDocParser\ValueObject\Type\SpacingAwareArrayTypeNode;
2929
use Rector\BetterPhpDocParser\ValueObject\Type\SpacingAwareCallableTypeNode;
3030
use Rector\Comments\NodeDocBlock\DocBlockUpdater;
31-
use Rector\NodeTypeResolver\Node\AttributeKey;
3231
use Rector\NodeTypeResolver\TypeComparator\TypeComparator;
3332
use Rector\StaticTypeMapper\StaticTypeMapper;
3433
use Rector\TypeDeclaration\PhpDocParser\ParamPhpDocNodeFactory;

0 commit comments

Comments
 (0)