diff --git a/build/target-repository/docs/rector_rules_overview.md b/build/target-repository/docs/rector_rules_overview.md index 8b43945591a..aee03445ba2 100644 --- a/build/target-repository/docs/rector_rules_overview.md +++ b/build/target-repository/docs/rector_rules_overview.md @@ -6230,7 +6230,7 @@ Adds param type declaration based on PHPUnit provider return type declaration - class: [`Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeBasedOnPHPUnitDataProviderRector`](../rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector.php) ```diff - use PHPUnit\Framework\TestCase + use PHPUnit\Framework\TestCase; final class SomeTest extends TestCase { diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/check_all_providers_using_attribute.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/check_all_providers_using_attribute.php.inc new file mode 100644 index 00000000000..e8f88d35623 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/check_all_providers_using_attribute.php.inc @@ -0,0 +1,24 @@ + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/check_all_providers_using_attribute_and_phpdoc.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/check_all_providers_using_attribute_and_phpdoc.php.inc new file mode 100644 index 00000000000..2e83a4bfd9a --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/check_all_providers_using_attribute_and_phpdoc.php.inc @@ -0,0 +1,26 @@ + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/fixture_using_attribute.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/fixture_using_attribute.php.inc new file mode 100644 index 00000000000..4a4e11ee0ad --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/fixture_using_attribute.php.inc @@ -0,0 +1,43 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/many_scalars_using_attribute.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/many_scalars_using_attribute.php.inc new file mode 100644 index 00000000000..420afbb1202 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/many_scalars_using_attribute.php.inc @@ -0,0 +1,55 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/return_array_using_attribute.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/return_array_using_attribute.php.inc new file mode 100644 index 00000000000..93711609955 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/return_array_using_attribute.php.inc @@ -0,0 +1,43 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/skip_existing_using_attribute.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/skip_existing_using_attribute.php.inc new file mode 100644 index 00000000000..80e46bc9487 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector/Fixture/skip_existing_using_attribute.php.inc @@ -0,0 +1,22 @@ +resolveDataProviderPhpDocTagNode($classMethod); - if ($dataProviderPhpDocTagNodes === []) { + $dataProviderNodes = $this->resolveDataProviderNodes($classMethod); + if ($dataProviderNodes->isEmpty()) { return null; } - $hasClassMethodChanged = $this->refactorClassMethod($classMethod, $node, $dataProviderPhpDocTagNodes); + $hasClassMethodChanged = $this->refactorClassMethod($classMethod, $node, $dataProviderNodes->nodes); if ($hasClassMethodChanged) { $hasChanged = true; } @@ -148,9 +152,9 @@ public function refactor(Node $node): ?Node return null; } - private function inferParam(Class_ $class, Param $param, PhpDocTagNode $dataProviderPhpDocTagNode): Type + private function inferParam(Class_ $class, Param $param, PhpDocTagNode | Attribute $dataProviderNode): Type { - $dataProviderClassMethod = $this->resolveDataProviderClassMethod($class, $dataProviderPhpDocTagNode); + $dataProviderClassMethod = $this->resolveDataProviderClassMethod($class, $dataProviderNode); if (! $dataProviderClassMethod instanceof ClassMethod) { return new MixedType(); } @@ -173,13 +177,22 @@ private function inferParam(Class_ $class, Param $param, PhpDocTagNode $dataProv private function resolveDataProviderClassMethod( Class_ $class, - PhpDocTagNode $dataProviderPhpDocTagNode + Attribute | PhpDocTagNode $dataProviderNode ): ?ClassMethod { - if (! $dataProviderPhpDocTagNode->value instanceof GenericTagValueNode) { + if ($dataProviderNode instanceof Attribute) { + $value = $dataProviderNode->args[0]->value; + + if (! $value instanceof String_) { + return null; + } + + $content = $value->value; + } elseif ($dataProviderNode->value instanceof GenericTagValueNode) { + $content = $dataProviderNode->value->value; + } else { return null; } - $content = $dataProviderPhpDocTagNode->value->value; $match = Strings::match($content, self::METHOD_NAME_REGEX); if ($match === null) { return null; @@ -282,27 +295,48 @@ private function resolveParamOnPositionTypes(Array_ $array, int $parameterPositi return $paramOnPositionTypes; } + private function resolveDataProviderNodes(ClassMethod $classMethod): DataProviderNodes + { + $attributes = $this->getPhpDataProviderAttributes($classMethod); + + $classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNode($classMethod); + + $phpdocNodes = $classMethodPhpDocInfo instanceof PhpDocInfo ? + $classMethodPhpDocInfo->getTagsByName('@dataProvider') : []; + + return new DataProviderNodes([...$attributes, ...$phpdocNodes]); + } + /** - * @return array + * @return array */ - private function resolveDataProviderPhpDocTagNode(ClassMethod $classMethod): array + private function getPhpDataProviderAttributes(ClassMethod $node): array { - $classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNode($classMethod); - if (! $classMethodPhpDocInfo instanceof PhpDocInfo) { - return []; + $attributeName = 'PHPUnit\Framework\Attributes\DataProvider'; + + /** @var AttributeGroup[] $attrGroups */ + $attrGroups = $node->attrGroups; + + $dataProviders = []; + + foreach ($attrGroups as $attrGroup) { + foreach ($attrGroup->attrs as $attribute) { + if (! $this->nodeNameResolver->isName($attribute->name, $attributeName)) { + continue; + } + + $dataProviders[] = $attribute; + } } - return $classMethodPhpDocInfo->getTagsByName('@dataProvider'); + return $dataProviders; } /** - * @param array $dataProviderPhpDocTagNodes + * @param array $dataProviderNodes */ - private function refactorClassMethod( - ClassMethod $classMethod, - Class_ $class, - array $dataProviderPhpDocTagNodes - ): bool { + private function refactorClassMethod(ClassMethod $classMethod, Class_ $class, array $dataProviderNodes): bool + { $hasChanged = false; foreach ($classMethod->getParams() as $param) { @@ -311,8 +345,8 @@ private function refactorClassMethod( } $paramTypes = []; - foreach ($dataProviderPhpDocTagNodes as $dataProviderPhpDocTagNode) { - $paramTypes[] = $this->inferParam($class, $param, $dataProviderPhpDocTagNode); + foreach ($dataProviderNodes as $dataProviderNode) { + $paramTypes[] = $this->inferParam($class, $param, $dataProviderNode); } $paramTypeDeclaration = TypeCombinator::union(...$paramTypes); diff --git a/rules/TypeDeclaration/ValueObject/DataProviderNodes.php b/rules/TypeDeclaration/ValueObject/DataProviderNodes.php new file mode 100644 index 00000000000..6da988eca2d --- /dev/null +++ b/rules/TypeDeclaration/ValueObject/DataProviderNodes.php @@ -0,0 +1,24 @@ + $nodes + */ + public function __construct( + public readonly array $nodes, + ) { + } + + public function isEmpty(): bool + { + return $this->nodes === []; + } +}