mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-30 06:33:31 +00:00
Merge pull request #1939 from rectorphp/correct-types
Add ReturnedNodesReturnTypeInferer + big *TypeDeclarationRector refactoring
This commit is contained in:
commit
a8643b7576
|
@ -10,7 +10,12 @@ use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInte
|
|||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
|
||||
use Rector\PhpParser\Node\Resolver\NameResolver;
|
||||
use Rector\TypeDeclaration\TypeInferer\ParamTypeInferer;
|
||||
|
||||
/**
|
||||
* @todo move to
|
||||
* @see ParamTypeInferer
|
||||
*/
|
||||
final class ParamTypeResolver implements PerNodeTypeResolverInterface
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -18,6 +18,11 @@ abstract class AbstractTypeInfo
|
|||
*/
|
||||
protected $isNullable = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isAlias = false;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
|
@ -110,7 +115,13 @@ abstract class AbstractTypeInfo
|
|||
return new Identifier($type);
|
||||
}
|
||||
|
||||
$name = $forceFqn ? new FullyQualified($type) : new Name($type);
|
||||
$type = ltrim($type, '\\');
|
||||
|
||||
if ($this->isAlias || $forceFqn === false) {
|
||||
$name = new Name($type);
|
||||
} else {
|
||||
$name = new FullyQualified($type);
|
||||
}
|
||||
|
||||
if ($this->isNullable) {
|
||||
return new NullableType($name);
|
||||
|
|
|
@ -11,6 +11,11 @@ final class ParamTypeInfo extends AbstractTypeInfo
|
|||
*/
|
||||
protected $typesToRemove = ['void', 'real'];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isAlias = false;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
@ -20,9 +25,15 @@ final class ParamTypeInfo extends AbstractTypeInfo
|
|||
* @param string[] $types
|
||||
* @param string[] $fqnTypes
|
||||
*/
|
||||
public function __construct(string $name, TypeAnalyzer $typeAnalyzer, array $types, array $fqnTypes = [])
|
||||
{
|
||||
public function __construct(
|
||||
string $name,
|
||||
TypeAnalyzer $typeAnalyzer,
|
||||
array $types,
|
||||
array $fqnTypes = [],
|
||||
bool $isAlias = false
|
||||
) {
|
||||
$this->name = $this->normalizeName($name);
|
||||
$this->isAlias = $isAlias;
|
||||
|
||||
parent::__construct($types, $typeAnalyzer, $fqnTypes);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,11 @@ namespace Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer;
|
|||
use Nette\Utils\Strings;
|
||||
use PhpParser\Comment\Doc;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode;
|
||||
|
@ -231,15 +235,16 @@ final class DocBlockManipulator
|
|||
/**
|
||||
* With "name" as key
|
||||
*
|
||||
* @param Function_|ClassMethod|Closure $functionLike
|
||||
* @return ParamTypeInfo[]
|
||||
*/
|
||||
public function getParamTypeInfos(Node $node): array
|
||||
public function getParamTypeInfos(FunctionLike $functionLike): array
|
||||
{
|
||||
if ($node->getDocComment() === null) {
|
||||
if ($functionLike->getDocComment() === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
|
||||
$phpDocInfo = $this->createPhpDocInfoFromNode($functionLike);
|
||||
$types = $phpDocInfo->getParamTagValues();
|
||||
if ($types === []) {
|
||||
return [];
|
||||
|
@ -251,12 +256,14 @@ final class DocBlockManipulator
|
|||
/** @var AttributeAwareParamTagValueNode $paramTagValueNode */
|
||||
foreach ($types as $i => $paramTagValueNode) {
|
||||
$fqnParamTagValueNode = $fqnTypes[$i];
|
||||
$isAlias = $this->isAlias((string) $paramTagValueNode->type, $functionLike);
|
||||
|
||||
$paramTypeInfo = new ParamTypeInfo(
|
||||
$paramTagValueNode->parameterName,
|
||||
$this->typeAnalyzer,
|
||||
$paramTagValueNode->getAttribute(Attribute::TYPE_AS_ARRAY),
|
||||
$fqnParamTagValueNode->getAttribute(Attribute::RESOLVED_NAMES)
|
||||
$fqnParamTagValueNode->getAttribute(Attribute::RESOLVED_NAMES),
|
||||
$isAlias
|
||||
);
|
||||
|
||||
$paramTypeInfos[$paramTypeInfo->getName()] = $paramTypeInfo;
|
||||
|
@ -300,6 +307,11 @@ final class DocBlockManipulator
|
|||
}
|
||||
}
|
||||
|
||||
$returnTypeInfo = new ReturnTypeInfo(explode('|', $type), $this->typeAnalyzer);
|
||||
if ($returnTypeInfo) {
|
||||
$type = implode('|', $returnTypeInfo->getDocTypes());
|
||||
}
|
||||
|
||||
$this->removeTagFromNode($node, 'return');
|
||||
$this->addTypeSpecificTag($node, 'return', $type);
|
||||
}
|
||||
|
@ -824,4 +836,27 @@ final class DocBlockManipulator
|
|||
|
||||
return $isShortClassUsed;
|
||||
}
|
||||
|
||||
private function isAlias(string $paramType, Node $node): bool
|
||||
{
|
||||
/** @var Node\Stmt\Use_[]|null $useNodes */
|
||||
$useNodes = $node->getAttribute(AttributeKey::USE_NODES);
|
||||
if ($useNodes === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($useNodes as $useNode) {
|
||||
foreach ($useNode->uses as $useUse) {
|
||||
if ($useUse->alias === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((string) $useUse->alias === $paramType) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\Contract\TypeInferer;
|
||||
|
||||
interface PriorityAwareTypeInfererInterface
|
||||
{
|
||||
/**
|
||||
* Higher priority goes first.
|
||||
*/
|
||||
public function getPriority(): int;
|
||||
}
|
|
@ -5,15 +5,10 @@ namespace Rector\TypeDeclaration\Contract\TypeInferer;
|
|||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\TypeDeclaration\ValueObject\IdentifierValueObject;
|
||||
|
||||
interface PropertyTypeInfererInterface
|
||||
interface PropertyTypeInfererInterface extends PriorityAwareTypeInfererInterface
|
||||
{
|
||||
/**
|
||||
* @return string[]|IdentifierValueObject[]
|
||||
*/
|
||||
public function inferProperty(Property $property): array;
|
||||
|
||||
/**
|
||||
* Higher priority goes first.
|
||||
*/
|
||||
public function getPriority(): int;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace Rector\TypeDeclaration\Contract\TypeInferer;
|
|||
|
||||
use PhpParser\Node\FunctionLike;
|
||||
|
||||
interface ReturnTypeInfererInterface
|
||||
interface ReturnTypeInfererInterface extends PriorityAwareTypeInfererInterface
|
||||
{
|
||||
/**
|
||||
* @return string[]
|
||||
|
|
|
@ -3,21 +3,21 @@
|
|||
namespace Rector\TypeDeclaration\Exception;
|
||||
|
||||
use Exception;
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\PropertyTypeInfererInterface;
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\PriorityAwareTypeInfererInterface;
|
||||
|
||||
final class ConflictingPriorityException extends Exception
|
||||
{
|
||||
public function __construct(
|
||||
PropertyTypeInfererInterface $firstPropertyTypeInfererInterface,
|
||||
PropertyTypeInfererInterface $secondPropertyTypeInfererInterface
|
||||
PriorityAwareTypeInfererInterface $firstPriorityAwareTypeInferer,
|
||||
PriorityAwareTypeInfererInterface $secondPriorityAwareTypeInferer
|
||||
) {
|
||||
$message = sprintf(
|
||||
'There are 2 property type inferers with %d priority:%s- %s%s- %s.%sChange value in "getPriority()" method in one of them to different value',
|
||||
$firstPropertyTypeInfererInterface->getPriority(),
|
||||
'There are 2 type inferers with %d priority:%s- %s%s- %s.%sChange value in "getPriority()" method in one of them to different value',
|
||||
$firstPriorityAwareTypeInferer->getPriority(),
|
||||
PHP_EOL,
|
||||
get_class($firstPropertyTypeInfererInterface),
|
||||
get_class($firstPriorityAwareTypeInferer),
|
||||
PHP_EOL,
|
||||
get_class($secondPropertyTypeInfererInterface),
|
||||
get_class($secondPriorityAwareTypeInferer),
|
||||
PHP_EOL
|
||||
);
|
||||
|
||||
|
|
|
@ -89,10 +89,13 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
$docTypes = $this->returnTypeInferer->inferFunctionLike($node);
|
||||
if ($docTypes !== []) {
|
||||
$docType = implode('|', $docTypes);
|
||||
$inferedTypes = $this->returnTypeInferer->inferFunctionLike($node);
|
||||
if ($inferedTypes === ['void']) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($inferedTypes !== []) {
|
||||
$docType = implode('|', $inferedTypes);
|
||||
$this->docBlockManipulator->addReturnTag($node, $docType);
|
||||
}
|
||||
|
||||
|
|
|
@ -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->getTypeNode();
|
||||
$returnTypeNode = $returnTypeInfo->getFqnTypeNode();
|
||||
if ($returnTypeNode === null) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ use Rector\NodeContainer\ParsedNodesByType;
|
|||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\Php\AbstractTypeInfo;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
|
||||
use Rector\PhpParser\Node\Manipulator\FunctionLikeManipulator;
|
||||
use Rector\Rector\AbstractRector;
|
||||
|
||||
/**
|
||||
|
@ -44,22 +43,15 @@ abstract class AbstractTypeDeclarationRector extends AbstractRector
|
|||
*/
|
||||
protected $parsedNodesByType;
|
||||
|
||||
/**
|
||||
* @var FunctionLikeManipulator
|
||||
*/
|
||||
protected $functionLikeManipulator;
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function autowireAbstractTypeDeclarationRector(
|
||||
DocBlockManipulator $docBlockManipulator,
|
||||
ParsedNodesByType $parsedNodesByType,
|
||||
FunctionLikeManipulator $functionLikeManipulator
|
||||
ParsedNodesByType $parsedNodesByType
|
||||
): void {
|
||||
$this->docBlockManipulator = $docBlockManipulator;
|
||||
$this->parsedNodesByType = $parsedNodesByType;
|
||||
$this->functionLikeManipulator = $functionLikeManipulator;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,7 +128,7 @@ abstract class AbstractTypeDeclarationRector extends AbstractRector
|
|||
* @param Name|NullableType|Identifier $possibleSubtype
|
||||
* @param Name|NullableType|Identifier $type
|
||||
*/
|
||||
protected function isSubtypeOf(Node $possibleSubtype, Node $type): bool
|
||||
protected function isSubtypeOf(Node $possibleSubtype, Node $type, string $kind): bool
|
||||
{
|
||||
$type = $type instanceof NullableType ? $type->type : $type;
|
||||
|
||||
|
@ -147,8 +139,17 @@ abstract class AbstractTypeDeclarationRector extends AbstractRector
|
|||
$possibleSubtype = $possibleSubtype->toString();
|
||||
$type = $type->toString();
|
||||
|
||||
if (is_a($possibleSubtype, $type, true)) {
|
||||
return true;
|
||||
if ($kind === 'return') {
|
||||
if ($this->isAtLeastPhpVersion('7.4')) {
|
||||
// @see https://wiki.php.net/rfc/covariant-returns-and-contravariant-parameters
|
||||
if (is_a($possibleSubtype, $type, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} elseif ($kind === 'param') {
|
||||
if (is_a($possibleSubtype, $type, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($possibleSubtype, ['array', 'Traversable'], true) && $type === 'iterable') {
|
||||
|
@ -167,14 +168,11 @@ abstract class AbstractTypeDeclarationRector extends AbstractRector
|
|||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod|Param $childClassMethodOrParam
|
||||
* @param ClassMethod|Param $node
|
||||
* @return Name|NullableType|Identifier|null
|
||||
*/
|
||||
protected function resolveChildType(
|
||||
AbstractTypeInfo $returnTypeInfo,
|
||||
Node $node,
|
||||
Node $childClassMethodOrParam
|
||||
): ?Node {
|
||||
protected function resolveChildType(AbstractTypeInfo $returnTypeInfo, Node $node): ?Node
|
||||
{
|
||||
$nakedType = $returnTypeInfo->getTypeNode() instanceof NullableType ? $returnTypeInfo->getTypeNode()->type : $returnTypeInfo->getTypeNode();
|
||||
|
||||
if ($nakedType === null) {
|
||||
|
@ -203,11 +201,6 @@ abstract class AbstractTypeDeclarationRector extends AbstractRector
|
|||
return $returnTypeInfo->isNullable() ? new NullableType($type) : $type;
|
||||
}
|
||||
|
||||
// is namespace the same? use short name
|
||||
if ($this->haveSameNamespace($node, $childClassMethodOrParam)) {
|
||||
return $returnTypeInfo->getTypeNode();
|
||||
}
|
||||
|
||||
// are namespaces different? → FQN name
|
||||
return $returnTypeInfo->getFqnTypeNode();
|
||||
}
|
||||
|
@ -264,10 +257,4 @@ abstract class AbstractTypeDeclarationRector extends AbstractRector
|
|||
|
||||
return $interfaces;
|
||||
}
|
||||
|
||||
private function haveSameNamespace(Node $firstNode, Node $secondNode): bool
|
||||
{
|
||||
return $firstNode->getAttribute(AttributeKey::NAMESPACE_NAME)
|
||||
=== $secondNode->getAttribute(AttributeKey::NAMESPACE_NAME);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,19 +140,19 @@ CODE_SAMPLE
|
|||
|
||||
if ($hasNewType) {
|
||||
// should override - is it subtype?
|
||||
$possibleOverrideNewReturnType = $paramTypeInfo->getTypeNode();
|
||||
$possibleOverrideNewReturnType = $paramTypeInfo->getFqnTypeNode();
|
||||
if ($possibleOverrideNewReturnType !== null) {
|
||||
if ($paramNode->type !== null) {
|
||||
if ($this->isSubtypeOf($possibleOverrideNewReturnType, $paramNode->type)) {
|
||||
if ($this->isSubtypeOf($possibleOverrideNewReturnType, $paramNode->type, 'param')) {
|
||||
// allow override
|
||||
$paramNode->type = $paramTypeInfo->getTypeNode();
|
||||
$paramNode->type = $paramTypeInfo->getFqnTypeNode();
|
||||
}
|
||||
} else {
|
||||
$paramNode->type = $paramTypeInfo->getTypeNode();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$paramNode->type = $paramTypeInfo->getTypeNode();
|
||||
$paramNode->type = $paramTypeInfo->getFqnTypeNode();
|
||||
|
||||
$paramNodeType = $paramNode->type instanceof NullableType ? $paramNode->type->type : $paramNode->type;
|
||||
// "resource" is valid phpdoc type, but it's not implemented in PHP
|
||||
|
@ -171,6 +171,7 @@ CODE_SAMPLE
|
|||
|
||||
/**
|
||||
* Add typehint to all children
|
||||
* @param ClassMethod|Function_ $node
|
||||
*/
|
||||
private function populateChildren(Node $node, int $position, ParamTypeInfo $paramTypeInfo): void
|
||||
{
|
||||
|
@ -178,9 +179,6 @@ CODE_SAMPLE
|
|||
return;
|
||||
}
|
||||
|
||||
/** @var string $methodName */
|
||||
$methodName = $this->getName($node);
|
||||
|
||||
/** @var string $className */
|
||||
$className = $node->getAttribute(AttributeKey::CLASS_NAME);
|
||||
// anonymous class
|
||||
|
@ -196,38 +194,39 @@ CODE_SAMPLE
|
|||
$usedTraits = $this->parsedNodesByType->findUsedTraitsInClass($childClassLike);
|
||||
|
||||
foreach ($usedTraits as $trait) {
|
||||
$this->addParamTypeToMethod($trait, $methodName, $position, $node, $paramTypeInfo);
|
||||
$this->addParamTypeToMethod($trait, $position, $node, $paramTypeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
$this->addParamTypeToMethod($childClassLike, $methodName, $position, $node, $paramTypeInfo);
|
||||
$this->addParamTypeToMethod($childClassLike, $position, $node, $paramTypeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private function addParamTypeToMethod(
|
||||
ClassLike $classLike,
|
||||
string $methodName,
|
||||
int $position,
|
||||
Node $node,
|
||||
ClassMethod $classMethod,
|
||||
ParamTypeInfo $paramTypeInfo
|
||||
): void {
|
||||
$classMethod = $classLike->getMethod($methodName);
|
||||
if ($classMethod === null) {
|
||||
$methodName = $this->getName($classMethod);
|
||||
|
||||
$currentClassMethod = $classLike->getMethod($methodName);
|
||||
if ($currentClassMethod === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! isset($classMethod->params[$position])) {
|
||||
if (! isset($currentClassMethod->params[$position])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paramNode = $classMethod->params[$position];
|
||||
$paramNode = $currentClassMethod->params[$position];
|
||||
|
||||
// already has a type
|
||||
if ($paramNode->type !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$resolvedChildType = $this->resolveChildType($paramTypeInfo, $node, $classMethod);
|
||||
$resolvedChildType = $this->resolveChildType($paramTypeInfo, $classMethod);
|
||||
if ($resolvedChildType === null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ use Rector\NodeTypeResolver\Php\ReturnTypeInfo;
|
|||
use Rector\Php\TypeAnalyzer;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
use Rector\TypeDeclaration\ReturnTypeResolver\ReturnTypeResolver;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
|
||||
final class ReturnTypeDeclarationRector extends AbstractTypeDeclarationRector
|
||||
|
@ -23,11 +22,6 @@ final class ReturnTypeDeclarationRector extends AbstractTypeDeclarationRector
|
|||
*/
|
||||
private const EXCLUDED_METHOD_NAMES = ['__construct', '__destruct', '__clone'];
|
||||
|
||||
/**
|
||||
* @var ReturnTypeResolver
|
||||
*/
|
||||
private $returnTypeResolver;
|
||||
|
||||
/**
|
||||
* @var ReturnTypeInferer
|
||||
*/
|
||||
|
@ -38,12 +32,8 @@ final class ReturnTypeDeclarationRector extends AbstractTypeDeclarationRector
|
|||
*/
|
||||
private $typeAnalyzer;
|
||||
|
||||
public function __construct(
|
||||
ReturnTypeResolver $returnTypeResolver,
|
||||
ReturnTypeInferer $returnTypeInferer,
|
||||
TypeAnalyzer $typeAnalyzer
|
||||
) {
|
||||
$this->returnTypeResolver = $returnTypeResolver;
|
||||
public function __construct(ReturnTypeInferer $returnTypeInferer, TypeAnalyzer $typeAnalyzer)
|
||||
{
|
||||
$this->returnTypeInferer = $returnTypeInferer;
|
||||
$this->typeAnalyzer = $typeAnalyzer;
|
||||
}
|
||||
|
@ -104,48 +94,33 @@ CODE_SAMPLE
|
|||
$hasNewType = $node->returnType->getAttribute(self::HAS_NEW_INHERITED_TYPE, false);
|
||||
}
|
||||
|
||||
$returnTypeInfo = $this->returnTypeResolver->resolveFunctionLikeReturnType($node);
|
||||
if ($returnTypeInfo === null) {
|
||||
$inferedTypes = $this->returnTypeInferer->inferFunctionLike($node);
|
||||
if ($inferedTypes === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// this could be void
|
||||
if ($returnTypeInfo->getTypeNode() === null) {
|
||||
if ($returnTypeInfo->getTypeCount() !== 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// use
|
||||
$returnTypeInfo = $this->functionLikeManipulator->resolveStaticReturnTypeInfo($node);
|
||||
if ($returnTypeInfo === null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
$returnTypeInfo = new ReturnTypeInfo($inferedTypes, $this->typeAnalyzer, $inferedTypes);
|
||||
|
||||
// @todo is it violation?
|
||||
if ($hasNewType) {
|
||||
// should override - is it subtype?
|
||||
$possibleOverrideNewReturnType = $returnTypeInfo->getTypeNode();
|
||||
|
||||
$possibleOverrideNewReturnType = $returnTypeInfo->getFqnTypeNode();
|
||||
if ($possibleOverrideNewReturnType !== null) {
|
||||
if ($node->returnType !== null) {
|
||||
if ($this->isSubtypeOf($possibleOverrideNewReturnType, $node->returnType)) {
|
||||
if ($this->isSubtypeOf($possibleOverrideNewReturnType, $node->returnType, 'return')) {
|
||||
// allow override
|
||||
$node->returnType = $returnTypeInfo->getTypeNode();
|
||||
$node->returnType = $returnTypeInfo->getFqnTypeNode();
|
||||
}
|
||||
} else {
|
||||
$node->returnType = $returnTypeInfo->getTypeNode();
|
||||
$node->returnType = $returnTypeInfo->getFqnTypeNode();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($returnTypeInfo->getTypeNode() === null) {
|
||||
$inferedTypes = $this->returnTypeInferer->inferFunctionLike($node);
|
||||
if ($inferedTypes) {
|
||||
$returnTypeInfo = new ReturnTypeInfo($inferedTypes, $this->typeAnalyzer, $inferedTypes);
|
||||
}
|
||||
if ($this->isReturnTypeAlreadyAdded($node, $returnTypeInfo)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$node->returnType = $returnTypeInfo->getTypeNode();
|
||||
$node->returnType = $returnTypeInfo->getFqnTypeNode();
|
||||
}
|
||||
|
||||
$this->populateChildren($node, $returnTypeInfo);
|
||||
|
@ -154,7 +129,7 @@ CODE_SAMPLE
|
|||
}
|
||||
|
||||
/**
|
||||
* Add typehint to all children
|
||||
* Add typehint to all children class methods
|
||||
*/
|
||||
private function populateChildren(Node $node, ReturnTypeInfo $returnTypeInfo): void
|
||||
{
|
||||
|
@ -182,45 +157,41 @@ CODE_SAMPLE
|
|||
|
||||
$usedTraits = $this->parsedNodesByType->findUsedTraitsInClass($childClassLike);
|
||||
foreach ($usedTraits as $trait) {
|
||||
$this->addReturnTypeToMethod($trait, $methodName, $node, $returnTypeInfo);
|
||||
$this->addReturnTypeToMethod($trait, $node, $returnTypeInfo);
|
||||
}
|
||||
|
||||
$this->addReturnTypeToMethod($childClassLike, $methodName, $node, $returnTypeInfo);
|
||||
$this->addReturnTypeToMethod($childClassLike, $node, $returnTypeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private function addReturnTypeToMethod(
|
||||
ClassLike $classLike,
|
||||
string $methodName,
|
||||
Node $node,
|
||||
ClassMethod $classMethod,
|
||||
ReturnTypeInfo $returnTypeInfo
|
||||
): void {
|
||||
$classMethod = $classLike->getMethod($methodName);
|
||||
if ($classMethod === null) {
|
||||
$methodName = $this->getName($classMethod);
|
||||
|
||||
$currentClassMethod = $classLike->getMethod($methodName);
|
||||
if ($currentClassMethod === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// already has a type
|
||||
if ($classMethod->returnType !== null) {
|
||||
if ($currentClassMethod->returnType !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$resolvedChildType = $this->resolveChildType($returnTypeInfo, $node, $classMethod);
|
||||
$resolvedChildType = $this->resolveChildType($returnTypeInfo, $classMethod);
|
||||
if ($resolvedChildType === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$resolvedChildType = $this->resolveChildType($returnTypeInfo, $node, $classMethod);
|
||||
if ($resolvedChildType === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$classMethod->returnType = $resolvedChildType;
|
||||
$currentClassMethod->returnType = $resolvedChildType;
|
||||
|
||||
// let the method now it was changed now
|
||||
$classMethod->returnType->setAttribute(self::HAS_NEW_INHERITED_TYPE, true);
|
||||
$currentClassMethod->returnType->setAttribute(self::HAS_NEW_INHERITED_TYPE, true);
|
||||
|
||||
$this->notifyNodeChangeFileInfo($classMethod);
|
||||
$this->notifyNodeChangeFileInfo($currentClassMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -234,4 +205,16 @@ CODE_SAMPLE
|
|||
|
||||
return $this->isNames($node, self::EXCLUDED_METHOD_NAMES);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod|Function_ $node
|
||||
*/
|
||||
private function isReturnTypeAlreadyAdded(Node $node, ReturnTypeInfo $returnTypeInfo): bool
|
||||
{
|
||||
if (ltrim($this->print($node->returnType), '\\') === $this->print($returnTypeInfo->getTypeNode())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\TypeInferer;
|
||||
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\PriorityAwareTypeInfererInterface;
|
||||
use Rector\TypeDeclaration\Exception\ConflictingPriorityException;
|
||||
|
||||
abstract class AbstractPriorityAwareTypeInferer
|
||||
{
|
||||
/**
|
||||
* @var PriorityAwareTypeInfererInterface[]
|
||||
*/
|
||||
private $sortedTypeInferers = [];
|
||||
|
||||
/**
|
||||
* @param PriorityAwareTypeInfererInterface[] $priorityAwareTypeInferers
|
||||
* @return PriorityAwareTypeInfererInterface[]
|
||||
*/
|
||||
protected function sortTypeInferersByPriority(array $priorityAwareTypeInferers): array
|
||||
{
|
||||
$this->sortedTypeInferers = [];
|
||||
|
||||
foreach ($priorityAwareTypeInferers as $propertyTypeInferer) {
|
||||
$this->ensurePriorityIsUnique($propertyTypeInferer);
|
||||
$this->sortedTypeInferers[$propertyTypeInferer->getPriority()] = $propertyTypeInferer;
|
||||
}
|
||||
|
||||
krsort($this->sortedTypeInferers);
|
||||
|
||||
return $this->sortedTypeInferers;
|
||||
}
|
||||
|
||||
private function ensurePriorityIsUnique(PriorityAwareTypeInfererInterface $priorityAwareTypeInferer): void
|
||||
{
|
||||
if (! isset($this->sortedTypeInferers[$priorityAwareTypeInferer->getPriority()])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$alreadySetPropertyTypeInferer = $this->sortedTypeInferers[$priorityAwareTypeInferer->getPriority()];
|
||||
|
||||
throw new ConflictingPriorityException($priorityAwareTypeInferer, $alreadySetPropertyTypeInferer);
|
||||
}
|
||||
}
|
|
@ -4,10 +4,9 @@ namespace Rector\TypeDeclaration\TypeInferer;
|
|||
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\PropertyTypeInfererInterface;
|
||||
use Rector\TypeDeclaration\Exception\ConflictingPriorityException;
|
||||
use Rector\TypeDeclaration\ValueObject\IdentifierValueObject;
|
||||
|
||||
final class PropertyTypeInferer
|
||||
final class PropertyTypeInferer extends AbstractPriorityAwareTypeInferer
|
||||
{
|
||||
/**
|
||||
* @var PropertyTypeInfererInterface[]
|
||||
|
@ -19,7 +18,7 @@ final class PropertyTypeInferer
|
|||
*/
|
||||
public function __construct(array $propertyTypeInferers)
|
||||
{
|
||||
$this->sortAndSetPropertyTypeInferers($propertyTypeInferers);
|
||||
$this->propertyTypeInferers = $this->sortTypeInferersByPriority($propertyTypeInferers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,28 +35,4 @@ final class PropertyTypeInferer
|
|||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PropertyTypeInfererInterface[] $propertyTypeInferers
|
||||
*/
|
||||
private function sortAndSetPropertyTypeInferers(array $propertyTypeInferers): void
|
||||
{
|
||||
foreach ($propertyTypeInferers as $propertyTypeInferer) {
|
||||
$this->ensurePriorityIsUnique($propertyTypeInferer);
|
||||
$this->propertyTypeInferers[$propertyTypeInferer->getPriority()] = $propertyTypeInferer;
|
||||
}
|
||||
|
||||
krsort($this->propertyTypeInferers);
|
||||
}
|
||||
|
||||
private function ensurePriorityIsUnique(PropertyTypeInfererInterface $propertyTypeInferer): void
|
||||
{
|
||||
if (! isset($this->propertyTypeInferers[$propertyTypeInferer->getPriority()])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$alreadySetPropertyTypeInferer = $this->propertyTypeInferers[$propertyTypeInferer->getPriority()];
|
||||
|
||||
throw new ConflictingPriorityException($propertyTypeInferer, $alreadySetPropertyTypeInferer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,6 @@ final class AllAssignNodePropertyTypeInferer extends AbstractTypeInferer impleme
|
|||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 500;
|
||||
return 610;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ namespace Rector\TypeDeclaration\TypeInferer;
|
|||
use PhpParser\Node\FunctionLike;
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\ReturnTypeInfererInterface;
|
||||
|
||||
final class ReturnTypeInferer
|
||||
final class ReturnTypeInferer extends AbstractPriorityAwareTypeInferer
|
||||
{
|
||||
/**
|
||||
* @var ReturnTypeInfererInterface[]
|
||||
|
@ -17,7 +17,7 @@ final class ReturnTypeInferer
|
|||
*/
|
||||
public function __construct(array $returnTypeInferers)
|
||||
{
|
||||
$this->returnTypeInferers = $returnTypeInferers;
|
||||
$this->returnTypeInferers = $this->sortTypeInferersByPriority($returnTypeInferers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<?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\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\ReturnTypeInfererInterface;
|
||||
use Rector\TypeDeclaration\TypeInferer\AbstractTypeInferer;
|
||||
|
||||
final class ReturnTagReturnTypeInferer extends AbstractTypeInferer implements ReturnTypeInfererInterface
|
||||
{
|
||||
/**
|
||||
* @var DocBlockManipulator
|
||||
*/
|
||||
private $docBlockManipulator;
|
||||
|
||||
public function __construct(DocBlockManipulator $docBlockManipulator)
|
||||
{
|
||||
$this->docBlockManipulator = $docBlockManipulator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod|Closure|Function_ $functionLike
|
||||
* @return string[]
|
||||
*/
|
||||
public function inferFunctionLike(FunctionLike $functionLike): array
|
||||
{
|
||||
$returnTypeInfo = $this->docBlockManipulator->getReturnTypeInfo($functionLike);
|
||||
if ($returnTypeInfo === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$fqnTypes = $returnTypeInfo->getFqnTypes();
|
||||
if ($returnTypeInfo->isNullable()) {
|
||||
$fqnTypes[] = 'null';
|
||||
}
|
||||
|
||||
return $fqnTypes;
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 400;
|
||||
}
|
||||
}
|
|
@ -1,35 +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() : [];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ArrowFunction;
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PhpParser\Node\Stmt\Trait_;
|
||||
use PhpParser\NodeTraverser;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\ReturnTypeInfererInterface;
|
||||
use Rector\TypeDeclaration\TypeInferer\AbstractTypeInferer;
|
||||
|
||||
final class ReturnedNodesReturnTypeInferer extends AbstractTypeInferer implements ReturnTypeInfererInterface
|
||||
{
|
||||
/**
|
||||
* @param ClassMethod|Closure|Function_ $functionLike
|
||||
* @return string[]
|
||||
*/
|
||||
public function inferFunctionLike(FunctionLike $functionLike): array
|
||||
{
|
||||
/** @var Class_|Trait_|Interface_|null $classLike */
|
||||
$classLike = $functionLike->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($functionLike instanceof ClassMethod) {
|
||||
if ($classLike instanceof Interface_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
$localReturnNodes = $this->collectReturns($functionLike);
|
||||
if ($localReturnNodes === []) {
|
||||
// void type
|
||||
if ($functionLike instanceof ClassMethod && ! $functionLike->isAbstract()) {
|
||||
return ['void'];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
$types = [];
|
||||
foreach ($localReturnNodes as $localReturnNode) {
|
||||
$types = array_merge($types, $this->nodeTypeResolver->resolveSingleTypeToStrings($localReturnNode->expr));
|
||||
}
|
||||
|
||||
// @todo add priority, because this gets last :)
|
||||
return $types;
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod|Closure|Function_ $functionLike
|
||||
* @return Return_[]
|
||||
*/
|
||||
private function collectReturns(FunctionLike $functionLike): array
|
||||
{
|
||||
$returns = [];
|
||||
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable((array) $functionLike->stmts, function (Node $node) use (
|
||||
&$returns
|
||||
): ?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;
|
||||
}
|
||||
|
||||
$returns[] = $node;
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
return $returns;
|
||||
}
|
||||
}
|
|
@ -57,6 +57,11 @@ final class ReturnedPropertyReturnTypeInferer extends AbstractTypeInferer implem
|
|||
return $this->propertyTypeInferer->inferProperty($property);
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 700;
|
||||
}
|
||||
|
||||
private function matchSingleStmtReturnPropertyFetch(ClassMethod $classMethod): ?PropertyFetch
|
||||
{
|
||||
if (count((array) $classMethod->stmts) !== 1) {
|
||||
|
|
|
@ -55,4 +55,9 @@ final class SetterNodeReturnTypeInferer extends AbstractTypeInferer implements R
|
|||
|
||||
return $this->staticTypeToStringResolver->resolveObjectType($assignedExprStaticType);
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 600;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
|
||||
use Iterator;
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\Expr\Yield_;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use Rector\Php\PhpVersionProvider;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\ReturnTypeInfererInterface;
|
||||
use Rector\TypeDeclaration\TypeInferer\AbstractTypeInferer;
|
||||
|
||||
final class YieldNodesReturnTypeInferer extends AbstractTypeInferer implements ReturnTypeInfererInterface
|
||||
{
|
||||
/**
|
||||
* @var BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
|
||||
/**
|
||||
* @var PhpVersionProvider
|
||||
*/
|
||||
private $phpVersionProvider;
|
||||
|
||||
public function __construct(BetterNodeFinder $betterNodeFinder, PhpVersionProvider $phpVersionProvider)
|
||||
{
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
$this->phpVersionProvider = $phpVersionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod|Function_|Closure $functionLike
|
||||
* @return string[]
|
||||
*/
|
||||
public function inferFunctionLike(FunctionLike $functionLike): array
|
||||
{
|
||||
/** @var Yield_[] $yieldNodes */
|
||||
$yieldNodes = $this->betterNodeFinder->findInstanceOf((array) $functionLike->stmts, Yield_::class);
|
||||
|
||||
$types = [];
|
||||
if (count($yieldNodes)) {
|
||||
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);
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 1200;
|
||||
}
|
||||
}
|
|
@ -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',
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRe
|
|||
* @param void $void
|
||||
* @param mixed $mixed
|
||||
* @param unknown $unknown
|
||||
* @param Class $class
|
||||
* @param AnyClass $class
|
||||
*/
|
||||
function aliases($integer, $boolean, $real, $double, $callback, $void, $mixed, $unkown, $class) {
|
||||
}
|
||||
|
@ -30,8 +30,8 @@ namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRe
|
|||
* @param void $void
|
||||
* @param mixed $mixed
|
||||
* @param unknown $unknown
|
||||
* @param Class $class
|
||||
* @param AnyClass $class
|
||||
*/
|
||||
function aliases(int $integer, bool $boolean, float $real, float $double, callable $callback, $void, $mixed, $unkown, Class $class) {
|
||||
function aliases(int $integer, bool $boolean, float $real, float $double, callable $callback, $void, $mixed, $unkown, \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRector\Fixture\Dunglas\AnyClass $class) {
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -31,9 +31,9 @@ function foo() {
|
|||
function foo2($a) {}
|
||||
|
||||
/** @param null|A $a */
|
||||
function foo3(?A $a = null) {}
|
||||
function foo3(?\Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRector\Fixture\Nikic\Null_\A $a = null) {}
|
||||
|
||||
/** @param null|A $a */
|
||||
function foo4(?A $a) {}
|
||||
function foo4(?\Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRector\Fixture\Nikic\Null_\A $a) {}
|
||||
|
||||
?>
|
||||
|
|
|
@ -18,6 +18,6 @@ namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRe
|
|||
* @param Foo|null $a
|
||||
* @param Bar|null $b
|
||||
*/
|
||||
function test2(?Foo $a, ?Bar $b = null) {}
|
||||
function test2(?\Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRector\Fixture\Nikic\Nullable\Foo $a, ?\Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRector\Fixture\Nikic\Nullable\Bar $b = null) {}
|
||||
|
||||
?>
|
||||
|
|
|
@ -19,11 +19,11 @@ namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRe
|
|||
|
||||
class A {
|
||||
/** @param Foo $a */
|
||||
public function test2(Foo $a) {}
|
||||
public function test2(\Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRector\Fixture\NullableInheritance\Foo $a) {}
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
/** @param null|Foo $a */
|
||||
public function test2(?Foo $a) {}
|
||||
public function test2(?\Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRector\Fixture\NullableInheritance\Foo $a) {}
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -9,5 +9,5 @@ interface Foo { /** @param Bar $bar */ function my_foo($bar); }
|
|||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRector\Fixture\PhpCsFixerParam\Interface_;
|
||||
|
||||
interface Foo { /** @param Bar $bar */ function my_foo(Bar $bar); }
|
||||
interface Foo { /** @param Bar $bar */ function my_foo(\Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRector\Fixture\PhpCsFixerParam\Interface_\Bar $bar); }
|
||||
?>
|
||||
|
|
|
@ -24,6 +24,6 @@ namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRe
|
|||
*/
|
||||
function my_foo(string $bar, int $baz, float $tab) {}
|
||||
|
||||
/** @param My\Bar $foo */ function my_foo2(My\Bar $foo) {}
|
||||
/** @param My\Bar $foo */ function my_foo2(\Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRector\Fixture\PhpCsFixerParam\TypehintAlreadyDefinedWithWrongPhpdocTypehint\My\Bar $foo) {}
|
||||
|
||||
?>
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRe
|
|||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRector\Fixture\PhpCsFixerParam\Nullable;
|
||||
|
||||
/** @param null|bool $foo */ function my_foo(?bool $foo) {}
|
||||
/** @param null|Foo $foo */ function my_foo2(?Foo $foo) {}
|
||||
/** @param null|Foo $foo */ function my_foo2(?\Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRector\Fixture\PhpCsFixerParam\Nullable\Foo $foo) {}
|
||||
/** @param null|callable $foo */ function my_foo3(?callable $foo) {}
|
||||
/** @param null|Foo[] $foo */ function my_foo4(?array $foo) {}
|
||||
/** @param null|iterable $foo */ function my_foo5(?iterable $foo) {}
|
||||
|
|
|
@ -23,5 +23,5 @@ namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRe
|
|||
* @param float $tab
|
||||
* @param bool $baz
|
||||
*/
|
||||
function my_foo(string $bar, int $foo, bool $baz, float $tab, Baz $hey) {}
|
||||
function my_foo(string $bar, int $foo, bool $baz, float $tab, \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRector\Fixture\PhpCsFixerParam\Unsorted\Baz $hey) {}
|
||||
?>
|
||||
|
|
|
@ -22,7 +22,6 @@ final class ParamTypeDeclarationRectorTest extends AbstractRectorTestCase
|
|||
__DIR__ . '/Fixture/this.php.inc',
|
||||
__DIR__ . '/Fixture/false.php.inc',
|
||||
__DIR__ . '/Fixture/undesired.php.inc',
|
||||
__DIR__ . '/Fixture/aliased.php.inc',
|
||||
__DIR__ . '/Fixture/external_scope.php.inc',
|
||||
__DIR__ . '/Fixture/local_and_external_scope.php.inc',
|
||||
__DIR__ . '/Fixture/local_scope_with_parent_interface.php.inc',
|
||||
|
@ -38,7 +37,6 @@ final class ParamTypeDeclarationRectorTest extends AbstractRectorTestCase
|
|||
__DIR__ . '/Fixture/php-cs-fixer-param/non_root_class_with_different_types_of_params.php.inc',
|
||||
__DIR__ . '/Fixture/php-cs-fixer-param/nullable.php.inc',
|
||||
__DIR__ . '/Fixture/php-cs-fixer-param/number.php.inc',
|
||||
__DIR__ . '/Fixture/php-cs-fixer-param/root_class.php.inc',
|
||||
__DIR__ . '/Fixture/php-cs-fixer-param/self_accessor.php.inc',
|
||||
__DIR__ . '/Fixture/php-cs-fixer-param/skip.php.inc',
|
||||
__DIR__ . '/Fixture/php-cs-fixer-param/unsorted.php.inc',
|
||||
|
@ -48,7 +46,6 @@ final class ParamTypeDeclarationRectorTest extends AbstractRectorTestCase
|
|||
__DIR__ . '/Fixture/nikic/iterable.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/null.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/nullable.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/nullable_inheritance.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/rename.php.inc',
|
||||
// dunglas set - https://github.com/dunglas/phpdoc-to-typehint/
|
||||
__DIR__ . '/Fixture/dunglas/array_no_types.php.inc',
|
||||
|
@ -59,9 +56,12 @@ final class ParamTypeDeclarationRectorTest extends AbstractRectorTestCase
|
|||
__DIR__ . '/Fixture/dunglas/Foo.php.inc',
|
||||
__DIR__ . '/Fixture/dunglas/functions.php.inc',
|
||||
__DIR__ . '/Fixture/dunglas/functions2.php.inc',
|
||||
__DIR__ . '/Fixture/dunglas/nullable_types.php.inc',
|
||||
__DIR__ . '/Fixture/dunglas/param_no_type.php.inc',
|
||||
__DIR__ . '/Fixture/dunglas/type_aliases_and_whitelisting.php.inc',
|
||||
__DIR__ . '/Fixture/php-cs-fixer-param/root_class.php.inc',
|
||||
__DIR__ . '/Fixture/dunglas/nullable_types.php.inc',
|
||||
__DIR__ . '/Fixture/aliased.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/nullable_inheritance.php.inc',
|
||||
];
|
||||
|
||||
$this->doTestFiles($integrationFiles);
|
||||
|
|
|
@ -9,7 +9,12 @@ final class CorrectionTest extends AbstractRectorTestCase
|
|||
{
|
||||
public function test(): void
|
||||
{
|
||||
$this->doTestFiles([__DIR__ . '/Fixture/Correction/constructor_property_assign_over_getter.php.inc']);
|
||||
$this->doTestFiles([
|
||||
// __DIR__ . '/Fixture/Correction/constructor_property_assign_over_getter.php.inc',
|
||||
__DIR__ . '/Fixture/Correction/prefix_fqn.php.inc',
|
||||
// skip
|
||||
// __DIR__ . '/Fixture/Correction/skip_override_of_the_same_class.php.inc',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\Correction;
|
||||
|
||||
use Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\RealReturnedClass;
|
||||
use Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\ReturnedClass;
|
||||
|
||||
class SkipOverrideOfTheSameClass
|
||||
{
|
||||
public function getReturnedClass(): ReturnedClass
|
||||
{
|
||||
return new RealReturnedClass();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\Correction;
|
||||
|
||||
use Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\RealReturnedClass;
|
||||
use Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\ReturnedClass;
|
||||
|
||||
class SkipOverrideOfTheSameClass
|
||||
{
|
||||
public function getReturnedClass(): \Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\RealReturnedClass
|
||||
{
|
||||
return new RealReturnedClass();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\Correction;
|
||||
|
||||
use Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\ReturnedClass;
|
||||
|
||||
class SkipOverrideOfTheSameClass
|
||||
{
|
||||
public function getReturnedClass(): ReturnedClass
|
||||
{
|
||||
return new ReturnedClass();
|
||||
}
|
||||
}
|
|
@ -15,18 +15,6 @@ class B extends A {
|
|||
}
|
||||
}
|
||||
|
||||
class C extends B {
|
||||
/**
|
||||
* Technically valid return type, but against PHP's variance restrictions.
|
||||
* We use "A" instead, which is less accurate but valid.
|
||||
*
|
||||
* @return C
|
||||
*/
|
||||
public function test() {
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
@ -35,25 +23,13 @@ namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationR
|
|||
|
||||
class A {
|
||||
/** @return A */
|
||||
public function test(): A {
|
||||
public function test(): \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\Inheritance\A {
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
public function test(): A {
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
class C extends B {
|
||||
/**
|
||||
* Technically valid return type, but against PHP's variance restrictions.
|
||||
* We use "A" instead, which is less accurate but valid.
|
||||
*
|
||||
* @return C
|
||||
*/
|
||||
public function test(): A {
|
||||
public function test(): \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\Inheritance\A {
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\Inheritance;
|
||||
|
||||
class ACovariance {
|
||||
/** @return ACovariance */
|
||||
public function test() {
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
class CCovariance extends ACovariance {
|
||||
/**
|
||||
* Technically valid return type, but against PHP's variance restrictions.
|
||||
* We use "ACovariance" instead, which is less accurate but valid.
|
||||
*
|
||||
* @return CCovariance
|
||||
*/
|
||||
public function test() {
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\Inheritance;
|
||||
|
||||
class ACovariance {
|
||||
/** @return ACovariance */
|
||||
public function test(): \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\Inheritance\ACovariance {
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
class CCovariance extends ACovariance {
|
||||
/**
|
||||
* Technically valid return type, but against PHP's variance restrictions.
|
||||
* We use "ACovariance" instead, which is less accurate but valid.
|
||||
*
|
||||
* @return CCovariance
|
||||
*/
|
||||
public function test(): \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\Inheritance\CCovariance {
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -50,27 +50,27 @@ function test1($value): \Bar {
|
|||
}
|
||||
|
||||
/** @return Bar */
|
||||
function test2($value): Bar {
|
||||
function test2($value): \Foo\Bar {
|
||||
return $value;
|
||||
}
|
||||
|
||||
/** @return Bar\Baz */
|
||||
function test3($value): Bar\Baz {
|
||||
function test3($value): \Foo\Bar\Baz {
|
||||
return $value;
|
||||
}
|
||||
|
||||
/** @return Baz */
|
||||
function test4($value): Baz {
|
||||
function test4($value): \ABC\Baz {
|
||||
return $value;
|
||||
}
|
||||
|
||||
/** @return Baz\Foo */
|
||||
function test5($value): Baz\Foo {
|
||||
function test5($value): \ABC\Baz\Foo {
|
||||
return $value;
|
||||
}
|
||||
|
||||
/** @return XYZ */
|
||||
function test6($value): XYZ {
|
||||
function test6($value): \Foo\XYZ {
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ function test($value) {
|
|||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\Nikic\Nullable;
|
||||
|
||||
/** @return Foo|null */
|
||||
function test($value): ?Foo {
|
||||
function test($value): ?\Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\Nikic\Nullable\Foo {
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,14 +23,14 @@ namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationR
|
|||
|
||||
class A {
|
||||
/** @return null|Foo */
|
||||
public function test($value): ?Foo {
|
||||
public function test($value): ?\Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\NullableInheritance\Foo {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
/** @return Foo */
|
||||
public function test($value): Foo {
|
||||
public function test($value): \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\NullableInheritance\Foo {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ class A {
|
|||
}
|
||||
class B extends A {
|
||||
/** @return Foo */
|
||||
public function getObject($value): Foo {
|
||||
public function getObject($value): \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\Object\Foo {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ class A {
|
|||
}
|
||||
class B extends A {
|
||||
/** @return Foo */
|
||||
public function getObject($value): Foo {
|
||||
public function getObject($value): \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\ObjectPhp72\Foo {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ function test($a = array()) {
|
|||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\ReturnTypePosition;
|
||||
|
||||
/** @return A */
|
||||
function test($a = array()): A {
|
||||
function test($a = array()): \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\ReturnTypePosition\A {
|
||||
return $a;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ class Foo
|
|||
}
|
||||
|
||||
/** @return Bar */
|
||||
function __construct($value): Bar {
|
||||
function __construct($value): \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\PhpCsFixerReturn\BlacklistedClassMethods\Bar {
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,11 +26,11 @@ function my_foo4($value) {
|
|||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\PhpCsFixerReturn\Nullables;
|
||||
|
||||
/** @return null|Bar */
|
||||
function my_foo($value): ?Bar {
|
||||
function my_foo($value): ?\Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\PhpCsFixerReturn\Nullables\Bar {
|
||||
return $value;
|
||||
}
|
||||
/** @return Bar|null */
|
||||
function my_foo2($value): ?Bar {
|
||||
function my_foo2($value): ?\Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\PhpCsFixerReturn\Nullables\Bar {
|
||||
return $value;
|
||||
}
|
||||
/** @return null|array */
|
||||
|
|
|
@ -24,10 +24,10 @@ interface Foo {
|
|||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\PhpCsFixerReturn\Various;
|
||||
|
||||
/** @return Bar */ function my_foo($value): Bar {
|
||||
/** @return Bar */ function my_foo($value): \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\PhpCsFixerReturn\Various\Bar {
|
||||
return $value;
|
||||
}
|
||||
/** @return My\Bar */ function my_foo2($value): My\Bar {
|
||||
/** @return My\Bar */ function my_foo2($value): \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\PhpCsFixerReturn\Various\My\Bar {
|
||||
return $value;
|
||||
}
|
||||
/** @return \My\Bar */ function my_foo3($value): \My\Bar {
|
||||
|
@ -35,7 +35,7 @@ namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationR
|
|||
}
|
||||
interface Foo {
|
||||
/** @return Bar */
|
||||
function my_foo4($value): Bar;
|
||||
function my_foo4($value): \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\PhpCsFixerReturn\Various\Bar;
|
||||
}
|
||||
/** @return void */ function my_foo5(): void {
|
||||
}
|
||||
|
|
|
@ -5,11 +5,14 @@ namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclaration
|
|||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Rector\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector;
|
||||
|
||||
/**
|
||||
* @sponsor Thanks https://spaceflow.io/ for sponsoring this rule - visit them on https://github.com/SpaceFlow-app
|
||||
*/
|
||||
final class ReturnTypeDeclarationRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
public function test(): void
|
||||
{
|
||||
$integrationFiles = [
|
||||
$files = [
|
||||
// static types
|
||||
__DIR__ . '/Fixture/void_type.php.inc',
|
||||
__DIR__ . '/Fixture/no_void_abstract.php.inc',
|
||||
|
@ -38,12 +41,11 @@ final class ReturnTypeDeclarationRectorTest extends AbstractRectorTestCase
|
|||
__DIR__ . '/Fixture/php-cs-fixer-return/skip.php.inc',
|
||||
__DIR__ . '/Fixture/php-cs-fixer-return/nullables.php.inc',
|
||||
// nikic set - https://github.com/nikic/TypeUtil/
|
||||
__DIR__ . '/Fixture/nikic/inheritance.php.inc',
|
||||
|
||||
__DIR__ . '/Fixture/nikic/iterable.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/name_resolution.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/null.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/nullable.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/nullable_inheritance.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/object.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/return_type_position.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/self_inheritance.php.inc',
|
||||
|
@ -58,7 +60,18 @@ final class ReturnTypeDeclarationRectorTest extends AbstractRectorTestCase
|
|||
__DIR__ . '/Fixture/a_new_class.php.inc',
|
||||
];
|
||||
|
||||
$this->doTestFiles($integrationFiles);
|
||||
$this->doTestFiles($files);
|
||||
}
|
||||
|
||||
public function testInheritance(): void
|
||||
{
|
||||
$files = [
|
||||
__DIR__ . '/Fixture/nikic/inheritance.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/inheritance_covariance.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/nullable_inheritance.php.inc',
|
||||
];
|
||||
|
||||
$this->doTestFiles($files);
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Source;
|
||||
|
||||
final class RealReturnedClass
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Source;
|
||||
|
||||
final class ReturnedClass
|
||||
{
|
||||
|
||||
}
|
|
@ -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',
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -194,6 +194,10 @@ parameters:
|
|||
- '#PHPDoc tag @param for parameter \$nodeWithStatements with type PhpParser\\Builder\\FunctionLike\|PhpParser\\Node\\Stmt\\ClassLike is not subtype of native type PhpParser\\Node#'
|
||||
- '#Access to an undefined property PhpParser\\Node\\FunctionLike\|PhpParser\\Node\\Stmt\\ClassLike\:\:\$stmts#'
|
||||
|
||||
- '#Property Rector\\TypeDeclaration\\TypeInferer\\(.*?)\:\:\$(.*?)TypeInferers \(array<Rector\\TypeDeclaration\\Contract\\TypeInferer\\(.*?)TypeInfererInterface\>\) does not accept array<Rector\\TypeDeclaration\\Contract\\TypeInferer\\PriorityAwareTypeInfererInterface\>#'
|
||||
# sense-less errors
|
||||
- '#Parameter \#1 \$functionLike of method Rector\\NodeTypeResolver\\PhpDoc\\NodeAnalyzer\\DocBlockManipulator\:\:getParamTypeInfos\(\) expects PhpParser\\Node\\Expr\\Closure\|PhpParser\\Node\\Stmt\\ClassMethod\|PhpParser\\Node\\Stmt\\Function_, PhpParser\\Node\\FunctionLike given#'
|
||||
|
||||
# PHP 7.4 1_000 support
|
||||
- '#Property PhpParser\\Node\\Scalar\\DNumber\:\:\$value \(float\) does not accept string#'
|
||||
- '#Call to function is_string\(\) with float will always evaluate to false#'
|
||||
|
|
|
@ -96,6 +96,8 @@ final class FunctionLikeManipulator
|
|||
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(
|
||||
|
@ -202,6 +204,7 @@ final class FunctionLikeManipulator
|
|||
}
|
||||
|
||||
/**
|
||||
* @covered
|
||||
* @param ClassMethod|Function_|Closure $functionLike
|
||||
* @return string[]
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue
Block a user