@@ -1268,95 +1268,32 @@ private function resolveType(Expr $node): Type
1268
1268
!($ node instanceof Node \Expr \BinaryOp \Pow || $ node instanceof Node \Expr \AssignOp \Pow)) {
1269
1269
1270
1270
if ($ leftType instanceof ConstantIntegerType) {
1271
- $ leftMin = $ leftType ->getValue ();
1272
- $ leftMax = $ leftType ->getValue ();
1271
+ return $ this ->integerRangeMath (
1272
+ $ leftType ,
1273
+ $ node ,
1274
+ $ rightType
1275
+ );
1273
1276
} elseif ($ leftType instanceof UnionType) {
1274
- $ leftMin = null ;
1275
- $ leftMax = null ;
1276
1277
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 = [];
1296
1279
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 ;
1317
1285
}
1318
1286
}
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 ;
1330
1287
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 ();
1333
1291
}
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 ;
1340
1292
1341
- if ($ min !== null && $ max !== null && $ min > $ max ) {
1342
- [$ min , $ max ] = [$ max , $ min ];
1343
- }
1293
+ return $ union ->toNumber ();
1344
1294
}
1345
1295
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 );
1360
1297
}
1361
1298
1362
1299
$ operatorSigil = null ;
@@ -5175,4 +5112,123 @@ private function propertyFetchType(Type $fetchedOnType, string $propertyName, Ex
5175
5112
return $ propertyReflection ->getReadableType ();
5176
5113
}
5177
5114
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
+
5178
5234
}
0 commit comments