From 717cda3581634eb23dc9598702745b2eebadf15b Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 14 Sep 2025 17:19:46 +0200 Subject: [PATCH 1/5] add fixture for multiple options --- .../Fixture/multiple_options.php.inc | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/multiple_options.php.inc diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/multiple_options.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/multiple_options.php.inc new file mode 100644 index 00000000000..38ae3020ca5 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/multiple_options.php.inc @@ -0,0 +1,50 @@ + +----- + From bdcfadc3648cb5997b7b159faebf5e85607576b6 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 14 Sep 2025 17:24:07 +0200 Subject: [PATCH 2/5] add yield fixture provider --- .../Fixture/yield_provider.php.inc | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/yield_provider.php.inc diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/yield_provider.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/yield_provider.php.inc new file mode 100644 index 00000000000..474e1e012fc --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/yield_provider.php.inc @@ -0,0 +1,48 @@ + +----- + + */ + public static function provideData() + { + yield ['data1', 'data2']; + yield ['item4', 'item5']; + } +} + +?> From dc86768d90e807ecbd26e5f804f1b13de2e25b5f Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 14 Sep 2025 17:32:37 +0200 Subject: [PATCH 3/5] extract couple yield-related services to re-use --- ...dReturnTypeDeclarationFromYieldsRector.php | 135 ++---------------- .../NodeFinder/YieldNodeFinder.php | 59 ++++++++ .../AddReturnDocblockDataProviderRector.php | 59 +++++--- .../TypeResolver/YieldTypeResolver.php | 100 +++++++++++++ 4 files changed, 210 insertions(+), 143 deletions(-) create mode 100644 rules/TypeDeclarationDocblocks/NodeFinder/YieldNodeFinder.php create mode 100644 rules/TypeDeclarationDocblocks/TypeResolver/YieldTypeResolver.php diff --git a/rules/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector.php b/rules/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector.php index bafac230f91..556532a1774 100644 --- a/rules/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector.php +++ b/rules/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector.php @@ -5,29 +5,14 @@ namespace Rector\TypeDeclaration\Rector\FunctionLike; use PhpParser\Node; -use PhpParser\Node\Expr; -use PhpParser\Node\Expr\Closure; -use PhpParser\Node\Expr\Yield_; -use PhpParser\Node\Expr\YieldFrom; -use PhpParser\Node\FunctionLike; -use PhpParser\Node\Identifier; -use PhpParser\Node\Name; -use PhpParser\Node\Stmt; -use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Function_; -use PhpParser\NodeVisitor; -use PHPStan\Type\MixedType; -use PHPStan\Type\Type; -use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; -use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; use Rector\PHPStan\ScopeFetcher; use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; use Rector\Rector\AbstractRector; use Rector\StaticTypeMapper\StaticTypeMapper; -use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedGenericObjectType; -use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; +use Rector\TypeDeclarationDocblocks\NodeFinder\YieldNodeFinder; +use Rector\TypeDeclarationDocblocks\TypeResolver\YieldTypeResolver; use Rector\ValueObject\PhpVersionFeature; use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard; use Rector\VersionBonding\Contract\MinPhpVersionInterface; @@ -40,10 +25,10 @@ final class AddReturnTypeDeclarationFromYieldsRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private readonly TypeFactory $typeFactory, - private readonly SimpleCallableNodeTraverser $simpleCallableNodeTraverser, private readonly StaticTypeMapper $staticTypeMapper, - private readonly ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard + private readonly ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, + private readonly YieldNodeFinder $yieldNodeFinder, + private readonly YieldTypeResolver $yieldTypeResolver, ) { } @@ -91,7 +76,8 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { $scope = ScopeFetcher::fetch($node); - $yieldNodes = $this->findCurrentScopeYieldNodes($node); + + $yieldNodes = $this->yieldNodeFinder->find($node); if ($yieldNodes === []) { return null; } @@ -111,7 +97,7 @@ public function refactor(Node $node): ?Node return null; } - $yieldType = $this->resolveYieldType($yieldNodes, $node); + $yieldType = $this->yieldTypeResolver->resolveFromYieldNodes($yieldNodes, $node); $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($yieldType, TypeKind::RETURN); if (! $returnTypeNode instanceof Node) { return null; @@ -125,109 +111,4 @@ public function provideMinPhpVersion(): int { return PhpVersionFeature::SCALAR_TYPES; } - - /** - * @return Yield_[]|YieldFrom[] - */ - private function findCurrentScopeYieldNodes(FunctionLike $functionLike): array - { - $yieldNodes = []; - - $this->simpleCallableNodeTraverser->traverseNodesWithCallable((array) $functionLike->getStmts(), static function ( - Node $node - ) use (&$yieldNodes): ?int { - // skip anonymous class and inner function - if ($node instanceof Class_) { - return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; - } - - // skip nested scope - if ($node instanceof FunctionLike) { - return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; - } - - if ($node instanceof Stmt && ! $node instanceof Expression) { - $yieldNodes = []; - return NodeVisitor::STOP_TRAVERSAL; - } - - if (! $node instanceof Yield_ && ! $node instanceof YieldFrom) { - return null; - } - - $yieldNodes[] = $node; - return null; - }); - - return $yieldNodes; - } - - private function resolveYieldValue(Yield_ | YieldFrom $yield): ?Expr - { - if ($yield instanceof Yield_) { - return $yield->value; - } - - return $yield->expr; - } - - /** - * @param array $yieldNodes - * @return Type[] - */ - private function resolveYieldedTypes(array $yieldNodes): array - { - $yieldedTypes = []; - - foreach ($yieldNodes as $yieldNode) { - $value = $this->resolveYieldValue($yieldNode); - if (! $value instanceof Expr) { - // one of the yields is empty - return []; - } - - $resolvedType = $this->nodeTypeResolver->getType($value); - if ($resolvedType instanceof MixedType) { - continue; - } - - $yieldedTypes[] = $resolvedType; - } - - return $yieldedTypes; - } - - /** - * @param array $yieldNodes - */ - private function resolveYieldType( - array $yieldNodes, - ClassMethod|Function_ $functionLike - ): FullyQualifiedObjectType|FullyQualifiedGenericObjectType { - $yieldedTypes = $this->resolveYieldedTypes($yieldNodes); - - $className = $this->resolveClassName($functionLike); - - if ($yieldedTypes === []) { - return new FullyQualifiedObjectType($className); - } - - $yieldedTypes = $this->typeFactory->createMixedPassedOrUnionType($yieldedTypes); - return new FullyQualifiedGenericObjectType($className, [$yieldedTypes]); - } - - private function resolveClassName(Function_|ClassMethod|Closure $functionLike): string - { - $returnTypeNode = $functionLike->getReturnType(); - - if ($returnTypeNode instanceof Identifier && $returnTypeNode->name === 'iterable') { - return 'Iterator'; - } - - if ($returnTypeNode instanceof Name && ! $this->isName($returnTypeNode, 'Generator')) { - return $this->getName($returnTypeNode); - } - - return 'Generator'; - } } diff --git a/rules/TypeDeclarationDocblocks/NodeFinder/YieldNodeFinder.php b/rules/TypeDeclarationDocblocks/NodeFinder/YieldNodeFinder.php new file mode 100644 index 00000000000..1d0a611c97d --- /dev/null +++ b/rules/TypeDeclarationDocblocks/NodeFinder/YieldNodeFinder.php @@ -0,0 +1,59 @@ +simpleCallableNodeTraverser->traverseNodesWithCallable((array) $functionLike->getStmts(), static function ( + Node $node + ) use (&$yieldNodes): ?int { + // skip anonymous class and inner function + if ($node instanceof Class_) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + // skip nested scope + if ($node instanceof FunctionLike) { + return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if ($node instanceof Stmt && ! $node instanceof Expression) { + $yieldNodes = []; + return NodeVisitor::STOP_TRAVERSAL; + } + + if (! $node instanceof Yield_ && ! $node instanceof YieldFrom) { + return null; + } + + $yieldNodes[] = $node; + return null; + }); + + return $yieldNodes; + } +} diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php index 026ff3d422a..0a9c7559053 100644 --- a/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php @@ -7,8 +7,11 @@ use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Return_; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; +use PHPStan\Type\Type; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer; @@ -17,6 +20,8 @@ use Rector\StaticTypeMapper\StaticTypeMapper; use Rector\TypeDeclarationDocblocks\NodeFinder\DataProviderMethodsFinder; use Rector\TypeDeclarationDocblocks\NodeFinder\ReturnNodeFinder; +use Rector\TypeDeclarationDocblocks\NodeFinder\YieldNodeFinder; +use Rector\TypeDeclarationDocblocks\TypeResolver\YieldTypeResolver; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -32,7 +37,9 @@ public function __construct( private readonly ReturnNodeFinder $returnNodeFinder, private readonly StaticTypeMapper $staticTypeMapper, private readonly DocBlockUpdater $docBlockUpdater, - private readonly TypeNormalizer $typeNormalizer + private readonly TypeNormalizer $typeNormalizer, + private readonly YieldTypeResolver $yieldTypeResolver, + private readonly YieldNodeFinder $yieldNodeFinder, ) { } @@ -125,26 +132,30 @@ public function refactor(Node $node): ?Node $soleReturn = $this->returnNodeFinder->findOnlyReturnWithExpr($dataProviderClassMethod); // unable to resolve type - if (! $soleReturn instanceof Return_) { - continue; - } + if ($soleReturn instanceof Return_) { + if (! $soleReturn->expr instanceof Expr) { + continue; + } - if (! $soleReturn->expr instanceof Expr) { - continue; - } + $soleReturnType = $this->getType($soleReturn->expr); - $soleReturnType = $this->getType($soleReturn->expr); + $this->addGeneratedTypeReturnDocblockType( + $soleReturnType, + $classMethodPhpDocInfo, + $dataProviderClassMethod + ); - $generalizedReturnType = $this->typeNormalizer->generalizeConstantBoolTypes($soleReturnType); - - // turn into rather generic short return type, to keep it open to extension later and readable to human - $docType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($generalizedReturnType); + $hasChanged = true; + continue; + } - $returnTagValueNode = new ReturnTagValueNode($docType, ''); - $classMethodPhpDocInfo->addTagValueNode($returnTagValueNode); + $yields = $this->yieldNodeFinder->find($dataProviderClassMethod); + if ($yields !== []) { + $yieldType = $this->yieldTypeResolver->resolveFromYieldNodes($yields, $dataProviderClassMethod); - $hasChanged = true; - $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($dataProviderClassMethod); + $this->addGeneratedTypeReturnDocblockType($yieldType, $classMethodPhpDocInfo, $dataProviderClassMethod); + $hasChanged = true; + } } if (! $hasChanged) { @@ -153,4 +164,20 @@ public function refactor(Node $node): ?Node return $node; } + + private function addGeneratedTypeReturnDocblockType( + Type $soleReturnType, + PhpDocInfo $classMethodPhpDocInfo, + ClassMethod $dataProviderClassMethod + ): void { + $generalizedReturnType = $this->typeNormalizer->generalizeConstantBoolTypes($soleReturnType); + + // turn into rather generic short return type, to keep it open to extension later and readable to human + $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($generalizedReturnType); + + $returnTagValueNode = new ReturnTagValueNode($typeNode, ''); + $classMethodPhpDocInfo->addTagValueNode($returnTagValueNode); + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($dataProviderClassMethod); + } } diff --git a/rules/TypeDeclarationDocblocks/TypeResolver/YieldTypeResolver.php b/rules/TypeDeclarationDocblocks/TypeResolver/YieldTypeResolver.php new file mode 100644 index 00000000000..e3bbc4b68b0 --- /dev/null +++ b/rules/TypeDeclarationDocblocks/TypeResolver/YieldTypeResolver.php @@ -0,0 +1,100 @@ + $yieldNodes + */ + public function resolveFromYieldNodes( + array $yieldNodes, + ClassMethod|Function_ $functionLike + ): FullyQualifiedObjectType|FullyQualifiedGenericObjectType { + $yieldedTypes = $this->resolveYieldedTypes($yieldNodes); + + $className = $this->resolveClassName($functionLike); + + if ($yieldedTypes === []) { + return new FullyQualifiedObjectType($className); + } + + $yieldedTypes = $this->typeFactory->createMixedPassedOrUnionType($yieldedTypes); + return new FullyQualifiedGenericObjectType($className, [$yieldedTypes]); + } + + private function resolveYieldValue(Yield_ | YieldFrom $yield): ?Expr + { + if ($yield instanceof Yield_) { + return $yield->value; + } + + return $yield->expr; + } + + /** + * @param array $yieldNodes + * @return Type[] + */ + private function resolveYieldedTypes(array $yieldNodes): array + { + $yieldedTypes = []; + + foreach ($yieldNodes as $yieldNode) { + $value = $this->resolveYieldValue($yieldNode); + if (! $value instanceof Expr) { + // one of the yields is empty + return []; + } + + $resolvedType = $this->nodeTypeResolver->getType($value); + if ($resolvedType instanceof MixedType) { + continue; + } + + $yieldedTypes[] = $resolvedType; + } + + return $yieldedTypes; + } + + private function resolveClassName(Function_|ClassMethod|Closure $functionLike): string + { + $returnTypeNode = $functionLike->getReturnType(); + + if ($returnTypeNode instanceof Identifier && $returnTypeNode->name === 'iterable') { + return 'Iterator'; + } + + if ($returnTypeNode instanceof Name && ! $this->nodeNameResolver->isName($returnTypeNode, 'Generator')) { + return $this->nodeNameResolver->getName($returnTypeNode); + } + + return 'Generator'; + } +} From 4940af98ae939de8bb119dc9f17d8a4145d3e459 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 14 Sep 2025 17:39:20 +0200 Subject: [PATCH 4/5] fix generalizeConstantBoolTypes() to generalizeConstantTypes() method naming --- .../CodeQuality/NodeFactory/PropertyTypeDecorator.php | 2 +- .../Privatization/TypeManipulator/TypeNormalizer.php | 11 ++++++++++- .../AddParamArrayDocblockFromDataProviderRector.php | 2 +- .../Class_/AddReturnDocblockDataProviderRector.php | 2 +- ...ssMethodArrayDocblockParamFromLocalCallsRector.php | 2 +- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/rules/CodeQuality/NodeFactory/PropertyTypeDecorator.php b/rules/CodeQuality/NodeFactory/PropertyTypeDecorator.php index 74a17d15bfb..bd0b22941d0 100644 --- a/rules/CodeQuality/NodeFactory/PropertyTypeDecorator.php +++ b/rules/CodeQuality/NodeFactory/PropertyTypeDecorator.php @@ -22,7 +22,7 @@ public function __construct( public function decorateProperty(Property $property, Type $propertyType): void { // generalize false/true type to bool, as mostly default value but accepts both - $propertyType = $this->typeNormalizer->generalizeConstantBoolTypes($propertyType); + $propertyType = $this->typeNormalizer->generalizeConstantTypes($propertyType); $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); $phpDocInfo->makeMultiLined(); diff --git a/rules/Privatization/TypeManipulator/TypeNormalizer.php b/rules/Privatization/TypeManipulator/TypeNormalizer.php index b00720ed0bb..13b3e3562f4 100644 --- a/rules/Privatization/TypeManipulator/TypeNormalizer.php +++ b/rules/Privatization/TypeManipulator/TypeNormalizer.php @@ -21,11 +21,20 @@ final class TypeNormalizer { + /** + * @deprecated This method is deprecated and will be removed in the next major release. + * Use @see generalizeConstantTypes() instead. + */ + public function generalizeConstantBoolTypes(\PHPStan\Type\Type $type): Type + { + return $this->generalizeConstantTypes($type); + } + /** * Generalize false/true constantArrayType to bool, * as mostly default value but accepts both */ - public function generalizeConstantBoolTypes(Type $type): Type + public function generalizeConstantTypes(Type $type): Type { return TypeTraverser::map($type, function (Type $type, callable $traverseCallback): BooleanType|Type { if ($type instanceof ConstantBooleanType) { diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector.php index 38b5fb1c078..4c0a2b09c21 100644 --- a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector.php +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector.php @@ -144,7 +144,7 @@ public function refactor(Node $node): ?Node $dataProviderNodes->getClassMethods() ); - $generalizedParameterType = $this->typeNormalizer->generalizeConstantBoolTypes($parameterType); + $generalizedParameterType = $this->typeNormalizer->generalizeConstantTypes($parameterType); $parameterTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode( $generalizedParameterType diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php index 0a9c7559053..501bdbd7da4 100644 --- a/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php @@ -170,7 +170,7 @@ private function addGeneratedTypeReturnDocblockType( PhpDocInfo $classMethodPhpDocInfo, ClassMethod $dataProviderClassMethod ): void { - $generalizedReturnType = $this->typeNormalizer->generalizeConstantBoolTypes($soleReturnType); + $generalizedReturnType = $this->typeNormalizer->generalizeConstantTypes($soleReturnType); // turn into rather generic short return type, to keep it open to extension later and readable to human $typeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($generalizedReturnType); diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector.php index 2c5696b0427..49267be1e19 100644 --- a/rules/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector.php +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector.php @@ -116,7 +116,7 @@ public function refactor(Node $node): ?Node continue; } - $normalizedResolvedParameterType = $this->typeNormalizer->generalizeConstantBoolTypes( + $normalizedResolvedParameterType = $this->typeNormalizer->generalizeConstantTypes( $resolvedParameterType ); $arrayDocTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode( From 05725d0f75f21308a07a60b2bb6dede32ca6ba17 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 14 Sep 2025 17:41:50 +0200 Subject: [PATCH 5/5] add fixture --- .../Fixture/yield_provider.php.inc | 2 +- rules/Privatization/TypeManipulator/TypeNormalizer.php | 2 +- .../Rector/Class_/AddReturnDocblockDataProviderRector.php | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/yield_provider.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/yield_provider.php.inc index 474e1e012fc..63e5009eb56 100644 --- a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/yield_provider.php.inc +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/yield_provider.php.inc @@ -36,7 +36,7 @@ final class YieldProvider extends TestCase } /** - * @return \Iterator + * @return \Iterator> */ public static function provideData() { diff --git a/rules/Privatization/TypeManipulator/TypeNormalizer.php b/rules/Privatization/TypeManipulator/TypeNormalizer.php index 13b3e3562f4..540dcd44a8c 100644 --- a/rules/Privatization/TypeManipulator/TypeNormalizer.php +++ b/rules/Privatization/TypeManipulator/TypeNormalizer.php @@ -36,7 +36,7 @@ public function generalizeConstantBoolTypes(\PHPStan\Type\Type $type): Type */ public function generalizeConstantTypes(Type $type): Type { - return TypeTraverser::map($type, function (Type $type, callable $traverseCallback): BooleanType|Type { + return TypeTraverser::map($type, function (Type $type, callable $traverseCallback): Type { if ($type instanceof ConstantBooleanType) { return new BooleanType(); } diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php index 501bdbd7da4..f1ccc16733e 100644 --- a/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php @@ -18,6 +18,7 @@ use Rector\Privatization\TypeManipulator\TypeNormalizer; use Rector\Rector\AbstractRector; use Rector\StaticTypeMapper\StaticTypeMapper; +use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedGenericObjectType; use Rector\TypeDeclarationDocblocks\NodeFinder\DataProviderMethodsFinder; use Rector\TypeDeclarationDocblocks\NodeFinder\ReturnNodeFinder; use Rector\TypeDeclarationDocblocks\NodeFinder\YieldNodeFinder; @@ -153,6 +154,13 @@ public function refactor(Node $node): ?Node if ($yields !== []) { $yieldType = $this->yieldTypeResolver->resolveFromYieldNodes($yields, $dataProviderClassMethod); + if ($yieldType instanceof FullyQualifiedGenericObjectType) { + if ($yieldType->getClassName() === 'Generator') { + // most likely, a static iterator is used in data test fixtures + $yieldType = new FullyQualifiedGenericObjectType('Iterator', $yieldType->getTypes()); + } + } + $this->addGeneratedTypeReturnDocblockType($yieldType, $classMethodPhpDocInfo, $dataProviderClassMethod); $hasChanged = true; }