add iterable return type for yield values in ReturnTypeDeclarationRector

This commit is contained in:
Tomas Votruba 2019-08-04 13:05:40 +02:00
parent 8b6eb0981c
commit 9410c0039e
3 changed files with 68 additions and 15 deletions

View File

@ -105,7 +105,6 @@ CODE_SAMPLE
// use
$returnTypeInfo = $this->functionLikeManipulator->resolveStaticReturnTypeInfo($node);
if ($returnTypeInfo === null) {
return null;
}

View File

@ -190,3 +190,4 @@ parameters:
# array is callable
- '#Parameter \#2 \$listener of method Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher\:\:getListenerPriority\(\) expects callable\(\)\: mixed, array given#'
- '#Parameter \#1 \$kernelClass of method Rector\\Symfony\\Bridge\\DependencyInjection\\SymfonyContainerFactory\:\:createFromKernelClass\(\) expects string, string\|null given#'
- '#If condition is always true#'

View File

@ -2,6 +2,7 @@
namespace Rector\PhpParser\Node\Manipulator;
use Iterator;
use PhpParser\Node;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\FunctionLike;
@ -15,6 +16,7 @@ use PhpParser\Node\Stmt\Return_;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\NodeTypeResolver\Php\ReturnTypeInfo;
use Rector\Php\PhpVersionProvider;
use Rector\Php\TypeAnalyzer;
use Rector\PhpParser\Node\BetterNodeFinder;
use Rector\PhpParser\Node\Resolver\NameResolver;
@ -42,16 +44,28 @@ final class FunctionLikeManipulator
*/
private $nameResolver;
/**
* @var bool
*/
private $isVoid = false;
/**
* @var PhpVersionProvider
*/
private $phpVersionProvider;
public function __construct(
BetterNodeFinder $betterNodeFinder,
TypeAnalyzer $typeAnalyzer,
NodeTypeResolver $nodeTypeResolver,
NameResolver $nameResolver
NameResolver $nameResolver,
PhpVersionProvider $phpVersionProvider
) {
$this->betterNodeFinder = $betterNodeFinder;
$this->typeAnalyzer = $typeAnalyzer;
$this->nodeTypeResolver = $nodeTypeResolver;
$this->nameResolver = $nameResolver;
$this->phpVersionProvider = $phpVersionProvider;
}
/**
@ -76,23 +90,17 @@ final class FunctionLikeManipulator
}
}
$this->isVoid = true;
// B. resolve from return $x nodes
/** @var Return_[] $returnNodes */
$returnNodes = $this->betterNodeFinder->findInstanceOf((array) $functionLike->stmts, Return_::class);
$types = $this->resolveTypesFromReturnNodes($functionLike);
$isVoid = true;
$types = [];
foreach ($returnNodes as $returnNode) {
if ($returnNode->expr === null) {
continue;
}
$types = array_merge($types, $this->nodeTypeResolver->resolveSingleTypeToStrings($returnNode->expr));
$isVoid = false;
// C. resolve from yields
if ($types === []) {
$types = $this->resolveFromYieldNodes($functionLike);
}
if ($isVoid) {
if ($this->isVoid) {
return new ReturnTypeInfo(['void'], $this->typeAnalyzer);
}
@ -137,4 +145,49 @@ final class FunctionLikeManipulator
return $types;
}
/**
* @param ClassMethod|Function_|Closure $functionLike
* @return string[]
*/
private function resolveTypesFromReturnNodes(FunctionLike $functionLike): array
{
/** @var Return_[] $returnNodes */
$returnNodes = $this->betterNodeFinder->findInstanceOf((array) $functionLike->stmts, Return_::class);
$types = [];
foreach ($returnNodes as $returnNode) {
if ($returnNode->expr === null) {
continue;
}
$types = array_merge($types, $this->nodeTypeResolver->resolveSingleTypeToStrings($returnNode->expr));
$this->isVoid = false;
}
return $types;
}
/**
* @param ClassMethod|Function_|Closure $functionLike
* @return string[]
*/
private function resolveFromYieldNodes(FunctionLike $functionLike): array
{
/** @var Node\Expr\Yield_[] $yieldNodes */
$yieldNodes = $this->betterNodeFinder->findInstanceOf((array) $functionLike->stmts, Node\Expr\Yield_::class);
if (count($yieldNodes)) {
$this->isVoid = false;
if ($this->phpVersionProvider->isAtLeast('7.1')) {
// @see https://www.php.net/manual/en/language.types.iterable.php
return ['iterable'];
}
return [Iterator::class];
}
return [];
}
}