Skip to content

Commit d4e0177

Browse files
committed
Calling static:: preserves generic types
1 parent 6d2cc5d commit d4e0177

File tree

5 files changed

+48
-47
lines changed

5 files changed

+48
-47
lines changed

src/Analyser/MutatingScope.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -1419,7 +1419,7 @@ private function resolveType(Expr $node): Type
14191419
return new ErrorType();
14201420
}
14211421

1422-
return new StaticType($this->getClassReflection()->getName());
1422+
return new StaticType($this->getClassReflection());
14231423
}
14241424
if ($lowercasedClassName === 'parent') {
14251425
return new NonexistentParentClassType();
@@ -1760,7 +1760,7 @@ private function resolveType(Expr $node): Type
17601760
$namesToResolve[] = 'static';
17611761
} elseif (strtolower($constantClass) === 'static') {
17621762
if (strtolower($constantName) === 'class') {
1763-
return new GenericClassStringType(new StaticType($this->getClassReflection()->getName()));
1763+
return new GenericClassStringType(new StaticType($this->getClassReflection()));
17641764
}
17651765
return new MixedType();
17661766
}
@@ -2452,7 +2452,7 @@ public function resolveTypeByName(Name $name): TypeWithClassName
24522452
if ($name->toLowerString() === 'static' && $this->isInClass()) {
24532453
$classReflection = $this->getClassReflection();
24542454

2455-
return new StaticType($classReflection->getName());
2455+
return new StaticType($classReflection);
24562456
}
24572457
$originalClass = $this->resolveName($name);
24582458
if ($this->isInClass()) {

src/Analyser/TypeSpecifier.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public function specifyTypesInCondition(
119119
if ($lowercasedClassName === 'self' && $scope->isInClass()) {
120120
$type = new ObjectType($scope->getClassReflection()->getName());
121121
} elseif ($lowercasedClassName === 'static' && $scope->isInClass()) {
122-
$type = new StaticType($scope->getClassReflection()->getName());
122+
$type = new StaticType($scope->getClassReflection());
123123
} elseif ($lowercasedClassName === 'parent') {
124124
if (
125125
$scope->isInClass()

src/Type/StaticType.php

+27-5
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22

33
namespace PHPStan\Type;
44

5+
use PHPStan\Broker\Broker;
56
use PHPStan\Reflection\ClassMemberAccessAnswerer;
67
use PHPStan\Reflection\ClassReflection;
78
use PHPStan\Reflection\ConstantReflection;
89
use PHPStan\Reflection\MethodReflection;
910
use PHPStan\Reflection\PropertyReflection;
1011
use PHPStan\TrinaryLogic;
12+
use PHPStan\Type\Generic\GenericObjectType;
13+
use PHPStan\Type\Generic\TemplateTypeHelper;
1114
use PHPStan\Type\Traits\NonGenericTypeTrait;
1215
use PHPStan\Type\Traits\UndecidedComparisonTypeTrait;
1316

@@ -17,13 +20,22 @@ class StaticType implements TypeWithClassName
1720
use NonGenericTypeTrait;
1821
use UndecidedComparisonTypeTrait;
1922

20-
private string $baseClass;
23+
private ClassReflection $classReflection;
2124

2225
private ?\PHPStan\Type\ObjectType $staticObjectType = null;
2326

24-
public function __construct(string $baseClass)
27+
private string $baseClass;
28+
29+
/**
30+
* @param string|ClassReflection $classReflection
31+
*/
32+
public function __construct($classReflection)
2533
{
26-
$this->baseClass = $baseClass;
34+
if (is_string($classReflection)) {
35+
$classReflection = Broker::getInstance()->getClass($classReflection);
36+
}
37+
$this->classReflection = $classReflection;
38+
$this->baseClass = $classReflection->getName();
2739
}
2840

2941
public function getClassName(): string
@@ -39,7 +51,17 @@ public function getAncestorWithClassName(string $className): ?ObjectType
3951
public function getStaticObjectType(): ObjectType
4052
{
4153
if ($this->staticObjectType === null) {
42-
$this->staticObjectType = new ObjectType($this->baseClass);
54+
if ($this->classReflection->isGeneric()) {
55+
$typeMap = $this->classReflection->getTemplateTypeMap()->map(static function (string $name, Type $type): Type {
56+
return TemplateTypeHelper::toArgument($type);
57+
});
58+
return $this->staticObjectType = new GenericObjectType(
59+
$this->classReflection->getName(),
60+
$this->classReflection->typeMapToList($typeMap)
61+
);
62+
}
63+
64+
return $this->staticObjectType = new ObjectType($this->classReflection->getName(), null, $this->classReflection);
4365
}
4466

4567
return $this->staticObjectType;
@@ -155,7 +177,7 @@ public function getConstant(string $constantName): ConstantReflection
155177

156178
public function changeBaseClass(ClassReflection $classReflection): self
157179
{
158-
return new self($classReflection->getName());
180+
return new self($classReflection);
159181
}
160182

161183
public function isIterable(): TrinaryLogic

src/Type/ThisType.php

-38
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,11 @@
22

33
namespace PHPStan\Type;
44

5-
use PHPStan\Broker\Broker;
65
use PHPStan\Reflection\ClassReflection;
7-
use PHPStan\Type\Generic\GenericObjectType;
8-
use PHPStan\Type\Generic\TemplateTypeHelper;
96

107
class ThisType extends StaticType
118
{
129

13-
private ClassReflection $classReflection;
14-
15-
private ?\PHPStan\Type\ObjectType $staticObjectType = null;
16-
17-
/**
18-
* @param string|ClassReflection $classReflection
19-
*/
20-
public function __construct($classReflection)
21-
{
22-
if (is_string($classReflection)) {
23-
$classReflection = Broker::getInstance()->getClass($classReflection);
24-
}
25-
parent::__construct($classReflection->getName());
26-
$this->classReflection = $classReflection;
27-
}
28-
29-
public function getStaticObjectType(): ObjectType
30-
{
31-
if ($this->staticObjectType === null) {
32-
if ($this->classReflection->isGeneric()) {
33-
$typeMap = $this->classReflection->getTemplateTypeMap()->map(static function (string $name, Type $type): Type {
34-
return TemplateTypeHelper::toArgument($type);
35-
});
36-
return $this->staticObjectType = new GenericObjectType(
37-
$this->classReflection->getName(),
38-
$this->classReflection->typeMapToList($typeMap)
39-
);
40-
}
41-
42-
return $this->staticObjectType = new ObjectType($this->classReflection->getName(), null, $this->classReflection);
43-
}
44-
45-
return $this->staticObjectType;
46-
}
47-
4810
public function changeBaseClass(ClassReflection $classReflection): StaticType
4911
{
5012
return new self($classReflection);

tests/PHPStan/Analyser/data/generic-parent.php

+17
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,20 @@ public function doFoo()
3939
}
4040

4141
}
42+
43+
class E {}
44+
45+
/**
46+
* @template T of E
47+
*/
48+
class R {
49+
50+
/** @return T */
51+
function ret() { return $this->e; } // nonsense, to silence missing return
52+
53+
function test(): void {
54+
assertType('T of GenericParent\E (class GenericParent\R, argument)', self::ret());
55+
assertType('T of GenericParent\E (class GenericParent\R, argument)', $this->ret());
56+
assertType('T of GenericParent\E (class GenericParent\R, argument)', static::ret());
57+
}
58+
}

0 commit comments

Comments
 (0)