diff --git a/packages/NodeTypeResolver/src/NodeTypeResolver.php b/packages/NodeTypeResolver/src/NodeTypeResolver.php index 1e20c5b8213..868b1d02ea1 100644 --- a/packages/NodeTypeResolver/src/NodeTypeResolver.php +++ b/packages/NodeTypeResolver/src/NodeTypeResolver.php @@ -35,6 +35,7 @@ use PHPStan\Type\IntersectionType; use PHPStan\Type\MixedType; use PHPStan\Type\NullType; use PHPStan\Type\ObjectType; +use PHPStan\Type\ObjectWithoutClassType; use PHPStan\Type\StringType; use PHPStan\Type\Type; use PHPStan\Type\UnionType; @@ -297,6 +298,12 @@ final class NodeTypeResolver return null; } + if ($node instanceof New_) { + if ($this->isAnonymousClass($node->class)) { + return new ObjectWithoutClassType(); + } + } + return $nodeScope->getType($node); } @@ -410,8 +417,7 @@ final class NodeTypeResolver // skip anonymous classes, ref https://github.com/rectorphp/rector/issues/1574 if ($node instanceof New_) { - $className = $this->nameResolver->resolve($node->class); - if ($className === null || Strings::contains($className, 'AnonymousClass')) { + if ($this->isAnonymousClass($node->class)) { return []; } } @@ -556,4 +562,15 @@ final class NodeTypeResolver return null; } + + private function isAnonymousClass(Node $node): bool + { + if (! $node instanceof Class_) { + return false; + } + + $className = $this->nameResolver->resolve($node); + + return $className === null || Strings::contains($className, 'AnonymousClass'); + } } diff --git a/packages/NodeTypeResolver/src/StaticTypeToStringResolver.php b/packages/NodeTypeResolver/src/StaticTypeToStringResolver.php index 1bde29b6a91..4d1f9aefad7 100644 --- a/packages/NodeTypeResolver/src/StaticTypeToStringResolver.php +++ b/packages/NodeTypeResolver/src/StaticTypeToStringResolver.php @@ -10,6 +10,7 @@ use PHPStan\Type\FloatType; use PHPStan\Type\IntegerType; use PHPStan\Type\NullType; use PHPStan\Type\ObjectType; +use PHPStan\Type\ObjectWithoutClassType; use PHPStan\Type\StringType; use PHPStan\Type\Type; use PHPStan\Type\UnionType; @@ -26,6 +27,7 @@ final class StaticTypeToStringResolver { $resolvers = [ IntegerType::class => ['int'], + ObjectWithoutClassType::class => ['object'], ClosureType::class => ['callable'], CallableType::class => ['callable'], FloatType::class => ['float'], diff --git a/packages/TypeDeclaration/tests/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/a_new_class.php.inc b/packages/TypeDeclaration/tests/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/a_new_class.php.inc new file mode 100644 index 00000000000..f182b803ece --- /dev/null +++ b/packages/TypeDeclaration/tests/Rector/FunctionLike/ReturnTypeDeclarationRector/Fixture/a_new_class.php.inc @@ -0,0 +1,27 @@ +<?php + +namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture; + +class ANewClass +{ + public function getObject() + { + return new class() {}; + } +} + +?> +----- +<?php + +namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ReturnTypeDeclarationRector\Fixture; + +class ANewClass +{ + public function getObject(): object + { + return new class() {}; + } +} + +?> diff --git a/packages/TypeDeclaration/tests/Rector/FunctionLike/ReturnTypeDeclarationRector/ReturnTypeDeclarationRectorTest.php b/packages/TypeDeclaration/tests/Rector/FunctionLike/ReturnTypeDeclarationRector/ReturnTypeDeclarationRectorTest.php index 8ac2a1b6111..779b43d4a7a 100644 --- a/packages/TypeDeclaration/tests/Rector/FunctionLike/ReturnTypeDeclarationRector/ReturnTypeDeclarationRectorTest.php +++ b/packages/TypeDeclaration/tests/Rector/FunctionLike/ReturnTypeDeclarationRector/ReturnTypeDeclarationRectorTest.php @@ -53,6 +53,8 @@ final class ReturnTypeDeclarationRectorTest extends AbstractRectorTestCase __DIR__ . '/Fixture/dunglas/BazTrait.php.inc', __DIR__ . '/Fixture/dunglas/Child.php.inc', __DIR__ . '/Fixture/dunglas/nullable_types.php.inc', + // anonymous class + __DIR__ . '/Fixture/a_new_class.php.inc', ]; $this->doTestFiles($integrationFiles);