diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/skip_yield_from_combined_with_yield.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/skip_yield_from_combined_with_yield.php.inc new file mode 100644 index 00000000000..4bd13a88e05 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/skip_yield_from_combined_with_yield.php.inc @@ -0,0 +1,28 @@ + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/skip_yield_variable_multiple_types.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/skip_yield_variable_multiple_types.php.inc new file mode 100644 index 00000000000..0c81a3da095 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/skip_yield_variable_multiple_types.php.inc @@ -0,0 +1,33 @@ +format('Y-m-d')]; + } + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/FixtureUnion/yield_variable_multiple_types.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/FixtureUnion/yield_variable_multiple_types.php.inc new file mode 100644 index 00000000000..3bb930b6852 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/FixtureUnion/yield_variable_multiple_types.php.inc @@ -0,0 +1,67 @@ +format('Y-m-d')]; + } + } +} + +?> +----- +format('Y-m-d')]; + } + } +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/UnionTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/UnionTest.php new file mode 100644 index 00000000000..4ef34239aaa --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/UnionTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureUnion'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/union_config.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/config/union_config.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/config/union_config.php new file mode 100644 index 00000000000..40a809d31f8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/config/union_config.php @@ -0,0 +1,13 @@ +rule(AddParamTypeBasedOnPHPUnitDataProviderRector::class); + + $rectorConfig->phpVersion(PhpVersionFeature::UNION_TYPES); +}; diff --git a/rules/TypeDeclaration/TypeAnalyzer/ParameterTypeFromDataProviderResolver.php b/rules/TypeDeclaration/TypeAnalyzer/ParameterTypeFromDataProviderResolver.php index 9d008d69a37..cf8cf419866 100644 --- a/rules/TypeDeclaration/TypeAnalyzer/ParameterTypeFromDataProviderResolver.php +++ b/rules/TypeDeclaration/TypeAnalyzer/ParameterTypeFromDataProviderResolver.php @@ -5,14 +5,17 @@ namespace Rector\TypeDeclaration\TypeAnalyzer; use PhpParser\Node\ArrayItem; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\Yield_; +use PhpParser\Node\Expr\YieldFrom; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Return_; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\MixedType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; +use PHPStan\Type\UnionType; use Rector\NodeTypeResolver\NodeTypeResolver; use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; use Rector\PhpParser\Node\BetterNodeFinder; @@ -51,6 +54,16 @@ private function resolveParameterTypeFromDataProvider( return $this->resolveReturnStaticArrayTypeByParameterPosition($returns, $parameterPosition); } + $yieldFromNodes = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped( + $dataProviderClassMethod, + YieldFrom::class + ); + + // "yield from" data sets are not resolved here → the resolved type would be incomplete + if ($yieldFromNodes !== []) { + return new MixedType(); + } + /** @var Yield_[] $yields */ $yields = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($dataProviderClassMethod, Yield_::class); return $this->resolveYieldStaticArrayTypeByParameterPosition($yields, $parameterPosition); @@ -83,22 +96,25 @@ private function resolveYieldStaticArrayTypeByParameterPosition(array $yields, i $paramOnPositionTypes = []; foreach ($yields as $yield) { - if (! $yield->value instanceof Array_) { - continue; + if (! $yield->value instanceof Expr) { + return new MixedType(); } - $type = $this->getTypeFromClassMethodYield($yield->value); + $constantArrayTypes = $this->resolveYieldedConstantArrayTypes($yield->value); - if (! $type instanceof ConstantArrayType) { - return $type; + // one of the yielded data sets cannot be resolved → the resolved type would be incomplete + if ($constantArrayTypes === []) { + return new MixedType(); } - foreach ($type->getValueTypes() as $position => $valueType) { - if ($position !== $parameterPosition) { - continue; - } + foreach ($constantArrayTypes as $constantArrayType) { + foreach ($constantArrayType->getValueTypes() as $position => $valueType) { + if ($position !== $parameterPosition) { + continue; + } - $paramOnPositionTypes[] = $valueType; + $paramOnPositionTypes[] = $valueType; + } } } @@ -109,16 +125,32 @@ private function resolveYieldStaticArrayTypeByParameterPosition(array $yields, i return $this->typeFactory->createMixedPassedOrUnionType($paramOnPositionTypes); } - private function getTypeFromClassMethodYield(Array_ $classMethodYieldArray): MixedType|ConstantArrayType + /** + * @return ConstantArrayType[] + */ + private function resolveYieldedConstantArrayTypes(Expr $expr): array { - $arrayType = $this->nodeTypeResolver->getType($classMethodYieldArray); + $yieldedType = $this->nodeTypeResolver->getType($expr); - // impossible to resolve - if (! $arrayType instanceof ConstantArrayType) { - return new MixedType(); + if ($yieldedType instanceof ConstantArrayType) { + return [$yieldedType]; + } + + if (! $yieldedType instanceof UnionType) { + return []; + } + + $constantArrayTypes = []; + foreach ($yieldedType->getTypes() as $unionedType) { + // impossible to resolve + if (! $unionedType instanceof ConstantArrayType) { + return []; + } + + $constantArrayTypes[] = $unionedType; } - return $arrayType; + return $constantArrayTypes; } /**