@@ -1268,95 +1268,32 @@ private function resolveType(Expr $node): Type
12681268 !($ node instanceof Node \Expr \BinaryOp \Pow || $ node instanceof Node \Expr \AssignOp \Pow)) {
12691269
12701270 if ($ leftType instanceof ConstantIntegerType) {
1271- $ leftMin = $ leftType ->getValue ();
1272- $ leftMax = $ leftType ->getValue ();
1271+ return $ this ->integerRangeMath (
1272+ $ leftType ,
1273+ $ node ,
1274+ $ rightType
1275+ );
12731276 } elseif ($ leftType instanceof UnionType) {
1274- $ leftMin = null ;
1275- $ leftMax = null ;
12761277
1277- foreach ($ leftType ->getTypes () as $ type ) {
1278- if ($ type instanceof IntegerRangeType) {
1279- $ leftMin = $ leftMin !== null ? min ($ leftMin , $ type ->getMin ()) : $ type ->getMin ();
1280- $ leftMax = max ($ leftMax , $ type ->getMax ());
1281- } elseif ($ type instanceof ConstantIntegerType) {
1282- if ($ node instanceof Node \Expr \BinaryOp \Minus || $ node instanceof Node \Expr \AssignOp \Minus ||
1283- $ node instanceof Node \Expr \BinaryOp \Div || $ node instanceof Node \Expr \AssignOp \Div) {
1284- $ leftMin = max ($ leftMin , $ type ->getValue ());
1285- $ leftMax = $ leftMax !== null ? min ($ leftMax , $ type ->getValue ()) : $ type ->getValue ();
1286- } else {
1287- $ leftMin = $ leftMin !== null ? min ($ leftMin , $ type ->getValue ()) : $ type ->getValue ();
1288- $ leftMax = max ($ leftMax , $ type ->getValue ());
1289- }
1290- }
1291- }
1292- } else {
1293- $ leftMin = $ leftType ->getMin ();
1294- $ leftMax = $ leftType ->getMax ();
1295- }
1278+ $ unionParts = [];
12961279
1297- if ($ rightType instanceof ConstantIntegerType) {
1298- $ rightMin = $ rightType ->getValue ();
1299- $ rightMax = $ rightType ->getValue ();
1300- } elseif ($ rightType instanceof UnionType) {
1301- $ rightMin = null ;
1302- $ rightMax = null ;
1303-
1304- foreach ($ rightType ->getTypes () as $ type ) {
1305- if ($ type instanceof IntegerRangeType) {
1306- $ rightMin = $ rightMin !== null ? min ($ rightMin , $ type ->getMin ()) : $ type ->getMin ();
1307- $ rightMax = max ($ rightMax , $ type ->getMax ());
1308- } elseif ($ type instanceof ConstantIntegerType) {
1309- if ($ node instanceof Node \Expr \BinaryOp \Minus || $ node instanceof Node \Expr \AssignOp \Minus ||
1310- $ node instanceof Node \Expr \BinaryOp \Div || $ node instanceof Node \Expr \AssignOp \Div) {
1311- $ rightMin = max ($ rightMin , $ type ->getValue ());
1312- $ rightMax = $ rightMax !== null ? min ($ rightMax , $ type ->getValue ()) : $ type ->getValue ();
1313- } else {
1314- $ rightMin = $ rightMin !== null ? min ($ rightMin , $ type ->getValue ()) : $ type ->getValue ();
1315- $ rightMax = max ($ rightMax , $ type ->getValue ());
1316- }
1280+ foreach ($ leftType ->getTypes () as $ type ) {
1281+ if ($ type instanceof IntegerRangeType || $ type instanceof ConstantIntegerType) {
1282+ $ unionParts [] = $ this ->integerRangeMath ($ type , $ node , $ rightType );
1283+ } else {
1284+ $ unionParts [] = $ type ;
13171285 }
13181286 }
1319- } else {
1320- $ rightMin = $ rightType ->getMin ();
1321- $ rightMax = $ rightType ->getMax ();
1322- }
1323-
1324- if ($ node instanceof Node \Expr \BinaryOp \Plus || $ node instanceof Node \Expr \AssignOp \Plus) {
1325- $ min = $ leftMin !== null && $ rightMin !== null ? $ leftMin + $ rightMin : null ;
1326- $ max = $ leftMax !== null && $ rightMax !== null ? $ leftMax + $ rightMax : null ;
1327- } elseif ($ node instanceof Node \Expr \BinaryOp \Minus || $ node instanceof Node \Expr \AssignOp \Minus) {
1328- $ min = $ leftMin !== null && $ rightMin !== null ? $ leftMin - $ rightMin : null ;
1329- $ max = $ leftMax !== null && $ rightMax !== null ? $ leftMax - $ rightMax : null ;
13301287
1331- if ($ min !== null && $ max !== null && $ min > $ max ) {
1332- [$ min , $ max ] = [$ max , $ min ];
1288+ $ union = TypeCombinator::union (...$ unionParts );
1289+ if ($ leftType instanceof BenevolentUnionType) {
1290+ return TypeUtils::toBenevolentUnion ($ union )->toNumber ();
13331291 }
1334- } elseif ($ node instanceof Node \Expr \BinaryOp \Mul || $ node instanceof Node \Expr \AssignOp \Mul) {
1335- $ min = $ leftMin !== null && $ rightMin !== null ? $ leftMin * $ rightMin : null ;
1336- $ max = $ leftMax !== null && $ rightMax !== null ? $ leftMax * $ rightMax : null ;
1337- } else {
1338- $ min = $ leftMin !== null && $ rightMin !== null && $ rightMin !== 0 ? (int ) ($ leftMin / $ rightMin ) : null ;
1339- $ max = $ leftMax !== null && $ rightMax !== null && $ rightMax !== 0 ? (int ) ($ leftMax / $ rightMax ) : null ;
13401292
1341- if ($ min !== null && $ max !== null && $ min > $ max ) {
1342- [$ min , $ max ] = [$ max , $ min ];
1343- }
1293+ return $ union ->toNumber ();
13441294 }
13451295
1346- if ($ min !== null || $ max !== null ) {
1347- $ integerRange = IntegerRangeType::fromInterval ($ min , $ max );
1348-
1349- if ($ node instanceof Node \Expr \BinaryOp \Div || $ node instanceof Node \Expr \AssignOp \Div) {
1350- if ($ min === $ max && $ min === 0 ) {
1351- // division of upper and lower bound turns into a tiny 0.x fraction, which casted to int turns into 0.
1352- // this leads to a useless 0|float type; we return only float instead.
1353- return new FloatType ();
1354- }
1355- return TypeCombinator::union ($ integerRange , new FloatType ());
1356- }
1357-
1358- return $ integerRange ;
1359- }
1296+ return $ this ->integerRangeMath ($ leftType , $ node , $ rightType );
13601297 }
13611298
13621299 $ operatorSigil = null ;
@@ -5175,4 +5112,123 @@ private function propertyFetchType(Type $fetchedOnType, string $propertyName, Ex
51755112 return $ propertyReflection ->getReadableType ();
51765113 }
51775114
5115+ /**
5116+ * @param ConstantIntegerType|IntegerRangeType $range
5117+ * @param \PhpParser\Node\Expr\AssignOp\Div|\PhpParser\Node\Expr\AssignOp\Minus|\PhpParser\Node\Expr\AssignOp\Mul|\PhpParser\Node\Expr\AssignOp\Plus|\PhpParser\Node\Expr\BinaryOp\Div|\PhpParser\Node\Expr\BinaryOp\Minus|\PhpParser\Node\Expr\BinaryOp\Mul|\PhpParser\Node\Expr\BinaryOp\Plus $node
5118+ * @param IntegerRangeType|ConstantIntegerType|UnionType $operand
5119+ */
5120+ private function integerRangeMath (Type $ range , Expr $ node , Type $ operand ): Type
5121+ {
5122+ if ($ range instanceof IntegerRangeType) {
5123+ $ rangeMin = $ range ->getMin ();
5124+ $ rangeMax = $ range ->getMax ();
5125+ } else {
5126+ $ rangeMin = $ range ->getValue ();
5127+ $ rangeMax = $ rangeMin ;
5128+ }
5129+
5130+ if ($ operand instanceof UnionType) {
5131+
5132+ $ unionParts = [];
5133+
5134+ foreach ($ operand ->getTypes () as $ type ) {
5135+ if ($ type instanceof IntegerRangeType || $ type instanceof ConstantIntegerType) {
5136+ $ unionParts [] = $ this ->integerRangeMath ($ range , $ node , $ type );
5137+ } else {
5138+ $ unionParts [] = $ type ->toNumber ();
5139+ }
5140+ }
5141+
5142+ $ union = TypeCombinator::union (...$ unionParts );
5143+ if ($ operand instanceof BenevolentUnionType) {
5144+ return TypeUtils::toBenevolentUnion ($ union )->toNumber ();
5145+ }
5146+
5147+ return $ union ->toNumber ();
5148+ }
5149+
5150+ if ($ node instanceof Node \Expr \BinaryOp \Plus || $ node instanceof Node \Expr \AssignOp \Plus) {
5151+ if ($ operand instanceof ConstantIntegerType) {
5152+ $ min = $ rangeMin !== null ? $ rangeMin + $ operand ->getValue () : null ;
5153+ $ max = $ rangeMax !== null ? $ rangeMax + $ operand ->getValue () : null ;
5154+ } else {
5155+ $ min = $ rangeMin !== null && $ operand ->getMin () !== null ? $ rangeMin + $ operand ->getMin () : null ;
5156+ $ max = $ rangeMax !== null && $ operand ->getMax () !== null ? $ rangeMax + $ operand ->getMax () : null ;
5157+ }
5158+ } elseif ($ node instanceof Node \Expr \BinaryOp \Minus || $ node instanceof Node \Expr \AssignOp \Minus) {
5159+ if ($ operand instanceof ConstantIntegerType) {
5160+ $ min = $ rangeMin !== null ? $ rangeMin - $ operand ->getValue () : null ;
5161+ $ max = $ rangeMax !== null ? $ rangeMax - $ operand ->getValue () : null ;
5162+ } else {
5163+ if ($ rangeMin === $ rangeMax && $ rangeMin !== null
5164+ && ($ operand ->getMin () === null || $ operand ->getMax () === null )) {
5165+ $ min = null ;
5166+ $ max = $ rangeMin ;
5167+ } else {
5168+ if ($ operand ->getMin () === null ) {
5169+ $ min = null ;
5170+ } elseif ($ rangeMin !== null ) {
5171+ $ min = $ rangeMin - $ operand ->getMin ();
5172+ } else {
5173+ $ min = null ;
5174+ }
5175+
5176+ if ($ operand ->getMax () === null ) {
5177+ $ max = null ;
5178+ } elseif ($ rangeMax !== null ) {
5179+ $ max = $ rangeMax - $ operand ->getMax ();
5180+ } else {
5181+ $ max = null ;
5182+ }
5183+
5184+ if ($ min !== null && $ max !== null && $ min > $ max ) {
5185+ [$ min , $ max ] = [$ max , $ min ];
5186+ }
5187+ }
5188+ }
5189+ } elseif ($ node instanceof Node \Expr \BinaryOp \Mul || $ node instanceof Node \Expr \AssignOp \Mul) {
5190+ if ($ operand instanceof ConstantIntegerType) {
5191+ $ min = $ rangeMin !== null ? $ rangeMin * $ operand ->getValue () : null ;
5192+ $ max = $ rangeMax !== null ? $ rangeMax * $ operand ->getValue () : null ;
5193+ } else {
5194+ $ min = $ rangeMin !== null && $ operand ->getMin () !== null ? $ rangeMin * $ operand ->getMin () : null ;
5195+ $ max = $ rangeMax !== null && $ operand ->getMax () !== null ? $ rangeMax * $ operand ->getMax () : null ;
5196+ }
5197+
5198+ if ($ min !== null && $ max !== null && $ min > $ max ) {
5199+ [$ min , $ max ] = [$ max , $ min ];
5200+ }
5201+
5202+ } else {
5203+ if ($ operand instanceof ConstantIntegerType) {
5204+ $ min = $ rangeMin !== null && $ operand ->getValue () !== 0 ? $ rangeMin / $ operand ->getValue () : null ;
5205+ $ max = $ rangeMax !== null && $ operand ->getValue () !== 0 ? $ rangeMax / $ operand ->getValue () : null ;
5206+ } else {
5207+ $ min = $ rangeMin !== null && $ operand ->getMin () !== null && $ operand ->getMin () !== 0 ? $ rangeMin / $ operand ->getMin () : null ;
5208+ $ max = $ rangeMax !== null && $ operand ->getMax () !== null && $ operand ->getMax () !== 0 ? $ rangeMax / $ operand ->getMax () : null ;
5209+ }
5210+
5211+ if ($ operand instanceof IntegerRangeType
5212+ && ($ operand ->getMin () === null || $ operand ->getMax () === null )
5213+ || ($ rangeMin === null || $ rangeMax === null )
5214+ || is_float ($ min ) || is_float ($ max )
5215+ ) {
5216+ if (is_float ($ min )) {
5217+ $ min = (int ) $ min ;
5218+ }
5219+ if (is_float ($ max )) {
5220+ $ max = (int ) $ max ;
5221+ }
5222+
5223+ if ($ min !== null && $ max !== null && $ min > $ max ) {
5224+ [$ min , $ max ] = [$ max , $ min ];
5225+ }
5226+
5227+ return TypeCombinator::union (IntegerRangeType::fromInterval ($ min , $ max ), new FloatType ());
5228+ }
5229+ }
5230+
5231+ return IntegerRangeType::fromInterval ($ min , $ max );
5232+ }
5233+
51785234}
0 commit comments