Skip to content

Commit

Permalink
[NodeTypeResolver] Handle isObjectType() on new $class dynamic variab…
Browse files Browse the repository at this point in the history
…le should return false compare to Object FQCN (#4936)

* [NodeTypeResolver] Handle isObjectType() on new $class dynamic variable should return false compare to Object FQCN

* Fixed 🎉

* use equal check

* return false directly, ObjectWithoutClassType is not ObjectType

* Fix

* failing fixture

* Fix phpstan

* Fixed 🎉

* Fixed 🎉

* [ci-review] Rector Rectify

---------

Co-authored-by: GitHub Action <actions@github.com>
  • Loading branch information
samsonasik and actions-user committed Sep 7, 2023
1 parent 2be5a4d commit e4f1f1b
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\NewTypeResolver;

use Iterator;
use PhpParser\Node\Expr\New_;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ObjectWithoutClassType;
use PHPStan\Type\Type;
use PHPUnit\Framework\Attributes\DataProvider;
use Rector\NodeTypeResolver\PHPStan\ObjectWithoutClassTypeWithParentTypes;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
use Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\AbstractNodeTypeResolverTestCase;

/**
* @see \Rector\NodeTypeResolver\NodeTypeResolver\NewTypeResolver
*/
final class NewTypeResolverTest extends AbstractNodeTypeResolverTestCase
{
#[DataProvider('provideData')]
public function test(string $file, int $nodePosition, Type $expectedType, bool $isObjectType): void
{
$newNodes = $this->getNodesForFileOfType($file, New_::class);

$resolvedType = $this->nodeTypeResolver->getType($newNodes[$nodePosition]);
$this->assertEquals($expectedType, $resolvedType);

$this->assertEquals(
$isObjectType,
$this->nodeTypeResolver->isObjectType(
$newNodes[$nodePosition],
new ObjectType('Symfony\Bundle\TwigBundle\Loader\FilesystemLoader')
)
);
}

/**
* @return Iterator<int[]|string[]|ObjectWithoutClassType[]|ObjectWithoutClassTypeWithParentTypes[]|bool[]>
*/
public static function provideData(): Iterator
{
$objectWithoutClassType = new ObjectWithoutClassType();

# test new
yield [__DIR__ . '/Source/NewDynamicNew.php', 0, $objectWithoutClassType, false];

$objectWithoutClassTypeWithParentTypes = new ObjectWithoutClassTypeWithParentTypes(
[
new FullyQualifiedObjectType('Symfony\Bundle\TwigBundle\Loader\FilesystemLoader')
]
);
yield [__DIR__ . '/Source/NewDynamicNewExtends.php', 0, $objectWithoutClassTypeWithParentTypes, true];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\NewTypeResolver\Source;

class NewDynamicNew
{
public function run($class)
{
new $class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Rector\Tests\NodeTypeResolver\PerNodeTypeResolver\NewTypeResolver\Source;

class NewDynamicNewExtends
{
public function run()
{
new class extends \Symfony\Bundle\TwigBundle\Loader\FilesystemLoader {};
}
}
20 changes: 20 additions & 0 deletions packages/NodeTypeResolver/NodeTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeCorrector\AccessoryNonEmptyStringTypeCorrector;
use Rector\NodeTypeResolver\NodeTypeCorrector\GenericClassStringTypeCorrector;
use Rector\NodeTypeResolver\PHPStan\ObjectWithoutClassTypeWithParentTypes;
use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType;
use Rector\StaticTypeMapper\ValueObject\Type\ShortenedObjectType;
use Rector\TypeDeclaration\PHPStan\ObjectTypeSpecifier;
Expand Down Expand Up @@ -112,6 +113,10 @@ public function isObjectType(Node $node, ObjectType $requiredObjectType): bool
}
}

if ($resolvedType instanceof ObjectWithoutClassType) {
return $this->isMatchObjectWithoutClassType($resolvedType, $requiredObjectType);
}

return $this->isMatchingUnionType($resolvedType, $requiredObjectType);
}

Expand Down Expand Up @@ -326,6 +331,21 @@ public function isMethodStaticCallOrClassMethodObjectType(Node $node, ObjectType
return $classReflection->isSubclassOf($objectType->getClassName());
}

private function isMatchObjectWithoutClassType(
ObjectWithoutClassType $objectWithoutClassType,
ObjectType $requiredObjectType
): bool {
if ($objectWithoutClassType instanceof ObjectWithoutClassTypeWithParentTypes) {
foreach ($objectWithoutClassType->getParentTypes() as $typeWithClassName) {
if ($requiredObjectType->isSuperTypeOf($typeWithClassName)->yes()) {
return true;
}
}
}

return false;
}

private function isAnonymousObjectType(Type $type): bool
{
return $type instanceof ObjectType && $this->classAnalyzer->isAnonymousClassName($type->getClassName());
Expand Down

0 comments on commit e4f1f1b

Please sign in to comment.