@@ -181,184 +181,7 @@ public function specifyTypesInCondition(
181
181
return $ this ->create ($ exprNode , new ObjectWithoutClassType (), $ context , false , $ scope , $ rootExpr );
182
182
}
183
183
} elseif ($ expr instanceof Node \Expr \BinaryOp \Identical) {
184
- $ leftExpr = $ expr ->left ;
185
- $ rightExpr = $ expr ->right ;
186
- if ($ rightExpr instanceof FuncCall && !$ leftExpr instanceof FuncCall) {
187
- [$ leftExpr , $ rightExpr ] = [$ rightExpr , $ leftExpr ];
188
- }
189
- $ unwrappedLeftExpr = $ leftExpr ;
190
- if ($ leftExpr instanceof AlwaysRememberedExpr) {
191
- $ unwrappedLeftExpr = $ leftExpr ->getExpr ();
192
- }
193
- $ rightType = $ scope ->getType ($ rightExpr );
194
- if (
195
- $ context ->true ()
196
- && $ unwrappedLeftExpr instanceof FuncCall
197
- && $ unwrappedLeftExpr ->name instanceof Name
198
- && strtolower ($ unwrappedLeftExpr ->name ->toString ()) === 'get_class '
199
- && isset ($ unwrappedLeftExpr ->getArgs ()[0 ])
200
- ) {
201
- if ($ rightType ->getClassStringObjectType ()->isObject ()->yes ()) {
202
- return $ this ->create (
203
- $ unwrappedLeftExpr ->getArgs ()[0 ]->value ,
204
- $ rightType ->getClassStringObjectType (),
205
- $ context ,
206
- false ,
207
- $ scope ,
208
- $ rootExpr ,
209
- )->unionWith ($ this ->create ($ leftExpr , $ rightType , $ context , false , $ scope , $ rootExpr ));
210
- }
211
- }
212
-
213
- if (count ($ rightType ->getConstantStrings ()) > 0 ) {
214
- $ types = null ;
215
- foreach ($ rightType ->getConstantStrings () as $ constantString ) {
216
- $ specifiedType = $ this ->specifyTypesForConstantStringBinaryExpression ($ unwrappedLeftExpr , $ constantString , $ context , $ scope , $ rootExpr );
217
- if ($ specifiedType === null ) {
218
- continue ;
219
- }
220
- if ($ types === null ) {
221
- $ types = $ specifiedType ;
222
- continue ;
223
- }
224
-
225
- $ types = $ types ->intersectWith ($ specifiedType );
226
- }
227
-
228
- if ($ types !== null ) {
229
- if ($ leftExpr !== $ unwrappedLeftExpr ) {
230
- $ types = $ types ->unionWith ($ this ->create ($ leftExpr , $ rightType , $ context , false , $ scope , $ rootExpr ));
231
- }
232
- return $ types ;
233
- }
234
- }
235
-
236
- $ expressions = $ this ->findTypeExpressionsFromBinaryOperation ($ scope , $ expr );
237
- if ($ expressions !== null ) {
238
- $ exprNode = $ expressions [0 ];
239
- $ constantType = $ expressions [1 ];
240
-
241
- $ specifiedType = $ this ->specifyTypesForConstantBinaryExpression ($ exprNode , $ constantType , $ context , $ scope , $ rootExpr );
242
- if ($ specifiedType !== null ) {
243
- return $ specifiedType ;
244
- }
245
- }
246
-
247
- if ($ rightExpr instanceof AlwaysRememberedExpr) {
248
- $ rightExpr = $ rightExpr ->getExpr ();
249
- }
250
-
251
- if ($ leftExpr instanceof AlwaysRememberedExpr) {
252
- $ leftExpr = $ leftExpr ->getExpr ();
253
- }
254
-
255
- if (
256
- $ context ->true () &&
257
- $ leftExpr instanceof ClassConstFetch &&
258
- $ leftExpr ->class instanceof Expr &&
259
- $ leftExpr ->name instanceof Node \Identifier &&
260
- $ rightExpr instanceof ClassConstFetch &&
261
- $ rightType instanceof ConstantStringType &&
262
- strtolower ($ leftExpr ->name ->toString ()) === 'class '
263
- ) {
264
- return $ this ->specifyTypesInCondition (
265
- $ scope ,
266
- new Instanceof_ (
267
- $ leftExpr ->class ,
268
- new Name ($ rightType ->getValue ()),
269
- ),
270
- $ context ,
271
- $ rootExpr ,
272
- )->unionWith ($ this ->create ($ expr ->left , $ rightType , $ context , false , $ scope , $ rootExpr ));
273
- }
274
-
275
- $ leftType = $ scope ->getType ($ leftExpr );
276
- if (
277
- $ context ->true () &&
278
- $ rightExpr instanceof ClassConstFetch &&
279
- $ rightExpr ->class instanceof Expr &&
280
- $ rightExpr ->name instanceof Node \Identifier &&
281
- $ leftExpr instanceof ClassConstFetch &&
282
- $ leftType instanceof ConstantStringType &&
283
- strtolower ($ rightExpr ->name ->toString ()) === 'class '
284
- ) {
285
- return $ this ->specifyTypesInCondition (
286
- $ scope ,
287
- new Instanceof_ (
288
- $ rightExpr ->class ,
289
- new Name ($ leftType ->getValue ()),
290
- ),
291
- $ context ,
292
- $ rootExpr ,
293
- )->unionWith ($ this ->create ($ expr ->right , $ leftType , $ context , false , $ scope , $ rootExpr ));
294
- }
295
-
296
- if ($ context ->false ()) {
297
- $ identicalType = $ scope ->getType ($ expr );
298
- if ($ identicalType instanceof ConstantBooleanType) {
299
- $ never = new NeverType ();
300
- $ contextForTypes = $ identicalType ->getValue () ? $ context ->negate () : $ context ;
301
- $ leftTypes = $ this ->create ($ expr ->left , $ never , $ contextForTypes , false , $ scope , $ rootExpr );
302
- $ rightTypes = $ this ->create ($ expr ->right , $ never , $ contextForTypes , false , $ scope , $ rootExpr );
303
- return $ leftTypes ->unionWith ($ rightTypes );
304
- }
305
- }
306
-
307
- $ types = null ;
308
- $ exprLeftType = $ scope ->getType ($ expr ->left );
309
- $ exprRightType = $ scope ->getType ($ expr ->right );
310
- if (
311
- count ($ exprLeftType ->getFiniteTypes ()) === 1
312
- || ($ exprLeftType ->isConstantValue ()->yes () && !$ exprRightType ->equals ($ exprLeftType ) && $ exprRightType ->isSuperTypeOf ($ exprLeftType )->yes ())
313
- ) {
314
- $ types = $ this ->create (
315
- $ expr ->right ,
316
- $ exprLeftType ,
317
- $ context ,
318
- false ,
319
- $ scope ,
320
- $ rootExpr ,
321
- );
322
- }
323
- if (
324
- count ($ exprRightType ->getFiniteTypes ()) === 1
325
- || ($ exprRightType ->isConstantValue ()->yes () && !$ exprLeftType ->equals ($ exprRightType ) && $ exprLeftType ->isSuperTypeOf ($ exprRightType )->yes ())
326
- ) {
327
- $ leftType = $ this ->create (
328
- $ expr ->left ,
329
- $ exprRightType ,
330
- $ context ,
331
- false ,
332
- $ scope ,
333
- $ rootExpr ,
334
- );
335
- if ($ types !== null ) {
336
- $ types = $ types ->unionWith ($ leftType );
337
- } else {
338
- $ types = $ leftType ;
339
- }
340
- }
341
-
342
- if ($ types !== null ) {
343
- return $ types ;
344
- }
345
-
346
- $ leftExprString = $ this ->exprPrinter ->printExpr ($ expr ->left );
347
- $ rightExprString = $ this ->exprPrinter ->printExpr ($ expr ->right );
348
- if ($ leftExprString === $ rightExprString ) {
349
- if (!$ expr ->left instanceof Expr \Variable || !$ expr ->right instanceof Expr \Variable) {
350
- return new SpecifiedTypes ([], [], false , [], $ rootExpr );
351
- }
352
- }
353
-
354
- if ($ context ->true ()) {
355
- $ leftTypes = $ this ->create ($ expr ->left , $ exprRightType , $ context , false , $ scope , $ rootExpr );
356
- $ rightTypes = $ this ->create ($ expr ->right , $ exprLeftType , $ context , false , $ scope , $ rootExpr );
357
- return $ leftTypes ->unionWith ($ rightTypes );
358
- } elseif ($ context ->false ()) {
359
- return $ this ->create ($ expr ->left , $ exprLeftType , $ context , false , $ scope , $ rootExpr )->normalize ($ scope )
360
- ->intersectWith ($ this ->create ($ expr ->right , $ exprRightType , $ context , false , $ scope , $ rootExpr )->normalize ($ scope ));
361
- }
184
+ return $ this ->resolveIdentical ($ expr , $ scope , $ context , $ rootExpr );
362
185
363
186
} elseif ($ expr instanceof Node \Expr \BinaryOp \NotIdentical) {
364
187
return $ this ->specifyTypesInCondition (
@@ -1811,4 +1634,188 @@ private function getTypeSpecifyingExtensionsForType(array $extensions, string $c
1811
1634
return array_merge (...$ extensionsForClass );
1812
1635
}
1813
1636
1637
+ public function resolveIdentical (Expr \BinaryOp \Identical $ expr , Scope $ scope , TypeSpecifierContext $ context , Expr $ rootExpr ): SpecifiedTypes
1638
+ {
1639
+ $ leftExpr = $ expr ->left ;
1640
+ $ rightExpr = $ expr ->right ;
1641
+ if ($ rightExpr instanceof FuncCall && !$ leftExpr instanceof FuncCall) {
1642
+ [$ leftExpr , $ rightExpr ] = [$ rightExpr , $ leftExpr ];
1643
+ }
1644
+ $ unwrappedLeftExpr = $ leftExpr ;
1645
+ if ($ leftExpr instanceof AlwaysRememberedExpr) {
1646
+ $ unwrappedLeftExpr = $ leftExpr ->getExpr ();
1647
+ }
1648
+ $ rightType = $ scope ->getType ($ rightExpr );
1649
+ if (
1650
+ $ context ->true ()
1651
+ && $ unwrappedLeftExpr instanceof FuncCall
1652
+ && $ unwrappedLeftExpr ->name instanceof Name
1653
+ && strtolower ($ unwrappedLeftExpr ->name ->toString ()) === 'get_class '
1654
+ && isset ($ unwrappedLeftExpr ->getArgs ()[0 ])
1655
+ ) {
1656
+ if ($ rightType ->getClassStringObjectType ()->isObject ()->yes ()) {
1657
+ return $ this ->create (
1658
+ $ unwrappedLeftExpr ->getArgs ()[0 ]->value ,
1659
+ $ rightType ->getClassStringObjectType (),
1660
+ $ context ,
1661
+ false ,
1662
+ $ scope ,
1663
+ $ rootExpr ,
1664
+ )->unionWith ($ this ->create ($ leftExpr , $ rightType , $ context , false , $ scope , $ rootExpr ));
1665
+ }
1666
+ }
1667
+
1668
+ if (count ($ rightType ->getConstantStrings ()) > 0 ) {
1669
+ $ types = null ;
1670
+ foreach ($ rightType ->getConstantStrings () as $ constantString ) {
1671
+ $ specifiedType = $ this ->specifyTypesForConstantStringBinaryExpression ($ unwrappedLeftExpr , $ constantString , $ context , $ scope , $ rootExpr );
1672
+ if ($ specifiedType === null ) {
1673
+ continue ;
1674
+ }
1675
+ if ($ types === null ) {
1676
+ $ types = $ specifiedType ;
1677
+ continue ;
1678
+ }
1679
+
1680
+ $ types = $ types ->intersectWith ($ specifiedType );
1681
+ }
1682
+
1683
+ if ($ types !== null ) {
1684
+ if ($ leftExpr !== $ unwrappedLeftExpr ) {
1685
+ $ types = $ types ->unionWith ($ this ->create ($ leftExpr , $ rightType , $ context , false , $ scope , $ rootExpr ));
1686
+ }
1687
+ return $ types ;
1688
+ }
1689
+ }
1690
+
1691
+ $ expressions = $ this ->findTypeExpressionsFromBinaryOperation ($ scope , $ expr );
1692
+ if ($ expressions !== null ) {
1693
+ $ exprNode = $ expressions [0 ];
1694
+ $ constantType = $ expressions [1 ];
1695
+
1696
+ $ specifiedType = $ this ->specifyTypesForConstantBinaryExpression ($ exprNode , $ constantType , $ context , $ scope , $ rootExpr );
1697
+ if ($ specifiedType !== null ) {
1698
+ return $ specifiedType ;
1699
+ }
1700
+ }
1701
+
1702
+ if ($ rightExpr instanceof AlwaysRememberedExpr) {
1703
+ $ rightExpr = $ rightExpr ->getExpr ();
1704
+ }
1705
+
1706
+ if ($ leftExpr instanceof AlwaysRememberedExpr) {
1707
+ $ leftExpr = $ leftExpr ->getExpr ();
1708
+ }
1709
+
1710
+ if (
1711
+ $ context ->true () &&
1712
+ $ leftExpr instanceof ClassConstFetch &&
1713
+ $ leftExpr ->class instanceof Expr &&
1714
+ $ leftExpr ->name instanceof Node \Identifier &&
1715
+ $ rightExpr instanceof ClassConstFetch &&
1716
+ $ rightType instanceof ConstantStringType &&
1717
+ strtolower ($ leftExpr ->name ->toString ()) === 'class '
1718
+ ) {
1719
+ return $ this ->specifyTypesInCondition (
1720
+ $ scope ,
1721
+ new Instanceof_ (
1722
+ $ leftExpr ->class ,
1723
+ new Name ($ rightType ->getValue ()),
1724
+ ),
1725
+ $ context ,
1726
+ $ rootExpr ,
1727
+ )->unionWith ($ this ->create ($ expr ->left , $ rightType , $ context , false , $ scope , $ rootExpr ));
1728
+ }
1729
+
1730
+ $ leftType = $ scope ->getType ($ leftExpr );
1731
+ if (
1732
+ $ context ->true () &&
1733
+ $ rightExpr instanceof ClassConstFetch &&
1734
+ $ rightExpr ->class instanceof Expr &&
1735
+ $ rightExpr ->name instanceof Node \Identifier &&
1736
+ $ leftExpr instanceof ClassConstFetch &&
1737
+ $ leftType instanceof ConstantStringType &&
1738
+ strtolower ($ rightExpr ->name ->toString ()) === 'class '
1739
+ ) {
1740
+ return $ this ->specifyTypesInCondition (
1741
+ $ scope ,
1742
+ new Instanceof_ (
1743
+ $ rightExpr ->class ,
1744
+ new Name ($ leftType ->getValue ()),
1745
+ ),
1746
+ $ context ,
1747
+ $ rootExpr ,
1748
+ )->unionWith ($ this ->create ($ expr ->right , $ leftType , $ context , false , $ scope , $ rootExpr ));
1749
+ }
1750
+
1751
+ if ($ context ->false ()) {
1752
+ $ identicalType = $ scope ->getType ($ expr );
1753
+ if ($ identicalType instanceof ConstantBooleanType) {
1754
+ $ never = new NeverType ();
1755
+ $ contextForTypes = $ identicalType ->getValue () ? $ context ->negate () : $ context ;
1756
+ $ leftTypes = $ this ->create ($ expr ->left , $ never , $ contextForTypes , false , $ scope , $ rootExpr );
1757
+ $ rightTypes = $ this ->create ($ expr ->right , $ never , $ contextForTypes , false , $ scope , $ rootExpr );
1758
+ return $ leftTypes ->unionWith ($ rightTypes );
1759
+ }
1760
+ }
1761
+
1762
+ $ types = null ;
1763
+ $ exprLeftType = $ scope ->getType ($ expr ->left );
1764
+ $ exprRightType = $ scope ->getType ($ expr ->right );
1765
+ if (
1766
+ count ($ exprLeftType ->getFiniteTypes ()) === 1
1767
+ || ($ exprLeftType ->isConstantValue ()->yes () && !$ exprRightType ->equals ($ exprLeftType ) && $ exprRightType ->isSuperTypeOf ($ exprLeftType )->yes ())
1768
+ ) {
1769
+ $ types = $ this ->create (
1770
+ $ expr ->right ,
1771
+ $ exprLeftType ,
1772
+ $ context ,
1773
+ false ,
1774
+ $ scope ,
1775
+ $ rootExpr ,
1776
+ );
1777
+ }
1778
+ if (
1779
+ count ($ exprRightType ->getFiniteTypes ()) === 1
1780
+ || ($ exprRightType ->isConstantValue ()->yes () && !$ exprLeftType ->equals ($ exprRightType ) && $ exprLeftType ->isSuperTypeOf ($ exprRightType )->yes ())
1781
+ ) {
1782
+ $ leftType = $ this ->create (
1783
+ $ expr ->left ,
1784
+ $ exprRightType ,
1785
+ $ context ,
1786
+ false ,
1787
+ $ scope ,
1788
+ $ rootExpr ,
1789
+ );
1790
+ if ($ types !== null ) {
1791
+ $ types = $ types ->unionWith ($ leftType );
1792
+ } else {
1793
+ $ types = $ leftType ;
1794
+ }
1795
+ }
1796
+
1797
+ if ($ types !== null ) {
1798
+ return $ types ;
1799
+ }
1800
+
1801
+ $ leftExprString = $ this ->exprPrinter ->printExpr ($ expr ->left );
1802
+ $ rightExprString = $ this ->exprPrinter ->printExpr ($ expr ->right );
1803
+ if ($ leftExprString === $ rightExprString ) {
1804
+ if (!$ expr ->left instanceof Expr \Variable || !$ expr ->right instanceof Expr \Variable) {
1805
+ return new SpecifiedTypes ([], [], false , [], $ rootExpr );
1806
+ }
1807
+ }
1808
+
1809
+ if ($ context ->true ()) {
1810
+ $ leftTypes = $ this ->create ($ expr ->left , $ exprRightType , $ context , false , $ scope , $ rootExpr );
1811
+ $ rightTypes = $ this ->create ($ expr ->right , $ exprLeftType , $ context , false , $ scope , $ rootExpr );
1812
+ return $ leftTypes ->unionWith ($ rightTypes );
1813
+ } elseif ($ context ->false ()) {
1814
+ return $ this ->create ($ expr ->left , $ exprLeftType , $ context , false , $ scope , $ rootExpr )->normalize ($ scope )
1815
+ ->intersectWith ($ this ->create ($ expr ->right , $ exprRightType , $ context , false , $ scope , $ rootExpr )->normalize ($ scope ));
1816
+ }
1817
+
1818
+ return new SpecifiedTypes ([], [], false , [], $ rootExpr );
1819
+ }
1820
+
1814
1821
}
0 commit comments