Skip to content

Commit 0c1f22a

Browse files
committed
GenericStaticType - support for static<...>
1 parent b57bcad commit 0c1f22a

21 files changed

+950
-13
lines changed

phpstan-baseline.neon

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,16 @@ parameters:
985985
count: 2
986986
path: src/Type/Generic/GenericObjectType.php
987987

988+
-
989+
message: "#^Doing instanceof PHPStan\\\\Type\\\\ObjectType is error\\-prone and deprecated\\. Use Type\\:\\:isObject\\(\\) or Type\\:\\:getObjectClassNames\\(\\) instead\\.$#"
990+
count: 1
991+
path: src/Type/Generic/GenericStaticType.php
992+
993+
-
994+
message: "#^Doing instanceof PHPStan\\\\Type\\\\TypeWithClassName is error\\-prone and deprecated\\. Use Type\\:\\:getObjectClassNames\\(\\) or Type\\:\\:getObjectClassReflections\\(\\) instead\\.$#"
995+
count: 1
996+
path: src/Type/Generic/GenericStaticType.php
997+
988998
-
989999
message: "#^Doing instanceof PHPStan\\\\Type\\\\IntersectionType is error\\-prone and deprecated\\.$#"
9901000
count: 3

src/Analyser/MutatingScope.php

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
use PHPStan\Type\GeneralizePrecision;
103103
use PHPStan\Type\Generic\GenericClassStringType;
104104
use PHPStan\Type\Generic\GenericObjectType;
105+
use PHPStan\Type\Generic\GenericStaticType;
105106
use PHPStan\Type\Generic\TemplateType;
106107
use PHPStan\Type\Generic\TemplateTypeHelper;
107108
use PHPStan\Type\Generic\TemplateTypeMap;
@@ -5437,8 +5438,17 @@ public function debug(): array
54375438
private function exactInstantiation(New_ $node, string $className): ?Type
54385439
{
54395440
$resolvedClassName = $this->resolveExactName(new Name($className));
5441+
$isStatic = false;
54405442
if ($resolvedClassName === null) {
5441-
return null;
5443+
if (strtolower($className) !== 'static') {
5444+
return null;
5445+
}
5446+
5447+
if (!$this->isInClass()) {
5448+
return null;
5449+
}
5450+
$resolvedClassName = $this->getClassReflection()->getName();
5451+
$isStatic = true;
54425452
}
54435453

54445454
if (!$this->reflectionProvider->hasClass($resolvedClassName)) {
@@ -5495,7 +5505,7 @@ private function exactInstantiation(New_ $node, string $className): ?Type
54955505
return $methodResult;
54965506
}
54975507

5498-
$objectType = new ObjectType($resolvedClassName);
5508+
$objectType = $isStatic ? new StaticType($classReflection) : new ObjectType($resolvedClassName);
54995509
if (!$classReflection->isGeneric()) {
55005510
return $objectType;
55015511
}
@@ -5528,6 +5538,14 @@ private function exactInstantiation(New_ $node, string $className): ?Type
55285538
}
55295539

55305540
if ($constructorMethod instanceof DummyConstructorReflection) {
5541+
if ($isStatic) {
5542+
return new GenericStaticType(
5543+
$classReflection,
5544+
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
5545+
null,
5546+
[],
5547+
);
5548+
}
55315549
return new GenericObjectType(
55325550
$resolvedClassName,
55335551
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
@@ -5536,6 +5554,15 @@ private function exactInstantiation(New_ $node, string $className): ?Type
55365554

55375555
if ($constructorMethod->getDeclaringClass()->getName() !== $classReflection->getName()) {
55385556
if (!$constructorMethod->getDeclaringClass()->isGeneric()) {
5557+
if ($isStatic) {
5558+
return new GenericStaticType(
5559+
$classReflection,
5560+
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
5561+
null,
5562+
[],
5563+
);
5564+
}
5565+
55395566
return new GenericObjectType(
55405567
$resolvedClassName,
55415568
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
@@ -5544,13 +5571,31 @@ private function exactInstantiation(New_ $node, string $className): ?Type
55445571
$newType = new GenericObjectType($resolvedClassName, $classReflection->typeMapToList($classReflection->getTemplateTypeMap()));
55455572
$ancestorType = $newType->getAncestorWithClassName($constructorMethod->getDeclaringClass()->getName());
55465573
if ($ancestorType === null) {
5574+
if ($isStatic) {
5575+
return new GenericStaticType(
5576+
$classReflection,
5577+
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
5578+
null,
5579+
[],
5580+
);
5581+
}
5582+
55475583
return new GenericObjectType(
55485584
$resolvedClassName,
55495585
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
55505586
);
55515587
}
55525588
$ancestorClassReflections = $ancestorType->getObjectClassReflections();
55535589
if (count($ancestorClassReflections) !== 1) {
5590+
if ($isStatic) {
5591+
return new GenericStaticType(
5592+
$classReflection,
5593+
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
5594+
null,
5595+
[],
5596+
);
5597+
}
5598+
55545599
return new GenericObjectType(
55555600
$resolvedClassName,
55565601
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
@@ -5561,6 +5606,15 @@ private function exactInstantiation(New_ $node, string $className): ?Type
55615606
$newParentType = $this->getType($newParentNode);
55625607
$newParentTypeClassReflections = $newParentType->getObjectClassReflections();
55635608
if (count($newParentTypeClassReflections) !== 1) {
5609+
if ($isStatic) {
5610+
return new GenericStaticType(
5611+
$classReflection,
5612+
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
5613+
null,
5614+
[],
5615+
);
5616+
}
5617+
55645618
return new GenericObjectType(
55655619
$resolvedClassName,
55665620
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()->resolveToBounds()),
@@ -5597,6 +5651,15 @@ private function exactInstantiation(New_ $node, string $className): ?Type
55975651
$resolvedTypeMap[$ancestorType->getName()] = TypeCombinator::union($resolvedTypeMap[$ancestorType->getName()], $type);
55985652
}
55995653

5654+
if ($isStatic) {
5655+
return new GenericStaticType(
5656+
$classReflection,
5657+
$classReflection->typeMapToList(new TemplateTypeMap($resolvedTypeMap)),
5658+
null,
5659+
[],
5660+
);
5661+
}
5662+
56005663
return new GenericObjectType(
56015664
$resolvedClassName,
56025665
$classReflection->typeMapToList(new TemplateTypeMap($resolvedTypeMap)),
@@ -5612,10 +5675,19 @@ private function exactInstantiation(New_ $node, string $className): ?Type
56125675

56135676
if ($this->explicitMixedInUnknownGenericNew) {
56145677
$resolvedTemplateTypeMap = $parametersAcceptor->getResolvedTemplateTypeMap();
5615-
return TypeTraverser::map(new GenericObjectType(
5678+
$newGenericType = new GenericObjectType(
56165679
$resolvedClassName,
56175680
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()),
5618-
), static function (Type $type, callable $traverse) use ($resolvedTemplateTypeMap): Type {
5681+
);
5682+
if ($isStatic) {
5683+
$newGenericType = new GenericStaticType(
5684+
$classReflection,
5685+
$classReflection->typeMapToList($classReflection->getTemplateTypeMap()),
5686+
null,
5687+
[],
5688+
);
5689+
}
5690+
return TypeTraverser::map($newGenericType, static function (Type $type, callable $traverse) use ($resolvedTemplateTypeMap): Type {
56195691
if ($type instanceof TemplateType && !$type->isArgument()) {
56205692
$newType = $resolvedTemplateTypeMap->getType($type->getName());
56215693
if ($newType === null || $newType instanceof ErrorType) {
@@ -5654,6 +5726,15 @@ private function exactInstantiation(New_ $node, string $className): ?Type
56545726
$list[] = $bound;
56555727
}
56565728

5729+
if ($isStatic) {
5730+
return new GenericStaticType(
5731+
$classReflection,
5732+
$list,
5733+
null,
5734+
[],
5735+
);
5736+
}
5737+
56575738
return new GenericObjectType(
56585739
$resolvedClassName,
56595740
$list,

src/PhpDoc/TypeNodeResolver.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
use PHPStan\Type\FloatType;
7171
use PHPStan\Type\Generic\GenericClassStringType;
7272
use PHPStan\Type\Generic\GenericObjectType;
73+
use PHPStan\Type\Generic\GenericStaticType;
7374
use PHPStan\Type\Generic\TemplateType;
7475
use PHPStan\Type\Generic\TemplateTypeFactory;
7576
use PHPStan\Type\Generic\TemplateTypeMap;
@@ -784,6 +785,14 @@ static function (string $variance): TemplateTypeVariance {
784785
return $type->isResolvable() ? $type->resolve() : $type;
785786
}
786787

788+
return new ErrorType();
789+
} elseif ($mainTypeName === 'static') {
790+
if ($nameScope->getClassName() !== null && $this->getReflectionProvider()->hasClass($nameScope->getClassName())) {
791+
$classReflection = $this->getReflectionProvider()->getClass($nameScope->getClassName());
792+
793+
return new GenericStaticType($classReflection, $genericTypes, null, $variances);
794+
}
795+
787796
return new ErrorType();
788797
}
789798

src/Reflection/ResolvedFunctionVariantWithOriginal.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\Type\ConditionalTypeForParameter;
88
use PHPStan\Type\ErrorType;
99
use PHPStan\Type\Generic\GenericObjectType;
10+
use PHPStan\Type\Generic\GenericStaticType;
1011
use PHPStan\Type\Generic\TemplateType;
1112
use PHPStan\Type\Generic\TemplateTypeHelper;
1213
use PHPStan\Type\Generic\TemplateTypeMap;
@@ -245,7 +246,7 @@ private function resolveResolvableTemplateTypes(Type $type, TemplateTypeVariance
245246
};
246247

247248
return TypeTraverser::map($type, function (Type $type, callable $traverse) use ($references, $objectCb): Type {
248-
if (BleedingEdgeToggle::isBleedingEdge() && $type instanceof GenericObjectType) {
249+
if (BleedingEdgeToggle::isBleedingEdge() && ($type instanceof GenericObjectType || $type instanceof GenericStaticType)) {
249250
return TypeTraverser::map($type, $objectCb);
250251
}
251252

src/Reflection/Type/CalledOnTypeUnresolvedMethodPrototypeReflection.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
use PHPStan\Reflection\ParametersAcceptorWithPhpDocs;
1111
use PHPStan\Reflection\Php\DummyParameterWithPhpDocs;
1212
use PHPStan\Reflection\ResolvedMethodReflection;
13+
use PHPStan\Type\Generic\GenericObjectType;
14+
use PHPStan\Type\Generic\GenericStaticType;
15+
use PHPStan\Type\ObjectType;
1316
use PHPStan\Type\StaticType;
1417
use PHPStan\Type\Type;
1518
use PHPStan\Type\TypeTraverser;
@@ -114,6 +117,13 @@ private function transformMethodWithStaticType(ClassReflection $declaringClass,
114117
private function transformStaticType(Type $type): Type
115118
{
116119
return TypeTraverser::map($type, function (Type $type, callable $traverse): Type {
120+
if ($type instanceof GenericStaticType) {
121+
if ($this->calledOnType instanceof ObjectType) {
122+
return new GenericObjectType($this->calledOnType->getClassName(), $type->getTypes());
123+
}
124+
125+
return $this->calledOnType;
126+
}
117127
if ($type instanceof StaticType) {
118128
return $this->calledOnType;
119129
}

src/Rules/Generics/GenericObjectTypeCheck.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use PHPStan\Rules\RuleErrorBuilder;
77
use PHPStan\ShouldNotHappenException;
88
use PHPStan\Type\Generic\GenericObjectType;
9+
use PHPStan\Type\Generic\GenericStaticType;
910
use PHPStan\Type\Generic\TemplateType;
1011
use PHPStan\Type\Generic\TemplateTypeHelper;
1112
use PHPStan\Type\Generic\TemplateTypeVariance;
@@ -166,15 +167,15 @@ public function check(
166167
}
167168

168169
/**
169-
* @return GenericObjectType[]
170+
* @return list<GenericObjectType|GenericStaticType>
170171
*/
171172
private function getGenericTypes(Type $phpDocType): array
172173
{
173174
$genericObjectTypes = [];
174175
TypeTraverser::map($phpDocType, static function (Type $type, callable $traverse) use (&$genericObjectTypes): Type {
175-
if ($type instanceof GenericObjectType) {
176+
if ($type instanceof GenericObjectType || $type instanceof GenericStaticType) {
176177
$resolvedType = TemplateTypeHelper::resolveToBounds($type);
177-
if (!$resolvedType instanceof GenericObjectType) {
178+
if (!$resolvedType instanceof GenericObjectType && !$resolvedType instanceof GenericStaticType) {
178179
throw new ShouldNotHappenException();
179180
}
180181
$genericObjectTypes[] = $resolvedType;

src/Rules/Methods/MethodSignatureRule.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use PHPStan\TrinaryLogic;
1919
use PHPStan\Type\Accessory\AccessoryArrayListType;
2020
use PHPStan\Type\ArrayType;
21+
use PHPStan\Type\Generic\GenericStaticType;
2122
use PHPStan\Type\Generic\TemplateTypeHelper;
2223
use PHPStan\Type\IntegerRangeType;
2324
use PHPStan\Type\IntersectionType;
@@ -269,6 +270,15 @@ private function checkParameterTypeCompatibility(
269270
private function transformStaticType(ClassReflection $declaringClass, Type $type): Type
270271
{
271272
return TypeTraverser::map($type, static function (Type $type, callable $traverse) use ($declaringClass): Type {
273+
if ($type instanceof GenericStaticType) {
274+
if ($declaringClass->isFinal()) {
275+
$changedType = $type->changeBaseClass($declaringClass)->getStaticObjectType();
276+
} else {
277+
$changedType = $type->changeBaseClass($declaringClass);
278+
}
279+
return $traverse($changedType);
280+
}
281+
272282
if ($type instanceof StaticType) {
273283
if ($declaringClass->isFinal()) {
274284
$changedType = new ObjectType($declaringClass->getName());

src/Rules/MissingTypehintCheck.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PHPStan\Type\ConditionalType;
1414
use PHPStan\Type\ConditionalTypeForParameter;
1515
use PHPStan\Type\Generic\GenericObjectType;
16+
use PHPStan\Type\Generic\GenericStaticType;
1617
use PHPStan\Type\Generic\TemplateType;
1718
use PHPStan\Type\Generic\TemplateTypeHelper;
1819
use PHPStan\Type\IntersectionType;
@@ -113,7 +114,7 @@ public function getNonGenericObjectTypesWithGenericClass(Type $type): array
113114

114115
$objectTypes = [];
115116
TypeTraverser::map($type, function (Type $type, callable $traverse) use (&$objectTypes): Type {
116-
if ($type instanceof GenericObjectType) {
117+
if ($type instanceof GenericObjectType || $type instanceof GenericStaticType) {
117118
$traverse($type);
118119
return $type;
119120
}

0 commit comments

Comments
 (0)