Skip to content

Commit 7be1910

Browse files
committed
extract ParameterTypeFromDataProviderResolver
1 parent 8f25178 commit 7be1910

4 files changed

Lines changed: 185 additions & 156 deletions

File tree

rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddParamArrayDocblockFromDataProviderRector/Fixture/some_test_with_data_provider.php

Lines changed: 0 additions & 21 deletions
This file was deleted.

rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector.php

Lines changed: 6 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,14 @@
55
namespace Rector\TypeDeclaration\Rector\ClassMethod;
66

77
use PhpParser\Node;
8-
use PhpParser\Node\ArrayItem;
9-
use PhpParser\Node\Expr\Array_;
10-
use PhpParser\Node\Expr\Yield_;
118
use PhpParser\Node\Stmt\Class_;
129
use PhpParser\Node\Stmt\ClassMethod;
13-
use PhpParser\Node\Stmt\Return_;
14-
use PHPStan\Type\Constant\ConstantArrayType;
1510
use PHPStan\Type\MixedType;
16-
use PHPStan\Type\Type;
17-
use PHPStan\Type\TypeCombinator;
18-
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
19-
use Rector\PhpParser\Node\BetterNodeFinder;
2011
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
2112
use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer;
2213
use Rector\Rector\AbstractRector;
2314
use Rector\StaticTypeMapper\StaticTypeMapper;
15+
use Rector\TypeDeclaration\TypeAnalyzer\ParameterTypeFromDataProviderResolver;
2416
use Rector\TypeDeclaration\ValueObject\DataProviderNodes;
2517
use Rector\TypeDeclarationDocblocks\NodeFinder\DataProviderMethodsFinder;
2618
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@@ -37,10 +29,9 @@ final class AddParamTypeBasedOnPHPUnitDataProviderRector extends AbstractRector
3729
private const ERROR_MESSAGE = 'Adds param type declaration based on PHPUnit provider return type declaration';
3830

3931
public function __construct(
40-
private readonly TypeFactory $typeFactory,
4132
private readonly TestsNodeAnalyzer $testsNodeAnalyzer,
4233
private readonly DataProviderMethodsFinder $dataProviderMethodsFinder,
43-
private readonly BetterNodeFinder $betterNodeFinder,
34+
private readonly ParameterTypeFromDataProviderResolver $parameterTypeFromDataProviderResolver,
4435
private readonly StaticTypeMapper $staticTypeMapper,
4536
) {
4637
}
@@ -136,111 +127,6 @@ public function refactor(Node $node): ?Node
136127
return null;
137128
}
138129

139-
private function inferParam(int $parameterPosition, ClassMethod $dataProviderClassMethod): Type
140-
{
141-
$returns = $this->betterNodeFinder->findReturnsScoped($dataProviderClassMethod);
142-
if ($returns !== []) {
143-
return $this->resolveReturnStaticArrayTypeByParameterPosition($returns, $parameterPosition);
144-
}
145-
146-
/** @var Yield_[] $yields */
147-
$yields = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($dataProviderClassMethod, Yield_::class);
148-
return $this->resolveYieldStaticArrayTypeByParameterPosition($yields, $parameterPosition);
149-
}
150-
151-
/**
152-
* @param Return_[] $returns
153-
*/
154-
private function resolveReturnStaticArrayTypeByParameterPosition(array $returns, int $parameterPosition): Type
155-
{
156-
$firstReturnedExpr = $returns[0]->expr;
157-
158-
if (! $firstReturnedExpr instanceof Array_) {
159-
return new MixedType();
160-
}
161-
162-
$paramOnPositionTypes = $this->resolveParamOnPositionTypes($firstReturnedExpr, $parameterPosition);
163-
if ($paramOnPositionTypes === []) {
164-
return new MixedType();
165-
}
166-
167-
return $this->typeFactory->createMixedPassedOrUnionType($paramOnPositionTypes);
168-
}
169-
170-
/**
171-
* @param Yield_[] $yields
172-
*/
173-
private function resolveYieldStaticArrayTypeByParameterPosition(array $yields, int $parameterPosition): Type
174-
{
175-
$paramOnPositionTypes = [];
176-
177-
foreach ($yields as $yield) {
178-
if (! $yield->value instanceof Array_) {
179-
continue;
180-
}
181-
182-
$type = $this->getTypeFromClassMethodYield($yield->value);
183-
184-
if (! $type instanceof ConstantArrayType) {
185-
return $type;
186-
}
187-
188-
foreach ($type->getValueTypes() as $position => $valueType) {
189-
if ($position !== $parameterPosition) {
190-
continue;
191-
}
192-
193-
$paramOnPositionTypes[] = $valueType;
194-
}
195-
}
196-
197-
if ($paramOnPositionTypes === []) {
198-
return new MixedType();
199-
}
200-
201-
return $this->typeFactory->createMixedPassedOrUnionType($paramOnPositionTypes);
202-
}
203-
204-
private function getTypeFromClassMethodYield(Array_ $classMethodYieldArray): MixedType | ConstantArrayType
205-
{
206-
$arrayType = $this->nodeTypeResolver->getType($classMethodYieldArray);
207-
208-
// impossible to resolve
209-
if (! $arrayType instanceof ConstantArrayType) {
210-
return new MixedType();
211-
}
212-
213-
return $arrayType;
214-
}
215-
216-
/**
217-
* @return Type[]
218-
*/
219-
private function resolveParamOnPositionTypes(Array_ $array, int $parameterPosition): array
220-
{
221-
$paramOnPositionTypes = [];
222-
223-
foreach ($array->items as $singleDataProvidedSet) {
224-
if (! $singleDataProvidedSet instanceof ArrayItem || ! $singleDataProvidedSet->value instanceof Array_) {
225-
return [];
226-
}
227-
228-
foreach ($singleDataProvidedSet->value->items as $position => $singleDataProvidedSetItem) {
229-
if ($position !== $parameterPosition) {
230-
continue;
231-
}
232-
233-
if (! $singleDataProvidedSetItem instanceof ArrayItem) {
234-
continue;
235-
}
236-
237-
$paramOnPositionTypes[] = $this->nodeTypeResolver->getType($singleDataProvidedSetItem->value);
238-
}
239-
}
240-
241-
return $paramOnPositionTypes;
242-
}
243-
244130
private function refactorClassMethod(ClassMethod $classMethod, DataProviderNodes $dataProviderNodes): bool
245131
{
246132
$hasChanged = false;
@@ -254,12 +140,11 @@ private function refactorClassMethod(ClassMethod $classMethod, DataProviderNodes
254140
continue;
255141
}
256142

257-
$paramTypes = [];
258-
foreach ($dataProviderNodes->getClassMethods() as $dataProviderClassMethod) {
259-
$paramTypes[] = $this->inferParam($parameterPosition, $dataProviderClassMethod);
260-
}
143+
$paramTypeDeclaration = $this->parameterTypeFromDataProviderResolver->resolve(
144+
$parameterPosition,
145+
$dataProviderNodes->getClassMethods()
146+
);
261147

262-
$paramTypeDeclaration = TypeCombinator::union(...$paramTypes);
263148
if ($paramTypeDeclaration instanceof MixedType) {
264149
continue;
265150
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\TypeDeclaration\TypeAnalyzer;
6+
7+
use PhpParser\Node\ArrayItem;
8+
use PhpParser\Node\Expr\Array_;
9+
use PhpParser\Node\Expr\Yield_;
10+
use PhpParser\Node\Stmt\ClassMethod;
11+
use PhpParser\Node\Stmt\Return_;
12+
use PHPStan\Type\Constant\ConstantArrayType;
13+
use PHPStan\Type\MixedType;
14+
use PHPStan\Type\Type;
15+
use PHPStan\Type\TypeCombinator;
16+
use Rector\NodeTypeResolver\NodeTypeResolver;
17+
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
18+
use Rector\PhpParser\Node\BetterNodeFinder;
19+
use Webmozart\Assert\Assert;
20+
21+
final readonly class ParameterTypeFromDataProviderResolver
22+
{
23+
public function __construct(
24+
private NodeTypeResolver $nodeTypeResolver,
25+
private BetterNodeFinder $betterNodeFinder,
26+
private TypeFactory $typeFactory
27+
) {
28+
}
29+
30+
/**
31+
* @param ClassMethod[] $dataProviderClassMethods
32+
*/
33+
public function resolve(int $parameterPosition, array $dataProviderClassMethods): Type
34+
{
35+
Assert::allIsInstanceOf($dataProviderClassMethods, ClassMethod::class);
36+
37+
$paramTypes = [];
38+
foreach ($dataProviderClassMethods as $dataProviderClassMethod) {
39+
$paramTypes[] = $this->resolveParameterTypeFromDataProvider($parameterPosition, $dataProviderClassMethod);
40+
}
41+
42+
return TypeCombinator::union(...$paramTypes);
43+
}
44+
45+
private function resolveParameterTypeFromDataProvider(
46+
int $parameterPosition,
47+
ClassMethod $dataProviderClassMethod
48+
): Type {
49+
$returns = $this->betterNodeFinder->findReturnsScoped($dataProviderClassMethod);
50+
if ($returns !== []) {
51+
return $this->resolveReturnStaticArrayTypeByParameterPosition($returns, $parameterPosition);
52+
}
53+
54+
/** @var Yield_[] $yields */
55+
$yields = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($dataProviderClassMethod, Yield_::class);
56+
return $this->resolveYieldStaticArrayTypeByParameterPosition($yields, $parameterPosition);
57+
}
58+
59+
/**
60+
* @param Return_[] $returns
61+
*/
62+
private function resolveReturnStaticArrayTypeByParameterPosition(array $returns, int $parameterPosition): Type
63+
{
64+
$firstReturnedExpr = $returns[0]->expr;
65+
66+
if (! $firstReturnedExpr instanceof Array_) {
67+
return new MixedType();
68+
}
69+
70+
$paramOnPositionTypes = $this->resolveParamOnPositionTypes($firstReturnedExpr, $parameterPosition);
71+
if ($paramOnPositionTypes === []) {
72+
return new MixedType();
73+
}
74+
75+
return $this->typeFactory->createMixedPassedOrUnionType($paramOnPositionTypes);
76+
}
77+
78+
/**
79+
* @param Yield_[] $yields
80+
*/
81+
private function resolveYieldStaticArrayTypeByParameterPosition(array $yields, int $parameterPosition): Type
82+
{
83+
$paramOnPositionTypes = [];
84+
85+
foreach ($yields as $yield) {
86+
if (! $yield->value instanceof Array_) {
87+
continue;
88+
}
89+
90+
$type = $this->getTypeFromClassMethodYield($yield->value);
91+
92+
if (! $type instanceof ConstantArrayType) {
93+
return $type;
94+
}
95+
96+
foreach ($type->getValueTypes() as $position => $valueType) {
97+
if ($position !== $parameterPosition) {
98+
continue;
99+
}
100+
101+
$paramOnPositionTypes[] = $valueType;
102+
}
103+
}
104+
105+
if ($paramOnPositionTypes === []) {
106+
return new MixedType();
107+
}
108+
109+
return $this->typeFactory->createMixedPassedOrUnionType($paramOnPositionTypes);
110+
}
111+
112+
private function getTypeFromClassMethodYield(Array_ $classMethodYieldArray): MixedType | ConstantArrayType
113+
{
114+
$arrayType = $this->nodeTypeResolver->getType($classMethodYieldArray);
115+
116+
// impossible to resolve
117+
if (! $arrayType instanceof ConstantArrayType) {
118+
return new MixedType();
119+
}
120+
121+
return $arrayType;
122+
}
123+
124+
/**
125+
* @return Type[]
126+
*/
127+
private function resolveParamOnPositionTypes(Array_ $array, int $parameterPosition): array
128+
{
129+
$paramOnPositionTypes = [];
130+
131+
foreach ($array->items as $singleDataProvidedSet) {
132+
if (! $singleDataProvidedSet instanceof ArrayItem || ! $singleDataProvidedSet->value instanceof Array_) {
133+
return [];
134+
}
135+
136+
foreach ($singleDataProvidedSet->value->items as $position => $singleDataProvidedSetItem) {
137+
if ($position !== $parameterPosition) {
138+
continue;
139+
}
140+
141+
if (! $singleDataProvidedSetItem instanceof ArrayItem) {
142+
continue;
143+
}
144+
145+
$paramOnPositionTypes[] = $this->nodeTypeResolver->getType($singleDataProvidedSetItem->value);
146+
}
147+
}
148+
149+
return $paramOnPositionTypes;
150+
}
151+
}

0 commit comments

Comments
 (0)