diff --git a/packages/TypeDeclaration/src/Rector/FunctionLike/ReturnTypeDeclarationRector.php b/packages/TypeDeclaration/src/Rector/FunctionLike/ReturnTypeDeclarationRector.php index 8ceda80efe1b..f1a615730532 100644 --- a/packages/TypeDeclaration/src/Rector/FunctionLike/ReturnTypeDeclarationRector.php +++ b/packages/TypeDeclaration/src/Rector/FunctionLike/ReturnTypeDeclarationRector.php @@ -2,12 +2,18 @@ namespace Rector\TypeDeclaration\Rector\FunctionLike; +use Iterator; use PhpParser\Node; use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; +use PHPStan\Type\ArrayType; +use PHPStan\Type\IntersectionType; +use PHPStan\Type\IterableType; use PHPStan\Type\MixedType; +use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use PHPStan\Type\UnionType; use Rector\Exception\ShouldNotHappenException; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\RectorDefinition\CodeSample; @@ -230,7 +236,12 @@ private function isReturnTypeAlreadyAdded(Node $node, Type $returnType): bool return false; } - if ($this->print($node->returnType) === $this->print($returnNode)) { + if ($this->areNodesEqual($node->returnType, $returnNode)) { + return true; + } + + // is array <=> iterable <=> Iterator co-type? → skip + if ($this->isArrayIterableIteratorCoType($node, $returnType)) { return true; } @@ -244,4 +255,42 @@ private function isReturnTypeAlreadyAdded(Node $node, Type $returnType): bool return false; } + + private function isStaticTypeIterable(Type $type): bool + { + if ($type instanceof ArrayType) { + return true; + } + + if ($type instanceof IterableType) { + return true; + } + + if ($type instanceof ObjectType) { + if ($type->getClassName() === Iterator::class) { + return true; + } + } + + if ($type instanceof UnionType || $type instanceof IntersectionType) { + foreach ($type->getTypes() as $joinedType) { + if (! $this->isStaticTypeIterable($joinedType)) { + return false; + } + } + + return true; + } + + return false; + } + + private function isArrayIterableIteratorCoType(Node $node, Type $returnType): bool + { + if (! $this->isNames($node->returnType, ['iterable', 'Iterator', 'array'])) { + return false; + } + + return $this->isStaticTypeIterable($returnType); + } } diff --git a/packages/TypeDeclaration/tests/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_iterable_array_iterator_co_type.php.inc b/packages/TypeDeclaration/tests/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_iterable_array_iterator_co_type.php.inc new file mode 100644 index 000000000000..1288763c3844 --- /dev/null +++ b/packages/TypeDeclaration/tests/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/skip_iterable_array_iterator_co_type.php.inc @@ -0,0 +1,13 @@ +