mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-07 20:00:50 +00:00
decouple UnionTypeAnalyzer and UnionTypeAnalysis
This commit is contained in:
parent
49b3ca04b8
commit
ba72bc847d
|
@ -6,3 +6,4 @@ services:
|
|||
Rector\PHPStanStaticTypeMapper\:
|
||||
resource: '../src'
|
||||
exclude:
|
||||
- '../src/ValueObject/*'
|
||||
|
|
|
@ -158,14 +158,6 @@ final class PHPStanStaticTypeMapper
|
|||
return new Identifier('callable');
|
||||
}
|
||||
|
||||
// if ($phpStanType instanceof ShortenedObjectType) {
|
||||
// return new FullyQualified($phpStanType->getFullyQualifiedName());
|
||||
// }
|
||||
//
|
||||
// if ($phpStanType instanceof AliasedObjectType) {
|
||||
// return new Name($phpStanType->getClassName());
|
||||
// }
|
||||
|
||||
if ($phpStanType instanceof TypeWithClassName) {
|
||||
$lowerCasedClassName = strtolower($phpStanType->getClassName());
|
||||
if ($lowerCasedClassName === 'callable') {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\PHPStanStaticTypeMapper\TypeAnalyzer;
|
||||
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\IterableType;
|
||||
use PHPStan\Type\NullType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\PHPStanStaticTypeMapper\ValueObject\UnionTypeAnalysis;
|
||||
use Traversable;
|
||||
|
||||
final class UnionTypeAnalyzer
|
||||
{
|
||||
public function analyseForNullableAndIterable(UnionType $unionType): ?UnionTypeAnalysis
|
||||
{
|
||||
$isNullableType = false;
|
||||
$hasIterable = false;
|
||||
|
||||
foreach ($unionType->getTypes() as $unionedType) {
|
||||
if ($unionedType instanceof IterableType) {
|
||||
$hasIterable = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($unionedType instanceof ArrayType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($unionedType instanceof NullType) {
|
||||
$isNullableType = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($unionedType instanceof ObjectType) {
|
||||
if ($unionedType->getClassName() === Traversable::class) {
|
||||
$hasIterable = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return new UnionTypeAnalysis($isNullableType, $hasIterable);
|
||||
}
|
||||
}
|
|
@ -4,19 +4,15 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\PHPStanStaticTypeMapper\TypeMapper;
|
||||
|
||||
use PHP_CodeSniffer\Reports\Full;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\NullableType;
|
||||
use PhpParser\Node\UnionType;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Type\Generic\GenericObjectType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
|
||||
use Rector\PHPStan\Type\AliasedObjectType;
|
||||
use Rector\PHPStan\Type\FullyQualifiedObjectType;
|
||||
use Rector\PHPStan\Type\ShortenedObjectType;
|
||||
|
@ -58,34 +54,9 @@ final class ObjectTypeMapper implements TypeMapperInterface
|
|||
if ($type->getClassName() === 'object') {
|
||||
return new Identifier('object');
|
||||
}
|
||||
|
||||
return new FullyQualified($type->getClassName());
|
||||
|
||||
// if ($type->getClassName()) {
|
||||
// dump($type);
|
||||
// die;
|
||||
// }
|
||||
//
|
||||
// if ($type->getClassName() === 'object') {
|
||||
// dump($type);
|
||||
// die;
|
||||
// } else {
|
||||
// die;
|
||||
// }
|
||||
//
|
||||
// return new Identifier('object');
|
||||
}
|
||||
|
||||
// ...
|
||||
dump($type->getFullyQualifiedName());
|
||||
die;
|
||||
|
||||
// fallback
|
||||
return new FullyQualified($type->getFullyQualifiedName());
|
||||
|
||||
dump($type);
|
||||
die;
|
||||
|
||||
return null;
|
||||
return new FullyQualified($type->getClassName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,12 @@ namespace Rector\PHPStanStaticTypeMapper\TypeMapper;
|
|||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\NullableType;
|
||||
use PhpParser\Node\UnionType as PhpParserUnionType;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\IterableType;
|
||||
use PHPStan\Type\NullType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use PHPStan\Type\UnionType;
|
||||
|
@ -20,8 +21,8 @@ use Rector\Php\PhpVersionProvider;
|
|||
use Rector\PHPStanStaticTypeMapper\Contract\PHPStanStaticTypeMapperAwareInterface;
|
||||
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
|
||||
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
|
||||
use Rector\PHPStanStaticTypeMapper\TypeAnalyzer\UnionTypeAnalyzer;
|
||||
use Rector\ValueObject\PhpVersionFeature;
|
||||
use Traversable;
|
||||
|
||||
final class UnionTypeMapper implements TypeMapperInterface, PHPStanStaticTypeMapperAwareInterface
|
||||
{
|
||||
|
@ -35,9 +36,15 @@ final class UnionTypeMapper implements TypeMapperInterface, PHPStanStaticTypeMap
|
|||
*/
|
||||
private $phpVersionProvider;
|
||||
|
||||
public function __construct(PhpVersionProvider $phpVersionProvider)
|
||||
/**
|
||||
* @var UnionTypeAnalyzer
|
||||
*/
|
||||
private $unionTypeAnalyzer;
|
||||
|
||||
public function __construct(PhpVersionProvider $phpVersionProvider, UnionTypeAnalyzer $unionTypeAnalyzer)
|
||||
{
|
||||
$this->phpVersionProvider = $phpVersionProvider;
|
||||
$this->unionTypeAnalyzer = $unionTypeAnalyzer;
|
||||
}
|
||||
|
||||
public function getNodeClass(): string
|
||||
|
@ -61,6 +68,9 @@ final class UnionTypeMapper implements TypeMapperInterface, PHPStanStaticTypeMap
|
|||
return new AttributeAwareUnionTypeNode($unionTypesNodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setPHPStanStaticTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void
|
||||
{
|
||||
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
|
||||
|
@ -84,16 +94,16 @@ final class UnionTypeMapper implements TypeMapperInterface, PHPStanStaticTypeMap
|
|||
return $this->matchTypeForUnionedObjectTypes($type);
|
||||
}
|
||||
|
||||
$nullabledTypeNode = $this->mapToPhpParserNode($nullabledType);
|
||||
$nullabledTypeNode = $this->phpStanStaticTypeMapper->mapToPhpParserNode($nullabledType);
|
||||
if ($nullabledTypeNode === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($nullabledTypeNode instanceof Node\NullableType) {
|
||||
if ($nullabledTypeNode instanceof NullableType) {
|
||||
return $nullabledTypeNode;
|
||||
}
|
||||
|
||||
if ($nullabledTypeNode instanceof Node\UnionType) {
|
||||
if ($nullabledTypeNode instanceof PhpParserUnionType) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
|
@ -102,36 +112,13 @@ final class UnionTypeMapper implements TypeMapperInterface, PHPStanStaticTypeMap
|
|||
|
||||
private function matchArrayTypes(UnionType $unionType): ?Identifier
|
||||
{
|
||||
$isNullableType = false;
|
||||
$hasIterable = false;
|
||||
|
||||
foreach ($unionType->getTypes() as $unionedType) {
|
||||
if ($unionedType instanceof IterableType) {
|
||||
$hasIterable = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($unionedType instanceof ArrayType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($unionedType instanceof NullType) {
|
||||
$isNullableType = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($unionedType instanceof ObjectType) {
|
||||
if ($unionedType->getClassName() === Traversable::class) {
|
||||
$hasIterable = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$unionTypeAnalysis = $this->unionTypeAnalyzer->analyseForNullableAndIterable($unionType);
|
||||
if ($unionTypeAnalysis === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = $hasIterable ? 'iterable' : 'array';
|
||||
if ($isNullableType) {
|
||||
$type = $unionTypeAnalysis->hasIterable() ? 'iterable' : 'array';
|
||||
if ($unionTypeAnalysis->isNullableType()) {
|
||||
return new Identifier('?' . $type);
|
||||
}
|
||||
|
||||
|
@ -158,9 +145,8 @@ final class UnionTypeMapper implements TypeMapperInterface, PHPStanStaticTypeMap
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Node\Name|Node\Name\FullyQualified|Node\UnionType|null
|
||||
* @return Name|FullyQualified|PhpParserUnionType|null
|
||||
*/
|
||||
private function matchTypeForUnionedObjectTypes(UnionType $unionType): ?Node
|
||||
{
|
||||
|
@ -169,7 +155,47 @@ final class UnionTypeMapper implements TypeMapperInterface, PHPStanStaticTypeMap
|
|||
return $phpParserUnionType;
|
||||
}
|
||||
|
||||
// do the type should be compatible with all other types, e.g. A extends B, B
|
||||
// the type should be compatible with all other types, e.g. A extends B, B
|
||||
$compatibleObjectCandidate = $this->resolveCompatibleObjectCandidate($unionType);
|
||||
if ($compatibleObjectCandidate === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new FullyQualified($compatibleObjectCandidate);
|
||||
}
|
||||
|
||||
private function matchPhpParserUnionType(UnionType $unionType): ?PhpParserUnionType
|
||||
{
|
||||
if (! $this->phpVersionProvider->isAtLeast(PhpVersionFeature::UNION_TYPES)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$phpParserUnionedTypes = [];
|
||||
|
||||
foreach ($unionType->getTypes() as $unionedType) {
|
||||
/** @var Identifier|Name|null $phpParserNode */
|
||||
$phpParserNode = $this->phpStanStaticTypeMapper->mapToPhpParserNode($unionedType);
|
||||
if ($phpParserNode === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$phpParserUnionedTypes[] = $phpParserNode;
|
||||
}
|
||||
|
||||
return new PhpParserUnionType($phpParserUnionedTypes);
|
||||
}
|
||||
|
||||
private function areTypeWithClassNamesRelated(TypeWithClassName $firstType, TypeWithClassName $secondType): bool
|
||||
{
|
||||
if (is_a($firstType->getClassName(), $secondType->getClassName(), true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return is_a($secondType->getClassName(), $firstType->getClassName(), true);
|
||||
}
|
||||
|
||||
private function resolveCompatibleObjectCandidate(UnionType $unionType): ?string
|
||||
{
|
||||
foreach ($unionType->getTypes() as $unionedType) {
|
||||
if (! $unionedType instanceof TypeWithClassName) {
|
||||
return null;
|
||||
|
@ -185,38 +211,9 @@ final class UnionTypeMapper implements TypeMapperInterface, PHPStanStaticTypeMap
|
|||
}
|
||||
}
|
||||
|
||||
return new Node\Name\FullyQualified($unionedType->getClassName());
|
||||
return $unionedType->getClassName();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function matchPhpParserUnionType(UnionType $unionType): ?Node\UnionType
|
||||
{
|
||||
if (! $this->phpVersionProvider->isAtLeast(PhpVersionFeature::UNION_TYPES)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$phpParserUnionedTypes = [];
|
||||
foreach ($unionType->getTypes() as $unionedType) {
|
||||
/** @var Identifier|Node\Name|null $phpParserNode */
|
||||
$phpParserNode = $this->mapToPhpParserNode($unionedType);
|
||||
if ($phpParserNode === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$phpParserUnionedTypes[] = $phpParserNode;
|
||||
}
|
||||
|
||||
return new Node\UnionType($phpParserUnionedTypes);
|
||||
}
|
||||
|
||||
private function areTypeWithClassNamesRelated(TypeWithClassName $firstType, TypeWithClassName $secondType): bool
|
||||
{
|
||||
if (is_a($firstType->getClassName(), $secondType->getClassName(), true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return is_a($secondType->getClassName(), $firstType->getClassName(), true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\PHPStanStaticTypeMapper\ValueObject;
|
||||
|
||||
final class UnionTypeAnalysis
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isNullableType = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $hasIterable = false;
|
||||
|
||||
public function __construct(bool $isNullableType, bool $hasIterable)
|
||||
{
|
||||
$this->isNullableType = $isNullableType;
|
||||
$this->hasIterable = $hasIterable;
|
||||
}
|
||||
|
||||
public function isNullableType(): bool
|
||||
{
|
||||
return $this->isNullableType;
|
||||
}
|
||||
|
||||
public function hasIterable(): bool
|
||||
{
|
||||
return $this->hasIterable;
|
||||
}
|
||||
}
|
|
@ -270,6 +270,7 @@ final class DumpNodesCommand extends AbstractCommand
|
|||
$node = new Function_('some_function');
|
||||
} elseif ($nodeClass === ClassMethod::class) {
|
||||
$node = new ClassMethod('someMethod');
|
||||
$node->flags |= Class_::MODIFIER_PUBLIC;
|
||||
} elseif ($nodeClass === Case_::class) {
|
||||
$node = new Case_(new ConstFetch(new Name('true')));
|
||||
} elseif ($nodeClass === Use_::class) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user