1: | <?php declare(strict_types = 1); |
2: | |
3: | namespace PHPStan\Analyser; |
4: | |
5: | use PhpParser\Node\Expr; |
6: | use PHPStan\Type\Type; |
7: | use PHPStan\Type\TypeCombinator; |
8: | |
9: | final class SpecifiedTypes |
10: | { |
11: | |
12: | private bool $overwrite = false; |
13: | |
14: | |
15: | private array $newConditionalExpressionHolders = []; |
16: | |
17: | private ?Expr $rootExpr = null; |
18: | |
19: | |
20: | |
21: | |
22: | |
23: | |
24: | public function __construct( |
25: | private array $sureTypes = [], |
26: | private array $sureNotTypes = [], |
27: | ) |
28: | { |
29: | } |
30: | |
31: | |
32: | |
33: | |
34: | |
35: | |
36: | |
37: | |
38: | |
39: | |
40: | |
41: | |
42: | |
43: | |
44: | |
45: | |
46: | |
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: | |
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: | |
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: | |
84: | |
85: | |
86: | |
87: | |
88: | public function getSureTypes(): array |
89: | { |
90: | return $this->sureTypes; |
91: | } |
92: | |
93: | |
94: | |
95: | |
96: | |
97: | public function getSureNotTypes(): array |
98: | { |
99: | return $this->sureNotTypes; |
100: | } |
101: | |
102: | public function shouldOverwrite(): bool |
103: | { |
104: | return $this->overwrite; |
105: | } |
106: | |
107: | |
108: | |
109: | |
110: | public function getNewConditionalExpressionHolders(): array |
111: | { |
112: | return $this->newConditionalExpressionHolders; |
113: | } |
114: | |
115: | public function getRootExpr(): ?Expr |
116: | { |
117: | return $this->rootExpr; |
118: | } |
119: | |
120: | |
121: | public function intersectWith(SpecifiedTypes $other): self |
122: | { |
123: | $sureTypeUnion = []; |
124: | $sureNotTypeUnion = []; |
125: | $rootExpr = $this->mergeRootExpr($this->rootExpr, $other->rootExpr); |
126: | |
127: | foreach ($this->sureTypes as $exprString => [$exprNode, $type]) { |
128: | if (!isset($other->sureTypes[$exprString])) { |
129: | continue; |
130: | } |
131: | |
132: | $sureTypeUnion[$exprString] = [ |
133: | $exprNode, |
134: | TypeCombinator::union($type, $other->sureTypes[$exprString][1]), |
135: | ]; |
136: | } |
137: | |
138: | foreach ($this->sureNotTypes as $exprString => [$exprNode, $type]) { |
139: | if (!isset($other->sureNotTypes[$exprString])) { |
140: | continue; |
141: | } |
142: | |
143: | $sureNotTypeUnion[$exprString] = [ |
144: | $exprNode, |
145: | TypeCombinator::intersect($type, $other->sureNotTypes[$exprString][1]), |
146: | ]; |
147: | } |
148: | |
149: | $result = new self($sureTypeUnion, $sureNotTypeUnion); |
150: | if ($this->overwrite && $other->overwrite) { |
151: | $result = $result->setAlwaysOverwriteTypes(); |
152: | } |
153: | |
154: | return $result->setRootExpr($rootExpr); |
155: | } |
156: | |
157: | |
158: | public function unionWith(SpecifiedTypes $other): self |
159: | { |
160: | $sureTypeUnion = $this->sureTypes + $other->sureTypes; |
161: | $sureNotTypeUnion = $this->sureNotTypes + $other->sureNotTypes; |
162: | $rootExpr = $this->mergeRootExpr($this->rootExpr, $other->rootExpr); |
163: | |
164: | foreach ($this->sureTypes as $exprString => [$exprNode, $type]) { |
165: | if (!isset($other->sureTypes[$exprString])) { |
166: | continue; |
167: | } |
168: | |
169: | $sureTypeUnion[$exprString] = [ |
170: | $exprNode, |
171: | TypeCombinator::intersect($type, $other->sureTypes[$exprString][1]), |
172: | ]; |
173: | } |
174: | |
175: | foreach ($this->sureNotTypes as $exprString => [$exprNode, $type]) { |
176: | if (!isset($other->sureNotTypes[$exprString])) { |
177: | continue; |
178: | } |
179: | |
180: | $sureNotTypeUnion[$exprString] = [ |
181: | $exprNode, |
182: | TypeCombinator::union($type, $other->sureNotTypes[$exprString][1]), |
183: | ]; |
184: | } |
185: | |
186: | $result = new self($sureTypeUnion, $sureNotTypeUnion); |
187: | if ($this->overwrite || $other->overwrite) { |
188: | $result = $result->setAlwaysOverwriteTypes(); |
189: | } |
190: | |
191: | return $result->setRootExpr($rootExpr); |
192: | } |
193: | |
194: | public function normalize(Scope $scope): self |
195: | { |
196: | $sureTypes = $this->sureTypes; |
197: | |
198: | foreach ($this->sureNotTypes as $exprString => [$exprNode, $sureNotType]) { |
199: | if (!isset($sureTypes[$exprString])) { |
200: | $sureTypes[$exprString] = [$exprNode, TypeCombinator::remove($scope->getType($exprNode), $sureNotType)]; |
201: | continue; |
202: | } |
203: | |
204: | $sureTypes[$exprString][1] = TypeCombinator::remove($sureTypes[$exprString][1], $sureNotType); |
205: | } |
206: | |
207: | $result = new self($sureTypes, []); |
208: | if ($this->overwrite) { |
209: | $result = $result->setAlwaysOverwriteTypes(); |
210: | } |
211: | |
212: | return $result->setRootExpr($this->rootExpr); |
213: | } |
214: | |
215: | private function mergeRootExpr(?Expr $rootExprA, ?Expr $rootExprB): ?Expr |
216: | { |
217: | if ($rootExprA === $rootExprB) { |
218: | return $rootExprA; |
219: | } |
220: | |
221: | if ($rootExprA === null || $rootExprB === null) { |
222: | return $rootExprA ?? $rootExprB; |
223: | } |
224: | |
225: | return null; |
226: | } |
227: | |
228: | } |
229: | |