remove ReturnTypeResolver

This commit is contained in:
Tomas Votruba 2019-09-02 11:07:18 +02:00
parent 8b6331bc01
commit 87e4c5e0a8
8 changed files with 199 additions and 122 deletions

View File

@ -6,21 +6,29 @@ use PhpParser\Node;
use PhpParser\Node\Expr\Closure;
use PHPStan\Analyser\Scope;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpParser\Node\Manipulator\FunctionLikeManipulator;
use Rector\NodeTypeResolver\Php\ReturnTypeInfo;
use Rector\Php\TypeAnalyzer;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
final class AddClosureReturnTypeRector extends AbstractRector
{
/**
* @var FunctionLikeManipulator
* @var ReturnTypeInferer
*/
private $functionLikeManipulator;
private $returnTypeInferer;
public function __construct(FunctionLikeManipulator $functionLikeManipulator)
/**
* @var TypeAnalyzer
*/
private $typeAnalyzer;
public function __construct(ReturnTypeInferer $returnTypeInferer, TypeAnalyzer $typeAnalyzer)
{
$this->functionLikeManipulator = $functionLikeManipulator;
$this->returnTypeInferer = $returnTypeInferer;
$this->typeAnalyzer = $typeAnalyzer;
}
public function getDefinition(): RectorDefinition
@ -81,12 +89,10 @@ CODE_SAMPLE
return null;
}
$staticReturnType = $this->functionLikeManipulator->resolveStaticReturnTypeInfo($node);
if ($staticReturnType === null) {
return null;
}
$inferedReturnTypes = $this->returnTypeInferer->inferFunctionLike($node);
$returnTypeInfo = new ReturnTypeInfo($inferedReturnTypes, $this->typeAnalyzer, $inferedReturnTypes);
$returnTypeNode = $staticReturnType->getFqnTypeNode();
$returnTypeNode = $returnTypeInfo->getFqnTypeNode();
if ($returnTypeNode === null) {
return null;
}

View File

@ -1,51 +0,0 @@
<?php declare(strict_types=1);
namespace Rector\TypeDeclaration\ReturnTypeResolver;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use Rector\NodeTypeResolver\Php\ReturnTypeInfo;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use Rector\PhpParser\Node\Manipulator\FunctionLikeManipulator;
final class ReturnTypeResolver
{
/**
* @var DocBlockManipulator
*/
private $docBlockManipulator;
/**
* @var FunctionLikeManipulator
*/
private $functionLikeManipulator;
public function __construct(
DocBlockManipulator $docBlockManipulator,
FunctionLikeManipulator $functionLikeManipulator
) {
$this->docBlockManipulator = $docBlockManipulator;
$this->functionLikeManipulator = $functionLikeManipulator;
}
/**
* @param ClassMethod|Function_ $functionLike
*/
public function resolveFunctionLikeReturnType(FunctionLike $functionLike): ?ReturnTypeInfo
{
$docReturnTypeInfo = $this->docBlockManipulator->getReturnTypeInfo($functionLike);
$codeReturnTypeInfo = $this->functionLikeManipulator->resolveStaticReturnTypeInfo($functionLike);
// code has priority over docblock
if ($docReturnTypeInfo === null) {
return $codeReturnTypeInfo;
}
if ($codeReturnTypeInfo && $codeReturnTypeInfo->getTypeNode()) {
return $codeReturnTypeInfo;
}
return $docReturnTypeInfo;
}
}

View File

@ -44,6 +44,6 @@ final class AllAssignNodePropertyTypeInferer extends AbstractTypeInferer impleme
public function getPriority(): int
{
return 500;
return 610;
}
}

View File

@ -0,0 +1,150 @@
<?php declare(strict_types=1);
namespace Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer;
use PhpParser\Node;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
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\TypeInferer\AbstractTypeInferer;
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer\ReturnedNodesReturnTypeInferer;
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer\ReturnTagReturnTypeInferer;
final class GetterPropertyTypeInferer extends AbstractTypeInferer implements PropertyTypeInfererInterface
{
/**
* @var ReturnedNodesReturnTypeInferer
*/
private $returnedNodesReturnTypeInferer;
/**
* @var ReturnTagReturnTypeInferer
*/
private $returnTagReturnTypeInferer;
public function __construct(
ReturnedNodesReturnTypeInferer $returnedNodesReturnTypeInferer,
ReturnTagReturnTypeInferer $returnTagReturnTypeInferer
) {
$this->returnedNodesReturnTypeInferer = $returnedNodesReturnTypeInferer;
$this->returnTagReturnTypeInferer = $returnTagReturnTypeInferer;
}
/**
* @return string[]
*/
public function inferProperty(Property $property): array
{
/** @var Class_ $class */
$class = $property->getAttribute(AttributeKey::CLASS_NODE);
/** @var string $propertyName */
$propertyName = $this->nameResolver->getName($property);
foreach ($class->getMethods() as $classMethod) {
if (! $this->hasClassMethodOnlyStatementReturnOfPropertyFetch($classMethod, $propertyName)) {
continue;
}
$returnTypes = $this->inferClassMethodReturnTypes($classMethod);
if ($returnTypes !== []) {
return $returnTypes;
}
}
return [];
}
public function getPriority(): int
{
return 600;
}
private function hasClassMethodOnlyStatementReturnOfPropertyFetch(
ClassMethod $classMethod,
string $propertyName
): bool {
if (count((array) $classMethod->stmts) !== 1) {
return false;
}
$onlyClassMethodStmt = $classMethod->stmts[0];
if (! $onlyClassMethodStmt instanceof Return_) {
return false;
}
/** @var Return_ $return */
$return = $onlyClassMethodStmt;
if (! $return->expr instanceof PropertyFetch) {
return false;
}
return $this->nameResolver->isName($return->expr, $propertyName);
}
/**
* Intentionally local method
* @todo possible move to ReturnTypeInferer, but allow to disable/enable in case of override returnType (99 %)
*
* @param ClassMethod|Function_|Closure $functionLike
* @return string[]
*/
private function resolveFunctionLikeReturnTypeDeclaration(FunctionLike $functionLike): array
{
if ($functionLike->returnType === null) {
return [];
}
return $this->resolveReturnTypeToString($functionLike->returnType);
}
/**
* @return string[]
*/
private function inferClassMethodReturnTypes(ClassMethod $classMethod): array
{
$returnTypeDeclarationTypes = $this->resolveFunctionLikeReturnTypeDeclaration($classMethod);
if ($returnTypeDeclarationTypes) {
return $returnTypeDeclarationTypes;
}
$inferedTypes = $this->returnedNodesReturnTypeInferer->inferFunctionLike($classMethod);
if ($inferedTypes) {
return $inferedTypes;
}
return $this->returnTagReturnTypeInferer->inferFunctionLike($classMethod);
}
/**
* @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;
}
}

View File

@ -2,28 +2,21 @@
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\ReturnTypeResolver\ReturnTypeResolver;
use Rector\TypeDeclaration\TypeInferer\AbstractTypeInferer;
final class GetterOrSetterPropertyTypeInferer extends AbstractTypeInferer implements PropertyTypeInfererInterface
final class GetterTypeDeclarationPropertyTypeInferer extends AbstractTypeInferer implements PropertyTypeInfererInterface
{
/**
* @var ReturnTypeResolver
*/
private $returnTypeResolver;
public function __construct(ReturnTypeResolver $returnTypeResolver)
{
$this->returnTypeResolver = $returnTypeResolver;
}
/**
* @return string[]
*/
@ -40,7 +33,12 @@ final class GetterOrSetterPropertyTypeInferer extends AbstractTypeInferer implem
continue;
}
$returnTypes = $this->resolveClassMethodReturnTypes($classMethod);
$returnTypes = $this->resolveReturnTypeToString($classMethod);
// let PhpDoc solve that later for more precise type
if ($returnTypes === ['array']) {
return [];
}
if ($returnTypes !== []) {
return $returnTypes;
}
@ -51,7 +49,7 @@ final class GetterOrSetterPropertyTypeInferer extends AbstractTypeInferer implem
public function getPriority(): int
{
return 600;
return 630;
}
private function hasClassMethodOnlyStatementReturnOfPropertyFetch(
@ -78,15 +76,29 @@ final class GetterOrSetterPropertyTypeInferer extends AbstractTypeInferer implem
}
/**
* @param Function_|ClassMethod|Closure $functionLike
* @return string[]
*/
private function resolveClassMethodReturnTypes(ClassMethod $classMethod): array
private function resolveReturnTypeToString(FunctionLike $functionLike): array
{
$returnType = $this->returnTypeResolver->resolveFunctionLikeReturnType($classMethod);
if ($returnType === null) {
if ($functionLike->getReturnType() === null) {
return [];
}
return $returnType->getDocTypes();
$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;
}
}

View File

@ -1,40 +0,0 @@
<?php declare(strict_types=1);
namespace Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use Rector\PhpParser\Node\Manipulator\FunctionLikeManipulator;
use Rector\TypeDeclaration\Contract\TypeInferer\ReturnTypeInfererInterface;
use Rector\TypeDeclaration\TypeInferer\AbstractTypeInferer;
final class ReturnedNodeReturnTypeInferer extends AbstractTypeInferer implements ReturnTypeInfererInterface
{
/**
* @var FunctionLikeManipulator
*/
private $functionLikeManipulator;
public function __construct(FunctionLikeManipulator $functionLikeManipulator)
{
$this->functionLikeManipulator = $functionLikeManipulator;
}
/**
* @param ClassMethod|Closure|Function_ $functionLike
* @return string[]
*/
public function inferFunctionLike(FunctionLike $functionLike): array
{
$resolvedReturnTypeInfo = $this->functionLikeManipulator->resolveStaticReturnTypeInfo($functionLike);
return $resolvedReturnTypeInfo ? $resolvedReturnTypeInfo->getDocTypes() : [];
}
public function getPriority(): int
{
return 500;
}
}

View File

@ -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',

View File

@ -26,7 +26,7 @@ final class PropertyTypeDeclarationRectorTest extends AbstractRectorTestCase
__DIR__ . '/Fixture/single_nullable_return.php.inc',
__DIR__ . '/Fixture/getter_type.php.inc',
__DIR__ . '/Fixture/setter_type.php.inc',
// skip
// skip
__DIR__ . '/Fixture/skip_multi_vars.php.inc',
]);
}