From 72884ba6a91216bd1c9cf3602ed3333a78469cba Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sat, 6 Aug 2022 13:53:26 +0200 Subject: [PATCH 1/4] add return anonymous class return --- ...dd_anonymous_class_implements_type.php.inc | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/add_anonymous_class_implements_type.php.inc diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/add_anonymous_class_implements_type.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/add_anonymous_class_implements_type.php.inc new file mode 100644 index 00000000000..5bb01fd7b72 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/add_anonymous_class_implements_type.php.inc @@ -0,0 +1,39 @@ + +----- + From 81a10d4c79cad7ea9a6a50d3a25803690a2068c7 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sat, 6 Aug 2022 13:58:18 +0200 Subject: [PATCH 2/4] [TypeDeclaration] Make anonymous class return specific type, if implements --- .../NodeTypeResolver/NewTypeResolver.php | 17 +++----- .../ObjectWithoutClassTypeWithParentTypes.php | 33 ++++++++++++++++ .../ObjectWithoutClassTypeMapper.php | 11 ++++++ .../StaticTypeMapper/StaticTypeMapper.php | 1 - .../add_anonymous_class_extends_type.php.inc | 39 +++++++++++++++++++ ...dd_anonymous_class_implements_type.php.inc | 4 +- .../generic_object_when_double_types.php.inc | 37 ++++++++++++++++++ .../Fixture/closure_nested_type.php.inc | 2 +- .../AnonymousClass/SomeEventDispatcher.php | 11 ++++++ 9 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 packages/NodeTypeResolver/PHPStan/ObjectWithoutClassTypeWithParentTypes.php create mode 100644 rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/AnonymousClass/add_anonymous_class_extends_type.php.inc rename rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/{ => AnonymousClass}/add_anonymous_class_implements_type.php.inc (89%) create mode 100644 rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/AnonymousClass/generic_object_when_double_types.php.inc create mode 100644 rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Source/AnonymousClass/SomeEventDispatcher.php diff --git a/packages/NodeTypeResolver/NodeTypeResolver/NewTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver/NewTypeResolver.php index cc5a0f3f7b1..b69c7f61a1e 100644 --- a/packages/NodeTypeResolver/NodeTypeResolver/NewTypeResolver.php +++ b/packages/NodeTypeResolver/NodeTypeResolver/NewTypeResolver.php @@ -13,12 +13,12 @@ use PHPStan\Type\ObjectType; use PHPStan\Type\ObjectWithoutClassType; use PHPStan\Type\Type; -use PHPStan\Type\UnionType; use Rector\Core\Enum\ObjectReference; use Rector\Core\NodeAnalyzer\ClassAnalyzer; use Rector\NodeNameResolver\NodeNameResolver; use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeTypeResolver\PHPStan\ObjectWithoutClassTypeWithParentTypes; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; /** @@ -72,27 +72,22 @@ private function resolveAnonymousClassType(New_ $new): ObjectWithoutClassType return new ObjectWithoutClassType(); } - $types = []; + $directParentTypes = []; /** @var Class_ $class */ $class = $new->class; if ($class->extends !== null) { $parentClass = (string) $class->extends; - $types[] = new FullyQualifiedObjectType($parentClass); + $directParentTypes[] = new FullyQualifiedObjectType($parentClass); } foreach ($class->implements as $implement) { $parentClass = (string) $implement; - $types[] = new FullyQualifiedObjectType($parentClass); + $directParentTypes[] = new FullyQualifiedObjectType($parentClass); } - if (count($types) > 1) { - $unionType = new UnionType($types); - return new ObjectWithoutClassType($unionType); - } - - if (count($types) === 1) { - return new ObjectWithoutClassType($types[0]); + if (count($directParentTypes) !== 0) { + return new ObjectWithoutClassTypeWithParentTypes($directParentTypes); } return new ObjectWithoutClassType(); diff --git a/packages/NodeTypeResolver/PHPStan/ObjectWithoutClassTypeWithParentTypes.php b/packages/NodeTypeResolver/PHPStan/ObjectWithoutClassTypeWithParentTypes.php new file mode 100644 index 00000000000..4f0b00c33df --- /dev/null +++ b/packages/NodeTypeResolver/PHPStan/ObjectWithoutClassTypeWithParentTypes.php @@ -0,0 +1,33 @@ +parentTypes = $parentTypes; + } + + /** + * @return TypeWithClassName[] + */ + public function getParentTypes(): array + { + return $this->parentTypes; + } +} diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/ObjectWithoutClassTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/ObjectWithoutClassTypeMapper.php index b72449fe4aa..343dd5b4cc1 100644 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/ObjectWithoutClassTypeMapper.php +++ b/packages/PHPStanStaticTypeMapper/TypeMapper/ObjectWithoutClassTypeMapper.php @@ -6,6 +6,7 @@ use PhpParser\Node; use PhpParser\Node\Name; +use PhpParser\Node\Name\FullyQualified; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; use PHPStan\PhpDocParser\Ast\Type\TypeNode; use PHPStan\Type\Generic\TemplateObjectWithoutClassType; @@ -14,6 +15,7 @@ use Rector\BetterPhpDocParser\ValueObject\Type\EmptyGenericTypeNode; use Rector\Core\Php\PhpVersionProvider; use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\NodeTypeResolver\PHPStan\ObjectWithoutClassTypeWithParentTypes; use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface; use Rector\StaticTypeMapper\ValueObject\Type\ParentObjectWithoutClassType; @@ -57,6 +59,15 @@ public function mapToPHPStanPhpDocTypeNode(Type $type, string $typeKind): TypeNo */ public function mapToPhpParserNode(Type $type, string $typeKind): ?Node { + // special case for anonymous classes that implement another type + if ($type instanceof ObjectWithoutClassTypeWithParentTypes) { + $parentTypes = $type->getParentTypes(); + if (count($parentTypes) === 1) { + $parentType = $parentTypes[0]; + return new FullyQualified($parentType->getClassName()); + } + } + if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::OBJECT_TYPE)) { return null; } diff --git a/packages/StaticTypeMapper/StaticTypeMapper.php b/packages/StaticTypeMapper/StaticTypeMapper.php index 66674c432c5..79a7342005e 100644 --- a/packages/StaticTypeMapper/StaticTypeMapper.php +++ b/packages/StaticTypeMapper/StaticTypeMapper.php @@ -65,7 +65,6 @@ public function mapPHPStanTypeToPHPStanPhpDocTypeNode(Type $phpStanType, string public function mapPHPStanTypeToPhpParserNode(Type $phpStanType, string $typeKind): ?Node { $node = $this->phpStanStaticTypeMapper->mapToPhpParserNode($phpStanType, $typeKind); - if (! $node instanceof Node) { return null; } diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/AnonymousClass/add_anonymous_class_extends_type.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/AnonymousClass/add_anonymous_class_extends_type.php.inc new file mode 100644 index 00000000000..d8fa3c393de --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/AnonymousClass/add_anonymous_class_extends_type.php.inc @@ -0,0 +1,39 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/add_anonymous_class_implements_type.php.inc b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/AnonymousClass/add_anonymous_class_implements_type.php.inc similarity index 89% rename from rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/add_anonymous_class_implements_type.php.inc rename to rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/AnonymousClass/add_anonymous_class_implements_type.php.inc index 5bb01fd7b72..e812c0dc264 100644 --- a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/add_anonymous_class_implements_type.php.inc +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/AnonymousClass/add_anonymous_class_implements_type.php.inc @@ -1,6 +1,6 @@ \ No newline at end of file +?> diff --git a/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Source/AnonymousClass/SomeEventDispatcher.php b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Source/AnonymousClass/SomeEventDispatcher.php new file mode 100644 index 00000000000..7ef9b319373 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FunctionLike/ReturnTypeDeclarationRector/Source/AnonymousClass/SomeEventDispatcher.php @@ -0,0 +1,11 @@ + Date: Sat, 6 Aug 2022 14:54:27 +0000 Subject: [PATCH 3/4] [ci-review] Rector Rectify --- .../ObjectWithoutClassTypeWithParentTypes.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/NodeTypeResolver/PHPStan/ObjectWithoutClassTypeWithParentTypes.php b/packages/NodeTypeResolver/PHPStan/ObjectWithoutClassTypeWithParentTypes.php index 4f0b00c33df..4b89d93898d 100644 --- a/packages/NodeTypeResolver/PHPStan/ObjectWithoutClassTypeWithParentTypes.php +++ b/packages/NodeTypeResolver/PHPStan/ObjectWithoutClassTypeWithParentTypes.php @@ -5,22 +5,19 @@ namespace Rector\NodeTypeResolver\PHPStan; use PHPStan\Type\ObjectWithoutClassType; +use PHPStan\Type\Type; use PHPStan\Type\TypeWithClassName; final class ObjectWithoutClassTypeWithParentTypes extends ObjectWithoutClassType { - /** - * @var TypeWithClassName[] - */ - private array $parentTypes; - /** * @param TypeWithClassName[] $parentTypes */ - public function __construct(array $parentTypes, ?\PHPStan\Type\Type $subtractedType = null) - { + public function __construct( + private readonly array $parentTypes, + ?Type $subtractedType = null + ) { parent::__construct($subtractedType); - $this->parentTypes = $parentTypes; } /** From 28a841dc3b92db5d07425ccfe7714825a99a98d6 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 6 Aug 2022 14:54:55 +0000 Subject: [PATCH 4/4] [ci-review] Rector Rectify --- packages/NodeTypeResolver/NodeTypeResolver/NewTypeResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/NodeTypeResolver/NodeTypeResolver/NewTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver/NewTypeResolver.php index b69c7f61a1e..968420f8288 100644 --- a/packages/NodeTypeResolver/NodeTypeResolver/NewTypeResolver.php +++ b/packages/NodeTypeResolver/NodeTypeResolver/NewTypeResolver.php @@ -86,7 +86,7 @@ private function resolveAnonymousClassType(New_ $new): ObjectWithoutClassType $directParentTypes[] = new FullyQualifiedObjectType($parentClass); } - if (count($directParentTypes) !== 0) { + if ($directParentTypes !== []) { return new ObjectWithoutClassTypeWithParentTypes($directParentTypes); }