Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector;

use Iterator;
use PHPUnit\Framework\Attributes\DataProvider;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class AddReturnDocblockFromMethodCallDocblockRectorTest extends AbstractRectorTestCase
{
#[DataProvider('provideData')]
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

public static function provideData(): Iterator
{
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Fixture;

use Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Source\SomeRepository;

final class SkipMissingArrayDeclaration
{
private SomeRepository $someRepository;

public function __construct(SomeRepository $someRepository)
{
$this->someRepository = $someRepository;
}

public function getAll()
{
return $this->someRepository->findAll();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Fixture;

use Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Source\SomeRepository;

final class SkipMissingArrayOnCall
{
private SomeRepository $someRepository;

public function __construct(SomeRepository $someRepository)
{
$this->someRepository = $someRepository;
}

public function getAll(): array
{
return $this->someRepository->findAllWithoutArray();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Fixture;

use Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Source\SomeRepository;

final class SomeClass
{
private SomeRepository $someRepository;

public function __construct(SomeRepository $someRepository)
{
$this->someRepository = $someRepository;
}

public function getAll(): array
{
return $this->someRepository->findAll();
}
}

?>
-----
<?php

declare(strict_types=1);

namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Fixture;

use Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Source\SomeRepository;

final class SomeClass
{
private SomeRepository $someRepository;

public function __construct(SomeRepository $someRepository)
{
$this->someRepository = $someRepository;
}

/**
* @return SomeEntity[]
*/
public function getAll(): array
{
return $this->someRepository->findAll();
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Source;

final class SomeEntity
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\Source;

final class SomeRepository
{
/**
* @return SomeEntity[]
*/
public function findAll(): array
{
return [];
}

/**
* @return SomeEntity[]
*/
public function findAllWithoutArray()
{
return [];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector;

return RectorConfig::configure()
->withRules([AddReturnDocblockFromMethodCallDocblockRector::class]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<?php

declare(strict_types=1);

namespace Rector\TypeDeclarationDocblocks\Rector\ClassMethod;

use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Return_;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\PhpParser\AstResolver;
use Rector\Rector\AbstractRector;
use Rector\TypeDeclarationDocblocks\NodeFinder\ReturnNodeFinder;
use Rector\TypeDeclarationDocblocks\TagNodeAnalyzer\UsefulArrayTagNodeAnalyzer;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* @see \Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockFromMethodCallDocblockRector\AddReturnDocblockFromMethodCallDocblockRectorTest
*/
final class AddReturnDocblockFromMethodCallDocblockRector extends AbstractRector
{
public function __construct(
private readonly PhpDocInfoFactory $phpDocInfoFactory,
private readonly ReturnNodeFinder $returnNodeFinder,
private readonly UsefulArrayTagNodeAnalyzer $usefulArrayTagNodeAnalyzer,
private readonly AstResolver $astResolver,
private readonly PhpDocTypeChanger $phpDocTypeChanger,
) {
}

public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Add @return docblock based on detailed type of method call docblock',
[
new CodeSample(
<<<'CODE_SAMPLE'
final class SomeController
{
public function getAll(): array
{
return $this->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<class-string<Node>>
*/
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;
}
}
3 changes: 3 additions & 0 deletions src/Config/Level/TypeDeclarationDocblocksLevel.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -63,5 +64,7 @@ final class TypeDeclarationDocblocksLevel

// run latter after other rules, as more generic
AddReturnDocblockForDimFetchArrayFromAssignsRector::class,

AddReturnDocblockFromMethodCallDocblockRector::class,
];
}