Skip to content

Commit 75ff705

Browse files
committed
Support for static return type
1 parent 05b6a54 commit 75ff705

File tree

6 files changed

+101
-0
lines changed

6 files changed

+101
-0
lines changed

build/phpstan.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ parameters:
2020
- %rootDir%/tests/PHPStan/Generics/functions.php
2121
- %rootDir%/tests/PHPStan/Reflection/UnionTypesTest.php
2222
- %rootDir%/tests/PHPStan/Reflection/MixedTypeTest.php
23+
- %rootDir%/tests/PHPStan/Reflection/StaticTypeTest.php
2324
- ../tests/e2e/magic-setter/*
2425
featureToggles:
2526
readComposerPhpVersion: false

src/Analyser/MutatingScope.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2667,6 +2667,11 @@ public function getFunctionType($type, bool $isNullable, bool $isVariadic): Type
26672667

26682668
return new NonexistentParentClassType();
26692669
}
2670+
2671+
if ($lowercasedClassName === 'static') {
2672+
return new StaticType($className);
2673+
}
2674+
26702675
return new ObjectType($className);
26712676
} elseif ($type instanceof Node\NullableType) {
26722677
return $this->getFunctionType($type->type, true, $isVariadic);

src/Type/TypehintHelper.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ private static function getTypeObjectFromTypehint(string $typeString, ?string $s
4545
}
4646
}
4747
return new NonexistentParentClassType();
48+
case 'static':
49+
return $selfClass !== null ? new StaticType($selfClass) : new ErrorType();
4850
default:
4951
return new ObjectType($typeString);
5052
}

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10009,6 +10009,15 @@ public function dataNativeMixedType(): array
1000910009
return $this->gatherAssertTypes(__DIR__ . '/../Reflection/data/mixedType.php');
1001010010
}
1001110011

10012+
public function dataNativeStaticReturnType(): array
10013+
{
10014+
if (PHP_VERSION_ID < 80000 && !self::$useStaticReflectionProvider) {
10015+
return [];
10016+
}
10017+
10018+
return $this->gatherAssertTypes(__DIR__ . '/../Reflection/data/staticReturnType.php');
10019+
}
10020+
1001210021
public function dataMinMaxReturnTypeWithArrays(): array
1001310022
{
1001410023
return $this->gatherAssertTypes(__DIR__ . '/data/minmax-arrays.php');
@@ -10076,6 +10085,7 @@ public function dataMinMaxReturnTypeWithArrays(): array
1007610085
* @dataProvider dataGraphicsDrawReturnTypes
1007710086
* @dataProvider dataNativeUnionTypes
1007810087
* @dataProvider dataMinMaxReturnTypeWithArrays
10088+
* @dataProvider dataNativeStaticReturnType
1007910089
* @param string $assertType
1008010090
* @param string $file
1008110091
* @param mixed ...$args
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Reflection;
4+
5+
use NativeStaticReturnType\Foo;
6+
use PHPStan\Testing\TestCase;
7+
use PHPStan\Type\StaticType;
8+
9+
class StaticTypeTest extends TestCase
10+
{
11+
12+
public function testMixedType(): void
13+
{
14+
if (PHP_VERSION_ID < 80000 && !self::$useStaticReflectionProvider) {
15+
$this->markTestSkipped('Test requires PHP 8.0');
16+
}
17+
18+
$reflectionProvider = $this->createBroker();
19+
$class = $reflectionProvider->getClass(Foo::class);
20+
21+
$method = $class->getNativeMethod('doFoo');
22+
$methodVariant = ParametersAcceptorSelector::selectSingle($method->getVariants());
23+
$methodReturnType = $methodVariant->getReturnType();
24+
$this->assertInstanceOf(StaticType::class, $methodReturnType);
25+
$this->assertSame(Foo::class, $methodReturnType->getClassName());
26+
}
27+
28+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php // lint >= 8.0
2+
3+
namespace NativeStaticReturnType;
4+
5+
use function PHPStan\Analyser\assertType;
6+
7+
class Foo
8+
{
9+
10+
public function doFoo(): static
11+
{
12+
return new static();
13+
}
14+
15+
public function doBar(): void
16+
{
17+
assertType('static(NativeStaticReturnType\Foo)', $this->doFoo());
18+
}
19+
20+
/**
21+
* @return callable(): static
22+
*/
23+
public function doBaz(): callable
24+
{
25+
$f = function (): static {
26+
return new static();
27+
};
28+
29+
assertType('static(NativeStaticReturnType\Foo)', $f());
30+
31+
return $f;
32+
}
33+
34+
}
35+
36+
class Bar extends Foo
37+
{
38+
39+
}
40+
41+
function (Foo $foo): void {
42+
assertType('NativeStaticReturnType\Foo', $foo->doFoo());
43+
44+
$callable = $foo->doBaz();
45+
assertType('callable(): NativeStaticReturnType\Foo', $callable);
46+
assertType('NativeStaticReturnType\Foo', $callable());
47+
};
48+
49+
function (Bar $bar): void {
50+
assertType('NativeStaticReturnType\Bar', $bar->doFoo());
51+
52+
$callable = $bar->doBaz();
53+
assertType('callable(): NativeStaticReturnType\Bar', $callable);
54+
assertType('NativeStaticReturnType\Bar', $callable());
55+
};

0 commit comments

Comments
 (0)