diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index f1dfb1f0da..a24fe0d9ef 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -4112,7 +4112,19 @@ private function generalizeType(Type $a, Type $b, int $depth): Type } if ($gotGreater && $gotSmaller) { - $resultTypes[] = new IntegerType(); + $newMin = $min; + $newMax = $max; + foreach ($constantIntegers['b'] as $int) { + if ($int->getValue() < $newMin) { + $newMin = $int->getValue(); + } + if ($int->getValue() <= $newMax) { + continue; + } + + $newMax = $int->getValue(); + } + $resultTypes[] = IntegerRangeType::fromInterval($newMin, $newMax); } elseif ($gotGreater) { $resultTypes[] = IntegerRangeType::fromInterval($min, null); } elseif ($gotSmaller) { diff --git a/tests/PHPStan/Analyser/nsrt/bug-12163.php b/tests/PHPStan/Analyser/nsrt/bug-12163.php new file mode 100644 index 0000000000..5e443f03ea --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-12163.php @@ -0,0 +1,71 @@ +', $rowIndex); + assertType('int<0, max>', $columnIndex); + if ($columnIndex < $columns) { + $columnIndex++; + } else { + $columnIndex = 0; + $rowIndex++; + } + } + } +} + +class Test2 +{ + public function iterateRowColumnIndicesDecrementing(int $rows, int $columns): void + { + if ($rows < 1 || $columns < 1) return; + $size = $rows * $columns; + + $rowIndex = 0; + $columnIndex = 0; + for ($i = 0; $i < $size; $i++) { + assertType('0', $rowIndex); + assertType('int', $columnIndex); + if ($columnIndex < $columns) { + $columnIndex--; + } else { + $columnIndex = 0; + $rowIndex++; + } + } + } +} + +class Test3 +{ + /** + * @param int<0, 30> $columnIndex + */ + public function iterateRowColumnIndicesDecrementing(int $rows, int $columns, int $columnIndex): void + { + if ($rows < 1 || $columns < 1) return; + $size = $rows * $columns; + + for ($i = 0; $i < $size; $i++) { + assertType('int', $columnIndex); + if ($columnIndex < 3) { + $columnIndex--; + } else { + $columnIndex = 0; + } + assertType('int', $columnIndex); + } + } +}