2020-01-14 17:59:15 +00:00
|
|
|
<?php
|
|
|
|
|
2021-05-09 20:15:43 +00:00
|
|
|
declare (strict_types=1);
|
2020-01-14 17:59:15 +00:00
|
|
|
namespace Rector\PHPStanStaticTypeMapper\TypeMapper;
|
|
|
|
|
|
|
|
use PhpParser\Node;
|
2021-11-14 12:07:09 +00:00
|
|
|
use PhpParser\Node\ComplexType;
|
2020-01-14 17:59:15 +00:00
|
|
|
use PhpParser\Node\Identifier;
|
2020-01-14 20:14:35 +00:00
|
|
|
use PhpParser\Node\Name;
|
|
|
|
use PhpParser\Node\Name\FullyQualified;
|
|
|
|
use PhpParser\Node\NullableType;
|
|
|
|
use PhpParser\Node\UnionType as PhpParserUnionType;
|
2021-03-07 14:27:17 +00:00
|
|
|
use PhpParser\NodeAbstract;
|
2020-01-14 17:59:15 +00:00
|
|
|
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
2021-04-10 23:28:08 +00:00
|
|
|
use PHPStan\Type\Constant\ConstantBooleanType;
|
2020-02-02 18:15:36 +00:00
|
|
|
use PHPStan\Type\IterableType;
|
2022-02-11 18:56:06 +00:00
|
|
|
use PHPStan\Type\MixedType;
|
2020-01-14 17:59:15 +00:00
|
|
|
use PHPStan\Type\NullType;
|
2021-02-17 15:40:29 +00:00
|
|
|
use PHPStan\Type\ObjectType;
|
2020-01-14 17:59:15 +00:00
|
|
|
use PHPStan\Type\Type;
|
|
|
|
use PHPStan\Type\TypeWithClassName;
|
|
|
|
use PHPStan\Type\UnionType;
|
2020-09-01 17:56:30 +00:00
|
|
|
use PHPStan\Type\VoidType;
|
2021-03-20 15:27:18 +00:00
|
|
|
use Rector\BetterPhpDocParser\ValueObject\Type\BracketsAwareUnionTypeNode;
|
2021-10-25 13:44:53 +00:00
|
|
|
use Rector\Core\Enum\ObjectReference;
|
2020-02-06 21:48:18 +00:00
|
|
|
use Rector\Core\Php\PhpVersionProvider;
|
2021-03-07 14:27:17 +00:00
|
|
|
use Rector\Core\Rector\AbstractRector;
|
2020-02-06 21:48:18 +00:00
|
|
|
use Rector\Core\ValueObject\PhpVersionFeature;
|
2021-09-06 12:31:44 +00:00
|
|
|
use Rector\NodeNameResolver\NodeNameResolver;
|
2020-01-14 17:59:15 +00:00
|
|
|
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
|
2020-03-24 20:25:08 +00:00
|
|
|
use Rector\PHPStanStaticTypeMapper\DoctrineTypeAnalyzer;
|
2021-10-27 19:25:02 +00:00
|
|
|
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
|
2020-01-14 17:59:15 +00:00
|
|
|
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
|
2021-01-18 19:23:10 +00:00
|
|
|
use Rector\PHPStanStaticTypeMapper\TypeAnalyzer\BoolUnionTypeAnalyzer;
|
2020-01-14 20:14:35 +00:00
|
|
|
use Rector\PHPStanStaticTypeMapper\TypeAnalyzer\UnionTypeAnalyzer;
|
2021-02-17 15:40:29 +00:00
|
|
|
use Rector\PHPStanStaticTypeMapper\TypeAnalyzer\UnionTypeCommonTypeNarrower;
|
2021-01-20 11:41:35 +00:00
|
|
|
use Rector\PHPStanStaticTypeMapper\ValueObject\UnionTypeAnalysis;
|
2022-03-31 11:04:24 +00:00
|
|
|
use RectorPrefix20220331\Symfony\Contracts\Service\Attribute\Required;
|
2021-06-30 11:36:37 +00:00
|
|
|
/**
|
|
|
|
* @implements TypeMapperInterface<UnionType>
|
|
|
|
*/
|
2021-05-10 22:23:08 +00:00
|
|
|
final class UnionTypeMapper implements \Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface
|
2020-01-14 17:59:15 +00:00
|
|
|
{
|
|
|
|
/**
|
2021-05-12 13:15:45 +00:00
|
|
|
* @var \Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper
|
2020-01-14 17:59:15 +00:00
|
|
|
*/
|
|
|
|
private $phpStanStaticTypeMapper;
|
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\PHPStanStaticTypeMapper\DoctrineTypeAnalyzer
|
2020-01-14 17:59:15 +00:00
|
|
|
*/
|
2021-05-10 23:39:21 +00:00
|
|
|
private $doctrineTypeAnalyzer;
|
2020-01-14 20:14:35 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\Core\Php\PhpVersionProvider
|
2020-01-14 20:14:35 +00:00
|
|
|
*/
|
2021-05-10 23:39:21 +00:00
|
|
|
private $phpVersionProvider;
|
2020-03-24 20:25:08 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\PHPStanStaticTypeMapper\TypeAnalyzer\UnionTypeAnalyzer
|
2020-03-24 20:25:08 +00:00
|
|
|
*/
|
2021-05-10 23:39:21 +00:00
|
|
|
private $unionTypeAnalyzer;
|
2021-01-18 19:23:10 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\PHPStanStaticTypeMapper\TypeAnalyzer\BoolUnionTypeAnalyzer
|
2021-01-18 19:23:10 +00:00
|
|
|
*/
|
|
|
|
private $boolUnionTypeAnalyzer;
|
2021-02-17 15:40:29 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\PHPStanStaticTypeMapper\TypeAnalyzer\UnionTypeCommonTypeNarrower
|
2021-02-17 15:40:29 +00:00
|
|
|
*/
|
|
|
|
private $unionTypeCommonTypeNarrower;
|
2021-09-06 12:31:44 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-09-06 12:31:44 +00:00
|
|
|
* @var \Rector\NodeNameResolver\NodeNameResolver
|
|
|
|
*/
|
|
|
|
private $nodeNameResolver;
|
|
|
|
public function __construct(\Rector\PHPStanStaticTypeMapper\DoctrineTypeAnalyzer $doctrineTypeAnalyzer, \Rector\Core\Php\PhpVersionProvider $phpVersionProvider, \Rector\PHPStanStaticTypeMapper\TypeAnalyzer\UnionTypeAnalyzer $unionTypeAnalyzer, \Rector\PHPStanStaticTypeMapper\TypeAnalyzer\BoolUnionTypeAnalyzer $boolUnionTypeAnalyzer, \Rector\PHPStanStaticTypeMapper\TypeAnalyzer\UnionTypeCommonTypeNarrower $unionTypeCommonTypeNarrower, \Rector\NodeNameResolver\NodeNameResolver $nodeNameResolver)
|
2021-05-09 20:15:43 +00:00
|
|
|
{
|
2021-05-10 23:39:21 +00:00
|
|
|
$this->doctrineTypeAnalyzer = $doctrineTypeAnalyzer;
|
2020-01-14 17:59:15 +00:00
|
|
|
$this->phpVersionProvider = $phpVersionProvider;
|
2020-01-14 20:14:35 +00:00
|
|
|
$this->unionTypeAnalyzer = $unionTypeAnalyzer;
|
2021-01-18 19:23:10 +00:00
|
|
|
$this->boolUnionTypeAnalyzer = $boolUnionTypeAnalyzer;
|
2021-02-17 15:40:29 +00:00
|
|
|
$this->unionTypeCommonTypeNarrower = $unionTypeCommonTypeNarrower;
|
2021-09-06 12:31:44 +00:00
|
|
|
$this->nodeNameResolver = $nodeNameResolver;
|
2020-01-14 17:59:15 +00:00
|
|
|
}
|
2020-01-15 02:16:22 +00:00
|
|
|
/**
|
|
|
|
* @required
|
|
|
|
*/
|
2021-11-28 19:07:44 +00:00
|
|
|
public function autowire(\Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper $phpStanStaticTypeMapper) : void
|
2020-01-15 02:16:22 +00:00
|
|
|
{
|
|
|
|
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
|
|
|
|
}
|
2021-03-06 21:16:18 +00:00
|
|
|
/**
|
|
|
|
* @return class-string<Type>
|
|
|
|
*/
|
2021-05-09 20:15:43 +00:00
|
|
|
public function getNodeClass() : string
|
2020-01-14 17:59:15 +00:00
|
|
|
{
|
2021-05-10 22:23:08 +00:00
|
|
|
return \PHPStan\Type\UnionType::class;
|
2020-01-14 17:59:15 +00:00
|
|
|
}
|
|
|
|
/**
|
2021-12-10 10:22:23 +00:00
|
|
|
* @param UnionType $type
|
2020-01-14 17:59:15 +00:00
|
|
|
*/
|
2021-12-10 10:22:23 +00:00
|
|
|
public function mapToPHPStanPhpDocTypeNode(\PHPStan\Type\Type $type, \Rector\PHPStanStaticTypeMapper\Enum\TypeKind $typeKind) : \PHPStan\PhpDocParser\Ast\Type\TypeNode
|
2020-01-14 17:59:15 +00:00
|
|
|
{
|
|
|
|
$unionTypesNodes = [];
|
2020-02-02 18:15:36 +00:00
|
|
|
$skipIterable = $this->shouldSkipIterable($type);
|
2020-01-14 17:59:15 +00:00
|
|
|
foreach ($type->getTypes() as $unionedType) {
|
2021-05-10 22:23:08 +00:00
|
|
|
if ($unionedType instanceof \PHPStan\Type\IterableType && $skipIterable) {
|
2020-02-02 18:15:36 +00:00
|
|
|
continue;
|
|
|
|
}
|
2021-07-04 13:13:54 +00:00
|
|
|
$unionTypesNodes[] = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($unionedType, $typeKind);
|
2020-01-14 17:59:15 +00:00
|
|
|
}
|
2021-05-09 20:15:43 +00:00
|
|
|
$unionTypesNodes = \array_unique($unionTypesNodes);
|
2021-05-10 22:23:08 +00:00
|
|
|
return new \Rector\BetterPhpDocParser\ValueObject\Type\BracketsAwareUnionTypeNode($unionTypesNodes);
|
2020-01-14 17:59:15 +00:00
|
|
|
}
|
|
|
|
/**
|
2021-12-10 10:22:23 +00:00
|
|
|
* @param UnionType $type
|
2020-01-14 17:59:15 +00:00
|
|
|
*/
|
2021-12-10 10:22:23 +00:00
|
|
|
public function mapToPhpParserNode(\PHPStan\Type\Type $type, \Rector\PHPStanStaticTypeMapper\Enum\TypeKind $typeKind) : ?\PhpParser\Node
|
2020-01-14 17:59:15 +00:00
|
|
|
{
|
|
|
|
$arrayNode = $this->matchArrayTypes($type);
|
|
|
|
if ($arrayNode !== null) {
|
|
|
|
return $arrayNode;
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
if ($this->boolUnionTypeAnalyzer->isNullableBoolUnionType($type) && !$this->phpVersionProvider->isAtLeastPhpVersion(\Rector\Core\ValueObject\PhpVersionFeature::UNION_TYPES)) {
|
|
|
|
return new \PhpParser\Node\NullableType(new \PhpParser\Node\Name('bool'));
|
2021-01-18 19:23:10 +00:00
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
if (!$this->phpVersionProvider->isAtLeastPhpVersion(\Rector\Core\ValueObject\PhpVersionFeature::UNION_TYPES) && $this->isFalseBoolUnion($type)) {
|
2021-04-10 23:28:08 +00:00
|
|
|
// return new Bool
|
2021-05-10 22:23:08 +00:00
|
|
|
return new \PhpParser\Node\Name('bool');
|
2021-04-10 23:28:08 +00:00
|
|
|
}
|
2020-01-14 17:59:15 +00:00
|
|
|
// special case for nullable
|
|
|
|
$nullabledType = $this->matchTypeForNullableUnionType($type);
|
2021-05-10 22:23:08 +00:00
|
|
|
if (!$nullabledType instanceof \PHPStan\Type\Type) {
|
2020-01-14 17:59:15 +00:00
|
|
|
// use first unioned type in case of unioned object types
|
2021-07-04 13:13:54 +00:00
|
|
|
return $this->matchTypeForUnionedObjectTypes($type, $typeKind);
|
2020-01-14 17:59:15 +00:00
|
|
|
}
|
2020-09-01 17:56:30 +00:00
|
|
|
// void cannot be nullable
|
2021-05-10 22:23:08 +00:00
|
|
|
if ($nullabledType instanceof \PHPStan\Type\VoidType) {
|
2020-09-01 17:56:30 +00:00
|
|
|
return null;
|
|
|
|
}
|
2021-07-04 13:13:54 +00:00
|
|
|
$nullabledTypeNode = $this->phpStanStaticTypeMapper->mapToPhpParserNode($nullabledType, $typeKind);
|
2021-05-10 22:23:08 +00:00
|
|
|
if (!$nullabledTypeNode instanceof \PhpParser\Node) {
|
2020-01-14 17:59:15 +00:00
|
|
|
return null;
|
|
|
|
}
|
2022-02-11 18:56:06 +00:00
|
|
|
if (\in_array(\get_class($nullabledTypeNode), [\PhpParser\Node\NullableType::class, \PhpParser\Node\ComplexType::class], \true)) {
|
2020-01-14 17:59:15 +00:00
|
|
|
return $nullabledTypeNode;
|
|
|
|
}
|
2022-02-11 18:56:06 +00:00
|
|
|
/** @var Name $nullabledTypeNode */
|
|
|
|
if (!$this->nodeNameResolver->isName($nullabledTypeNode, 'false')) {
|
|
|
|
return new \PhpParser\Node\NullableType($nullabledTypeNode);
|
2020-01-14 17:59:15 +00:00
|
|
|
}
|
2022-02-11 18:56:06 +00:00
|
|
|
return null;
|
2020-01-14 17:59:15 +00:00
|
|
|
}
|
2021-07-05 23:06:30 +00:00
|
|
|
private function shouldSkipIterable(\PHPStan\Type\UnionType $unionType) : bool
|
2020-04-26 00:57:47 +00:00
|
|
|
{
|
|
|
|
$unionTypeAnalysis = $this->unionTypeAnalyzer->analyseForNullableAndIterable($unionType);
|
2021-05-10 22:23:08 +00:00
|
|
|
if (!$unionTypeAnalysis instanceof \Rector\PHPStanStaticTypeMapper\ValueObject\UnionTypeAnalysis) {
|
2021-05-09 20:15:43 +00:00
|
|
|
return \false;
|
2020-04-26 00:57:47 +00:00
|
|
|
}
|
2021-05-09 20:15:43 +00:00
|
|
|
if (!$unionTypeAnalysis->hasIterable()) {
|
|
|
|
return \false;
|
2020-12-19 15:24:53 +00:00
|
|
|
}
|
|
|
|
return $unionTypeAnalysis->hasArray();
|
2020-04-26 00:57:47 +00:00
|
|
|
}
|
2020-05-10 21:02:46 +00:00
|
|
|
/**
|
2021-05-30 10:12:56 +00:00
|
|
|
* @return \PhpParser\Node\Name|\PhpParser\Node\NullableType|null
|
2020-05-10 21:02:46 +00:00
|
|
|
*/
|
2021-07-05 23:06:30 +00:00
|
|
|
private function matchArrayTypes(\PHPStan\Type\UnionType $unionType)
|
2020-01-14 17:59:15 +00:00
|
|
|
{
|
2020-01-14 20:14:35 +00:00
|
|
|
$unionTypeAnalysis = $this->unionTypeAnalyzer->analyseForNullableAndIterable($unionType);
|
2021-05-10 22:23:08 +00:00
|
|
|
if (!$unionTypeAnalysis instanceof \Rector\PHPStanStaticTypeMapper\ValueObject\UnionTypeAnalysis) {
|
2020-01-14 17:59:15 +00:00
|
|
|
return null;
|
|
|
|
}
|
2020-01-14 20:14:35 +00:00
|
|
|
$type = $unionTypeAnalysis->hasIterable() ? 'iterable' : 'array';
|
|
|
|
if ($unionTypeAnalysis->isNullableType()) {
|
2021-05-10 22:23:08 +00:00
|
|
|
return new \PhpParser\Node\NullableType($type);
|
2020-01-14 17:59:15 +00:00
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
return new \PhpParser\Node\Name($type);
|
2020-01-14 17:59:15 +00:00
|
|
|
}
|
2021-07-05 23:06:30 +00:00
|
|
|
private function matchTypeForNullableUnionType(\PHPStan\Type\UnionType $unionType) : ?\PHPStan\Type\Type
|
2020-01-14 17:59:15 +00:00
|
|
|
{
|
2021-05-09 20:15:43 +00:00
|
|
|
if (\count($unionType->getTypes()) !== 2) {
|
2020-01-14 17:59:15 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
$firstType = $unionType->getTypes()[0];
|
|
|
|
$secondType = $unionType->getTypes()[1];
|
2021-05-10 22:23:08 +00:00
|
|
|
if ($firstType instanceof \PHPStan\Type\NullType) {
|
2020-01-14 17:59:15 +00:00
|
|
|
return $secondType;
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
if ($secondType instanceof \PHPStan\Type\NullType) {
|
2020-01-14 17:59:15 +00:00
|
|
|
return $firstType;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2021-09-06 12:31:44 +00:00
|
|
|
private function hasObjectAndStaticType(\PhpParser\Node\UnionType $phpParserUnionType) : bool
|
|
|
|
{
|
|
|
|
$typeNames = $this->nodeNameResolver->getNames($phpParserUnionType->types);
|
2021-10-25 13:44:53 +00:00
|
|
|
$diff = \array_diff(['object', \Rector\Core\Enum\ObjectReference::STATIC()->getValue()], $typeNames);
|
2021-09-06 12:31:44 +00:00
|
|
|
return $diff === [];
|
|
|
|
}
|
2020-01-14 17:59:15 +00:00
|
|
|
/**
|
2021-10-11 06:45:23 +00:00
|
|
|
* @return Name|FullyQualified|PhpParserUnionType|NullableType|null
|
2020-01-14 17:59:15 +00:00
|
|
|
*/
|
2021-10-27 19:25:02 +00:00
|
|
|
private function matchTypeForUnionedObjectTypes(\PHPStan\Type\UnionType $unionType, \Rector\PHPStanStaticTypeMapper\Enum\TypeKind $typeKind) : ?\PhpParser\Node
|
2020-01-14 17:59:15 +00:00
|
|
|
{
|
2021-07-04 13:13:54 +00:00
|
|
|
$phpParserUnionType = $this->matchPhpParserUnionType($unionType, $typeKind);
|
2020-01-14 17:59:15 +00:00
|
|
|
if ($phpParserUnionType !== null) {
|
2021-05-10 22:23:08 +00:00
|
|
|
if (!$this->phpVersionProvider->isAtLeastPhpVersion(\Rector\Core\ValueObject\PhpVersionFeature::UNION_TYPES)) {
|
2021-01-18 19:23:10 +00:00
|
|
|
// maybe all one type?
|
|
|
|
if ($this->boolUnionTypeAnalyzer->isBoolUnionType($unionType)) {
|
2021-05-10 22:23:08 +00:00
|
|
|
return new \PhpParser\Node\Name('bool');
|
2021-01-18 19:23:10 +00:00
|
|
|
}
|
2020-10-12 14:34:28 +00:00
|
|
|
return null;
|
|
|
|
}
|
2021-09-06 12:31:44 +00:00
|
|
|
if ($this->hasObjectAndStaticType($phpParserUnionType)) {
|
|
|
|
return null;
|
|
|
|
}
|
2020-01-14 17:59:15 +00:00
|
|
|
return $phpParserUnionType;
|
|
|
|
}
|
2021-01-18 19:23:10 +00:00
|
|
|
if ($this->boolUnionTypeAnalyzer->isBoolUnionType($unionType)) {
|
2021-05-10 22:23:08 +00:00
|
|
|
return new \PhpParser\Node\Name('bool');
|
2021-01-18 19:23:10 +00:00
|
|
|
}
|
2021-10-11 06:45:23 +00:00
|
|
|
return $this->processResolveCompatibleObjectCandidates($unionType);
|
|
|
|
}
|
|
|
|
private function processResolveCompatibleObjectCandidates(\PHPStan\Type\UnionType $unionType) : ?\PhpParser\Node
|
|
|
|
{
|
2020-01-14 20:14:35 +00:00
|
|
|
// the type should be compatible with all other types, e.g. A extends B, B
|
2021-02-17 15:40:29 +00:00
|
|
|
$compatibleObjectType = $this->resolveCompatibleObjectCandidate($unionType);
|
2021-10-11 06:45:23 +00:00
|
|
|
if ($compatibleObjectType instanceof \PHPStan\Type\UnionType) {
|
|
|
|
$type = $this->matchTypeForNullableUnionType($compatibleObjectType);
|
|
|
|
if ($type instanceof \PHPStan\Type\ObjectType) {
|
|
|
|
return new \PhpParser\Node\NullableType(new \PhpParser\Node\Name\FullyQualified($type->getClassName()));
|
|
|
|
}
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
if (!$compatibleObjectType instanceof \PHPStan\Type\ObjectType) {
|
2020-01-14 20:14:35 +00:00
|
|
|
return null;
|
2020-01-14 17:59:15 +00:00
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
return new \PhpParser\Node\Name\FullyQualified($compatibleObjectType->getClassName());
|
2020-01-14 17:59:15 +00:00
|
|
|
}
|
2021-10-27 19:25:02 +00:00
|
|
|
private function matchPhpParserUnionType(\PHPStan\Type\UnionType $unionType, \Rector\PHPStanStaticTypeMapper\Enum\TypeKind $typeKind) : ?\PhpParser\Node\UnionType
|
2020-01-14 17:59:15 +00:00
|
|
|
{
|
2021-05-10 22:23:08 +00:00
|
|
|
if (!$this->phpVersionProvider->isAtLeastPhpVersion(\Rector\Core\ValueObject\PhpVersionFeature::UNION_TYPES)) {
|
2020-01-14 17:59:15 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
$phpParserUnionedTypes = [];
|
|
|
|
foreach ($unionType->getTypes() as $unionedType) {
|
2022-02-11 18:56:06 +00:00
|
|
|
// void type and mixed type are not allowed in union
|
|
|
|
if (\in_array(\get_class($unionedType), [\PHPStan\Type\MixedType::class, \PHPStan\Type\VoidType::class], \true)) {
|
2020-12-09 18:38:45 +00:00
|
|
|
return null;
|
|
|
|
}
|
2022-02-11 08:39:14 +00:00
|
|
|
/**
|
|
|
|
* NullType inside UnionType is allowed
|
|
|
|
* make it on TypeKind property as changing other type, eg: return type may conflict with parent child implementation
|
|
|
|
*
|
|
|
|
* @var Identifier|Name|null $phpParserNode
|
|
|
|
*/
|
|
|
|
$phpParserNode = $unionedType instanceof \PHPStan\Type\NullType && $typeKind->equals(\Rector\PHPStanStaticTypeMapper\Enum\TypeKind::PROPERTY()) ? new \PhpParser\Node\Name('null') : $this->phpStanStaticTypeMapper->mapToPhpParserNode($unionedType, $typeKind);
|
2020-01-14 17:59:15 +00:00
|
|
|
if ($phpParserNode === null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
$phpParserUnionedTypes[] = $phpParserNode;
|
|
|
|
}
|
2021-06-06 10:19:58 +00:00
|
|
|
$phpParserUnionedTypes = \array_unique($phpParserUnionedTypes);
|
2022-02-11 08:39:14 +00:00
|
|
|
if (\count($phpParserUnionedTypes) < 2) {
|
|
|
|
return null;
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
return new \PhpParser\Node\UnionType($phpParserUnionedTypes);
|
2020-01-14 17:59:15 +00:00
|
|
|
}
|
2021-10-11 06:45:23 +00:00
|
|
|
/**
|
2021-10-30 14:18:31 +00:00
|
|
|
* @return \PHPStan\Type\TypeWithClassName|\PHPStan\Type\UnionType|null
|
2021-10-11 06:45:23 +00:00
|
|
|
*/
|
|
|
|
private function resolveCompatibleObjectCandidate(\PHPStan\Type\UnionType $unionType)
|
2020-01-14 20:14:35 +00:00
|
|
|
{
|
2020-03-24 20:25:08 +00:00
|
|
|
if ($this->doctrineTypeAnalyzer->isDoctrineCollectionWithIterableUnionType($unionType)) {
|
2021-10-11 06:45:23 +00:00
|
|
|
$objectType = new \PHPStan\Type\ObjectType('Doctrine\\Common\\Collections\\Collection');
|
2021-10-12 01:37:01 +00:00
|
|
|
return $this->unionTypeAnalyzer->isNullable($unionType) ? new \PHPStan\Type\UnionType([new \PHPStan\Type\NullType(), $objectType]) : $objectType;
|
2020-03-24 20:25:08 +00:00
|
|
|
}
|
2021-10-27 09:24:45 +00:00
|
|
|
$typesWithClassNames = $this->unionTypeAnalyzer->matchExclusiveTypesWithClassNames($unionType);
|
|
|
|
if ($typesWithClassNames === []) {
|
2020-03-24 20:25:08 +00:00
|
|
|
return null;
|
|
|
|
}
|
2021-10-27 09:24:45 +00:00
|
|
|
$sharedTypeWithClassName = $this->matchTwoObjectTypes($typesWithClassNames);
|
2021-05-10 22:23:08 +00:00
|
|
|
if ($sharedTypeWithClassName instanceof \PHPStan\Type\TypeWithClassName) {
|
2021-03-07 14:27:17 +00:00
|
|
|
return $this->correctObjectType($sharedTypeWithClassName);
|
2021-02-17 15:40:29 +00:00
|
|
|
}
|
|
|
|
// find least common denominator
|
2021-04-06 22:00:14 +00:00
|
|
|
return $this->unionTypeCommonTypeNarrower->narrowToSharedObjectType($unionType);
|
2020-01-14 20:14:35 +00:00
|
|
|
}
|
2021-10-27 09:24:45 +00:00
|
|
|
/**
|
|
|
|
* @param TypeWithClassName[] $typesWithClassNames
|
|
|
|
*/
|
|
|
|
private function matchTwoObjectTypes(array $typesWithClassNames) : ?\PHPStan\Type\TypeWithClassName
|
2021-02-17 15:40:29 +00:00
|
|
|
{
|
2021-10-27 09:24:45 +00:00
|
|
|
foreach ($typesWithClassNames as $typeWithClassName) {
|
|
|
|
foreach ($typesWithClassNames as $nestedTypeWithClassName) {
|
|
|
|
if (!$this->areTypeWithClassNamesRelated($typeWithClassName, $nestedTypeWithClassName)) {
|
2021-02-17 15:40:29 +00:00
|
|
|
continue 2;
|
|
|
|
}
|
|
|
|
}
|
2021-10-27 09:24:45 +00:00
|
|
|
return $typeWithClassName;
|
2021-02-17 15:40:29 +00:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2021-07-05 23:06:30 +00:00
|
|
|
private function areTypeWithClassNamesRelated(\PHPStan\Type\TypeWithClassName $firstType, \PHPStan\Type\TypeWithClassName $secondType) : bool
|
2021-03-21 21:31:18 +00:00
|
|
|
{
|
2021-05-09 20:15:43 +00:00
|
|
|
if ($firstType->accepts($secondType, \false)->yes()) {
|
|
|
|
return \true;
|
2021-03-21 21:31:18 +00:00
|
|
|
}
|
2021-05-09 20:15:43 +00:00
|
|
|
return $secondType->accepts($firstType, \false)->yes();
|
2021-03-21 21:31:18 +00:00
|
|
|
}
|
2021-07-05 23:06:30 +00:00
|
|
|
private function correctObjectType(\PHPStan\Type\TypeWithClassName $typeWithClassName) : \PHPStan\Type\TypeWithClassName
|
2021-03-07 14:27:17 +00:00
|
|
|
{
|
2021-05-10 22:23:08 +00:00
|
|
|
if ($typeWithClassName->getClassName() === \PhpParser\NodeAbstract::class) {
|
|
|
|
return new \PHPStan\Type\ObjectType('PhpParser\\Node');
|
2021-03-07 14:27:17 +00:00
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
if ($typeWithClassName->getClassName() === \Rector\Core\Rector\AbstractRector::class) {
|
|
|
|
return new \PHPStan\Type\ObjectType('Rector\\Core\\Contract\\Rector\\RectorInterface');
|
2021-03-07 14:27:17 +00:00
|
|
|
}
|
|
|
|
return $typeWithClassName;
|
|
|
|
}
|
2021-07-05 23:06:30 +00:00
|
|
|
private function isFalseBoolUnion(\PHPStan\Type\UnionType $unionType) : bool
|
2021-04-10 23:28:08 +00:00
|
|
|
{
|
2021-05-09 20:15:43 +00:00
|
|
|
if (\count($unionType->getTypes()) !== 2) {
|
|
|
|
return \false;
|
2021-04-10 23:28:08 +00:00
|
|
|
}
|
|
|
|
foreach ($unionType->getTypes() as $unionedType) {
|
2021-05-10 22:23:08 +00:00
|
|
|
if ($unionedType instanceof \PHPStan\Type\Constant\ConstantBooleanType) {
|
2021-04-10 23:28:08 +00:00
|
|
|
continue;
|
|
|
|
}
|
2021-05-09 20:15:43 +00:00
|
|
|
return \false;
|
2021-04-10 23:28:08 +00:00
|
|
|
}
|
2021-05-09 20:15:43 +00:00
|
|
|
return \true;
|
2021-04-10 23:28:08 +00:00
|
|
|
}
|
2020-01-14 17:59:15 +00:00
|
|
|
}
|