@@ -194,119 +194,7 @@ public function specifyTypesInCondition(
194194 $ rootExpr ,
195195 );
196196 } elseif ($ expr instanceof Node \Expr \BinaryOp \Equal) {
197- $ expressions = $ this ->findTypeExpressionsFromBinaryOperation ($ scope , $ expr );
198- if ($ expressions !== null ) {
199- $ exprNode = $ expressions [0 ];
200- $ constantType = $ expressions [1 ];
201- if (!$ context ->null () && ($ constantType ->getValue () === false || $ constantType ->getValue () === null )) {
202- return $ this ->specifyTypesInCondition (
203- $ scope ,
204- $ exprNode ,
205- $ context ->true () ? TypeSpecifierContext::createFalsey () : TypeSpecifierContext::createFalsey ()->negate (),
206- $ rootExpr ,
207- );
208- }
209-
210- if (!$ context ->null () && $ constantType ->getValue () === true ) {
211- return $ this ->specifyTypesInCondition (
212- $ scope ,
213- $ exprNode ,
214- $ context ->true () ? TypeSpecifierContext::createTruthy () : TypeSpecifierContext::createTruthy ()->negate (),
215- $ rootExpr ,
216- );
217- }
218-
219- if (
220- $ exprNode instanceof FuncCall
221- && $ exprNode ->name instanceof Name
222- && strtolower ($ exprNode ->name ->toString ()) === 'gettype '
223- && isset ($ exprNode ->getArgs ()[0 ])
224- && $ constantType ->isString ()->yes ()
225- ) {
226- return $ this ->specifyTypesInCondition ($ scope , new Expr \BinaryOp \Identical ($ expr ->left , $ expr ->right ), $ context , $ rootExpr );
227- }
228-
229- if (
230- $ exprNode instanceof FuncCall
231- && $ exprNode ->name instanceof Name
232- && strtolower ($ exprNode ->name ->toString ()) === 'get_class '
233- && isset ($ exprNode ->getArgs ()[0 ])
234- && $ constantType ->isString ()->yes ()
235- ) {
236- return $ this ->specifyTypesInCondition ($ scope , new Expr \BinaryOp \Identical ($ expr ->left , $ expr ->right ), $ context , $ rootExpr );
237- }
238- }
239-
240- $ leftType = $ scope ->getType ($ expr ->left );
241- $ rightType = $ scope ->getType ($ expr ->right );
242-
243- $ leftBooleanType = $ leftType ->toBoolean ();
244- if ($ leftBooleanType instanceof ConstantBooleanType && $ rightType ->isBoolean ()->yes ()) {
245- return $ this ->specifyTypesInCondition (
246- $ scope ,
247- new Expr \BinaryOp \Identical (
248- new ConstFetch (new Name ($ leftBooleanType ->getValue () ? 'true ' : 'false ' )),
249- $ expr ->right ,
250- ),
251- $ context ,
252- $ rootExpr ,
253- );
254- }
255-
256- $ rightBooleanType = $ rightType ->toBoolean ();
257- if ($ rightBooleanType instanceof ConstantBooleanType && $ leftType ->isBoolean ()->yes ()) {
258- return $ this ->specifyTypesInCondition (
259- $ scope ,
260- new Expr \BinaryOp \Identical (
261- $ expr ->left ,
262- new ConstFetch (new Name ($ rightBooleanType ->getValue () ? 'true ' : 'false ' )),
263- ),
264- $ context ,
265- $ rootExpr ,
266- );
267- }
268-
269- if (
270- !$ context ->null ()
271- && $ rightType ->isArray ()->yes ()
272- && $ leftType ->isConstantArray ()->yes () && $ leftType ->isIterableAtLeastOnce ()->no ()
273- ) {
274- return $ this ->create ($ expr ->right , new NonEmptyArrayType (), $ context ->negate (), false , $ scope , $ rootExpr );
275- }
276-
277- if (
278- !$ context ->null ()
279- && $ leftType ->isArray ()->yes ()
280- && $ rightType ->isConstantArray ()->yes () && $ rightType ->isIterableAtLeastOnce ()->no ()
281- ) {
282- return $ this ->create ($ expr ->left , new NonEmptyArrayType (), $ context ->negate (), false , $ scope , $ rootExpr );
283- }
284-
285- $ integerType = new IntegerType ();
286- $ floatType = new FloatType ();
287- if (
288- ($ leftType ->isString ()->yes () && $ rightType ->isString ()->yes ())
289- || ($ integerType ->isSuperTypeOf ($ leftType )->yes () && $ integerType ->isSuperTypeOf ($ rightType )->yes ())
290- || ($ floatType ->isSuperTypeOf ($ leftType )->yes () && $ floatType ->isSuperTypeOf ($ rightType )->yes ())
291- || ($ leftType ->isEnum ()->yes () && $ rightType ->isEnum ()->yes ())
292- ) {
293- return $ this ->specifyTypesInCondition ($ scope , new Expr \BinaryOp \Identical ($ expr ->left , $ expr ->right ), $ context , $ rootExpr );
294- }
295-
296- $ leftExprString = $ this ->exprPrinter ->printExpr ($ expr ->left );
297- $ rightExprString = $ this ->exprPrinter ->printExpr ($ expr ->right );
298- if ($ leftExprString === $ rightExprString ) {
299- if (!$ expr ->left instanceof Expr \Variable || !$ expr ->right instanceof Expr \Variable) {
300- return new SpecifiedTypes ([], [], false , [], $ rootExpr );
301- }
302- }
303-
304- $ leftTypes = $ this ->create ($ expr ->left , $ leftType , $ context , false , $ scope , $ rootExpr );
305- $ rightTypes = $ this ->create ($ expr ->right , $ rightType , $ context , false , $ scope , $ rootExpr );
306-
307- return $ context ->true ()
308- ? $ leftTypes ->unionWith ($ rightTypes )
309- : $ leftTypes ->normalize ($ scope )->intersectWith ($ rightTypes ->normalize ($ scope ));
197+ return $ this ->resolveEqual ($ expr , $ scope , $ context , $ rootExpr );
310198 } elseif ($ expr instanceof Node \Expr \BinaryOp \NotEqual) {
311199 return $ this ->specifyTypesInCondition (
312200 $ scope ,
@@ -1668,6 +1556,123 @@ private function getTypeSpecifyingExtensionsForType(array $extensions, string $c
16681556 return array_merge (...$ extensionsForClass );
16691557 }
16701558
1559+ public function resolveEqual (Expr \BinaryOp \Equal $ expr , Scope $ scope , TypeSpecifierContext $ context , Expr $ rootExpr ): SpecifiedTypes
1560+ {
1561+ $ expressions = $ this ->findTypeExpressionsFromBinaryOperation ($ scope , $ expr );
1562+ if ($ expressions !== null ) {
1563+ $ exprNode = $ expressions [0 ];
1564+ $ constantType = $ expressions [1 ];
1565+ if (!$ context ->null () && ($ constantType ->getValue () === false || $ constantType ->getValue () === null )) {
1566+ return $ this ->specifyTypesInCondition (
1567+ $ scope ,
1568+ $ exprNode ,
1569+ $ context ->true () ? TypeSpecifierContext::createFalsey () : TypeSpecifierContext::createFalsey ()->negate (),
1570+ $ rootExpr ,
1571+ );
1572+ }
1573+
1574+ if (!$ context ->null () && $ constantType ->getValue () === true ) {
1575+ return $ this ->specifyTypesInCondition (
1576+ $ scope ,
1577+ $ exprNode ,
1578+ $ context ->true () ? TypeSpecifierContext::createTruthy () : TypeSpecifierContext::createTruthy ()->negate (),
1579+ $ rootExpr ,
1580+ );
1581+ }
1582+
1583+ if (
1584+ $ exprNode instanceof FuncCall
1585+ && $ exprNode ->name instanceof Name
1586+ && strtolower ($ exprNode ->name ->toString ()) === 'gettype '
1587+ && isset ($ exprNode ->getArgs ()[0 ])
1588+ && $ constantType ->isString ()->yes ()
1589+ ) {
1590+ return $ this ->specifyTypesInCondition ($ scope , new Expr \BinaryOp \Identical ($ expr ->left , $ expr ->right ), $ context , $ rootExpr );
1591+ }
1592+
1593+ if (
1594+ $ exprNode instanceof FuncCall
1595+ && $ exprNode ->name instanceof Name
1596+ && strtolower ($ exprNode ->name ->toString ()) === 'get_class '
1597+ && isset ($ exprNode ->getArgs ()[0 ])
1598+ && $ constantType ->isString ()->yes ()
1599+ ) {
1600+ return $ this ->specifyTypesInCondition ($ scope , new Expr \BinaryOp \Identical ($ expr ->left , $ expr ->right ), $ context , $ rootExpr );
1601+ }
1602+ }
1603+
1604+ $ leftType = $ scope ->getType ($ expr ->left );
1605+ $ rightType = $ scope ->getType ($ expr ->right );
1606+
1607+ $ leftBooleanType = $ leftType ->toBoolean ();
1608+ if ($ leftBooleanType instanceof ConstantBooleanType && $ rightType ->isBoolean ()->yes ()) {
1609+ return $ this ->specifyTypesInCondition (
1610+ $ scope ,
1611+ new Expr \BinaryOp \Identical (
1612+ new ConstFetch (new Name ($ leftBooleanType ->getValue () ? 'true ' : 'false ' )),
1613+ $ expr ->right ,
1614+ ),
1615+ $ context ,
1616+ $ rootExpr ,
1617+ );
1618+ }
1619+
1620+ $ rightBooleanType = $ rightType ->toBoolean ();
1621+ if ($ rightBooleanType instanceof ConstantBooleanType && $ leftType ->isBoolean ()->yes ()) {
1622+ return $ this ->specifyTypesInCondition (
1623+ $ scope ,
1624+ new Expr \BinaryOp \Identical (
1625+ $ expr ->left ,
1626+ new ConstFetch (new Name ($ rightBooleanType ->getValue () ? 'true ' : 'false ' )),
1627+ ),
1628+ $ context ,
1629+ $ rootExpr ,
1630+ );
1631+ }
1632+
1633+ if (
1634+ !$ context ->null ()
1635+ && $ rightType ->isArray ()->yes ()
1636+ && $ leftType ->isConstantArray ()->yes () && $ leftType ->isIterableAtLeastOnce ()->no ()
1637+ ) {
1638+ return $ this ->create ($ expr ->right , new NonEmptyArrayType (), $ context ->negate (), false , $ scope , $ rootExpr );
1639+ }
1640+
1641+ if (
1642+ !$ context ->null ()
1643+ && $ leftType ->isArray ()->yes ()
1644+ && $ rightType ->isConstantArray ()->yes () && $ rightType ->isIterableAtLeastOnce ()->no ()
1645+ ) {
1646+ return $ this ->create ($ expr ->left , new NonEmptyArrayType (), $ context ->negate (), false , $ scope , $ rootExpr );
1647+ }
1648+
1649+ $ integerType = new IntegerType ();
1650+ $ floatType = new FloatType ();
1651+ if (
1652+ ($ leftType ->isString ()->yes () && $ rightType ->isString ()->yes ())
1653+ || ($ integerType ->isSuperTypeOf ($ leftType )->yes () && $ integerType ->isSuperTypeOf ($ rightType )->yes ())
1654+ || ($ floatType ->isSuperTypeOf ($ leftType )->yes () && $ floatType ->isSuperTypeOf ($ rightType )->yes ())
1655+ || ($ leftType ->isEnum ()->yes () && $ rightType ->isEnum ()->yes ())
1656+ ) {
1657+ return $ this ->specifyTypesInCondition ($ scope , new Expr \BinaryOp \Identical ($ expr ->left , $ expr ->right ), $ context , $ rootExpr );
1658+ }
1659+
1660+ $ leftExprString = $ this ->exprPrinter ->printExpr ($ expr ->left );
1661+ $ rightExprString = $ this ->exprPrinter ->printExpr ($ expr ->right );
1662+ if ($ leftExprString === $ rightExprString ) {
1663+ if (!$ expr ->left instanceof Expr \Variable || !$ expr ->right instanceof Expr \Variable) {
1664+ return new SpecifiedTypes ([], [], false , [], $ rootExpr );
1665+ }
1666+ }
1667+
1668+ $ leftTypes = $ this ->create ($ expr ->left , $ leftType , $ context , false , $ scope , $ rootExpr );
1669+ $ rightTypes = $ this ->create ($ expr ->right , $ rightType , $ context , false , $ scope , $ rootExpr );
1670+
1671+ return $ context ->true ()
1672+ ? $ leftTypes ->unionWith ($ rightTypes )
1673+ : $ leftTypes ->normalize ($ scope )->intersectWith ($ rightTypes ->normalize ($ scope ));
1674+ }
1675+
16711676 public function resolveIdentical (Expr \BinaryOp \Identical $ expr , Scope $ scope , TypeSpecifierContext $ context , Expr $ rootExpr ): SpecifiedTypes
16721677 {
16731678 $ leftExpr = $ expr ->left ;
0 commit comments