Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b39369c
Fix non-negative-int generalized to int in for loop
github-actions[bot] Mar 9, 2026
4b1d4cf
test invers
staabm Mar 10, 2026
46b553a
Update bug-12163.php
staabm Mar 10, 2026
541b77a
Apply symmetric fix for integer range generalization in gotGreater &&…
phpstan-bot Mar 11, 2026
ec611b6
Update bug-12163.php
staabm Mar 11, 2026
b41d360
Revert "Update bug-12163.php"
staabm Mar 12, 2026
95d62ef
Revert "Apply symmetric fix for integer range generalization in gotGr…
staabm Mar 12, 2026
2279ebd
Update NumberComparisonOperatorsConstantConditionRuleTest.php
staabm Mar 12, 2026
1ea5d36
add rule test
staabm Mar 12, 2026
781ce01
add origin bug test
staabm Mar 12, 2026
78346da
Update NumberComparisonOperatorsConstantConditionRuleTest.php
staabm Mar 12, 2026
bc7792a
Apply symmetric fix for integer range generalization in gotGreater &&…
phpstan-bot Mar 12, 2026
8ac04f0
Revert "Apply symmetric fix for integer range generalization in gotGr…
staabm Mar 12, 2026
f8275b7
Update bug-12163.php
staabm Mar 12, 2026
92fe388
Apply symmetric fix for integer range generalization in gotGreater &&…
phpstan-bot Mar 12, 2026
14a2154
Revert "Apply symmetric fix for integer range generalization in gotGr…
staabm Mar 12, 2026
309179a
Merge foreach loops for gotGreater/gotSmaller and newMin/newMax compu…
phpstan-bot Mar 12, 2026
6a1c710
Revert "Merge foreach loops for gotGreater/gotSmaller and newMin/newM…
staabm Mar 12, 2026
992768b
merge loops
staabm Mar 12, 2026
cb6972e
cleanup
staabm Mar 12, 2026
5cc5312
Apply symmetric fix for integer range generalization in both-directio…
phpstan-bot Mar 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -4098,21 +4098,25 @@ private function generalizeType(Type $a, Type $b, int $depth): Type
$max = $int->getValue();
}

$newMin = $min;
$newMax = $max;
$gotGreater = false;
$gotSmaller = false;
foreach ($constantIntegers['b'] as $int) {
if ($int->getValue() > $max) {
$newMax = $int->getValue();
$gotGreater = true;
}
if ($int->getValue() >= $min) {
continue;
}

$newMin = $int->getValue();
$gotSmaller = true;
}

if ($gotGreater && $gotSmaller) {
$resultTypes[] = new IntegerType();
$resultTypes[] = IntegerRangeType::fromInterval($newMin, $newMax);
} elseif ($gotGreater) {
$resultTypes[] = IntegerRangeType::fromInterval($min, null);
} elseif ($gotSmaller) {
Expand Down
102 changes: 102 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-12163.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php declare(strict_types = 1);

namespace Bug12163;

use function PHPStan\Testing\assertType;

class Test
{
public function iterateRowColumnIndicesIncrementing(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('int<0, max>', $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); // `0`, because the IF in line 41 is always TRUE
assertType('int<min, 0>', $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<min, 30>', $columnIndex);
if ($columnIndex < 3) {
$columnIndex--;
} else {
$columnIndex = 0;
}
assertType('int<min, 1>', $columnIndex);
}
}
}

class Bug12163
{
/**
* @param non-negative-int $value
* @return void
*/
private function checkNonNegative(int $value): void
{
sleep(1);
}

public function iterateRowColumnIndices(int $rows, int $columns): void
{
if ($rows < 1 || $columns < 1) return;
$size = $rows * $columns;

$rowIndex = 0;
$columnIndex = 0;
for ($i = 0; $i < $size; $i++) {
$this->checkNonNegative($rowIndex);
$this->checkNonNegative($columnIndex);
if ($columnIndex < $columns) {
$columnIndex++;
} else {
$columnIndex = 0;
$rowIndex++;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -288,4 +288,14 @@ public function testBug13874(): void
$this->analyse([__DIR__ . '/data/bug-13874.php'], []);
}

public function testBug12163(): void
{
$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-12163.php'], [
[
'Comparison operation "<" between int<min, 0> and int<1, max> is always true.',
41,
],
]);
}

}
Loading