-
-
Notifications
You must be signed in to change notification settings - Fork 335
/
TypedPropertyFromStrictSetUpRector.php
149 lines (126 loc) · 4.55 KB
/
TypedPropertyFromStrictSetUpRector.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<?php
declare(strict_types=1);
namespace Rector\TypeDeclaration\Rector\Property;
use PHPStan\Type\ObjectType;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\ValueObject\Type\FullyQualifiedIdentifierTypeNode;
use Rector\Comments\NodeDocBlock\DocBlockUpdater;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\Rector\AbstractRector;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer\TrustedClassMethodPropertyTypeInferer;
use Rector\ValueObject\MethodName;
use Rector\ValueObject\PhpVersionFeature;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromStrictSetUpRector\TypedPropertyFromStrictSetUpRectorTest
*/
final class TypedPropertyFromStrictSetUpRector extends AbstractRector implements MinPhpVersionInterface
{
public function __construct(
private readonly TrustedClassMethodPropertyTypeInferer $trustedClassMethodPropertyTypeInferer,
private readonly StaticTypeMapper $staticTypeMapper,
private readonly PhpDocInfoFactory $phpDocInfoFactory,
private readonly DocBlockUpdater $docBlockUpdater
) {
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Add strict typed property based on setUp() strict typed assigns in TestCase', [
new CodeSample(
<<<'CODE_SAMPLE'
use PHPUnit\Framework\TestCase;
final class SomeClass extends TestCase
{
private $value;
public function setUp()
{
$this->value = 1000;
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
use PHPUnit\Framework\TestCase;
final class SomeClass extends TestCase
{
private int $value;
public function setUp()
{
$this->value = 1000;
}
}
CODE_SAMPLE
),
]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [Class_::class];
}
/**
* @param Class_ $node
*/
public function refactor(Node $node): ?Node
{
$setUpClassMethod = $node->getMethod(MethodName::SET_UP);
if (! $setUpClassMethod instanceof ClassMethod) {
return null;
}
$hasChanged = false;
foreach ($node->getProperties() as $property) {
// type is already set
if ($property->type !== null) {
continue;
}
// is not private? we cannot be sure about other usage
if (! $property->isPrivate()) {
continue;
}
$propertyType = $this->trustedClassMethodPropertyTypeInferer->inferProperty(
$node,
$property,
$setUpClassMethod
);
$propertyTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode(
$propertyType,
TypeKind::PROPERTY
);
if (! $propertyTypeNode instanceof Node) {
continue;
}
if ($propertyType instanceof ObjectType && $propertyType->getClassName() === 'PHPUnit\Framework\MockObject\MockObject') {
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
$varTag = $phpDocInfo->getVarTagValueNode();
$varType = $phpDocInfo->getVarType();
if ($varTag instanceof VarTagValueNode && $varType instanceof ObjectType && $varType->getClassName() !== 'PHPUnit\Framework\MockObject\MockObject') {
$varTag->type = new IntersectionTypeNode([
new FullyQualifiedIdentifierTypeNode($propertyType->getClassName()),
new FullyQualifiedIdentifierTypeNode($varType->getClassName())
]);
$this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($property);
}
}
$property->type = $propertyTypeNode;
$hasChanged = true;
}
if ($hasChanged) {
return $node;
}
return null;
}
public function provideMinPhpVersion(): int
{
return PhpVersionFeature::TYPED_PROPERTIES;
}
}