mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-26 04:42:36 +00:00
add AbstarctPriorityAwareTypeInferer
This commit is contained in:
parent
8af87e51c9
commit
8b6331bc01
|
@ -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[]
|
||||
*/
|
||||
|
@ -112,7 +117,11 @@ abstract class AbstractTypeInfo
|
|||
|
||||
$type = ltrim($type, '\\');
|
||||
|
||||
$name = $forceFqn ? new FullyQualified($type) : new Name($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);
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
$returnTypeNode = $staticReturnType->getTypeNode();
|
||||
$returnTypeNode = $staticReturnType->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,50 +94,28 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
// $inferedTypes = $this->returnTypeInferer->inferFunctionLike($node);
|
||||
// $inferedReturnTypeInfo = new ReturnTypeInfo($inferedTypes, $this->typeAnalyzer, $inferedTypes);
|
||||
$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;
|
||||
}
|
||||
|
@ -189,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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -247,9 +211,6 @@ CODE_SAMPLE
|
|||
*/
|
||||
private function isReturnTypeAlreadyAdded(Node $node, ReturnTypeInfo $returnTypeInfo): bool
|
||||
{
|
||||
// dump($this->print($node->returnType), '\\');
|
||||
// dump($this->print($returnTypeInfo->getTypeNode()));
|
||||
|
||||
if (ltrim($this->print($node->returnType), '\\') === $this->print($returnTypeInfo->getTypeNode())) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -32,4 +32,9 @@ final class ReturnedNodeReturnTypeInferer extends AbstractTypeInferer implements
|
|||
|
||||
return $resolvedReturnTypeInfo ? $resolvedReturnTypeInfo->getDocTypes() : [];
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 500;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,14 @@ 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;
|
||||
|
||||
|
@ -21,8 +25,21 @@ final class ReturnedNodesReturnTypeInferer extends AbstractTypeInferer implement
|
|||
*/
|
||||
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 [];
|
||||
}
|
||||
|
||||
|
@ -35,8 +52,13 @@ final class ReturnedNodesReturnTypeInferer extends AbstractTypeInferer implement
|
|||
return $types;
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod|Function_ $functionLike
|
||||
* @param ClassMethod|Closure|Function_ $functionLike
|
||||
* @return Return_[]
|
||||
*/
|
||||
private function collectReturns(FunctionLike $functionLike): array
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -24,7 +24,7 @@ use Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector
|
|||
|
||||
class SkipOverrideOfTheSameClass
|
||||
{
|
||||
public function getReturnedClass(): RealReturnedClass
|
||||
public function getReturnedClass(): \Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\RealReturnedClass
|
||||
{
|
||||
return new RealReturnedClass();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -41,19 +29,7 @@ class A {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -30,7 +30,7 @@ class A {
|
|||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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#'
|
||||
|
|
Loading…
Reference in New Issue
Block a user