Skip to content

Commit c2c30d7

Browse files
committed
[BCB] Refactor TypeSpecifier::create() method and SpecifiedTypes constructor parameters
1 parent 7e366e0 commit c2c30d7

26 files changed

+310
-313
lines changed

UPGRADING.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,24 @@ Now, `throw $e;` is represented as a `Stmt\Expression` that contains an `Expr\Th
2626

2727
See [UPGRADING](https://github.com/phpstan/phpdoc-parser/blob/2.0.x/UPGRADING.md) guide for phpstan/phpdoc-parser.
2828

29-
TODO
29+
### Changed `TypeSpecifier::create()` and `SpecifiedTypes` constructor parameters
30+
31+
[`PHPStan\Analyser\TypeSpecifier::create()`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.TypeSpecifier.html#_create) now accepts (all parameters are required):
32+
33+
* `Expr $expr`
34+
* `Type $type`
35+
* `TypeSpecifierContext $context`
36+
* `Scope $scope`
37+
38+
If you want to change `$overwrite` or `$rootExpr` (previous parameters also used to be accepted by this method), call `setAlwaysOverwriteTypes()` and `setRootExpr()` on [`SpecifiedTypes`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.SpecifiedTypes.html) (object returned by `TypeSpecifier::create()`). These methods return a new object (SpecifiedTypes is immutable).
39+
40+
[`SpecifiedTypes`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.SpecifiedTypes.html) constructor now accepts:
41+
42+
* `array $sureTypes`
43+
* `array $sureNotTypes`
44+
45+
If you want to change `$overwrite` or `$rootExpr` (previous parameters also used to be accepted by the constructor), call `setAlwaysOverwriteTypes()` and `setRootExpr()`. These methods return a new object (SpecifiedTypes is immutable).
46+
47+
### Changed `TypeSpecifier::specifyTypesInCondition()`
48+
49+
This method now longer accepts `Expr $rootExpr`. If you want to change it, call `setRootExpr()` on [`SpecifiedTypes`](https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.SpecifiedTypes.html) (object returned by `TypeSpecifier::specifyTypesInCondition()`). `setRootExpr()` method returns a new object (SpecifiedTypes is immutable).

src/Analyser/SpecifiedTypes.php

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,78 @@
99
final class SpecifiedTypes
1010
{
1111

12+
private bool $overwrite = false;
13+
14+
/** @var array<string, ConditionalExpressionHolder[]> */
15+
private array $newConditionalExpressionHolders = [];
16+
17+
private ?Expr $rootExpr = null;
18+
1219
/**
1320
* @api
1421
* @param array<string, array{Expr, Type}> $sureTypes
1522
* @param array<string, array{Expr, Type}> $sureNotTypes
16-
* @param array<string, ConditionalExpressionHolder[]> $newConditionalExpressionHolders
1723
*/
1824
public function __construct(
1925
private array $sureTypes = [],
2026
private array $sureNotTypes = [],
21-
private bool $overwrite = false,
22-
private array $newConditionalExpressionHolders = [],
23-
private ?Expr $rootExpr = null,
2427
)
2528
{
2629
}
2730

31+
/**
32+
* Normally, $sureTypes in truthy context are used to intersect with the pre-existing type.
33+
* And $sureNotTypes are used to remove type from the pre-existing type.
34+
*
35+
* Example: By default, non-empty-string intersected with '' (ConstantStringType) will lead to NeverType.
36+
* Because it's not possible to narrow non-empty-string to an empty string.
37+
*
38+
* In rare cases, a type-specifying extension might want to overwrite the pre-existing types
39+
* without taking the pre-existing types into consideration.
40+
*
41+
* In that case it should also call setAlwaysOverwriteTypes() on
42+
* the returned object.
43+
*
44+
* ! Only do this if you're certain. Otherwise, this is a source of common bugs. !
45+
*
46+
* @api
47+
*/
48+
public function setAlwaysOverwriteTypes(): self
49+
{
50+
$self = new self($this->sureTypes, $this->sureNotTypes);
51+
$self->overwrite = true;
52+
$self->newConditionalExpressionHolders = $this->newConditionalExpressionHolders;
53+
$self->rootExpr = $this->rootExpr;
54+
55+
return $self;
56+
}
57+
58+
/**
59+
* @api
60+
*/
61+
public function setRootExpr(?Expr $rootExpr): self
62+
{
63+
$self = new self($this->sureTypes, $this->sureNotTypes);
64+
$self->overwrite = $this->overwrite;
65+
$self->newConditionalExpressionHolders = $this->newConditionalExpressionHolders;
66+
$self->rootExpr = $rootExpr;
67+
68+
return $self;
69+
}
70+
71+
/**
72+
* @param array<string, ConditionalExpressionHolder[]> $newConditionalExpressionHolders
73+
*/
74+
public function setNewConditionalExpressionHolders(array $newConditionalExpressionHolders): self
75+
{
76+
$self = new self($this->sureTypes, $this->sureNotTypes);
77+
$self->overwrite = $this->overwrite;
78+
$self->newConditionalExpressionHolders = $newConditionalExpressionHolders;
79+
$self->rootExpr = $this->rootExpr;
80+
81+
return $self;
82+
}
83+
2884
/**
2985
* @api
3086
* @return array<string, array{Expr, Type}>
@@ -90,7 +146,12 @@ public function intersectWith(SpecifiedTypes $other): self
90146
];
91147
}
92148

93-
return new self($sureTypeUnion, $sureNotTypeUnion, $this->overwrite && $other->overwrite, [], $rootExpr);
149+
$result = new self($sureTypeUnion, $sureNotTypeUnion);
150+
if ($this->overwrite && $other->overwrite) {
151+
$result = $result->setAlwaysOverwriteTypes();
152+
}
153+
154+
return $result->setRootExpr($rootExpr);
94155
}
95156

96157
/** @api */
@@ -122,7 +183,12 @@ public function unionWith(SpecifiedTypes $other): self
122183
];
123184
}
124185

125-
return new self($sureTypeUnion, $sureNotTypeUnion, $this->overwrite || $other->overwrite, [], $rootExpr);
186+
$result = new self($sureTypeUnion, $sureNotTypeUnion);
187+
if ($this->overwrite || $other->overwrite) {
188+
$result = $result->setAlwaysOverwriteTypes();
189+
}
190+
191+
return $result->setRootExpr($rootExpr);
126192
}
127193

128194
public function normalize(Scope $scope): self
@@ -138,7 +204,12 @@ public function normalize(Scope $scope): self
138204
$sureTypes[$exprString][1] = TypeCombinator::remove($sureTypes[$exprString][1], $sureNotType);
139205
}
140206

141-
return new self($sureTypes, [], $this->overwrite, $this->newConditionalExpressionHolders, $this->rootExpr);
207+
$result = new self($sureTypes, []);
208+
if ($this->overwrite) {
209+
$result = $result->setAlwaysOverwriteTypes();
210+
}
211+
212+
return $result->setRootExpr($this->rootExpr);
142213
}
143214

144215
private function mergeRootExpr(?Expr $rootExprA, ?Expr $rootExprB): ?Expr

0 commit comments

Comments
 (0)