[Renaming] Handle var static docblock in union on RenameClassRector (#1248)

* [Renaming] Handle var sstatic docblock in union on RenameClassRector

* update fixture

* more fixture

* more fixture

* more fixture

* Fixed 🎉

* add array static

* Fixed 🎉

* Fixed 🎉

* add failing fixture for array<static>

* Fixed 🎉

* better to return early on MethodTagValueNode

* move check to IdentifierTypeMapper

* clean up

* final touch: rename method

* final touch: clean up resolve class name

* final touch: verify if not already a ClassLike
This commit is contained in:
Abdul Malik Ikhsan 2021-11-16 17:51:26 +07:00 committed by GitHub
parent 5d637a8040
commit 50096d7955
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 319 additions and 40 deletions

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer;
use PhpParser\Node\Name;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\NodeTypeResolver\PhpDoc\PhpDocNodeTraverser\RenamingPhpDocNodeVisitorFactory;
use Rector\NodeTypeResolver\PhpDocNodeVisitor\ClassRenamePhpDocNodeVisitor;
@ -27,26 +26,6 @@ final class DocBlockClassRenamer
return;
}
$phpDocNode = $phpDocInfo->getPhpDocNode();
$tags = $phpDocNode->getTags();
foreach ($tags as $tag) {
$tagValueNode = $tag->value;
$tagName = $phpDocInfo->resolveNameForPhpDocTagValueNode($tagValueNode);
if (! is_string($tagName)) {
continue;
}
$tagValues = $phpDocInfo->getTagsByName($tagName);
foreach ($tagValues as $tagValue) {
$name = new Name((string) $tagValue->value);
if ($name->isSpecialClassName()) {
return;
}
}
}
$phpDocNodeTraverser = $this->renamingPhpDocNodeVisitorFactory->create();
$this->classRenamePhpDocNodeVisitor->setOldToNewTypes($oldToNewTypes);

View File

@ -7,10 +7,10 @@ namespace Rector\StaticTypeMapper\PhpDocParser;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassLike;
use PHPStan\Analyser\NameScope;
use PHPStan\Analyser\Scope;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\ClassStringType;
use PHPStan\Type\IterableType;
use PHPStan\Type\MixedType;
@ -19,7 +19,6 @@ use PHPStan\Type\StaticType;
use PHPStan\Type\Type;
use Rector\Core\Enum\ObjectReference;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\NodeCollector\ScopeResolver\ParentClassScopeResolver;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\StaticTypeMapper\Contract\PhpDocParser\PhpDocTypeMapperInterface;
@ -33,9 +32,9 @@ final class IdentifierTypeMapper implements PhpDocTypeMapperInterface
public function __construct(
private ObjectTypeSpecifier $objectTypeSpecifier,
private ScalarStringToTypeMapper $scalarStringToTypeMapper,
private ParentClassScopeResolver $parentClassScopeResolver,
private BetterNodeFinder $betterNodeFinder,
private NodeNameResolver $nodeNameResolver
private NodeNameResolver $nodeNameResolver,
private ReflectionProvider $reflectionProvider
) {
}
@ -67,18 +66,16 @@ final class IdentifierTypeMapper implements PhpDocTypeMapperInterface
return new ClassStringType();
}
$scope = $node->getAttribute(AttributeKey::SCOPE);
if ($loweredName === ObjectReference::SELF()->getValue()) {
return $this->mapSelf($node);
}
if ($loweredName === ObjectReference::PARENT()->getValue()) {
return $this->mapParent($scope);
return $this->mapParent($node);
}
if ($loweredName === ObjectReference::STATIC()->getValue()) {
return $this->mapStatic($scope);
return $this->mapStatic($node);
}
if ($loweredName === 'iterable') {
@ -93,13 +90,8 @@ final class IdentifierTypeMapper implements PhpDocTypeMapperInterface
private function mapSelf(Node $node): MixedType | SelfObjectType
{
$classLike = $this->betterNodeFinder->findParentType($node, ClassLike::class);
if (! $classLike instanceof ClassLike) {
return new MixedType();
}
// @todo check FQN
$className = $this->nodeNameResolver->getName($classLike);
$className = $this->resolveClassName($node);
if (! is_string($className)) {
// self outside the class, e.g. in a function
return new MixedType();
@ -108,9 +100,18 @@ final class IdentifierTypeMapper implements PhpDocTypeMapperInterface
return new SelfObjectType($className);
}
private function mapParent(Scope $scope): ParentStaticType | MixedType
private function mapParent(Node $node): ParentStaticType | MixedType
{
$parentClassReflection = $this->parentClassScopeResolver->resolveParentClassReflection($scope);
$className = $this->resolveClassName($node);
if (! is_string($className)) {
// parent outside the class, e.g. in a function
return new MixedType();
}
/** @var ClassReflection $classReflection */
$classReflection = $this->reflectionProvider->getClass($className);
$parentClassReflection = $classReflection->getParentClass();
if (! $parentClassReflection instanceof ClassReflection) {
return new MixedType();
}
@ -118,13 +119,38 @@ final class IdentifierTypeMapper implements PhpDocTypeMapperInterface
return new ParentStaticType($parentClassReflection);
}
private function mapStatic(Scope $scope): MixedType | StaticType
private function mapStatic(Node $node): MixedType | StaticType
{
$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
$className = $this->resolveClassName($node);
if (! is_string($className)) {
// static outside the class, e.g. in a function
return new MixedType();
}
/** @var ClassReflection $classReflection */
$classReflection = $this->reflectionProvider->getClass($className);
return new StaticType($classReflection);
}
private function resolveClassName(Node $node): ?string
{
$classLike = $node instanceof ClassLike
? $node
: $this->betterNodeFinder->findParentType($node, ClassLike::class);
if (! $classLike instanceof ClassLike) {
return null;
}
$className = $this->nodeNameResolver->getName($classLike);
if (! is_string($className)) {
return null;
}
if (! $this->reflectionProvider->hasClass($className)) {
return null;
}
return $className;
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture;
use DateTime;
use DateTimeInterface;
class ArrayStatic extends \DateTime
{
/**
* @var static[]|null
*/
private $dateTime;
public function __construct(DateTime $dateTime)
{
$this->dateTime = rand(0,1)
? [$dateTime]
: null;
}
}
?>
-----
<?php
namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture;
use DateTime;
use DateTimeInterface;
class ArrayStatic extends \DateTime
{
/**
* @var static[]|null
*/
private $dateTime;
public function __construct(\DateTimeInterface $dateTime)
{
$this->dateTime = rand(0,1)
? [$dateTime]
: null;
}
}
?>

View File

@ -0,0 +1,43 @@
<?php
namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture;
use DateTime;
use DateTimeInterface;
class ArrayStatic2 extends \DateTime
{
/**
* @var static[]
*/
private $dateTime;
public function __construct(DateTime $dateTime)
{
$this->dateTime = [$dateTime];
}
}
?>
-----
<?php
namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture;
use DateTime;
use DateTimeInterface;
class ArrayStatic2 extends \DateTime
{
/**
* @var static[]
*/
private $dateTime;
public function __construct(\DateTimeInterface $dateTime)
{
$this->dateTime = [$dateTime];
}
}
?>

View File

@ -0,0 +1,43 @@
<?php
namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture;
use DateTime;
use DateTimeInterface;
class ArrayStatic3 extends \DateTime
{
/**
* @var array<static>
*/
private $dateTime;
public function __construct(DateTime $dateTime)
{
$this->dateTime = [$dateTime];
}
}
?>
-----
<?php
namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture;
use DateTime;
use DateTimeInterface;
class ArrayStatic3 extends \DateTime
{
/**
* @var array<static>
*/
private $dateTime;
public function __construct(\DateTimeInterface $dateTime)
{
$this->dateTime = [$dateTime];
}
}
?>

View File

@ -0,0 +1,47 @@
<?php
namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture;
use DateTime;
use DateTimeInterface;
class VarStaticInNullable extends \DateTime
{
/**
* @var static|null
*/
private $dateTime;
public function __construct(DateTime $dateTime)
{
$this->dateTime = rand(0,1)
? $dateTime
: null;
}
}
?>
-----
<?php
namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture;
use DateTime;
use DateTimeInterface;
class VarStaticInNullable extends \DateTime
{
/**
* @var static|null
*/
private $dateTime;
public function __construct(\DateTimeInterface $dateTime)
{
$this->dateTime = rand(0,1)
? $dateTime
: null;
}
}
?>

View File

@ -0,0 +1,47 @@
<?php
namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture;
use DateTime;
use DateTimeInterface;
class VarStaticInNullable extends \DateTime
{
/**
* @var ?static
*/
private $dateTime;
public function __construct(DateTime $dateTime)
{
$this->dateTime = rand(0,1)
? $dateTime
: null;
}
}
?>
-----
<?php
namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture;
use DateTime;
use DateTimeInterface;
class VarStaticInNullable extends \DateTime
{
/**
* @var ?static
*/
private $dateTime;
public function __construct(\DateTimeInterface $dateTime)
{
$this->dateTime = rand(0,1)
? $dateTime
: null;
}
}
?>

View File

@ -0,0 +1,47 @@
<?php
namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture;
use DateTime;
use DateTimeInterface;
class VarStaticInUnion extends \DateTime
{
/**
* @var DateTimeInterface|static|null
*/
private $dateTime;
public function __construct(DateTime $dateTime)
{
$this->dateTime = rand(0,1)
? $dateTime
: null;
}
}
?>
-----
<?php
namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture;
use DateTime;
use DateTimeInterface;
class VarStaticInUnion extends \DateTime
{
/**
* @var DateTimeInterface|static|null
*/
private $dateTime;
public function __construct(\DateTimeInterface $dateTime)
{
$this->dateTime = rand(0,1)
? $dateTime
: null;
}
}
?>