Skip to content

Commit

Permalink
Fix generalizing ConstantIntegerType in Scope
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jan 13, 2022
1 parent b494a81 commit d6c7595
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 7 deletions.
56 changes: 55 additions & 1 deletion src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -4717,7 +4717,6 @@ private static function generalizeType(Type $a, Type $b): Type

$resultTypes = [];
foreach ([
$constantIntegers,
$constantFloats,
$constantBooleans,
$constantStrings,
Expand Down Expand Up @@ -4766,6 +4765,8 @@ private static function generalizeType(Type $a, Type $b): Type
);
}
}
} elseif (count($constantArrays['b']) > 0) {
$resultTypes[] = TypeCombinator::union(...$constantArrays['b']);
}

if (count($generalArrays['a']) > 0) {
Expand Down Expand Up @@ -4801,6 +4802,59 @@ private static function generalizeType(Type $a, Type $b): Type
TypeCombinator::union(self::generalizeType($aValueType, $bValueType)),
);
}
} elseif (count($generalArrays['b']) > 0) {
$resultTypes[] = TypeCombinator::union(...$generalArrays['b']);
}

if (count($constantIntegers['a']) > 0) {
if (count($constantIntegers['b']) === 0) {
$resultTypes[] = TypeCombinator::union(...$constantIntegers['a']);
} else {
$constantIntegersA = TypeCombinator::union(...$constantIntegers['a']);
$constantIntegersB = TypeCombinator::union(...$constantIntegers['b']);

if ($constantIntegersA->equals($constantIntegersB)) {
$resultTypes[] = $constantIntegersA;
} else {
$min = null;
$max = null;
foreach ($constantIntegers['a'] as $int) {
if ($min === null || $int->getValue() < $min) {
$min = $int->getValue();
}
if ($max !== null && $int->getValue() <= $max) {
continue;
}

$max = $int->getValue();
}

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

$gotSmaller = true;
}

if ($gotGreater && $gotSmaller) {
$resultTypes[] = new IntegerType();
} elseif ($gotGreater) {
$resultTypes[] = IntegerRangeType::fromInterval($min, null);
} elseif ($gotSmaller) {
$resultTypes[] = IntegerRangeType::fromInterval(null, $max);
} else {
$resultTypes[] = TypeCombinator::union($constantIntegersA, $constantIntegersB);
}
}
}
} elseif (count($constantIntegers['b']) > 0) {
$resultTypes[] = TypeCombinator::union(...$constantIntegers['b']);
}

return TypeCombinator::union(...$resultTypes, ...$otherTypes);
Expand Down
3 changes: 3 additions & 0 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -2943,6 +2943,9 @@ private function processClosureNode(
if ($closureScope->equals($prevScope)) {
break;
}
if ($count >= self::GENERALIZE_AFTER_ITERATION) {
$closureScope = $prevScope->generalizeWith($closureScope);
}
$count++;
} while ($count < self::LOOP_SCOPE_ITERATIONS);

Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,7 @@ public function dataConstantTypes(): array
[
$testScope,
'yetAnotherVariableInClosurePassedByReference',
'int<0, max>', // could be 0|1
'0|1',
],
[
$testScope,
Expand Down
6 changes: 3 additions & 3 deletions tests/PHPStan/Analyser/data/for-loop-i-type.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public function doBar() {
assertType(\stdClass::class, $foo);

for($i = 50; $i > 0; $i--) {
assertType('int<1, max>', $i); // could be int<1, 50>
assertType('int<1, 50>', $i);
}

assertType('0', $i);
Expand Down Expand Up @@ -48,10 +48,10 @@ public function doCount2() {

public function doBaz() {
for($i = 1; $i < 50; $i += 2) {
assertType('int<1, 49>', $i);
assertType('1|int<3, 49>', $i);
}

assertType('int<50, 51>', $i);
assertType('int<50, max>', $i);
}

public function doLOrem() {
Expand Down
4 changes: 2 additions & 2 deletions tests/PHPStan/Analyser/data/integer-range-types.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,12 @@ function () {

$i = 5;
while ($i-- > 0) {
assertType('int<0, max>', $i); // should improved to be int<0, 4>
assertType('int<0, 4>', $i);
}

$i = 5;
while (--$i > 0) {
assertType('int<1, max>', $i); // should improved to be int<1, 4>
assertType('int<1, 4>', $i);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,11 @@ function (int $i): void {
}
}
};

function (): void {
for ($X = -3; $X <= 3; ++$X){
for ($Z = -3; $Z <= 3; ++$Z){

}
}
};

0 comments on commit d6c7595

Please sign in to comment.