mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-29 06:03:30 +00:00
cleanup type resolving
This commit is contained in:
parent
a8643b7576
commit
ac08e7c320
|
@ -14,10 +14,10 @@ use Rector\NetteToSymfony\Route\RouteInfo;
|
|||
use Rector\NetteToSymfony\Route\RouteInfoFactory;
|
||||
use Rector\NodeContainer\ParsedNodesByType;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
|
||||
use Rector\PhpParser\Node\Manipulator\ClassMethodManipulator;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
use Rector\Util\RectorStrings;
|
||||
use ReflectionMethod;
|
||||
|
||||
|
@ -58,15 +58,15 @@ final class RouterListToControllerAnnotationsRector extends AbstractRector
|
|||
private $routeInfoFactory;
|
||||
|
||||
/**
|
||||
* @var ClassMethodManipulator
|
||||
* @var ReturnTypeInferer
|
||||
*/
|
||||
private $classMethodManipulator;
|
||||
private $returnTypeInferer;
|
||||
|
||||
public function __construct(
|
||||
ParsedNodesByType $parsedNodesByType,
|
||||
ClassMethodManipulator $classMethodManipulator,
|
||||
DocBlockManipulator $docBlockManipulator,
|
||||
RouteInfoFactory $routeInfoFactory,
|
||||
ReturnTypeInferer $returnTypeInferer,
|
||||
string $routeListClass = 'Nette\Application\Routers\RouteList',
|
||||
string $routerClass = 'Nette\Application\IRouter',
|
||||
string $routeAnnotationClass = 'Symfony\Component\Routing\Annotation\Route'
|
||||
|
@ -77,7 +77,7 @@ final class RouterListToControllerAnnotationsRector extends AbstractRector
|
|||
$this->docBlockManipulator = $docBlockManipulator;
|
||||
$this->routeAnnotationClass = $routeAnnotationClass;
|
||||
$this->routeInfoFactory = $routeInfoFactory;
|
||||
$this->classMethodManipulator = $classMethodManipulator;
|
||||
$this->returnTypeInferer = $returnTypeInferer;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
|
@ -153,12 +153,8 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
$nodeReturnTypes = $this->classMethodManipulator->resolveReturnType($node);
|
||||
if ($nodeReturnTypes === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! in_array($this->routeListClass, $nodeReturnTypes, true)) {
|
||||
$inferedReturnTypes = $this->returnTypeInferer->inferFunctionLike($node);
|
||||
if (! in_array($this->routeListClass, $inferedReturnTypes, true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -176,13 +172,7 @@ CODE_SAMPLE
|
|||
continue;
|
||||
}
|
||||
|
||||
$phpDocTagNode = new SymfonyRoutePhpDocTagNode(
|
||||
$this->routeAnnotationClass,
|
||||
$routeInfo->getPath(),
|
||||
null,
|
||||
$routeInfo->getHttpMethods()
|
||||
);
|
||||
|
||||
$phpDocTagNode = $this->createSymfonyRoutePhpDocTagNode($routeInfo);
|
||||
$this->docBlockManipulator->addTag($classMethod, $phpDocTagNode);
|
||||
}
|
||||
|
||||
|
@ -338,4 +328,14 @@ CODE_SAMPLE
|
|||
|
||||
return $presenterPart . '/' . $actionPart;
|
||||
}
|
||||
|
||||
private function createSymfonyRoutePhpDocTagNode(RouteInfo $routeInfo): SymfonyRoutePhpDocTagNode
|
||||
{
|
||||
return new SymfonyRoutePhpDocTagNode(
|
||||
$this->routeAnnotationClass,
|
||||
$routeInfo->getPath(),
|
||||
null,
|
||||
$routeInfo->getHttpMethods()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use Rector\Rector\AbstractRector;
|
|||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer\ReturnTypeDeclarationReturnTypeInferer;
|
||||
|
||||
/**
|
||||
* @sponsor Thanks https://spaceflow.io/ for sponsoring this rule - visit them on https://github.com/SpaceFlow-app
|
||||
|
@ -89,16 +90,22 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
$inferedTypes = $this->returnTypeInferer->inferFunctionLike($node);
|
||||
$inferedTypes = $this->returnTypeInferer->inferFunctionLikeWithExcludedInferers(
|
||||
$node,
|
||||
[ReturnTypeDeclarationReturnTypeInferer::class]
|
||||
);
|
||||
if ($inferedTypes === ['void']) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($inferedTypes !== []) {
|
||||
$docType = implode('|', $inferedTypes);
|
||||
$this->docBlockManipulator->addReturnTag($node, $docType);
|
||||
if ($inferedTypes === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$docType = implode('|', $inferedTypes);
|
||||
|
||||
$this->docBlockManipulator->addReturnTag($node, $docType);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ use Rector\Php\TypeAnalyzer;
|
|||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer\ReturnTypeDeclarationReturnTypeInferer;
|
||||
|
||||
final class ReturnTypeDeclarationRector extends AbstractTypeDeclarationRector
|
||||
{
|
||||
|
@ -94,7 +95,11 @@ CODE_SAMPLE
|
|||
$hasNewType = $node->returnType->getAttribute(self::HAS_NEW_INHERITED_TYPE, false);
|
||||
}
|
||||
|
||||
$inferedTypes = $this->returnTypeInferer->inferFunctionLike($node);
|
||||
$inferedTypes = $this->returnTypeInferer->inferFunctionLikeWithExcludedInferers(
|
||||
$node,
|
||||
[ReturnTypeDeclarationReturnTypeInferer::class]
|
||||
);
|
||||
|
||||
if ($inferedTypes === []) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration;
|
||||
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\NullableType;
|
||||
use Rector\PhpParser\Node\Resolver\NameResolver;
|
||||
|
||||
final class TypeDeclarationToStringConverter
|
||||
{
|
||||
/**
|
||||
* @var NameResolver
|
||||
*/
|
||||
private $nameResolver;
|
||||
|
||||
public function __construct(NameResolver $nameResolver)
|
||||
{
|
||||
$this->nameResolver = $nameResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function resolveFunctionLikeReturnTypeToString(FunctionLike $functionLike): array
|
||||
{
|
||||
if ($functionLike->getReturnType() === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$returnType = $functionLike->getReturnType();
|
||||
|
||||
$types = [];
|
||||
|
||||
$type = $returnType instanceof NullableType ? $returnType->type : $returnType;
|
||||
$result = $this->nameResolver->getName($type);
|
||||
if ($result !== null) {
|
||||
$types[] = $result;
|
||||
}
|
||||
|
||||
if ($returnType instanceof NullableType) {
|
||||
$types[] = 'null';
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
}
|
|
@ -2,21 +2,28 @@
|
|||
|
||||
namespace Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer;
|
||||
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\NullableType;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\PropertyTypeInfererInterface;
|
||||
use Rector\TypeDeclaration\TypeDeclarationToStringConverter;
|
||||
use Rector\TypeDeclaration\TypeInferer\AbstractTypeInferer;
|
||||
|
||||
final class GetterTypeDeclarationPropertyTypeInferer extends AbstractTypeInferer implements PropertyTypeInfererInterface
|
||||
{
|
||||
/**
|
||||
* @var TypeDeclarationToStringConverter
|
||||
*/
|
||||
private $typeDeclarationToStringConverter;
|
||||
|
||||
public function __construct(TypeDeclarationToStringConverter $typeDeclarationToStringConverter)
|
||||
{
|
||||
$this->typeDeclarationToStringConverter = $typeDeclarationToStringConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
|
@ -33,7 +40,7 @@ final class GetterTypeDeclarationPropertyTypeInferer extends AbstractTypeInferer
|
|||
continue;
|
||||
}
|
||||
|
||||
$returnTypes = $this->resolveReturnTypeToString($classMethod);
|
||||
$returnTypes = $this->typeDeclarationToStringConverter->resolveFunctionLikeReturnTypeToString($classMethod);
|
||||
// let PhpDoc solve that later for more precise type
|
||||
if ($returnTypes === ['array']) {
|
||||
return [];
|
||||
|
@ -74,31 +81,4 @@ final class GetterTypeDeclarationPropertyTypeInferer extends AbstractTypeInferer
|
|||
|
||||
return $this->nameResolver->isName($return->expr, $propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Function_|ClassMethod|Closure $functionLike
|
||||
* @return string[]
|
||||
*/
|
||||
private function resolveReturnTypeToString(FunctionLike $functionLike): array
|
||||
{
|
||||
if ($functionLike->getReturnType() === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$returnType = $functionLike->getReturnType();
|
||||
|
||||
$types = [];
|
||||
|
||||
$type = $returnType instanceof NullableType ? $returnType->type : $returnType;
|
||||
$result = $this->nameResolver->getName($type);
|
||||
if ($result !== null) {
|
||||
$types[] = $result;
|
||||
}
|
||||
|
||||
if ($returnType instanceof NullableType) {
|
||||
$types[] = 'null';
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,8 +25,30 @@ final class ReturnTypeInferer extends AbstractPriorityAwareTypeInferer
|
|||
*/
|
||||
public function inferFunctionLike(FunctionLike $functionLike): array
|
||||
{
|
||||
foreach ($this->returnTypeInferers as $returnTypeInferers) {
|
||||
$types = $returnTypeInferers->inferFunctionLike($functionLike);
|
||||
foreach ($this->returnTypeInferers as $returnTypeInferer) {
|
||||
$types = $returnTypeInferer->inferFunctionLike($functionLike);
|
||||
if ($types !== []) {
|
||||
return $types;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $excludedInferers
|
||||
* @return string[]
|
||||
*/
|
||||
public function inferFunctionLikeWithExcludedInferers(FunctionLike $functionLike, array $excludedInferers): array
|
||||
{
|
||||
foreach ($this->returnTypeInferers as $returnTypeInferer) {
|
||||
foreach ($excludedInferers as $excludedInferer) {
|
||||
if (is_a($returnTypeInferer, $excludedInferer, true)) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
$types = $returnTypeInferer->inferFunctionLike($functionLike);
|
||||
if ($types !== []) {
|
||||
return $types;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\ReturnTypeInfererInterface;
|
||||
use Rector\TypeDeclaration\TypeDeclarationToStringConverter;
|
||||
use Rector\TypeDeclaration\TypeInferer\AbstractTypeInferer;
|
||||
|
||||
final class ReturnTypeDeclarationReturnTypeInferer extends AbstractTypeInferer implements ReturnTypeInfererInterface
|
||||
{
|
||||
/**
|
||||
* @var TypeDeclarationToStringConverter
|
||||
*/
|
||||
private $typeDeclarationToStringConverter;
|
||||
|
||||
public function __construct(TypeDeclarationToStringConverter $typeDeclarationToStringConverter)
|
||||
{
|
||||
$this->typeDeclarationToStringConverter = $typeDeclarationToStringConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function inferFunctionLike(FunctionLike $functionLike): array
|
||||
{
|
||||
if ($functionLike->getReturnType() === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// resolve later with more precise type, e.g. Type[]
|
||||
if ($this->nameResolver->isNames($functionLike->getReturnType(), ['array', 'iterable'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->typeDeclarationToStringConverter->resolveFunctionLikeReturnTypeToString($functionLike);
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 2000;
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ final class AddArrayReturnDocTypeRectorTest extends AbstractRectorTestCase
|
|||
__DIR__ . '/Fixture/simple_array.php.inc',
|
||||
__DIR__ . '/Fixture/add_without_return_type_declaration.php.inc',
|
||||
__DIR__ . '/Fixture/fix_incorrect_array.php.inc',
|
||||
// skip
|
||||
// skip
|
||||
__DIR__ . '/Fixture/skip_constructor.php.inc',
|
||||
__DIR__ . '/Fixture/skip_array_after_array_type.php.inc',
|
||||
__DIR__ . '/Fixture/skip_shorten_class_name.php.inc',
|
||||
|
|
|
@ -33,11 +33,6 @@ final class ClassMethodManipulator
|
|||
*/
|
||||
private $nodeTypeResolver;
|
||||
|
||||
/**
|
||||
* @var FunctionLikeManipulator
|
||||
*/
|
||||
private $functionLikeManipulator;
|
||||
|
||||
/**
|
||||
* @var NameResolver
|
||||
*/
|
||||
|
@ -52,14 +47,12 @@ final class ClassMethodManipulator
|
|||
BetterNodeFinder $betterNodeFinder,
|
||||
BetterStandardPrinter $betterStandardPrinter,
|
||||
NodeTypeResolver $nodeTypeResolver,
|
||||
FunctionLikeManipulator $functionLikeManipulator,
|
||||
NameResolver $nameResolver,
|
||||
ValueResolver $valueResolver
|
||||
) {
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
$this->betterStandardPrinter = $betterStandardPrinter;
|
||||
$this->nodeTypeResolver = $nodeTypeResolver;
|
||||
$this->functionLikeManipulator = $functionLikeManipulator;
|
||||
$this->nameResolver = $nameResolver;
|
||||
$this->valueResolver = $valueResolver;
|
||||
}
|
||||
|
@ -121,33 +114,6 @@ final class ClassMethodManipulator
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function resolveReturnType(ClassMethod $classMethod): array
|
||||
{
|
||||
if ($classMethod->returnType !== null) {
|
||||
return $this->nodeTypeResolver->resolve($classMethod->returnType);
|
||||
}
|
||||
|
||||
$staticReturnType = $this->functionLikeManipulator->resolveStaticReturnTypeInfo($classMethod);
|
||||
if ($staticReturnType === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$getFqnTypeNode = $staticReturnType->getFqnTypeNode();
|
||||
if ($getFqnTypeNode === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$fqnTypeName = $this->nameResolver->getName($getFqnTypeNode);
|
||||
if ($fqnTypeName === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [$fqnTypeName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Is method actually static, or has some $this-> calls?
|
||||
*/
|
||||
|
|
|
@ -2,62 +2,20 @@
|
|||
|
||||
namespace Rector\PhpParser\Node\Manipulator;
|
||||
|
||||
use Iterator;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ArrowFunction;
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\Expr\Yield_;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\NullableType;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PhpParser\NodeTraverser;
|
||||
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;
|
||||
use Rector\PhpParser\NodeTraverser\CallableNodeTraverser;
|
||||
use Rector\TypeDeclaration\Rector\FunctionLike\AbstractTypeDeclarationRector;
|
||||
|
||||
final class FunctionLikeManipulator
|
||||
{
|
||||
/**
|
||||
* @var BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
|
||||
/**
|
||||
* @var TypeAnalyzer
|
||||
*/
|
||||
private $typeAnalyzer;
|
||||
|
||||
/**
|
||||
* @var NodeTypeResolver
|
||||
*/
|
||||
private $nodeTypeResolver;
|
||||
|
||||
/**
|
||||
* @var NameResolver
|
||||
*/
|
||||
private $nameResolver;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isVoid = false;
|
||||
|
||||
/**
|
||||
* @var PhpVersionProvider
|
||||
*/
|
||||
private $phpVersionProvider;
|
||||
|
||||
/**
|
||||
* @var CallableNodeTraverser
|
||||
*/
|
||||
|
@ -69,70 +27,15 @@ final class FunctionLikeManipulator
|
|||
private $propertyFetchManipulator;
|
||||
|
||||
public function __construct(
|
||||
BetterNodeFinder $betterNodeFinder,
|
||||
TypeAnalyzer $typeAnalyzer,
|
||||
NodeTypeResolver $nodeTypeResolver,
|
||||
NameResolver $nameResolver,
|
||||
PhpVersionProvider $phpVersionProvider,
|
||||
CallableNodeTraverser $callableNodeTraverser,
|
||||
PropertyFetchManipulator $propertyFetchManipulator
|
||||
) {
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
$this->typeAnalyzer = $typeAnalyzer;
|
||||
$this->nodeTypeResolver = $nodeTypeResolver;
|
||||
$this->nameResolver = $nameResolver;
|
||||
$this->phpVersionProvider = $phpVersionProvider;
|
||||
$this->callableNodeTraverser = $callableNodeTraverser;
|
||||
$this->propertyFetchManipulator = $propertyFetchManipulator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on static analysis of code, looking for return types
|
||||
* @param ClassMethod|Function_|Closure $functionLike
|
||||
*/
|
||||
public function resolveStaticReturnTypeInfo(FunctionLike $functionLike): ?ReturnTypeInfo
|
||||
{
|
||||
if ($this->shouldSkip($functionLike)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// @todo decouple to return type inferer B-)
|
||||
|
||||
// A. resolve from function return type
|
||||
// skip "array" to get more precise types
|
||||
if ($functionLike->returnType !== null && ! $this->nameResolver->isNames(
|
||||
$functionLike->returnType,
|
||||
['array', 'iterable']
|
||||
)) {
|
||||
$types = $this->resolveReturnTypeToString($functionLike->returnType);
|
||||
|
||||
// do not override freshly added type declaration
|
||||
if (! $functionLike->returnType->getAttribute(
|
||||
AbstractTypeDeclarationRector::HAS_NEW_INHERITED_TYPE
|
||||
) && $types !== []) {
|
||||
return new ReturnTypeInfo($types, $this->typeAnalyzer, $types);
|
||||
}
|
||||
}
|
||||
|
||||
$this->isVoid = true;
|
||||
|
||||
// B. resolve from return $x nodes
|
||||
$types = $this->resolveTypesFromReturnNodes($functionLike);
|
||||
|
||||
// C. resolve from yields
|
||||
if ($types === []) {
|
||||
$types = $this->resolveFromYieldNodes($functionLike);
|
||||
}
|
||||
|
||||
if ($this->isVoid) {
|
||||
return new ReturnTypeInfo(['void'], $this->typeAnalyzer, ['void']);
|
||||
}
|
||||
|
||||
$types = array_filter($types);
|
||||
|
||||
return new ReturnTypeInfo($types, $this->typeAnalyzer, $types);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
|
@ -165,118 +68,4 @@ final class FunctionLikeManipulator
|
|||
|
||||
return $returnedLocalPropertyNames;
|
||||
}
|
||||
|
||||
private function shouldSkip(FunctionLike $functionLike): bool
|
||||
{
|
||||
if (! $functionLike instanceof ClassMethod) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$classNode = $functionLike->getAttribute(AttributeKey::CLASS_NODE);
|
||||
// only class or trait method body can be analyzed for returns
|
||||
if ($classNode instanceof Interface_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// only methods that are not abstract can be analyzed for returns
|
||||
return $functionLike->isAbstract();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Identifier|Name|NullableType $node
|
||||
* @return string[]
|
||||
*/
|
||||
private function resolveReturnTypeToString(Node $node): array
|
||||
{
|
||||
$types = [];
|
||||
|
||||
$type = $node instanceof NullableType ? $node->type : $node;
|
||||
$result = $this->nameResolver->getName($type);
|
||||
if ($result !== null) {
|
||||
$types[] = $result;
|
||||
}
|
||||
|
||||
if ($node instanceof NullableType) {
|
||||
$types[] = 'null';
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covered
|
||||
* @param ClassMethod|Function_|Closure $functionLike
|
||||
* @return string[]
|
||||
*/
|
||||
private function resolveTypesFromReturnNodes(FunctionLike $functionLike): array
|
||||
{
|
||||
// local
|
||||
/** @var Return_[] $localReturnNodes */
|
||||
$localReturnNodes = [];
|
||||
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable((array) $functionLike->stmts, function (Node $node) use (
|
||||
&$localReturnNodes
|
||||
): ?int {
|
||||
if ($node instanceof Function_ || $node instanceof Closure || $node instanceof ArrowFunction) {
|
||||
// skip Return_ nodes in nested functions
|
||||
return NodeTraverser::DONT_TRAVERSE_CHILDREN;
|
||||
}
|
||||
|
||||
if (! $node instanceof Return_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// skip void returns
|
||||
if ($node->expr === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$localReturnNodes[] = $node;
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
$types = [];
|
||||
foreach ($localReturnNodes as $localReturnNode) {
|
||||
$types = array_merge($types, $this->nodeTypeResolver->resolveSingleTypeToStrings($localReturnNode->expr));
|
||||
$this->isVoid = false;
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod|Function_|Closure $functionLike
|
||||
* @return string[]
|
||||
*/
|
||||
private function resolveFromYieldNodes(FunctionLike $functionLike): array
|
||||
{
|
||||
/** @var Yield_[] $yieldNodes */
|
||||
$yieldNodes = $this->betterNodeFinder->findInstanceOf((array) $functionLike->stmts, Yield_::class);
|
||||
|
||||
$types = [];
|
||||
if (count($yieldNodes)) {
|
||||
$this->isVoid = false;
|
||||
|
||||
foreach ($yieldNodes as $yieldNode) {
|
||||
if ($yieldNode->value === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$resolvedTypes = $this->nodeTypeResolver->resolveSingleTypeToStrings($yieldNode->value);
|
||||
foreach ($resolvedTypes as $resolvedType) {
|
||||
$types[] = $resolvedType . '[]';
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->phpVersionProvider->isAtLeast('7.1')) {
|
||||
// @see https://www.php.net/manual/en/language.types.iterable.php
|
||||
$types[] = 'iterable';
|
||||
} else {
|
||||
$types[] = Iterator::class;
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($types);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user