Skip to content

Fix phpstan/phpstan#13773: Index for loop inference should only work in lists#5215

Merged
staabm merged 2 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-oiexzbi
Mar 13, 2026
Merged

Fix phpstan/phpstan#13773: Index for loop inference should only work in lists#5215
staabm merged 2 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-oiexzbi

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

For loops like for ($i = 0; $i < count($array); $i++) were incorrectly inferring that $array[$i] always exists, even for non-list arrays like array<int, string>. This is only valid for lists where keys are sequential integers starting from 0. A non-list array<int, string> could have keys like [100 => "hey"], making $array[0] undefined.

Changes

  • Added isList() check in src/Analyser/NodeScopeResolver.php in inferForLoopExpressions() for both $i < count($items) and count($items) > $i patterns
  • The inference now only applies when the array type (PHPDoc or native) is confirmed to be a list
  • New regression test in tests/PHPStan/Rules/Arrays/data/bug-13773.php covering both list and non-list cases in both condition orderings

Root cause

The inferForLoopExpressions() method (introduced in PR #4403) unconditionally assigned the array's iterable value type to $array[$i] inside for loops with the $i = 0; $i < count($array); $i++ pattern. This made PHPStan believe the offset always exists, suppressing "offset might not exist" errors. However, this assumption is only correct for lists (arrays with sequential integer keys 0, 1, 2, ...), not for general array<int, string> where keys can be arbitrary integers.

Test

Added tests/PHPStan/Rules/Arrays/data/bug-13773.php with four test cases:

  • testNonListArray() - array<int, string> with $i < count(...) - should report error
  • testList() - list<string> with $i < count(...) - no error (inference is correct)
  • testListReversed() - list<string> with count(...) > $i - no error
  • testNonListReversed() - array<int, string> with count(...) > $i - should report error

Fixes phpstan/phpstan#13773

…for lists

- Added isList() check in inferForLoopExpressions() before assigning $array[$i] type
- The inference that $array[$i] exists in `for ($i=0; $i<count($array); $i++)` is
  only valid when $array is a list (sequential integer keys starting from 0)
- New regression test in tests/PHPStan/Rules/Arrays/data/bug-13773.php
Comment on lines +4548 to +4550
$arrayType = $bodyScope->getType($arrayArg);
$arrayNativeType = $bodyScope->getNativeType($arrayArg);
if ($arrayType->isList()->yes() || $arrayNativeType->isList()->yes()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Checking the arrayType (and not the arrayNative type) should be enough

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Everything is already up to date. The reviewer's feedback was already addressed by the da0f51464 ("simplify") commit, which removed the $arrayNativeType check and only uses $arrayType->isList()->yes() — exactly what VincentLanglet suggested.

Comment on lines +4573 to +4575
$arrayType = $bodyScope->getType($arrayArg);
$arrayNativeType = $bodyScope->getNativeType($arrayArg);
if ($arrayType->isList()->yes() || $arrayNativeType->isList()->yes()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Checking the arrayType( and not the arrayNativeType) should be enough

@phpstan-bot
Copy link
Collaborator Author

The review is an APPROVED review with no new change requests. The previous feedback from VincentLanglet (suggesting that checking only $arrayType is sufficient, without $arrayNativeType) was already addressed by the da0f51464 ("simplify") commit that's already in the branch.

There are no remaining changes to make - the PR is approved and ready to merge.

@staabm staabm merged commit 4919ff4 into phpstan:2.1.x Mar 13, 2026
643 of 652 checks passed
@staabm staabm deleted the create-pull-request/patch-oiexzbi branch March 13, 2026 08:10
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