From dee6271e7d203edcec651642e2e6796152b57a11 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Fri, 2 Jan 2026 20:18:35 +0100 Subject: [PATCH] [tdd] Add AddReturnDocblockFromMethodCallDocblockRector --- ...cblockFromMethodCallDocblockRectorTest.php | 28 ++++ .../skip_missing_array_declaration.php.inc | 22 +++ .../skip_missing_array_on_call.php.inc | 22 +++ .../Fixture/some_class.php.inc | 52 +++++++ .../Source/SomeEntity.php | 8 + .../Source/SomeRepository.php | 24 +++ .../config/configured_rule.php | 9 ++ ...rnDocblockFromMethodCallDocblockRector.php | 147 ++++++++++++++++++ .../Level/TypeDeclarationDocblocksLevel.php | 3 + 9 files changed, 315 insertions(+) create mode 100644 rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/AddReturnDocblockFromMethodCallDocblockRectorTest.php create mode 100644 rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_missing_array_declaration.php.inc create mode 100644 rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_missing_array_on_call.php.inc create mode 100644 rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/some_class.php.inc create mode 100644 rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Source/SomeEntity.php create mode 100644 rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Source/SomeRepository.php create mode 100644 rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/config/configured_rule.php create mode 100644 rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector.php diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/AddReturnDocblockFromMethodCallDocblockRectorTest.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/AddReturnDocblockFromMethodCallDocblockRectorTest.php new file mode 100644 index 00000000000..c57fcf989d0 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/AddReturnDocblockFromMethodCallDocblockRectorTest.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/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_missing_array_declaration.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_missing_array_declaration.php.inc new file mode 100644 index 00000000000..87710eeb5be --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_missing_array_declaration.php.inc @@ -0,0 +1,22 @@ +someRepository = $someRepository; + } + + public function getAll() + { + return $this->someRepository->findAll(); + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_missing_array_on_call.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_missing_array_on_call.php.inc new file mode 100644 index 00000000000..f3523603717 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/skip_missing_array_on_call.php.inc @@ -0,0 +1,22 @@ +someRepository = $someRepository; + } + + public function getAll(): array + { + return $this->someRepository->findAllWithoutArray(); + } +} diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/some_class.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..564c0ecae1f --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Fixture/some_class.php.inc @@ -0,0 +1,52 @@ +someRepository = $someRepository; + } + + public function getAll(): array + { + return $this->someRepository->findAll(); + } +} + +?> +----- +someRepository = $someRepository; + } + + /** + * @return SomeEntity[] + */ + public function getAll(): array + { + return $this->someRepository->findAll(); + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Source/SomeEntity.php b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Source/SomeEntity.php new file mode 100644 index 00000000000..175c7266b1f --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector/Source/SomeEntity.php @@ -0,0 +1,8 @@ +withRules([AddReturnDocblockFromMethodCallDocblockRector::class]); diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector.php new file mode 100644 index 00000000000..460cf7f53ce --- /dev/null +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockFromMethodCallDocblockRector.php @@ -0,0 +1,147 @@ +repository->findAll(); + } +} + +final class Repository +{ + /** + * @return SomeEntity[] + */ + public function findAll(): array + { + // ... + } +} +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +final class SomeController +{ + /** + * @return SomeEntity[] + */ + public function getAll(): array + { + return $this->repository->findAll(); + } +} + +final class Repository +{ + /** + * @return SomeEntity[] + */ + public function findAll(): array + { + // ... + } +} +CODE_SAMPLE + ), + + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + if ($this->usefulArrayTagNodeAnalyzer->isUsefulArrayTag($phpDocInfo->getReturnTagValue())) { + return null; + } + + // definitely not an array return + if (! $node->returnType instanceof Node || ! $this->isName($node->returnType, 'array')) { + return null; + } + + $onlyReturnWithExpr = $this->returnNodeFinder->findOnlyReturnWithExpr($node); + if (! $onlyReturnWithExpr instanceof Return_ || ! $onlyReturnWithExpr->expr instanceof MethodCall) { + return null; + } + + $returnedMethodCall = $onlyReturnWithExpr->expr; + + $calledClassMethod = $this->astResolver->resolveClassMethodFromCall($returnedMethodCall); + if (! $calledClassMethod instanceof ClassMethod) { + return null; + } + + if (! $calledClassMethod->returnType instanceof Identifier) { + return null; + } + + if (! $this->isName($calledClassMethod->returnType, 'array')) { + return null; + } + + $calledClassMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($calledClassMethod); + + $calledReturnTagValue = $calledClassMethodPhpDocInfo->getReturnTagValue(); + if (! $calledReturnTagValue instanceof ReturnTagValueNode) { + return null; + } + + $this->phpDocTypeChanger->changeReturnTypeNode($node, $phpDocInfo, $calledReturnTagValue->type); + + return $node; + } +} diff --git a/src/Config/Level/TypeDeclarationDocblocksLevel.php b/src/Config/Level/TypeDeclarationDocblocksLevel.php index bd1d0bbee59..21bb1f0e094 100644 --- a/src/Config/Level/TypeDeclarationDocblocksLevel.php +++ b/src/Config/Level/TypeDeclarationDocblocksLevel.php @@ -21,6 +21,7 @@ use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockForCommonObjectDenominatorRector; use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockForDimFetchArrayFromAssignsRector; use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockForJsonArrayRector; +use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector; use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\DocblockGetterReturnArrayFromPropertyDocblockVarRector; use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\DocblockReturnArrayFromDirectArrayInstanceRector; @@ -63,5 +64,7 @@ final class TypeDeclarationDocblocksLevel // run latter after other rules, as more generic AddReturnDocblockForDimFetchArrayFromAssignsRector::class, + + AddReturnDocblockFromMethodCallDocblockRector::class, ]; }