@@ -1572,15 +1572,8 @@ private function findTypeExpressionsFromBinaryOperation(Scope $scope, Node\Expr\
1572
1572
$ leftType = $ scope ->getType ($ binaryOperation ->left );
1573
1573
$ rightType = $ scope ->getType ($ binaryOperation ->right );
1574
1574
1575
- $ rightExpr = $ binaryOperation ->right ;
1576
- if ($ rightExpr instanceof AlwaysRememberedExpr) {
1577
- $ rightExpr = $ rightExpr ->getExpr ();
1578
- }
1579
-
1580
- $ leftExpr = $ binaryOperation ->left ;
1581
- if ($ leftExpr instanceof AlwaysRememberedExpr) {
1582
- $ leftExpr = $ leftExpr ->getExpr ();
1583
- }
1575
+ $ rightExpr = $ this ->extractExpression ($ binaryOperation ->right );
1576
+ $ leftExpr = $ this ->extractExpression ($ binaryOperation ->left );
1584
1577
1585
1578
if (
1586
1579
$ leftType instanceof ConstantScalarType
@@ -1599,6 +1592,39 @@ private function findTypeExpressionsFromBinaryOperation(Scope $scope, Node\Expr\
1599
1592
return null ;
1600
1593
}
1601
1594
1595
+ /**
1596
+ * @return array{Expr, Type, Type}|null
1597
+ */
1598
+ private function findEnumTypeExpressionsFromBinaryOperation (Scope $ scope , Node \Expr \BinaryOp $ binaryOperation ): ?array
1599
+ {
1600
+ $ leftType = $ scope ->getType ($ binaryOperation ->left );
1601
+ $ rightType = $ scope ->getType ($ binaryOperation ->right );
1602
+
1603
+ $ rightExpr = $ this ->extractExpression ($ binaryOperation ->right );
1604
+ $ leftExpr = $ this ->extractExpression ($ binaryOperation ->left );
1605
+
1606
+ if (
1607
+ $ leftType ->getEnumCases () === [$ leftType ]
1608
+ && !$ rightExpr instanceof ConstFetch
1609
+ && !$ rightExpr instanceof ClassConstFetch
1610
+ ) {
1611
+ return [$ binaryOperation ->right , $ leftType , $ rightType ];
1612
+ } elseif (
1613
+ $ rightType ->getEnumCases () === [$ rightType ]
1614
+ && !$ leftExpr instanceof ConstFetch
1615
+ && !$ leftExpr instanceof ClassConstFetch
1616
+ ) {
1617
+ return [$ binaryOperation ->left , $ rightType , $ leftType ];
1618
+ }
1619
+
1620
+ return null ;
1621
+ }
1622
+
1623
+ private function extractExpression (Expr $ expr ): Expr
1624
+ {
1625
+ return $ expr instanceof AlwaysRememberedExpr ? $ expr ->getExpr () : $ expr ;
1626
+ }
1627
+
1602
1628
/** @api */
1603
1629
public function create (
1604
1630
Expr $ expr ,
@@ -1990,6 +2016,27 @@ public function resolveEqual(Expr\BinaryOp\Equal $expr, Scope $scope, TypeSpecif
1990
2016
) {
1991
2017
return $ this ->specifyTypesInCondition ($ scope , new Expr \BinaryOp \Identical ($ expr ->left , $ expr ->right ), $ context )->setRootExpr ($ expr );
1992
2018
}
2019
+
2020
+ if (!$ context ->null () && TypeCombinator::containsNull ($ otherType )) {
2021
+ if ($ constantType ->toBoolean ()->isTrue ()->yes ()) {
2022
+ $ otherType = TypeCombinator::remove ($ otherType , new NullType ());
2023
+ }
2024
+
2025
+ if (!$ otherType ->isSuperTypeOf ($ constantType )->no ()) {
2026
+ return $ this ->create ($ exprNode , TypeCombinator::intersect ($ constantType , $ otherType ), $ context , $ scope )->setRootExpr ($ expr );
2027
+ }
2028
+ }
2029
+ }
2030
+
2031
+ $ expressions = $ this ->findEnumTypeExpressionsFromBinaryOperation ($ scope , $ expr );
2032
+ if ($ expressions !== null ) {
2033
+ $ exprNode = $ expressions [0 ];
2034
+ $ enumCaseObjectType = $ expressions [1 ];
2035
+ $ otherType = $ expressions [2 ];
2036
+
2037
+ if (!$ context ->null ()) {
2038
+ return $ this ->create ($ exprNode , TypeCombinator::intersect ($ enumCaseObjectType , $ otherType ), $ context , $ scope )->setRootExpr ($ expr );
2039
+ }
1993
2040
}
1994
2041
1995
2042
$ leftType = $ scope ->getType ($ expr ->left );
0 commit comments