Skip to content

Commit ac0441e

Browse files
committed
MutatingScope: extract type-traverser classes
1 parent 65de89f commit ac0441e

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;
@@ -1137,23 +1142,9 @@ private function resolveType(string $exprString, Expr $node): Type
11371142
}
11381143
} else {
11391144
$classType = $this->getType($node->class);
1140-
$classType = TypeTraverser::map($classType, static function (Type $type, callable $traverse) use (&$uncertainty): Type {
1141-
if ($type instanceof UnionType || $type instanceof IntersectionType) {
1142-
return $traverse($type);
1143-
}
1144-
if ($type->getObjectClassNames() !== []) {
1145-
$uncertainty = true;
1146-
return $type;
1147-
}
1148-
if ($type instanceof GenericClassStringType) {
1149-
$uncertainty = true;
1150-
return $type->getGenericType();
1151-
}
1152-
if ($type instanceof ConstantStringType) {
1153-
return new ObjectType($type->getValue());
1154-
}
1155-
return new MixedType();
1156-
});
1145+
$traverser = new InstanceOfClassTypeTraverser();
1146+
$classType = TypeTraverser::map($classType, $traverser);
1147+
$uncertainty = $traverser->getUncertainty();
11571148
}
11581149

11591150
if ($classType->isSuperTypeOf(new MixedType())->yes()) {
@@ -1287,17 +1278,7 @@ private function resolveType(string $exprString, Expr $node): Type
12871278

12881279
if ($node instanceof Expr\Clone_) {
12891280
$cloneType = TypeCombinator::intersect($this->getType($node->expr), new ObjectWithoutClassType());
1290-
1291-
return TypeTraverser::map($cloneType, static function (Type $type, callable $traverse): Type {
1292-
if ($type instanceof UnionType || $type instanceof IntersectionType) {
1293-
return $traverse($type);
1294-
}
1295-
if ($type instanceof ThisType) {
1296-
return new StaticType($type->getClassReflection(), $type->getSubtractedType());
1297-
}
1298-
1299-
return $type;
1300-
});
1281+
return TypeTraverser::map($cloneType, new CloneTypeTraverser());
13011282
}
13021283

13031284
if ($node instanceof Node\Scalar\Int_) {
@@ -2569,17 +2550,7 @@ private function transformVoidToNull(Type $type, Node $node): Type
25692550
return $type;
25702551
}
25712552

2572-
return TypeTraverser::map($type, static function (Type $type, callable $traverse): Type {
2573-
if ($type instanceof UnionType || $type instanceof IntersectionType) {
2574-
return $traverse($type);
2575-
}
2576-
2577-
if ($type->isVoid()->yes()) {
2578-
return new NullType();
2579-
}
2580-
2581-
return $type;
2582-
});
2553+
return TypeTraverser::map($type, new VoidToNullTraverser());
25832554
}
25842555

25852556
/**
@@ -3244,21 +3215,7 @@ public function enterPropertyHook(
32443215

32453216
private function transformStaticType(Type $type): Type
32463217
{
3247-
return TypeTraverser::map($type, function (Type $type, callable $traverse): Type {
3248-
if (!$this->isInClass()) {
3249-
return $type;
3250-
}
3251-
if ($type instanceof StaticType) {
3252-
$classReflection = $this->getClassReflection();
3253-
$changedType = $type->changeBaseClass($classReflection);
3254-
if ($classReflection->isFinal() && !$type instanceof ThisType) {
3255-
$changedType = $changedType->getStaticObjectType();
3256-
}
3257-
return $traverse($changedType);
3258-
}
3259-
3260-
return $traverse($type);
3261-
});
3218+
return TypeTraverser::map($type, new TransformStaticTypeTraverser($this));
32623219
}
32633220

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

5998-
return $traverse($type);
5999-
});
5946+
$traverser = new ConstructorClassTemplateTraverser($classTemplateTypes);
5947+
foreach ($constructorVariant->getParameters() as $parameter) {
5948+
TypeTraverser::map($parameter->getType(), $traverser);
60005949
}
5950+
$classTemplateTypes = $traverser->getClassTemplateTypes();
60015951

60025952
if (count($classTemplateTypes) === count($originalClassTemplateTypes)) {
60035953
$propertyType = TypeCombinator::removeNull($this->getType($assignedToProperty));
@@ -6166,18 +6116,7 @@ classReflection: $classReflection->withTypes($types)->asFinal(),
61666116
[],
61676117
);
61686118
}
6169-
return TypeTraverser::map($newGenericType, static function (Type $type, callable $traverse) use ($resolvedTemplateTypeMap): Type {
6170-
if ($type instanceof TemplateType && !$type->isArgument()) {
6171-
$newType = $resolvedTemplateTypeMap->getType($type->getName());
6172-
if ($newType === null || $newType instanceof ErrorType) {
6173-
return $type->getDefault() ?? $type->getBound();
6174-
}
6175-
6176-
return TemplateTypeHelper::generalizeInferredTemplateType($type, $newType);
6177-
}
6178-
6179-
return $traverse($type);
6180-
});
6119+
return TypeTraverser::map($newGenericType, new GenericTypeTemplateTraverser($resolvedTemplateTypeMap));
61816120
}
61826121

61836122
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)