diff --git a/config/sets/phpunit-code-quality.php b/config/sets/phpunit-code-quality.php index 9ac2ba9f..007215e5 100644 --- a/config/sets/phpunit-code-quality.php +++ b/config/sets/phpunit-code-quality.php @@ -3,6 +3,7 @@ declare(strict_types=1); use Rector\Config\RectorConfig; +use Rector\PHPUnit\CodeQuality\Rector\Class_\AddParamTypeFromDependsRector; use Rector\PHPUnit\CodeQuality\Rector\Class_\ConstructClassMethodToSetUpTestCaseRector; use Rector\PHPUnit\CodeQuality\Rector\Class_\NarrowUnusedSetUpDefinedPropertyRector; use Rector\PHPUnit\CodeQuality\Rector\Class_\PreferPHPUnitThisCallRector; @@ -70,6 +71,7 @@ // type declarations TypeWillReturnCallableArrowFunctionRector::class, StringCastAssertStringContainsStringRector::class, + AddParamTypeFromDependsRector::class, NarrowUnusedSetUpDefinedPropertyRector::class, diff --git a/rules-tests/CodeQuality/Rector/Class_/AddParamTypeFromDependsRector/AddParamTypeFromDependsRectorTest.php b/rules-tests/CodeQuality/Rector/Class_/AddParamTypeFromDependsRector/AddParamTypeFromDependsRectorTest.php new file mode 100644 index 00000000..c41552a1 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/AddParamTypeFromDependsRector/AddParamTypeFromDependsRectorTest.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/CodeQuality/Rector/Class_/AddParamTypeFromDependsRector/Fixture/depends_with_brackets.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddParamTypeFromDependsRector/Fixture/depends_with_brackets.php.inc new file mode 100644 index 00000000..ad5f5a6a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/AddParamTypeFromDependsRector/Fixture/depends_with_brackets.php.inc @@ -0,0 +1,45 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/AddParamTypeFromDependsRector/Fixture/skip_known_param.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddParamTypeFromDependsRector/Fixture/skip_known_param.php.inc new file mode 100644 index 00000000..9c3f0c2d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/AddParamTypeFromDependsRector/Fixture/skip_known_param.php.inc @@ -0,0 +1,20 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/AddParamTypeFromDependsRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Class_/AddParamTypeFromDependsRector/config/configured_rule.php new file mode 100644 index 00000000..dd722ede --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/AddParamTypeFromDependsRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([AddParamTypeFromDependsRector::class]); diff --git a/rules/CodeQuality/Rector/Class_/AddParamTypeFromDependsRector.php b/rules/CodeQuality/Rector/Class_/AddParamTypeFromDependsRector.php new file mode 100644 index 00000000..e57f8925 --- /dev/null +++ b/rules/CodeQuality/Rector/Class_/AddParamTypeFromDependsRector.php @@ -0,0 +1,153 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->testsNodeAnalyzer->isInTestClass($node)) { + return null; + } + + $hasChanged = false; + + foreach ($node->getMethods() as $classMethod) { + if (! $classMethod->isPublic()) { + continue; + } + + if (count($classMethod->params) !== 1) { + continue; + } + + $soleParam = $classMethod->getParams()[0]; + + // already known type + if ($soleParam->type instanceof Node) { + continue; + } + + $dependsReturnType = $this->resolveReturnTypeOfDependsMethod($classMethod, $node); + if (! $dependsReturnType instanceof Node) { + continue; + } + + $soleParam->type = $dependsReturnType; + $hasChanged = true; + } + + if ($hasChanged === false) { + return null; + } + + return $node; + } + + private function resolveReturnTypeOfDependsMethod(ClassMethod $classMethod, Class_ $class): ?Node + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($classMethod); + if (! $phpDocInfo instanceof PhpDocInfo) { + return null; + } + + $dependsTagValueNode = $phpDocInfo->getByName('depends'); + if (! $dependsTagValueNode instanceof PhpDocTagNode) { + return null; + } + + $dependsMethodName = (string) $dependsTagValueNode->value; + $dependsMethodName = trim($dependsMethodName, '()'); + + $dependsClassMethod = $class->getMethod($dependsMethodName); + + if (! $dependsClassMethod instanceof ClassMethod) { + return null; + } + + // resolve return type here + return $dependsClassMethod->returnType; + } +}