@@ -1643,15 +1643,8 @@ private function findTypeExpressionsFromBinaryOperation(Scope $scope, Node\Expr\
1643
1643
$ leftType = $ scope ->getType ($ binaryOperation ->left );
1644
1644
$ rightType = $ scope ->getType ($ binaryOperation ->right );
1645
1645
1646
- $ rightExpr = $ binaryOperation ->right ;
1647
- if ($ rightExpr instanceof AlwaysRememberedExpr) {
1648
- $ rightExpr = $ rightExpr ->getExpr ();
1649
- }
1650
-
1651
- $ leftExpr = $ binaryOperation ->left ;
1652
- if ($ leftExpr instanceof AlwaysRememberedExpr) {
1653
- $ leftExpr = $ leftExpr ->getExpr ();
1654
- }
1646
+ $ rightExpr = $ this ->extractExpression ($ binaryOperation ->right );
1647
+ $ leftExpr = $ this ->extractExpression ($ binaryOperation ->left );
1655
1648
1656
1649
if (
1657
1650
$ leftType instanceof ConstantScalarType
@@ -1670,6 +1663,39 @@ private function findTypeExpressionsFromBinaryOperation(Scope $scope, Node\Expr\
1670
1663
return null ;
1671
1664
}
1672
1665
1666
+ /**
1667
+ * @return array{Expr, Type, Type}|null
1668
+ */
1669
+ private function findEnumTypeExpressionsFromBinaryOperation (Scope $ scope , Node \Expr \BinaryOp $ binaryOperation ): ?array
1670
+ {
1671
+ $ leftType = $ scope ->getType ($ binaryOperation ->left );
1672
+ $ rightType = $ scope ->getType ($ binaryOperation ->right );
1673
+
1674
+ $ rightExpr = $ this ->extractExpression ($ binaryOperation ->right );
1675
+ $ leftExpr = $ this ->extractExpression ($ binaryOperation ->left );
1676
+
1677
+ if (
1678
+ $ leftType ->getEnumCases () === [$ leftType ]
1679
+ && !$ rightExpr instanceof ConstFetch
1680
+ && !$ rightExpr instanceof ClassConstFetch
1681
+ ) {
1682
+ return [$ binaryOperation ->right , $ leftType , $ rightType ];
1683
+ } elseif (
1684
+ $ rightType ->getEnumCases () === [$ rightType ]
1685
+ && !$ leftExpr instanceof ConstFetch
1686
+ && !$ leftExpr instanceof ClassConstFetch
1687
+ ) {
1688
+ return [$ binaryOperation ->left , $ rightType , $ leftType ];
1689
+ }
1690
+
1691
+ return null ;
1692
+ }
1693
+
1694
+ private function extractExpression (Expr $ expr ): Expr
1695
+ {
1696
+ return $ expr instanceof AlwaysRememberedExpr ? $ expr ->getExpr () : $ expr ;
1697
+ }
1698
+
1673
1699
/** @api */
1674
1700
public function create (
1675
1701
Expr $ expr ,
@@ -2061,6 +2087,27 @@ public function resolveEqual(Expr\BinaryOp\Equal $expr, Scope $scope, TypeSpecif
2061
2087
) {
2062
2088
return $ this ->specifyTypesInCondition ($ scope , new Expr \BinaryOp \Identical ($ expr ->left , $ expr ->right ), $ context )->setRootExpr ($ expr );
2063
2089
}
2090
+
2091
+ if (!$ context ->null () && TypeCombinator::containsNull ($ otherType )) {
2092
+ if ($ constantType ->toBoolean ()->isTrue ()->yes ()) {
2093
+ $ otherType = TypeCombinator::remove ($ otherType , new NullType ());
2094
+ }
2095
+
2096
+ if (!$ otherType ->isSuperTypeOf ($ constantType )->no ()) {
2097
+ return $ this ->create ($ exprNode , TypeCombinator::intersect ($ constantType , $ otherType ), $ context , $ scope )->setRootExpr ($ expr );
2098
+ }
2099
+ }
2100
+ }
2101
+
2102
+ $ expressions = $ this ->findEnumTypeExpressionsFromBinaryOperation ($ scope , $ expr );
2103
+ if ($ expressions !== null ) {
2104
+ $ exprNode = $ expressions [0 ];
2105
+ $ enumCaseObjectType = $ expressions [1 ];
2106
+ $ otherType = $ expressions [2 ];
2107
+
2108
+ if (!$ context ->null ()) {
2109
+ return $ this ->create ($ exprNode , TypeCombinator::intersect ($ enumCaseObjectType , $ otherType ), $ context , $ scope )->setRootExpr ($ expr );
2110
+ }
2064
2111
}
2065
2112
2066
2113
$ leftType = $ scope ->getType ($ expr ->left );
0 commit comments