Skip to content

Commit 3aa878f

Browse files
committed
Fix calling method statically on string
1 parent ab0245c commit 3aa878f

File tree

3 files changed

+101
-4
lines changed

3 files changed

+101
-4
lines changed

src/Analyser/MutatingScope.php

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2235,10 +2235,21 @@ private function resolveType(Expr $node): Type
22352235
if ($node->class instanceof Name) {
22362236
$staticMethodCalledOnType = $this->resolveTypeByName($node->class);
22372237
} else {
2238-
$staticMethodCalledOnType = $this->getType($node->class);
2239-
if ($staticMethodCalledOnType instanceof GenericClassStringType) {
2240-
$staticMethodCalledOnType = $staticMethodCalledOnType->getGenericType();
2241-
}
2238+
$staticMethodCalledOnType = TypeTraverser::map($this->getType($node->class), static function (Type $type, callable $traverse): Type {
2239+
if ($type instanceof UnionType) {
2240+
return $traverse($type);
2241+
}
2242+
2243+
if ($type instanceof GenericClassStringType) {
2244+
return $type->getGenericType();
2245+
}
2246+
2247+
if ($type instanceof ConstantStringType && $type->isClassString()) {
2248+
return new ObjectType($type->getValue());
2249+
}
2250+
2251+
return $type;
2252+
});
22422253
}
22432254

22442255
$returnType = $this->methodCallReturnType(

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,7 @@ public function dataFileAsserts(): iterable
609609

610610
yield from $this->gatherAssertTypes(__DIR__ . '/data/weird-array_key_exists-issue.php');
611611
yield from $this->gatherAssertTypes(__DIR__ . '/data/equal.php');
612+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6404.php');
612613
}
613614

614615
/**
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
namespace Bug6404;
4+
5+
6+
use function PHPStan\Testing\assertType;
7+
8+
class Foo
9+
{
10+
public static function getCode(): int
11+
{
12+
return 1;
13+
}
14+
}
15+
16+
class Bar
17+
{
18+
private const FOOS = [
19+
Foo::class,
20+
];
21+
22+
/**
23+
* @var array<int, bool>
24+
*/
25+
private array $someMap = [];
26+
27+
public function build(): void
28+
{
29+
foreach (self::FOOS as $fooClass) {
30+
if (is_a($fooClass, Foo::class, true)) {
31+
assertType("'Bug6404\\\\Foo'", $fooClass);
32+
assertType('int', $fooClass::getCode());
33+
$this->someMap[$fooClass::getCode()] = true;
34+
}
35+
}
36+
}
37+
38+
/**
39+
* @param object[] $objects
40+
* @return void
41+
*/
42+
public function build2(array $objects): void
43+
{
44+
foreach ($objects as $fooClass) {
45+
if (is_a($fooClass, Foo::class)) {
46+
assertType(Foo::class, $fooClass);
47+
assertType('int', $fooClass::getCode());
48+
$this->someMap[$fooClass::getCode()] = true;
49+
}
50+
}
51+
}
52+
53+
/**
54+
* @param mixed[] $mixeds
55+
* @return void
56+
*/
57+
public function build3(array $mixeds): void
58+
{
59+
foreach ($mixeds as $fooClass) {
60+
if (is_a($fooClass, Foo::class, true)) {
61+
assertType('Bug6404\\Foo|class-string<Bug6404\\Foo>', $fooClass);
62+
assertType('int', $fooClass::getCode());
63+
$this->someMap[$fooClass::getCode()] = true;
64+
}
65+
}
66+
}
67+
68+
/**
69+
* @param class-string<Foo> $classString
70+
* @return void
71+
*/
72+
public function doBar(string $classString): void
73+
{
74+
assertType("class-string<" . Foo::class . ">", $classString);
75+
assertType('int', $classString::getCode());
76+
}
77+
78+
/**
79+
* @return array<int, bool>
80+
*/
81+
public function getAll(): array
82+
{
83+
return $this->someMap;
84+
}
85+
}

0 commit comments

Comments
 (0)