From e87a48b7d80fd8f29b1c9411e8f8f7fe8a3b1026 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sat, 14 Jun 2025 18:39:46 +0200 Subject: [PATCH] add docblock support to NarrowArrayCollectionToCollectionRector --- .../Fixture/union_doblock.php.inc | 31 ++++++ ...arrowArrayCollectionToCollectionRector.php | 98 +++++++++++++------ 2 files changed, 97 insertions(+), 32 deletions(-) create mode 100644 rules-tests/TypedCollections/Rector/ClassMethod/NarrowArrayCollectionToCollectionRector/Fixture/union_doblock.php.inc diff --git a/rules-tests/TypedCollections/Rector/ClassMethod/NarrowArrayCollectionToCollectionRector/Fixture/union_doblock.php.inc b/rules-tests/TypedCollections/Rector/ClassMethod/NarrowArrayCollectionToCollectionRector/Fixture/union_doblock.php.inc new file mode 100644 index 00000000..e9738458 --- /dev/null +++ b/rules-tests/TypedCollections/Rector/ClassMethod/NarrowArrayCollectionToCollectionRector/Fixture/union_doblock.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules/TypedCollections/Rector/ClassMethod/NarrowArrayCollectionToCollectionRector.php b/rules/TypedCollections/Rector/ClassMethod/NarrowArrayCollectionToCollectionRector.php index 5b903b5e..e8c6cea9 100644 --- a/rules/TypedCollections/Rector/ClassMethod/NarrowArrayCollectionToCollectionRector.php +++ b/rules/TypedCollections/Rector/ClassMethod/NarrowArrayCollectionToCollectionRector.php @@ -18,6 +18,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; +use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\Comments\NodeDocBlock\DocBlockUpdater; @@ -85,28 +86,12 @@ public function refactor(Node $node): ClassMethod|Property|null return $this->refactorClassMethod($node); } - $hasChanged = false; - - if ($this->processNativeType($node)) { - $hasChanged = true; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - - $varTagValueNode = $phpDocInfo->getVarTagValueNode(); - if ($varTagValueNode instanceof VarTagValueNode && $this->processTagValueNode($varTagValueNode)) { - $hasChanged = true; - } - - if ($hasChanged) { - return $node; - } - - return null; + return $this->refactorProperty($node); } private function processTagValueNode(VarTagValueNode|ReturnTagValueNode|ParamTagValueNode $tagValueNode): bool { + // 1. generic type if ($tagValueNode->type instanceof GenericTypeNode) { $genericTypeNode = $tagValueNode->type; if ($genericTypeNode->type->name === 'ArrayCollection') { @@ -115,6 +100,25 @@ private function processTagValueNode(VarTagValueNode|ReturnTagValueNode|ParamTag } } + // 2. union type + if ($tagValueNode->type instanceof UnionTypeNode) { + $unionTypeNode = $tagValueNode->type; + foreach ($unionTypeNode->types as $key => $unionedType) { + if (! $unionedType instanceof IdentifierTypeNode) { + continue; + } + + if (! in_array($unionedType->name, ['ArrayCollection', DoctrineClass::ARRAY_COLLECTION], true)) { + continue; + } + + $unionTypeNode->types[$key] = new IdentifierTypeNode('\\' . DoctrineClass::COLLECTION); + + return true; + } + } + + // 3. handle single type if (! $tagValueNode->type instanceof IdentifierTypeNode) { return false; } @@ -181,23 +185,10 @@ private function refactorClassMethod(ClassMethod $classMethod): ?ClassMethod // docblocks $classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNode($classMethod); - if (! $classMethodPhpDocInfo instanceof PhpDocInfo) { - return null; - } - - // return tag - $returnTagValueNode = $classMethodPhpDocInfo->getReturnTagValue(); - if ($returnTagValueNode instanceof ReturnTagValueNode && $this->processTagValueNode($returnTagValueNode)) { + if ($classMethodPhpDocInfo instanceof PhpDocInfo && $this->refactorClassMethodDocblock($classMethodPhpDocInfo)) { $hasChanged = true; } - // param tags - foreach ($classMethodPhpDocInfo->getParamTagValueNodes() as $paramTagValueNode) { - if ($this->processTagValueNode($paramTagValueNode)) { - $hasChanged = true; - } - } - if (! $hasChanged) { return null; } @@ -264,4 +255,47 @@ private function hasCollectionName(Property|Param|ClassMethod $stmts): bool return $collectionName instanceof Name; } + + private function refactorClassMethodDocblock(PhpDocInfo $classMethodPhpDocInfo): bool + { + $hasChanged = false; + + // return tag + $returnTagValueNode = $classMethodPhpDocInfo->getReturnTagValue(); + if ($returnTagValueNode instanceof ReturnTagValueNode && $this->processTagValueNode($returnTagValueNode)) { + $hasChanged = true; + } + + // param tags + foreach ($classMethodPhpDocInfo->getParamTagValueNodes() as $paramTagValueNode) { + if ($this->processTagValueNode($paramTagValueNode)) { + $hasChanged = true; + } + } + + return $hasChanged; + } + + private function refactorProperty(Property $property): Property|null + { + $hasChanged = false; + if ($this->processNativeType($property)) { + $hasChanged = true; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + + $varTagValueNode = $phpDocInfo->getVarTagValueNode(); + if ($varTagValueNode instanceof VarTagValueNode && $this->processTagValueNode($varTagValueNode)) { + $hasChanged = true; + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($property); + } + + if ($hasChanged) { + + return $property; + } + + return null; + } }