mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-07 03:40:50 +00:00
[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:
parent
008b65aecb
commit
2468277687
2
.github/workflows/rector.yaml
vendored
2
.github/workflows/rector.yaml
vendored
|
@ -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: .
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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';
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user