Skip to content

Commit 93b2914

Browse files
committed
MutatingScope: extract type-traverser classes
1 parent 1d00c34 commit 93b2914

8 files changed

+272
-78
lines changed

phpstan-baseline.neon

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,36 @@ parameters:
7272
count: 1
7373
path: src/Analyser/RuleErrorTransformer.php
7474

75+
-
76+
rawMessage: Doing instanceof PHPStan\Type\IntersectionType is error-prone and deprecated.
77+
identifier: phpstanApi.instanceofType
78+
count: 1
79+
path: src/Analyser/Traverser/CloneTypeTraverser.php
80+
81+
-
82+
rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantStringType is error-prone and deprecated. Use Type::getConstantStrings() instead.'
83+
identifier: phpstanApi.instanceofType
84+
count: 1
85+
path: src/Analyser/Traverser/InstanceOfClassTypeTraverser.php
86+
87+
-
88+
rawMessage: 'Doing instanceof PHPStan\Type\Generic\GenericClassStringType is error-prone and deprecated. Use Type::isClassStringType() and Type::getClassStringObjectType() instead.'
89+
identifier: phpstanApi.instanceofType
90+
count: 1
91+
path: src/Analyser/Traverser/InstanceOfClassTypeTraverser.php
92+
93+
-
94+
rawMessage: Doing instanceof PHPStan\Type\IntersectionType is error-prone and deprecated.
95+
identifier: phpstanApi.instanceofType
96+
count: 1
97+
path: src/Analyser/Traverser/InstanceOfClassTypeTraverser.php
98+
99+
-
100+
rawMessage: Doing instanceof PHPStan\Type\IntersectionType is error-prone and deprecated.
101+
identifier: phpstanApi.instanceofType
102+
count: 1
103+
path: src/Analyser/Traverser/VoidToNullTraverser.php
104+
75105
-
76106
rawMessage: 'Doing instanceof PHPStan\Type\ConstantScalarType is error-prone and deprecated. Use Type::isConstantScalarValue() or Type::getConstantScalarTypes() or Type::getConstantScalarValues() instead.'
77107
identifier: phpstanApi.instanceofType

src/Analyser/MutatingScope.php

Lines changed: 17 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
use PhpParser\Node\Stmt\ClassMethod;
3030
use PhpParser\Node\Stmt\Function_;
3131
use PhpParser\NodeFinder;
32+
use PHPStan\Analyser\Traverser\CloneTypeTraverser;
33+
use PHPStan\Analyser\Traverser\ConstructorClassTemplateTraverser;
34+
use PHPStan\Analyser\Traverser\GenericTypeTemplateTraverser;
35+
use PHPStan\Analyser\Traverser\InstanceOfClassTypeTraverser;
36+
use PHPStan\Analyser\Traverser\TransformStaticTypeTraverser;
37+
use PHPStan\Analyser\Traverser\VoidToNullTraverser;
3238
use PHPStan\Node\ExecutionEndNode;
3339
use PHPStan\Node\Expr\AlwaysRememberedExpr;
3440
use PHPStan\Node\Expr\ExistingArrayDimFetch;
@@ -107,7 +113,6 @@
107113
use PHPStan\Type\ExpressionTypeResolverExtensionRegistry;
108114
use PHPStan\Type\FloatType;
109115
use PHPStan\Type\GeneralizePrecision;
110-
use PHPStan\Type\Generic\GenericClassStringType;
111116
use PHPStan\Type\Generic\GenericObjectType;
112117
use PHPStan\Type\Generic\GenericStaticType;
113118
use PHPStan\Type\Generic\TemplateType;
@@ -1139,23 +1144,9 @@ private function resolveType(string $exprString, Expr $node): Type
11391144
}
11401145
} else {
11411146
$classType = $this->getType($node->class);
1142-
$classType = TypeTraverser::map($classType, static function (Type $type, callable $traverse) use (&$uncertainty): Type {
1143-
if ($type instanceof UnionType || $type instanceof IntersectionType) {
1144-
return $traverse($type);
1145-
}
1146-
if ($type->getObjectClassNames() !== []) {
1147-
$uncertainty = true;
1148-
return $type;
1149-
}
1150-
if ($type instanceof GenericClassStringType) {
1151-
$uncertainty = true;
1152-
return $type->getGenericType();
1153-
}
1154-
if ($type instanceof ConstantStringType) {
1155-
return new ObjectType($type->getValue());
1156-
}
1157-
return new MixedType();
1158-
});
1147+
$traverser = new InstanceOfClassTypeTraverser();
1148+
$classType = TypeTraverser::map($classType, $traverser);
1149+
$uncertainty = $traverser->getUncertainty();
11591150
}
11601151

11611152
if ($classType->isSuperTypeOf(new MixedType())->yes()) {
@@ -1289,17 +1280,7 @@ private function resolveType(string $exprString, Expr $node): Type
12891280

12901281
if ($node instanceof Expr\Clone_) {
12911282
$cloneType = TypeCombinator::intersect($this->getType($node->expr), new ObjectWithoutClassType());
1292-
1293-
return TypeTraverser::map($cloneType, static function (Type $type, callable $traverse): Type {
1294-
if ($type instanceof UnionType || $type instanceof IntersectionType) {
1295-
return $traverse($type);
1296-
}
1297-
if ($type instanceof ThisType) {
1298-
return new StaticType($type->getClassReflection(), $type->getSubtractedType());
1299-
}
1300-
1301-
return $type;
1302-
});
1283+
return TypeTraverser::map($cloneType, new CloneTypeTraverser());
13031284
}
13041285

13051286
if ($node instanceof Node\Scalar\Int_) {
@@ -2571,17 +2552,7 @@ private function transformVoidToNull(Type $type, Node $node): Type
25712552
return $type;
25722553
}
25732554

2574-
return TypeTraverser::map($type, static function (Type $type, callable $traverse): Type {
2575-
if ($type instanceof UnionType || $type instanceof IntersectionType) {
2576-
return $traverse($type);
2577-
}
2578-
2579-
if ($type->isVoid()->yes()) {
2580-
return new NullType();
2581-
}
2582-
2583-
return $type;
2584-
});
2555+
return TypeTraverser::map($type, new VoidToNullTraverser());
25852556
}
25862557

25872558
/**
@@ -3246,21 +3217,7 @@ public function enterPropertyHook(
32463217

32473218
private function transformStaticType(Type $type): Type
32483219
{
3249-
return TypeTraverser::map($type, function (Type $type, callable $traverse): Type {
3250-
if (!$this->isInClass()) {
3251-
return $type;
3252-
}
3253-
if ($type instanceof StaticType) {
3254-
$classReflection = $this->getClassReflection();
3255-
$changedType = $type->changeBaseClass($classReflection);
3256-
if ($classReflection->isFinal() && !$type instanceof ThisType) {
3257-
$changedType = $changedType->getStaticObjectType();
3258-
}
3259-
return $traverse($changedType);
3260-
}
3261-
3262-
return $traverse($type);
3263-
});
3220+
return TypeTraverser::map($type, new TransformStaticTypeTraverser($this));
32643221
}
32653222

32663223
/**
@@ -5987,19 +5944,12 @@ private function exactInstantiation(New_ $node, Name $className): Type
59875944
$constructorVariant = $constructorVariants[0];
59885945
$classTemplateTypes = $classReflection->getTemplateTypeMap()->getTypes();
59895946
$originalClassTemplateTypes = $classTemplateTypes;
5990-
foreach ($constructorVariant->getParameters() as $parameter) {
5991-
TypeTraverser::map($parameter->getType(), static function (Type $type, callable $traverse) use (&$classTemplateTypes): Type {
5992-
if ($type instanceof TemplateType && array_key_exists($type->getName(), $classTemplateTypes)) {
5993-
$classTemplateType = $classTemplateTypes[$type->getName()];
5994-
if ($classTemplateType instanceof TemplateType && $classTemplateType->getScope()->equals($type->getScope())) {
5995-
unset($classTemplateTypes[$type->getName()]);
5996-
}
5997-
return $type;
5998-
}
59995947

6000-
return $traverse($type);
6001-
});
5948+
$traverser = new ConstructorClassTemplateTraverser($classTemplateTypes);
5949+
foreach ($constructorVariant->getParameters() as $parameter) {
5950+
TypeTraverser::map($parameter->getType(), $traverser);
60025951
}
5952+
$classTemplateTypes = $traverser->getClassTemplateTypes();
60035953

60045954
if (count($classTemplateTypes) === count($originalClassTemplateTypes)) {
60055955
$propertyType = TypeCombinator::removeNull($this->getType($assignedToProperty));
@@ -6168,18 +6118,7 @@ classReflection: $classReflection->withTypes($types)->asFinal(),
61686118
[],
61696119
);
61706120
}
6171-
return TypeTraverser::map($newGenericType, static function (Type $type, callable $traverse) use ($resolvedTemplateTypeMap): Type {
6172-
if ($type instanceof TemplateType && !$type->isArgument()) {
6173-
$newType = $resolvedTemplateTypeMap->getType($type->getName());
6174-
if ($newType === null || $newType instanceof ErrorType) {
6175-
return $type->getDefault() ?? $type->getBound();
6176-
}
6177-
6178-
return TemplateTypeHelper::generalizeInferredTemplateType($type, $newType);
6179-
}
6180-
6181-
return $traverse($type);
6182-
});
6121+
return TypeTraverser::map($newGenericType, new GenericTypeTemplateTraverser($resolvedTemplateTypeMap));
61836122
}
61846123

61856124
private function filterTypeWithMethod(Type $typeWithMethod, string $methodName): ?Type
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\Traverser;
4+
5+
use PHPStan\Type\IntersectionType;
6+
use PHPStan\Type\StaticType;
7+
use PHPStan\Type\ThisType;
8+
use PHPStan\Type\Type;
9+
use PHPStan\Type\UnionType;
10+
11+
final class CloneTypeTraverser
12+
{
13+
14+
/**
15+
* @param callable(Type): Type $traverse
16+
*/
17+
public function __invoke(Type $type, callable $traverse): Type
18+
{
19+
if ($type instanceof UnionType || $type instanceof IntersectionType) {
20+
return $traverse($type);
21+
}
22+
if ($type instanceof ThisType) {
23+
return new StaticType($type->getClassReflection(), $type->getSubtractedType());
24+
}
25+
26+
return $type;
27+
}
28+
29+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\Traverser;
4+
5+
use PHPStan\Type\Generic\TemplateType;
6+
use PHPStan\Type\Type;
7+
use function array_key_exists;
8+
9+
final class ConstructorClassTemplateTraverser
10+
{
11+
12+
/**
13+
* @param array<string, Type> $classTemplateTypes
14+
*/
15+
public function __construct(
16+
private array $classTemplateTypes,
17+
)
18+
{
19+
}
20+
21+
/**
22+
* @param callable(Type): Type $traverse
23+
*/
24+
public function __invoke(Type $type, callable $traverse): Type
25+
{
26+
if ($type instanceof TemplateType && array_key_exists($type->getName(), $this->classTemplateTypes)) {
27+
$classTemplateType = $this->classTemplateTypes[$type->getName()];
28+
if ($classTemplateType instanceof TemplateType && $classTemplateType->getScope()->equals($type->getScope())) {
29+
unset($this->classTemplateTypes[$type->getName()]);
30+
}
31+
return $type;
32+
}
33+
34+
return $traverse($type);
35+
}
36+
37+
/**
38+
* @return array<string, Type>
39+
*/
40+
public function getClassTemplateTypes(): array
41+
{
42+
return $this->classTemplateTypes;
43+
}
44+
45+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\Traverser;
4+
5+
use PHPStan\Type\ErrorType;
6+
use PHPStan\Type\Generic\TemplateType;
7+
use PHPStan\Type\Generic\TemplateTypeHelper;
8+
use PHPStan\Type\Generic\TemplateTypeMap;
9+
use PHPStan\Type\Type;
10+
11+
final class GenericTypeTemplateTraverser
12+
{
13+
14+
public function __construct(
15+
private TemplateTypeMap $resolvedTemplateTypeMap,
16+
)
17+
{
18+
}
19+
20+
/**
21+
* @param callable(Type): Type $traverse
22+
*/
23+
public function __invoke(Type $type, callable $traverse): Type
24+
{
25+
if ($type instanceof TemplateType && !$type->isArgument()) {
26+
$newType = $this->resolvedTemplateTypeMap->getType($type->getName());
27+
if ($newType === null || $newType instanceof ErrorType) {
28+
return $type->getDefault() ?? $type->getBound();
29+
}
30+
31+
return TemplateTypeHelper::generalizeInferredTemplateType($type, $newType);
32+
}
33+
34+
return $traverse($type);
35+
}
36+
37+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\Traverser;
4+
5+
use PHPStan\Type\Constant\ConstantStringType;
6+
use PHPStan\Type\Generic\GenericClassStringType;
7+
use PHPStan\Type\IntersectionType;
8+
use PHPStan\Type\MixedType;
9+
use PHPStan\Type\ObjectType;
10+
use PHPStan\Type\Type;
11+
use PHPStan\Type\UnionType;
12+
13+
final class InstanceOfClassTypeTraverser
14+
{
15+
16+
private bool $uncertainty = false;
17+
18+
/**
19+
* @param callable(Type): Type $traverse
20+
*/
21+
public function __invoke(Type $type, callable $traverse): Type
22+
{
23+
if ($type instanceof UnionType || $type instanceof IntersectionType) {
24+
return $traverse($type);
25+
}
26+
27+
if ($type->getObjectClassNames() !== []) {
28+
$this->uncertainty = true;
29+
return $type;
30+
}
31+
if ($type instanceof GenericClassStringType) {
32+
$this->uncertainty = true;
33+
return $type->getGenericType();
34+
}
35+
if ($type instanceof ConstantStringType) {
36+
return new ObjectType($type->getValue());
37+
}
38+
return new MixedType();
39+
}
40+
41+
public function getUncertainty(): bool
42+
{
43+
return $this->uncertainty;
44+
}
45+
46+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\Traverser;
4+
5+
use PHPStan\Analyser\Scope;
6+
use PHPStan\Type\StaticType;
7+
use PHPStan\Type\ThisType;
8+
use PHPStan\Type\Type;
9+
10+
final class TransformStaticTypeTraverser
11+
{
12+
13+
public function __construct(
14+
private readonly Scope $scope,
15+
)
16+
{
17+
}
18+
19+
/**
20+
* @param callable(Type): Type $traverse
21+
*/
22+
public function __invoke(Type $type, callable $traverse): Type
23+
{
24+
if (!$this->scope->isInClass()) {
25+
return $type;
26+
}
27+
if ($type instanceof StaticType) {
28+
$classReflection = $this->scope->getClassReflection();
29+
$changedType = $type->changeBaseClass($classReflection);
30+
if ($classReflection->isFinal() && !$type instanceof ThisType) {
31+
$changedType = $changedType->getStaticObjectType();
32+
}
33+
return $traverse($changedType);
34+
}
35+
36+
return $traverse($type);
37+
}
38+
39+
}

0 commit comments

Comments
 (0)