Skip to content

Commit 8f80fde

Browse files
committed
add nullable property doclbock support to RemoveNullFromNullableCollectionTypeRector
1 parent 13d6685 commit 8f80fde

2 files changed

Lines changed: 110 additions & 8 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Rector\Doctrine\Tests\TypedCollections\Rector\ClassMethod\RemoveNullFromNullableCollectionTypeRector\Fixture;
4+
5+
use Doctrine\Common\Collections\ArrayCollection;
6+
use Doctrine\Common\Collections\Collection;
7+
8+
final class RemoveFromPropertyDocblock
9+
{
10+
/**
11+
* @var ?Collection<int, string>
12+
*/
13+
private Collection $collection;
14+
15+
public function __construct()
16+
{
17+
$this->collection = new ArrayCollection([]);
18+
}
19+
}
20+
21+
?>
22+
-----
23+
<?php
24+
25+
namespace Rector\Doctrine\Tests\TypedCollections\Rector\ClassMethod\RemoveNullFromNullableCollectionTypeRector\Fixture;
26+
27+
use Doctrine\Common\Collections\ArrayCollection;
28+
use Doctrine\Common\Collections\Collection;
29+
30+
final class RemoveFromPropertyDocblock
31+
{
32+
/**
33+
* @var Collection<int, string>
34+
*/
35+
private Collection $collection;
36+
37+
public function __construct()
38+
{
39+
$this->collection = new ArrayCollection([]);
40+
}
41+
}
42+
43+
?>

rules/TypedCollections/Rector/ClassMethod/RemoveNullFromNullableCollectionTypeRector.php

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,15 @@
55
namespace Rector\Doctrine\TypedCollections\Rector\ClassMethod;
66

77
use PhpParser\Node;
8+
use PhpParser\Node\Name;
89
use PhpParser\Node\NullableType;
910
use PhpParser\Node\Stmt\ClassMethod;
11+
use PhpParser\Node\Stmt\Property;
12+
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
13+
use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
14+
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
15+
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
16+
use Rector\Comments\NodeDocBlock\DocBlockUpdater;
1017
use Rector\Doctrine\Enum\DoctrineClass;
1118
use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer;
1219
use Rector\Rector\AbstractRector;
@@ -19,7 +26,9 @@
1926
final class RemoveNullFromNullableCollectionTypeRector extends AbstractRector
2027
{
2128
public function __construct(
22-
private readonly TestsNodeAnalyzer $testsNodeAnalyzer
29+
private readonly TestsNodeAnalyzer $testsNodeAnalyzer,
30+
private readonly PhpDocInfoFactory $phpDocInfoFactory,
31+
private readonly DocBlockUpdater $docBlockUpdater,
2332
) {
2433
}
2534

@@ -64,25 +73,34 @@ public function setItems(Collection $items): void
6473

6574
public function getNodeTypes(): array
6675
{
67-
return [ClassMethod::class];
76+
return [ClassMethod::class, Property::class];
6877
}
6978

7079
/**
71-
* @param ClassMethod $node
80+
* @param ClassMethod|Property $node
7281
*/
73-
public function refactor(Node $node): ClassMethod|null
82+
public function refactor(Node $node): ClassMethod|Property|null
7483
{
75-
if (count($node->params) !== 1) {
84+
if ($node instanceof Property) {
85+
return $this->refactorProperty($node);
86+
}
87+
88+
return $this->refactorClassMethod($node);
89+
}
90+
91+
private function refactorClassMethod(ClassMethod $classMethod): null|ClassMethod
92+
{
93+
if (count($classMethod->params) !== 1) {
7694
return null;
7795
}
7896

79-
if ($this->testsNodeAnalyzer->isInTestClass($node)) {
97+
if ($this->testsNodeAnalyzer->isInTestClass($classMethod)) {
8098
return null;
8199
}
82100

83101
$hasChanged = false;
84102

85-
foreach ($node->params as $param) {
103+
foreach ($classMethod->params as $param) {
86104
if (! $param->type instanceof NullableType) {
87105
continue;
88106
}
@@ -97,9 +115,50 @@ public function refactor(Node $node): ClassMethod|null
97115
}
98116

99117
if ($hasChanged) {
100-
return $node;
118+
return $classMethod;
101119
}
102120

103121
return null;
104122
}
123+
124+
private function refactorProperty(Property $property): ?Property
125+
{
126+
if (! $this->hasNativeCollectionType($property)) {
127+
return null;
128+
}
129+
130+
$phpDocInfo = $this->phpDocInfoFactory->createFromNode($property);
131+
if (! $phpDocInfo instanceof PhpDocInfo) {
132+
return null;
133+
}
134+
135+
$varTagValueNode = $phpDocInfo->getVarTagValueNode();
136+
if (! $varTagValueNode instanceof VarTagValueNode) {
137+
return null;
138+
}
139+
140+
// remove nullable if has one
141+
if (! $varTagValueNode->type instanceof NullableTypeNode) {
142+
return null;
143+
}
144+
145+
// unwrap nullable type
146+
$varTagValueNode->type = $varTagValueNode->type->type;
147+
148+
$phpDocInfo->removeByType(VarTagValueNode::class);
149+
$phpDocInfo->addTagValueNode($varTagValueNode);
150+
151+
$this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($property);
152+
153+
return $property;
154+
}
155+
156+
private function hasNativeCollectionType(Property $property): bool
157+
{
158+
if (! $property->type instanceof Name) {
159+
return false;
160+
}
161+
162+
return $this->isName($property->type, DoctrineClass::COLLECTION);
163+
}
105164
}

0 commit comments

Comments
 (0)