Skip to content

Commit d01431c

Browse files
committed
1 parent adacab8 commit d01431c

20 files changed

+427
-31
lines changed

src/Analyser/MutatingScope.php

+10-2
Original file line numberDiff line numberDiff line change
@@ -854,8 +854,16 @@ private function resolveType(Expr $node): Type
854854
$uncertainty = false;
855855

856856
if ($node->class instanceof Node\Name) {
857-
$className = $this->resolveName($node->class);
858-
$classType = new ObjectType($className);
857+
$unresolvedClassName = $node->class->toString();
858+
if (
859+
strtolower($unresolvedClassName) === 'static'
860+
&& $this->isInClass()
861+
) {
862+
$classType = new StaticType($this->getClassReflection());
863+
} else {
864+
$className = $this->resolveName($node->class);
865+
$classType = new ObjectType($className);
866+
}
859867
} else {
860868
$classType = $this->getType($node->class);
861869
$classType = TypeTraverser::map($classType, static function (Type $type, callable $traverse) use (&$uncertainty): Type {

src/Type/StaticType.php

+7-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,13 @@ public function isSuperTypeOf(Type $type): TrinaryLogic
129129
}
130130

131131
if ($type instanceof ObjectType) {
132-
return TrinaryLogic::createMaybe()->and($this->getStaticObjectType()->isSuperTypeOf($type));
132+
$result = $this->getStaticObjectType()->isSuperTypeOf($type);
133+
$classReflection = $type->getClassReflection();
134+
if ($result->yes() && $classReflection !== null && $classReflection->isFinal()) {
135+
return $result;
136+
}
137+
138+
return TrinaryLogic::createMaybe()->and($result);
133139
}
134140

135141
if ($type instanceof CompoundType) {

tests/PHPStan/Analyser/AnalyserIntegrationTest.php

+6
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,12 @@ public function testBug3922(): void
330330
$this->assertCount(0, $errors);
331331
}
332332

333+
public function testBug1843(): void
334+
{
335+
$errors = $this->runAnalyse(__DIR__ . '/data/bug-1843.php');
336+
$this->assertCount(0, $errors);
337+
}
338+
333339
/**
334340
* @param string $file
335341
* @return \PHPStan\Analyser\Error[]

tests/PHPStan/Analyser/NodeScopeResolverTest.php

+24
Original file line numberDiff line numberDiff line change
@@ -5666,6 +5666,26 @@ public function dataBug4267(): array
56665666
return $this->gatherAssertTypes(__DIR__ . '/data/bug-4267.php');
56675667
}
56685668

5669+
public function dataBug2231(): array
5670+
{
5671+
return $this->gatherAssertTypes(__DIR__ . '/data/bug-2231.php');
5672+
}
5673+
5674+
public function dataBug3558(): array
5675+
{
5676+
return $this->gatherAssertTypes(__DIR__ . '/data/bug-3558.php');
5677+
}
5678+
5679+
public function dataBug3351(): array
5680+
{
5681+
return $this->gatherAssertTypes(__DIR__ . '/data/bug-3351.php');
5682+
}
5683+
5684+
public function dataBug4213(): array
5685+
{
5686+
return $this->gatherAssertTypes(__DIR__ . '/data/bug-4213.php');
5687+
}
5688+
56695689
/**
56705690
* @dataProvider dataArrayFunctions
56715691
* @param string $description
@@ -11282,6 +11302,10 @@ private function gatherAssertTypes(string $file): array
1128211302
* @dataProvider dataGenericParent
1128311303
* @dataProvider dataBug4247
1128411304
* @dataProvider dataBug4267
11305+
* @dataProvider dataBug2231
11306+
* @dataProvider dataBug3558
11307+
* @dataProvider dataBug3351
11308+
* @dataProvider dataBug4213
1128511309
* @param string $assertType
1128611310
* @param string $file
1128711311
* @param mixed ...$args
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Bug1843;
4+
5+
class HelloWorld
6+
{
7+
const W = '1';
8+
9+
const P = [
10+
self::W => [
11+
'A' => '2',
12+
'B' => '3',
13+
'C' => '4',
14+
'D' => '5',
15+
'E' => '6',
16+
'F' => '7',
17+
],
18+
];
19+
20+
public function sayHello(): void
21+
{
22+
echo self::P[self::W]['A'];
23+
}
24+
}
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Bug2231;
4+
5+
use function PHPStan\Analyser\assertType;
6+
7+
class Foo
8+
{
9+
10+
public function doFoo(?Foo $x): void
11+
{
12+
if ($x !== null && !($x instanceof static)) {
13+
throw new \TypeError('custom error');
14+
}
15+
16+
assertType('static(Bug2231\Foo)|null', $x);
17+
}
18+
19+
public function doBar(?Foo $x): void
20+
{
21+
if ($x !== null && !($x instanceof self)) {
22+
throw new \TypeError('custom error');
23+
}
24+
25+
assertType('Bug2231\Foo|null', $x);
26+
}
27+
28+
public function doBaz($x): void
29+
{
30+
if ($x instanceof self) {
31+
assertType('Bug2231\Foo', $x);
32+
}
33+
34+
if ($x instanceof static) {
35+
assertType('static(Bug2231\Foo)', $x);
36+
}
37+
}
38+
39+
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
use function PHPStan\Analyser\assertType;
4+
5+
class HelloWorld
6+
{
7+
public function sayHello(): void
8+
{
9+
$a = ['a', 'b', 'c'];
10+
$b = [1, 2, 3];
11+
12+
$c = $this->combine($a, $b);
13+
assertType('array<string, int>|false', $c);
14+
15+
assertType('array(\'a\' => 1, \'b\' => 2, \'c\' => 3)', array_combine($a, $b));
16+
}
17+
18+
/**
19+
* @template TKey
20+
* @template TValue
21+
* @param array<TKey> $keys
22+
* @param array<TValue> $values
23+
*
24+
* @return array<TKey, TValue>|false
25+
*/
26+
private function combine(array $keys, array $values)
27+
{
28+
return array_combine($keys, $values);
29+
}
30+
}
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Bug3558;
4+
5+
use function PHPStan\Analyser\assertType;
6+
7+
function (): void {
8+
$idGroups = [];
9+
10+
if(time() > 3){
11+
$idGroups[] = [1,2];
12+
$idGroups[] = [1,2];
13+
$idGroups[] = [1,2];
14+
}
15+
16+
if(count($idGroups) > 0){
17+
assertType('array(array(1, 2), array(1, 2), array(1, 2))', $idGroups);
18+
}
19+
};
20+
21+
function (): void {
22+
$idGroups = [1];
23+
24+
if(time() > 3){
25+
$idGroups[] = [1,2];
26+
$idGroups[] = [1,2];
27+
$idGroups[] = [1,2];
28+
}
29+
30+
if(count($idGroups) > 1){
31+
assertType('array(0 => 1, ?1 => array(1, 2), ?2 => array(1, 2), ?3 => array(1, 2))', $idGroups);
32+
}
33+
};
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
namespace Bug4213;
4+
5+
use function PHPStan\Analyser\assertType;
6+
7+
abstract class BaseEnum
8+
{
9+
private string $value;
10+
11+
final private function __construct(string $value)
12+
{
13+
$this->value = $value;
14+
}
15+
/**
16+
* @return static
17+
*/
18+
public static function get(string $value): self {
19+
return new static($value);
20+
}
21+
}
22+
23+
final class Enum extends BaseEnum
24+
{
25+
}
26+
27+
final class Entity {
28+
public function setEnums(Enum ...$enums): void {
29+
}
30+
/**
31+
* @param Enum[] $enums
32+
*/
33+
public function setEnumsWithoutSplat(array $enums): void {
34+
}
35+
}
36+
37+
function (): void {
38+
assertType('Bug4213\Enum', Enum::get('test'));
39+
assertType('array(Bug4213\Enum)', array_map([Enum::class, 'get'], ['test']));
40+
};
41+
42+
43+
class Foo
44+
{
45+
/**
46+
* @return static
47+
*/
48+
public static function create() : Foo
49+
{
50+
return new static();
51+
}
52+
}
53+
54+
55+
class Bar extends Foo
56+
{
57+
}
58+
59+
function (): void {
60+
$cbFoo = [Foo::class, 'create'];
61+
$cbBar = [Bar::class, 'create'];
62+
assertType('Bug4213\Foo', $cbFoo());
63+
assertType('Bug4213\Bar', $cbBar());
64+
};

tests/PHPStan/Reflection/StaticTypeTest.php

-28
This file was deleted.

tests/PHPStan/Rules/Comparison/BooleanAndConstantConditionRuleTest.php

+11
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,15 @@ public function testBugComposerDependentVariables(): void
190190
$this->analyse([__DIR__ . '/data/bug-composer-dependent-variables.php'], []);
191191
}
192192

193+
public function testBug2231(): void
194+
{
195+
$this->treatPhpDocTypesAsCertain = true;
196+
$this->analyse([__DIR__ . '/../../Analyser/data/bug-2231.php'], [
197+
[
198+
'Result of && is always false.',
199+
21,
200+
],
201+
]);
202+
}
203+
193204
}

tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php

+7
Original file line numberDiff line numberDiff line change
@@ -290,4 +290,11 @@ public function testBug4084(): void
290290
$this->analyse([__DIR__ . '/data/bug-4084.php'], []);
291291
}
292292

293+
public function testBug3523(): void
294+
{
295+
$this->reportMaybes = true;
296+
$this->reportStatic = true;
297+
$this->analyse([__DIR__ . '/data/bug-3523.php'], []);
298+
}
299+
293300
}

tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php

+15
Original file line numberDiff line numberDiff line change
@@ -459,4 +459,19 @@ public function testReturnStatic(): void
459459
$this->analyse([__DIR__ . '/data/return-static.php'], []);
460460
}
461461

462+
public function testBug4648(): void
463+
{
464+
$this->analyse([__DIR__ . '/data/bug-4648.php'], []);
465+
}
466+
467+
public function testBug3523(): void
468+
{
469+
$this->analyse([__DIR__ . '/data/bug-3523.php'], []);
470+
}
471+
472+
public function testBug3120(): void
473+
{
474+
$this->analyse([__DIR__ . '/data/bug-3120.php'], []);
475+
}
476+
462477
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace Bug3120;
4+
5+
class A {
6+
/** @return static */
7+
public static function getInstance() {
8+
$class = static::class;
9+
return new $class();
10+
}
11+
}
12+
13+
final class AChild extends A {
14+
public static function getInstance() {
15+
return new AChild();
16+
}
17+
}
18+
19+
class Test
20+
{
21+
final public function __construct()
22+
{}
23+
24+
/**
25+
* @return static
26+
*/
27+
public function foo(): self
28+
{
29+
return self::bar(new static());
30+
}
31+
32+
/**
33+
* @phpstan-template T of Test
34+
* @phpstan-param T $object
35+
* @phpstan-return T
36+
*/
37+
public function bar(Test $object): self
38+
{
39+
return $object;
40+
}
41+
42+
}

0 commit comments

Comments
 (0)