Skip to content

Fix non-existent offset when guarding#4539

Closed
SplotyCode wants to merge 4 commits intophpstan:2.1.xfrom
SplotyCode:fix/index-guard
Closed

Fix non-existent offset when guarding#4539
SplotyCode wants to merge 4 commits intophpstan:2.1.xfrom
SplotyCode:fix/index-guard

Conversation

@SplotyCode
Copy link
Copy Markdown
Contributor

@SplotyCode SplotyCode commented Nov 8, 2025

This pr is trying to fix a false positive.

/**
 * @param list<int> $array
 * @param positive-int $index
 */
public function guardNotSafeLowerBound(array $array, int $index) {
    if ($index < count($array)) { // index is within 0..count(array)-1
        return $array[$index]; // phpstan reports a potentially unsafe array access
    }
    return null;
}

Note that 1) $array is a list 2) $index is positive (or at least >= 0)

Comment thread tests/PHPStan/Rules/Arrays/data/report-possibly-nonexistent-array-offset.php Outdated
Comment on lines +294 to +312
if (
$context->true()
&& $expr instanceof Node\Expr\BinaryOp\Smaller
&& $argType->isList()->yes()
&& IntegerRangeType::fromInterval(0, null)->isSuperTypeOf($leftType)->yes()
) {
$dimFetch = new ArrayDimFetch(
$expr->right->getArgs()[0]->value,
$expr->left,
);
$result = $result->unionWith(
$this->create(
$dimFetch,
$argType->getIterableValueType(),
TypeSpecifierContext::createTrue(),
$scope,
)
);
}
Copy link
Copy Markdown
Contributor

@staabm staabm Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this PR trying to fix phpstan/phpstan#13773 ?
(since the PR does not at all contain a description.. I can only guess)

if so, you should either adjust the logic added with #4403 or maybe even delete it in case you want to re-implement it in a different place

Copy link
Copy Markdown
Contributor Author

@SplotyCode SplotyCode Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing this out! I updated the description of this pr.

I have not opened a issue for this pr. But i will also try and fix phpstan/phpstan#13773 later in a diffrent pr. Here i am trying to fix array access on lists if we explicitly guard the index.

@SplotyCode
Copy link
Copy Markdown
Contributor Author

SplotyCode commented Nov 11, 2025

There seems to be a regression

$var = null;
if ($index < count($array) {
    $var = 2;
}
if ($index < count($array) {
    /* phpstan does not recognize that $var is always 2 now */
}

This did work before this pr. I am wondering if this is also true for the other checks in the method i changed.

if (count($c) > 0) {
$c = array_map(fn() => new stdClass(), $c);
assertType('non-empty-list<stdClass>', $c);
assertType('non-empty-list<stdClass>&hasOffsetValue(0, stdClass)', $c);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this type does not make sense, because non-empty-list<stdClass> already implies that the list has a offset 0 with value stdClass

$context->true()
&& $expr instanceof Node\Expr\BinaryOp\Smaller
&& $argType->isList()->yes()
&& $argExpr instanceof Expr\Variable
Copy link
Copy Markdown
Contributor

@staabm staabm Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instanceof Expr\Variable is usually a bug as most logic works for other expressions (e.g. a pure function call can also be remembered).

most of the time this works just by dropping this line

$this->create(
$dimFetch,
$argType->getIterableValueType(),
TypeSpecifierContext::createTrue(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

usually we pass thru the $context, instead of creating a new one.

@ondrejmirtes
Copy link
Copy Markdown
Member

This PR looks abandonded. Feel free to reopen on latest 2.1.x if you still want to fix it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants