From fcc91091b0a4a3925566c508ce93ca35e42a65b8 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 27 Oct 2025 21:18:10 +0100 Subject: [PATCH] [type-declaration] Add ReturnIteratorInDataProviderRector --- .../Fixture/skip_already_known.php.inc | 21 +++ .../Fixture/skip_non_data_provider.php.inc | 18 +++ .../Fixture/some_class.php.inc | 47 +++++++ ...ReturnIteratorInDataProviderRectorTest.php | 28 ++++ .../config/configured_rule.php | 10 ++ .../ReturnIteratorInDataProviderRector.php | 123 ++++++++++++++++++ src/Config/Level/TypeDeclarationLevel.php | 2 + 7 files changed, 249 insertions(+) create mode 100644 rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/Fixture/skip_already_known.php.inc create mode 100644 rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/Fixture/skip_non_data_provider.php.inc create mode 100644 rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/Fixture/some_class.php.inc create mode 100644 rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/ReturnIteratorInDataProviderRectorTest.php create mode 100644 rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/config/configured_rule.php create mode 100644 rules/CodeQuality/Rector/Class_/ReturnIteratorInDataProviderRector.php diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/Fixture/skip_already_known.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/Fixture/skip_already_known.php.inc new file mode 100644 index 00000000000..d838c90fc8e --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/Fixture/skip_already_known.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/ReturnIteratorInDataProviderRectorTest.php b/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/ReturnIteratorInDataProviderRectorTest.php new file mode 100644 index 00000000000..484cf523761 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/ReturnIteratorInDataProviderRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/config/configured_rule.php new file mode 100644 index 00000000000..81ec5e6cac8 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Class_/ReturnIteratorInDataProviderRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(ReturnIteratorInDataProviderRector::class); +}; diff --git a/rules/CodeQuality/Rector/Class_/ReturnIteratorInDataProviderRector.php b/rules/CodeQuality/Rector/Class_/ReturnIteratorInDataProviderRector.php new file mode 100644 index 00000000000..2ecba009a54 --- /dev/null +++ b/rules/CodeQuality/Rector/Class_/ReturnIteratorInDataProviderRector.php @@ -0,0 +1,123 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->testsNodeAnalyzer->isInTestClass($node)) { + return null; + } + + $dataProviderClassMethods = $this->dataProviderMethodsFinder->findDataProviderNodesInClass($node); + + $hasChanged = false; + + foreach ($dataProviderClassMethods as $dataProviderClassMethod) { + if ($dataProviderClassMethod->returnType instanceof Node) { + continue; + } + + if (! $this->hasYields($dataProviderClassMethod)) { + continue; + } + + $dataProviderClassMethod->returnType = new FullyQualified('Iterator'); + $hasChanged = true; + } + + if ($hasChanged) { + return $node; + } + + return null; + } + + private function hasYields(ClassMethod $classMethod): bool + { + $yields = $this->betterNodeFinder->findInstancesOfScoped((array) $classMethod->stmts, Yield_::class); + + return $yields !== []; + } +} diff --git a/src/Config/Level/TypeDeclarationLevel.php b/src/Config/Level/TypeDeclarationLevel.php index 86d9818b931..5a521ba7f7c 100644 --- a/src/Config/Level/TypeDeclarationLevel.php +++ b/src/Config/Level/TypeDeclarationLevel.php @@ -4,6 +4,7 @@ namespace Rector\Config\Level; +use Rector\CodeQuality\Rector\Class_\ReturnIteratorInDataProviderRector; use Rector\Contract\Rector\RectorInterface; use Rector\Symfony\CodeQuality\Rector\ClassMethod\ResponseReturnTypeControllerActionRector; use Rector\TypeDeclaration\Rector\ArrowFunction\AddArrowFunctionReturnTypeRector; @@ -78,6 +79,7 @@ final class TypeDeclarationLevel AddClosureVoidReturnTypeWhereNoReturnRector::class, AddFunctionVoidReturnTypeWhereNoReturnRector::class, AddTestsVoidReturnTypeWhereNoReturnRector::class, + ReturnIteratorInDataProviderRector::class, ReturnTypeFromMockObjectRector::class, TypedPropertyFromCreateMockAssignRector::class,