Skip to content

Commit f387269

Browse files
authored
Fix invokable objects incorrectly labeled as instances of Closure (#4797)
1 parent fb48001 commit f387269

4 files changed

Lines changed: 63 additions & 1 deletion

File tree

src/Type/Accessory/HasMethodType.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Type\Accessory;
44

5+
use Closure;
56
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
67
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
78
use PHPStan\Reflection\ClassMemberAccessAnswerer;
@@ -17,6 +18,7 @@
1718
use PHPStan\Type\ErrorType;
1819
use PHPStan\Type\IntersectionType;
1920
use PHPStan\Type\IsSuperTypeOfResult;
21+
use PHPStan\Type\ObjectType;
2022
use PHPStan\Type\StringType;
2123
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
2224
use PHPStan\Type\Traits\NonGenericTypeTrait;
@@ -83,7 +85,11 @@ public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult
8385
return $otherType->isSuperTypeOf($this);
8486
}
8587

86-
if ($this->isCallable()->yes() && $otherType->isCallable()->yes()) {
88+
if (
89+
$this->isCallable()->yes()
90+
&& $otherType->isCallable()->yes()
91+
&& !(new ObjectType(Closure::class))->isSuperTypeOf($otherType)->yes()
92+
) {
8793
return IsSuperTypeOfResult::createYes();
8894
}
8995

tests/PHPStan/Rules/Classes/ImpossibleInstanceOfRuleTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,4 +569,17 @@ public function testBug13469(): void
569569
]);
570570
}
571571

572+
public static function dataBug13975(): iterable
573+
{
574+
yield [__DIR__ . '/data/bug-13975-a.php'];
575+
yield [__DIR__ . '/data/bug-13975-b.php'];
576+
}
577+
578+
#[DataProvider('dataBug13975')]
579+
public function testBug13975(string $file): void
580+
{
581+
$this->treatPhpDocTypesAsCertain = true;
582+
$this->analyse([$file], []);
583+
}
584+
572585
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Bug13975a;
4+
5+
function foo(): callable
6+
{
7+
return new class () {
8+
public function __invoke(): void
9+
{
10+
}
11+
};
12+
}
13+
14+
$foo = foo();
15+
16+
if (\is_object($foo) && method_exists($foo, '__invoke') && !$foo instanceof \Closure) {
17+
echo 'true';
18+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Bug13975b;
4+
5+
class X {}
6+
7+
function foo(): callable
8+
{
9+
return new class () {
10+
public function __invoke(): void
11+
{
12+
}
13+
};
14+
}
15+
16+
$foo = foo();
17+
18+
$class = \Closure::class;
19+
if (rand(0, 1)) {
20+
$class = X::class;
21+
}
22+
23+
if (\is_object($foo) && method_exists($foo, '__invoke') && !$foo instanceof $class) {
24+
echo 'true';
25+
}

0 commit comments

Comments
 (0)