Skip to content

Commit

Permalink
Allow conditions inside loops to narrow int range
Browse files Browse the repository at this point in the history
Fixes #8020, fixes #8865
  • Loading branch information
theodorejb committed Dec 18, 2022
1 parent c529709 commit 2aaea01
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 44 deletions.
2 changes: 0 additions & 2 deletions src/Psalm/Internal/Diff/FileDiffer.php
Expand Up @@ -77,8 +77,6 @@ private static function extractDiff(array $trace, int $x, int $y, array $a, arra
{
$result = [];
for ($d = count($trace) - 1; $d >= 0; --$d) {
// Todo: fix integer ranges in fors
/** @var int<0, max> $d */
$v = $trace[$d];
$k = $x - $y;

Expand Down
4 changes: 0 additions & 4 deletions src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php
Expand Up @@ -123,10 +123,6 @@ public static function isContainedBy(
}

foreach ($input_type_part->type_params as $i => $input_param) {
if ($i > 1) {
break;
}

$container_param = $container_type_part->type_params[$i];

if ($i === 0
Expand Down
28 changes: 10 additions & 18 deletions src/Psalm/Internal/Type/SimpleAssertionReconciler.php
Expand Up @@ -1962,10 +1962,6 @@ private static function reconcileIsGreaterThan(
}

foreach ($existing_var_type->getAtomicTypes() as $atomic_type) {
if ($inside_loop) {
continue;
}

if ($atomic_type instanceof TIntRange) {
if ($atomic_type->contains($assertion_value)) {
// if the range contains the assertion, the range must be adapted
Expand Down Expand Up @@ -1995,15 +1991,15 @@ private static function reconcileIsGreaterThan(
if ($atomic_type->value < $assertion_value) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());
} /*elseif ($inside_loop) {
} elseif ($inside_loop) {
//when inside a loop, allow the range to extends the type
$existing_var_type->removeType($atomic_type->getKey());
/*$existing_var_type->removeType($atomic_type->getKey());
if ($atomic_type->value < $assertion_value) {
$existing_var_type->addType(new TIntRange($atomic_type->value, $assertion_value));
} else {
$existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value));
}
}*/
}*/
}
} elseif ($atomic_type instanceof TInt) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());
Expand All @@ -2015,7 +2011,7 @@ private static function reconcileIsGreaterThan(
}
}

if (!$inside_loop && !$did_remove_type && $var_id && $code_location) {
if (!$did_remove_type && $var_id && $code_location) {
self::triggerIssueForImpossible(
$existing_var_type,
$old_var_type_string,
Expand Down Expand Up @@ -2075,10 +2071,6 @@ private static function reconcileIsLessThan(
}

foreach ($existing_var_type->getAtomicTypes() as $atomic_type) {
if ($inside_loop) {
continue;
}

if ($atomic_type instanceof TIntRange) {
if ($atomic_type->contains($assertion_value)) {
// if the range contains the assertion, the range must be adapted
Expand All @@ -2105,15 +2097,15 @@ private static function reconcileIsLessThan(
if ($atomic_type->value > $assertion_value) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());
} /* elseif ($inside_loop) {
} elseif ($inside_loop) {
//when inside a loop, allow the range to extends the type
$existing_var_type->removeType($atomic_type->getKey());
/*$existing_var_type->removeType($atomic_type->getKey());
if ($atomic_type->value < $assertion_value) {
$existing_var_type->addType(new TIntRange($atomic_type->value, $assertion_value));
} else {
$existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value));
}
}*/
}*/
}
} elseif ($atomic_type instanceof TInt) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());
Expand All @@ -2125,7 +2117,7 @@ private static function reconcileIsLessThan(
}
}

if (!$inside_loop && !$did_remove_type && $var_id && $code_location) {
if (!$did_remove_type && $var_id && $code_location) {
self::triggerIssueForImpossible(
$existing_var_type,
$old_var_type_string,
Expand Down
28 changes: 10 additions & 18 deletions src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php
Expand Up @@ -1840,10 +1840,6 @@ private static function reconcileIsLessThanOrEqualTo(
}

foreach ($existing_var_type->getAtomicTypes() as $atomic_type) {
if ($inside_loop) {
continue;
}

if ($atomic_type instanceof TIntRange) {
if ($atomic_type->contains($assertion_value)) {
// if the range contains the assertion, the range must be adapted
Expand Down Expand Up @@ -1872,15 +1868,15 @@ private static function reconcileIsLessThanOrEqualTo(
if ($atomic_type->value > $assertion_value) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());
} /*elseif ($inside_loop) {
} elseif ($inside_loop) {
//when inside a loop, allow the range to extends the type
$existing_var_type->removeType($atomic_type->getKey());
/*$existing_var_type->removeType($atomic_type->getKey());
if ($atomic_type->value < $assertion_value) {
$existing_var_type->addType(new TIntRange($atomic_type->value, $assertion_value));
} else {
$existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value));
}
}*/
}*/
}
} elseif ($atomic_type instanceof TInt) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());
Expand All @@ -1892,7 +1888,7 @@ private static function reconcileIsLessThanOrEqualTo(
}
}

if (!$inside_loop && !$did_remove_type && $var_id && $code_location) {
if (!$did_remove_type && $var_id && $code_location) {
self::triggerIssueForImpossible(
$existing_var_type,
$old_var_type_string,
Expand Down Expand Up @@ -1951,10 +1947,6 @@ private static function reconcileIsGreaterThanOrEqualTo(
}

foreach ($existing_var_type->getAtomicTypes() as $atomic_type) {
if ($inside_loop) {
continue;
}

if ($atomic_type instanceof TIntRange) {
if ($atomic_type->contains($assertion_value)) {
// if the range contains the assertion, the range must be adapted
Expand All @@ -1981,15 +1973,15 @@ private static function reconcileIsGreaterThanOrEqualTo(
if ($atomic_type->value < $assertion_value) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());
} /* elseif ($inside_loop) {
} elseif ($inside_loop) {
//when inside a loop, allow the range to extends the type
$existing_var_type->removeType($atomic_type->getKey());
/*$existing_var_type->removeType($atomic_type->getKey());
if ($atomic_type->value < $assertion_value) {
$existing_var_type->addType(new TIntRange($atomic_type->value, $assertion_value));
} else {
$existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value));
}
}*/
}*/
}
} elseif ($atomic_type instanceof TInt) {
$did_remove_type = true;
$existing_var_type->removeType($atomic_type->getKey());
Expand All @@ -2001,7 +1993,7 @@ private static function reconcileIsGreaterThanOrEqualTo(
}
}

if (!$inside_loop && !$did_remove_type && $var_id && $code_location) {
if (!$did_remove_type && $var_id && $code_location) {
self::triggerIssueForImpossible(
$existing_var_type,
$old_var_type_string,
Expand Down
3 changes: 1 addition & 2 deletions tests/IntRangeTest.php
Expand Up @@ -443,9 +443,8 @@ function getInt()
'$remainder===' => 'int<min, 1>'
]
],
'SKIPPED-IntRangeRestrictWhenUntouched' => [
'IntRangeRestrictWhenUntouched' => [
'code' => '<?php
//skipped, int range in loops not supported yet
foreach ([1, 2, 3] as $i) {
if ($i > 1) {
takesInt($i);
Expand Down

0 comments on commit 2aaea01

Please sign in to comment.