Skip to content

Commit f0210bf

Browse files
authored
[type-declaration-docblocks] Add fixture for data provider return options (#7268)
* add fixture for multiple options * add yield fixture provider * extract couple yield-related services to re-use * fix generalizeConstantBoolTypes() to generalizeConstantTypes() method naming * add fixture
1 parent 3803546 commit f0210bf

10 files changed

Lines changed: 329 additions & 147 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockFromDataProviderRector\Fixture;
4+
5+
use PHPUnit\Framework\Attributes\DataProvider;
6+
use PHPUnit\Framework\TestCase;
7+
8+
final class MultipleOptions extends TestCase
9+
{
10+
#[DataProvider('provideData')]
11+
public function test(array $names): void
12+
{
13+
}
14+
15+
public static function provideData()
16+
{
17+
yield [['Tom', 'John']];
18+
yield [[]];
19+
yield [['John', 'Doe']];
20+
}
21+
}
22+
23+
?>
24+
-----
25+
<?php
26+
27+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockFromDataProviderRector\Fixture;
28+
29+
use PHPUnit\Framework\Attributes\DataProvider;
30+
use PHPUnit\Framework\TestCase;
31+
32+
final class MultipleOptions extends TestCase
33+
{
34+
/**
35+
* @param string[] $names
36+
*/
37+
#[DataProvider('provideData')]
38+
public function test(array $names): void
39+
{
40+
}
41+
42+
public static function provideData()
43+
{
44+
yield [['Tom', 'John']];
45+
yield [[]];
46+
yield [['John', 'Doe']];
47+
}
48+
}
49+
50+
?>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\AddReturnDocblockDataProviderRector\Fixture;
4+
5+
use PHPUnit\Framework\Attributes\DataProvider;
6+
use PHPUnit\Framework\TestCase;
7+
8+
final class YieldProvider extends TestCase
9+
{
10+
#[DataProvider('provideData')]
11+
public function testSomething()
12+
{
13+
}
14+
15+
public static function provideData()
16+
{
17+
yield ['data1', 'data2'];
18+
yield ['item4', 'item5'];
19+
}
20+
}
21+
22+
?>
23+
-----
24+
<?php
25+
26+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\AddReturnDocblockDataProviderRector\Fixture;
27+
28+
use PHPUnit\Framework\Attributes\DataProvider;
29+
use PHPUnit\Framework\TestCase;
30+
31+
final class YieldProvider extends TestCase
32+
{
33+
#[DataProvider('provideData')]
34+
public function testSomething()
35+
{
36+
}
37+
38+
/**
39+
* @return \Iterator<array<int, string>>
40+
*/
41+
public static function provideData()
42+
{
43+
yield ['data1', 'data2'];
44+
yield ['item4', 'item5'];
45+
}
46+
}
47+
48+
?>

rules/CodeQuality/NodeFactory/PropertyTypeDecorator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public function __construct(
2222
public function decorateProperty(Property $property, Type $propertyType): void
2323
{
2424
// generalize false/true type to bool, as mostly default value but accepts both
25-
$propertyType = $this->typeNormalizer->generalizeConstantBoolTypes($propertyType);
25+
$propertyType = $this->typeNormalizer->generalizeConstantTypes($propertyType);
2626

2727
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
2828
$phpDocInfo->makeMultiLined();

rules/Privatization/TypeManipulator/TypeNormalizer.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,22 @@
2121

2222
final class TypeNormalizer
2323
{
24+
/**
25+
* @deprecated This method is deprecated and will be removed in the next major release.
26+
* Use @see generalizeConstantTypes() instead.
27+
*/
28+
public function generalizeConstantBoolTypes(\PHPStan\Type\Type $type): Type
29+
{
30+
return $this->generalizeConstantTypes($type);
31+
}
32+
2433
/**
2534
* Generalize false/true constantArrayType to bool,
2635
* as mostly default value but accepts both
2736
*/
28-
public function generalizeConstantBoolTypes(Type $type): Type
37+
public function generalizeConstantTypes(Type $type): Type
2938
{
30-
return TypeTraverser::map($type, function (Type $type, callable $traverseCallback): BooleanType|Type {
39+
return TypeTraverser::map($type, function (Type $type, callable $traverseCallback): Type {
3140
if ($type instanceof ConstantBooleanType) {
3241
return new BooleanType();
3342
}

rules/TypeDeclaration/Rector/FunctionLike/AddReturnTypeDeclarationFromYieldsRector.php

Lines changed: 8 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,14 @@
55
namespace Rector\TypeDeclaration\Rector\FunctionLike;
66

77
use PhpParser\Node;
8-
use PhpParser\Node\Expr;
9-
use PhpParser\Node\Expr\Closure;
10-
use PhpParser\Node\Expr\Yield_;
11-
use PhpParser\Node\Expr\YieldFrom;
12-
use PhpParser\Node\FunctionLike;
13-
use PhpParser\Node\Identifier;
14-
use PhpParser\Node\Name;
15-
use PhpParser\Node\Stmt;
16-
use PhpParser\Node\Stmt\Class_;
178
use PhpParser\Node\Stmt\ClassMethod;
18-
use PhpParser\Node\Stmt\Expression;
199
use PhpParser\Node\Stmt\Function_;
20-
use PhpParser\NodeVisitor;
21-
use PHPStan\Type\MixedType;
22-
use PHPStan\Type\Type;
23-
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
24-
use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser;
2510
use Rector\PHPStan\ScopeFetcher;
2611
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
2712
use Rector\Rector\AbstractRector;
2813
use Rector\StaticTypeMapper\StaticTypeMapper;
29-
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedGenericObjectType;
30-
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
14+
use Rector\TypeDeclarationDocblocks\NodeFinder\YieldNodeFinder;
15+
use Rector\TypeDeclarationDocblocks\TypeResolver\YieldTypeResolver;
3116
use Rector\ValueObject\PhpVersionFeature;
3217
use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard;
3318
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
@@ -40,10 +25,10 @@
4025
final class AddReturnTypeDeclarationFromYieldsRector extends AbstractRector implements MinPhpVersionInterface
4126
{
4227
public function __construct(
43-
private readonly TypeFactory $typeFactory,
44-
private readonly SimpleCallableNodeTraverser $simpleCallableNodeTraverser,
4528
private readonly StaticTypeMapper $staticTypeMapper,
46-
private readonly ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard
29+
private readonly ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard,
30+
private readonly YieldNodeFinder $yieldNodeFinder,
31+
private readonly YieldTypeResolver $yieldTypeResolver,
4732
) {
4833
}
4934

@@ -91,7 +76,8 @@ public function getNodeTypes(): array
9176
public function refactor(Node $node): ?Node
9277
{
9378
$scope = ScopeFetcher::fetch($node);
94-
$yieldNodes = $this->findCurrentScopeYieldNodes($node);
79+
80+
$yieldNodes = $this->yieldNodeFinder->find($node);
9581
if ($yieldNodes === []) {
9682
return null;
9783
}
@@ -111,7 +97,7 @@ public function refactor(Node $node): ?Node
11197
return null;
11298
}
11399

114-
$yieldType = $this->resolveYieldType($yieldNodes, $node);
100+
$yieldType = $this->yieldTypeResolver->resolveFromYieldNodes($yieldNodes, $node);
115101
$returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($yieldType, TypeKind::RETURN);
116102
if (! $returnTypeNode instanceof Node) {
117103
return null;
@@ -125,109 +111,4 @@ public function provideMinPhpVersion(): int
125111
{
126112
return PhpVersionFeature::SCALAR_TYPES;
127113
}
128-
129-
/**
130-
* @return Yield_[]|YieldFrom[]
131-
*/
132-
private function findCurrentScopeYieldNodes(FunctionLike $functionLike): array
133-
{
134-
$yieldNodes = [];
135-
136-
$this->simpleCallableNodeTraverser->traverseNodesWithCallable((array) $functionLike->getStmts(), static function (
137-
Node $node
138-
) use (&$yieldNodes): ?int {
139-
// skip anonymous class and inner function
140-
if ($node instanceof Class_) {
141-
return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
142-
}
143-
144-
// skip nested scope
145-
if ($node instanceof FunctionLike) {
146-
return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
147-
}
148-
149-
if ($node instanceof Stmt && ! $node instanceof Expression) {
150-
$yieldNodes = [];
151-
return NodeVisitor::STOP_TRAVERSAL;
152-
}
153-
154-
if (! $node instanceof Yield_ && ! $node instanceof YieldFrom) {
155-
return null;
156-
}
157-
158-
$yieldNodes[] = $node;
159-
return null;
160-
});
161-
162-
return $yieldNodes;
163-
}
164-
165-
private function resolveYieldValue(Yield_ | YieldFrom $yield): ?Expr
166-
{
167-
if ($yield instanceof Yield_) {
168-
return $yield->value;
169-
}
170-
171-
return $yield->expr;
172-
}
173-
174-
/**
175-
* @param array<Yield_|YieldFrom> $yieldNodes
176-
* @return Type[]
177-
*/
178-
private function resolveYieldedTypes(array $yieldNodes): array
179-
{
180-
$yieldedTypes = [];
181-
182-
foreach ($yieldNodes as $yieldNode) {
183-
$value = $this->resolveYieldValue($yieldNode);
184-
if (! $value instanceof Expr) {
185-
// one of the yields is empty
186-
return [];
187-
}
188-
189-
$resolvedType = $this->nodeTypeResolver->getType($value);
190-
if ($resolvedType instanceof MixedType) {
191-
continue;
192-
}
193-
194-
$yieldedTypes[] = $resolvedType;
195-
}
196-
197-
return $yieldedTypes;
198-
}
199-
200-
/**
201-
* @param array<Yield_|YieldFrom> $yieldNodes
202-
*/
203-
private function resolveYieldType(
204-
array $yieldNodes,
205-
ClassMethod|Function_ $functionLike
206-
): FullyQualifiedObjectType|FullyQualifiedGenericObjectType {
207-
$yieldedTypes = $this->resolveYieldedTypes($yieldNodes);
208-
209-
$className = $this->resolveClassName($functionLike);
210-
211-
if ($yieldedTypes === []) {
212-
return new FullyQualifiedObjectType($className);
213-
}
214-
215-
$yieldedTypes = $this->typeFactory->createMixedPassedOrUnionType($yieldedTypes);
216-
return new FullyQualifiedGenericObjectType($className, [$yieldedTypes]);
217-
}
218-
219-
private function resolveClassName(Function_|ClassMethod|Closure $functionLike): string
220-
{
221-
$returnTypeNode = $functionLike->getReturnType();
222-
223-
if ($returnTypeNode instanceof Identifier && $returnTypeNode->name === 'iterable') {
224-
return 'Iterator';
225-
}
226-
227-
if ($returnTypeNode instanceof Name && ! $this->isName($returnTypeNode, 'Generator')) {
228-
return $this->getName($returnTypeNode);
229-
}
230-
231-
return 'Generator';
232-
}
233114
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\TypeDeclarationDocblocks\NodeFinder;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Expr\Yield_;
9+
use PhpParser\Node\Expr\YieldFrom;
10+
use PhpParser\Node\FunctionLike;
11+
use PhpParser\Node\Stmt;
12+
use PhpParser\Node\Stmt\Class_;
13+
use PhpParser\Node\Stmt\Expression;
14+
use PhpParser\NodeVisitor;
15+
use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser;
16+
17+
final readonly class YieldNodeFinder
18+
{
19+
public function __construct(
20+
private SimpleCallableNodeTraverser $simpleCallableNodeTraverser
21+
) {
22+
}
23+
24+
/**
25+
* @return Yield_[]|YieldFrom[]
26+
*/
27+
public function find(FunctionLike $functionLike): array
28+
{
29+
$yieldNodes = [];
30+
31+
$this->simpleCallableNodeTraverser->traverseNodesWithCallable((array) $functionLike->getStmts(), static function (
32+
Node $node
33+
) use (&$yieldNodes): ?int {
34+
// skip anonymous class and inner function
35+
if ($node instanceof Class_) {
36+
return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
37+
}
38+
39+
// skip nested scope
40+
if ($node instanceof FunctionLike) {
41+
return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
42+
}
43+
44+
if ($node instanceof Stmt && ! $node instanceof Expression) {
45+
$yieldNodes = [];
46+
return NodeVisitor::STOP_TRAVERSAL;
47+
}
48+
49+
if (! $node instanceof Yield_ && ! $node instanceof YieldFrom) {
50+
return null;
51+
}
52+
53+
$yieldNodes[] = $node;
54+
return null;
55+
});
56+
57+
return $yieldNodes;
58+
}
59+
}

rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public function refactor(Node $node): ?Node
144144
$dataProviderNodes->getClassMethods()
145145
);
146146

147-
$generalizedParameterType = $this->typeNormalizer->generalizeConstantBoolTypes($parameterType);
147+
$generalizedParameterType = $this->typeNormalizer->generalizeConstantTypes($parameterType);
148148

149149
$parameterTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode(
150150
$generalizedParameterType

0 commit comments

Comments
 (0)