From 3cd8915886fd04e07c2de1f6e5c34fac424e051d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 15 May 2026 22:16:18 +0000 Subject: [PATCH 1/3] Implement TODO: handle multiple type nodes in AddReturnDocblockForDimFetchArrayFromAssignsRector When a method returns a variable whose PHPStan-inferred type is a union of multiple distinct ConstantArrayTypes, the rector previously only used the first generalized type node and discarded the rest. Now it correctly creates a BracketsAwareUnionTypeNode from all non-empty type nodes, producing a union @return docblock (e.g. array|array). Also adds a guard for the case where all union members are empty arrays (would previously crash with an undefined index on $genericUnionedTypeNodes[0]). https://claude.ai/code/session_01L2wfcmkihik6MVWq5qwEfw --- .../Fixture/union_from_if_else_assign.php.inc | 46 +++++++++++++++++++ ...blockForDimFetchArrayFromAssignsRector.php | 12 ++++- 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/Fixture/union_from_if_else_assign.php.inc diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/Fixture/union_from_if_else_assign.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/Fixture/union_from_if_else_assign.php.inc new file mode 100644 index 00000000000..e92411f0748 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector/Fixture/union_from_if_else_assign.php.inc @@ -0,0 +1,46 @@ + 1]; + } else { + $items = ['name' => 'test']; + } + + return $items; + } +} + +?> +----- +|array + */ + public function toArray(): array + { + $items = []; + + if (mt_rand(0, 1)) { + $items = ['id' => 1]; + } else { + $items = ['name' => 'test']; + } + + return $items; + } +} + +?> diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php index 3698f2bb697..f87e0f01bab 100644 --- a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php @@ -16,6 +16,7 @@ use PHPStan\Type\UnionType; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; +use Rector\BetterPhpDocParser\ValueObject\Type\BracketsAwareUnionTypeNode; use Rector\Rector\AbstractRector; use Rector\TypeDeclarationDocblocks\NodeFinder\ReturnNodeFinder; use Rector\TypeDeclarationDocblocks\TagNodeAnalyzer\UsefulArrayTagNodeAnalyzer; @@ -166,8 +167,15 @@ public function refactor(Node $node): ?ClassMethod return $node; } - // @todo handle multiple type nodes - $this->phpDocTypeChanger->changeReturnTypeNode($node, $phpDocInfo, $genericUnionedTypeNodes[0]); + if ($genericUnionedTypeNodes === []) { + return null; + } + + $typeNode = count($genericUnionedTypeNodes) === 1 + ? $genericUnionedTypeNodes[0] + : new BracketsAwareUnionTypeNode($genericUnionedTypeNodes); + + $this->phpDocTypeChanger->changeReturnTypeNode($node, $phpDocInfo, $typeNode); return $node; } From a61f2a52bbb54de0f2c23d937511873190df3b98 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 16 May 2026 22:09:19 +0000 Subject: [PATCH 2/3] Sort union type nodes for deterministic output across PHP versions PHPStan may return union type members in different order depending on the PHP binary version used during analysis. Sorting by string representation ensures the generated @return docblock is consistent regardless of the PHP version running the tests. https://claude.ai/code/session_01L2wfcmkihik6MVWq5qwEfw --- .../AddReturnDocblockForDimFetchArrayFromAssignsRector.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php index f87e0f01bab..fa53c3c011e 100644 --- a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php @@ -171,6 +171,9 @@ public function refactor(Node $node): ?ClassMethod return null; } + // sort for deterministic output across PHP versions + usort($genericUnionedTypeNodes, static fn ($a, $b): int => (string) $a <=> (string) $b); + $typeNode = count($genericUnionedTypeNodes) === 1 ? $genericUnionedTypeNodes[0] : new BracketsAwareUnionTypeNode($genericUnionedTypeNodes); From d2715edbaa6c12a8d492c54155ae099c1988aa24 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 16 May 2026 22:11:55 +0000 Subject: [PATCH 3/3] Add TypeNode type hints to usort closure for PHPStan compliance PHPStan level 8 requires typed closure parameters. Rector's analysis flagged the missing types in the sorting callback. https://claude.ai/code/session_01L2wfcmkihik6MVWq5qwEfw --- .../AddReturnDocblockForDimFetchArrayFromAssignsRector.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php index fa53c3c011e..8448031d710 100644 --- a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php @@ -11,6 +11,7 @@ use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Return_; +use PHPStan\PhpDocParser\Ast\Type\TypeNode; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Type; use PHPStan\Type\UnionType; @@ -172,7 +173,7 @@ public function refactor(Node $node): ?ClassMethod } // sort for deterministic output across PHP versions - usort($genericUnionedTypeNodes, static fn ($a, $b): int => (string) $a <=> (string) $b); + usort($genericUnionedTypeNodes, static fn (TypeNode $a, TypeNode $b): int => (string) $a <=> (string) $b); $typeNode = count($genericUnionedTypeNodes) === 1 ? $genericUnionedTypeNodes[0]