mirror of
https://github.com/rectorphp/rector.git
synced 2025-02-21 01:41:00 +01:00
improve covariance resolution
This commit is contained in:
parent
34f8319292
commit
72b363f60a
@ -140,11 +140,8 @@ abstract class AbstractTypeDeclarationRector extends AbstractRector
|
||||
$type = $type->toString();
|
||||
|
||||
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;
|
||||
}
|
||||
if (is_a($possibleSubtype, $type, true)) {
|
||||
return true;
|
||||
}
|
||||
} elseif ($kind === 'param') {
|
||||
if (is_a($possibleSubtype, $type, true)) {
|
||||
|
@ -4,7 +4,6 @@ namespace Rector\TypeDeclaration\Rector\FunctionLike;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
@ -17,6 +16,9 @@ use Rector\RectorDefinition\RectorDefinition;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer\ReturnTypeDeclarationReturnTypeInferer;
|
||||
|
||||
/**
|
||||
* @sponsor Thanks https://spaceflow.io/ for sponsoring this rule - visit them on https://github.com/SpaceFlow-app
|
||||
*/
|
||||
final class ReturnTypeDeclarationRector extends AbstractTypeDeclarationRector
|
||||
{
|
||||
/**
|
||||
@ -39,8 +41,11 @@ final class ReturnTypeDeclarationRector extends AbstractTypeDeclarationRector
|
||||
*/
|
||||
private $overrideExistingReturnTypes = true;
|
||||
|
||||
public function __construct(ReturnTypeInferer $returnTypeInferer, TypeAnalyzer $typeAnalyzer, bool $overrideExistingReturnTypes = true)
|
||||
{
|
||||
public function __construct(
|
||||
ReturnTypeInferer $returnTypeInferer,
|
||||
TypeAnalyzer $typeAnalyzer,
|
||||
bool $overrideExistingReturnTypes = true
|
||||
) {
|
||||
$this->returnTypeInferer = $returnTypeInferer;
|
||||
$this->typeAnalyzer = $typeAnalyzer;
|
||||
$this->overrideExistingReturnTypes = $overrideExistingReturnTypes;
|
||||
@ -114,7 +119,10 @@ CODE_SAMPLE
|
||||
$possibleOverrideNewReturnType = $returnTypeInfo->getFqnTypeNode();
|
||||
if ($possibleOverrideNewReturnType !== null) {
|
||||
if ($node->returnType !== null) {
|
||||
if ($this->isSubtypeOf($possibleOverrideNewReturnType, $node->returnType, 'return')) {
|
||||
$isSubtype = $this->isSubtypeOf($possibleOverrideNewReturnType, $node->returnType, 'return');
|
||||
|
||||
// @see https://wiki.php.net/rfc/covariant-returns-and-contravariant-parameters
|
||||
if ($isSubtype && $this->isAtLeastPhpVersion('7.4')) {
|
||||
// allow override
|
||||
$node->returnType = $returnTypeInfo->getFqnTypeNode();
|
||||
}
|
||||
@ -127,7 +135,19 @@ CODE_SAMPLE
|
||||
return null;
|
||||
}
|
||||
|
||||
$node->returnType = $returnTypeInfo->getFqnTypeNode();
|
||||
// should be previosu node overriden?
|
||||
if ($node->returnType !== null && $returnTypeInfo->getFqnTypeNode() !== null) {
|
||||
$isSubtype = $this->isSubtypeOf($returnTypeInfo->getFqnTypeNode(), $node->returnType, 'return');
|
||||
|
||||
// @see https://wiki.php.net/rfc/covariant-returns-and-contravariant-parameters
|
||||
if ($this->isAtLeastPhpVersion('7.4') && $isSubtype) {
|
||||
$node->returnType = $returnTypeInfo->getFqnTypeNode();
|
||||
} elseif ($isSubtype === false) {
|
||||
$node->returnType = $returnTypeInfo->getFqnTypeNode();
|
||||
}
|
||||
} else {
|
||||
$node->returnType = $returnTypeInfo->getFqnTypeNode();
|
||||
}
|
||||
}
|
||||
|
||||
$this->populateChildren($node, $returnTypeInfo);
|
||||
|
@ -1,118 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PhpParser\Node\Stmt\Trait_;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\ReturnTypeInfererInterface;
|
||||
use Rector\TypeDeclaration\TypeInferer\AbstractTypeInferer;
|
||||
use Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer;
|
||||
use Rector\TypeDeclaration\ValueObject\IdentifierValueObject;
|
||||
|
||||
/**
|
||||
* Infer return type from $this->variable → and get type $this->variable from @var annotation
|
||||
*/
|
||||
final class ReturnedPropertyReturnTypeInferer extends AbstractTypeInferer implements ReturnTypeInfererInterface
|
||||
{
|
||||
/**
|
||||
* @var PropertyTypeInferer
|
||||
*/
|
||||
private $propertyTypeInferer;
|
||||
|
||||
public function __construct(PropertyTypeInferer $propertyTypeInferer)
|
||||
{
|
||||
$this->propertyTypeInferer = $propertyTypeInferer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod|Closure|Function_ $functionLike
|
||||
* @return string[]|IdentifierValueObject[]
|
||||
*/
|
||||
public function inferFunctionLike(FunctionLike $functionLike): array
|
||||
{
|
||||
if (! $functionLike instanceof ClassMethod) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$propertyFetch = $this->matchSingleStmtReturnPropertyFetch($functionLike);
|
||||
if ($propertyFetch === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$property = $this->getPropertyByPropertyFetch($propertyFetch);
|
||||
if ($property === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->propertyTypeInferer->inferProperty($property);
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 700;
|
||||
}
|
||||
|
||||
private function matchSingleStmtReturnPropertyFetch(ClassMethod $classMethod): ?PropertyFetch
|
||||
{
|
||||
if (count((array) $classMethod->stmts) !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$singleStmt = $classMethod->stmts[0];
|
||||
if ($singleStmt instanceof Expression) {
|
||||
$singleStmt = $singleStmt->expr;
|
||||
}
|
||||
|
||||
// is it return?
|
||||
if (! $singleStmt instanceof Return_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $singleStmt->expr instanceof PropertyFetch) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$propertyFetch = $singleStmt->expr;
|
||||
if (! $this->nameResolver->isName($propertyFetch->var, 'this')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $propertyFetch;
|
||||
}
|
||||
|
||||
private function getPropertyByPropertyFetch(PropertyFetch $propertyFetch): ?Property
|
||||
{
|
||||
/** @var Class_|Trait_|Interface_|null $class */
|
||||
$class = $propertyFetch->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($class === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var string $propertyName */
|
||||
$propertyName = $this->nameResolver->getName($propertyFetch->name);
|
||||
|
||||
foreach ($class->stmts as $stmt) {
|
||||
if (! $stmt instanceof Property) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $this->nameResolver->isName($stmt, $propertyName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -10,10 +10,10 @@ final class CorrectionTest extends AbstractRectorTestCase
|
||||
public function test(): void
|
||||
{
|
||||
$this->doTestFiles([
|
||||
// __DIR__ . '/Fixture/Correction/constructor_property_assign_over_getter.php.inc',
|
||||
__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',
|
||||
__DIR__ . '/Fixture/Correction/skip_override_of_the_same_class.php.inc',
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationR
|
||||
use Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\RealReturnedClass;
|
||||
use Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\ReturnedClass;
|
||||
|
||||
class SkipOverrideOfTheSameClass
|
||||
class PrefixFqn
|
||||
{
|
||||
public function getReturnedClass(): ReturnedClass
|
||||
{
|
||||
@ -22,7 +22,7 @@ namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationR
|
||||
use Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\RealReturnedClass;
|
||||
use Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\ReturnedClass;
|
||||
|
||||
class SkipOverrideOfTheSameClass
|
||||
class PrefixFqn
|
||||
{
|
||||
public function getReturnedClass(): \Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Source\RealReturnedClass
|
||||
{
|
||||
|
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\Covariance;
|
||||
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
interface CommandInterface {
|
||||
public function getInput(): InputInterface;
|
||||
}
|
||||
|
||||
class SomeCommand implements CommandInterface {
|
||||
public function getInput(): InputInterface
|
||||
{
|
||||
return new ArrayInput([]);
|
||||
}
|
||||
}
|
@ -41,7 +41,7 @@ class CCovariance extends ACovariance {
|
||||
*
|
||||
* @return CCovariance
|
||||
*/
|
||||
public function test(): \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\Inheritance\CCovariance {
|
||||
public function test(): \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\Inheritance\ACovariance {
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class A {
|
||||
}
|
||||
class B extends A {
|
||||
/** @return Foo */
|
||||
public function getObject($value): \Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture\ObjectPhp72\Foo {
|
||||
public function getObject($value): object {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
@ -13,12 +13,27 @@ final class Php72RectorTest extends AbstractRectorTestCase
|
||||
parent::setUp();
|
||||
|
||||
$parameterProvider = self::$container->get(ParameterProvider::class);
|
||||
$parameterProvider->changeParameter(' php_version_features', '7.0');
|
||||
$parameterProvider->changeParameter('php_version_features', '7.0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed to restore previous version
|
||||
*/
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
$parameterProvider = self::$container->get(ParameterProvider::class);
|
||||
$parameterProvider->changeParameter('php_version_features', '10.0');
|
||||
}
|
||||
|
||||
public function test(): void
|
||||
{
|
||||
$this->doTestFiles([__DIR__ . '/Fixture/nikic/object_php72.php.inc']);
|
||||
$this->doTestFiles([
|
||||
__DIR__ . '/Fixture/nikic/object_php72.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/inheritance_covariance.php.inc',
|
||||
__DIR__ . '/Fixture/Covariance/return_interface_to_class.php.inc',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
|
@ -5,9 +5,6 @@ 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
|
||||
@ -67,7 +64,6 @@ final class ReturnTypeDeclarationRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
$files = [
|
||||
__DIR__ . '/Fixture/nikic/inheritance.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/inheritance_covariance.php.inc',
|
||||
__DIR__ . '/Fixture/nikic/nullable_inheritance.php.inc',
|
||||
];
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user