[TypeDeclaration] Handle infered ThisType on auto import on ReturnTypeDeclarationRector (#1760)

Co-authored-by: oprypkhantc <54406427+oprypkhantc@users.noreply.github.com>
This commit is contained in:
Abdul Malik Ikhsan 2022-02-02 16:03:15 +07:00 committed by GitHub
parent 008b65aecb
commit 2468277687
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 190 additions and 8 deletions

View File

@ -68,7 +68,7 @@ jobs:
-
# commit only to core contributors who have repository access
if: github.event.pull_request.head.repo.full_name == github.repository
uses: EndBug/add-and-commit@v8.0.1
uses: EndBug/add-and-commit@v7.5.0
with:
# The arguments for the `git add` command (see the paragraph below for more info)
add: .

View File

@ -0,0 +1,51 @@
<?php
namespace T1 {
class A {
/**
* @return $this
*/
public function itself()
{
return $this;
}
}
}
namespace T2 {
use T1\A;
class B {
public function test(): A
{
return (new A)->itself();
}
}
}
?>
-----
<?php
namespace T1 {
class A {
/**
* @return $this
*/
public function itself(): self
{
return $this;
}
}
}
namespace T2 {
use T1\A;
class B {
public function test(): A
{
return (new A)->itself();
}
}
}
?>

View File

@ -0,0 +1,51 @@
<?php
namespace T1 {
class A {
/**
* @return $this
*/
public function itself()
{
return $this;
}
}
}
namespace T2 {
use T1\A;
class B {
public function test(): A
{
return (new A)->itself();
}
}
}
?>
-----
<?php
namespace T1 {
class A {
/**
* @return $this
*/
public function itself(): self
{
return $this;
}
}
}
namespace T2 {
use T1\A;
class B {
public function test(): A
{
return (new A)->itself();
}
}
}
?>

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class ImportedTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
/**
* @return Iterator<SmartFileInfo>
*/
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureImported');
}
public function provideConfigFilePath(): string
{
return __DIR__ . '/config/import_names.php';
}
}

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::STATIC_RETURN_TYPE - 1);
$parameters->set(Option::PHPSTAN_FOR_RECTOR_PATH, __DIR__ . '/../../../../../../phpstan-for-rector.neon');
$parameters->set(Option::AUTO_IMPORT_NAMES, true);
$services = $containerConfigurator->services();
$services->set(ReturnTypeDeclarationRector::class);
};

View File

@ -9,6 +9,7 @@ use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Return_;
@ -30,6 +31,7 @@ use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Php\PhpVersionProvider;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
use Rector\TypeDeclaration\Contract\TypeInferer\ReturnTypeInfererInterface;
@ -50,14 +52,15 @@ final class ReturnTypeInferer
*/
public function __construct(
array $returnTypeInferers,
private readonly TypeNormalizer $typeNormalizer,
private readonly TypeNormalizer $typeNormalizer,
PriorityAwareSorter $priorityAwareSorter,
private readonly GenericClassStringTypeNormalizer $genericClassStringTypeNormalizer,
private readonly PhpVersionProvider $phpVersionProvider,
private readonly ParameterProvider $parameterProvider,
private readonly BetterNodeFinder $betterNodeFinder,
private readonly ReflectionProvider $reflectionProvider,
private readonly NodeTypeResolver $nodeTypeResolver
private readonly GenericClassStringTypeNormalizer $genericClassStringTypeNormalizer,
private readonly PhpVersionProvider $phpVersionProvider,
private readonly ParameterProvider $parameterProvider,
private readonly BetterNodeFinder $betterNodeFinder,
private readonly ReflectionProvider $reflectionProvider,
private readonly NodeTypeResolver $nodeTypeResolver,
private readonly NodeNameResolver $nodeNameResolver
) {
$this->returnTypeInferers = $priorityAwareSorter->sort($returnTypeInferers);
}
@ -105,6 +108,11 @@ final class ReturnTypeInferer
continue;
}
$type = $this->verifyThisType($type, $functionLike);
if (! $type instanceof Type) {
continue;
}
// normalize ConstStringType to ClassStringType
$resolvedType = $this->genericClassStringTypeNormalizer->normalize($type);
return $this->resolveTypeWithVoidHandling($functionLike, $resolvedType);
@ -127,6 +135,27 @@ final class ReturnTypeInferer
return $type;
}
public function verifyThisType(Type $type, FunctionLike $functionLike): ?Type
{
if (! $type instanceof ThisType) {
return $type;
}
$class = $this->betterNodeFinder->findParentType($functionLike, Class_::class);
$objectType = $type->getStaticObjectType();
$objectTypeClassName = $objectType->getClassName();
if (! $class instanceof Class_) {
return $type;
}
if ($this->nodeNameResolver->isName($class, $objectTypeClassName)) {
return $type;
}
return new MixedType();
}
private function resolveTypeWithVoidHandling(ClassMethod|Function_|Closure $functionLike, Type $resolvedType): Type
{
if ($resolvedType instanceof VoidType) {