Fix phpstan/phpstan#13770: Nonexistent offset detection on lists (if ($index < count($array)))#5090
Conversation
| } | ||
|
|
||
| // infer $list[$index] after $index < count($list) | ||
| if ( |
There was a problem hiding this comment.
While it seems correct, I feel like it should be in the specifyTypesForCountFuncCall method.
There was a problem hiding this comment.
specifyTypesForCountFuncCall has some complex expressions already and the if-case here seems independent from what happens in specifyTypesForCountFuncCall.
do you have already something specific in mind? I feel like keeping this if here separate is easier to follow
There was a problem hiding this comment.
No I had nothing in mind, but was surprised of the
&& !$leftType instanceof ConstantIntegerType
and understood it was maybe handled in specifyTypesForCountFuncCall, so it would have been clearer to group them.
904a6ae to
33fba32
Compare
- Added type specification in TypeSpecifier for list dim fetch when a non-negative integer variable is compared with count() using strict less-than - When $index < count($list) holds and $index is non-negative, $list[$index] is guaranteed to be a valid offset for a list (sequential 0-based keys) - Excluded ConstantIntegerType left operands since those are already handled by specifyTypesForCountFuncCall - New regression test in tests/PHPStan/Rules/Arrays/data/bug-13770.php
33fba32 to
aef2632
Compare
VincentLanglet
left a comment
There was a problem hiding this comment.
For consistency we should support
if ($index <= count($array) - 1) {
return $array[$index]; // should not report
}
But the count($array) - 1 expression might be more complex so I dunno how hard it is.
| } | ||
|
|
||
| // infer $list[$index] after $index < count($list) | ||
| if ( |
There was a problem hiding this comment.
No I had nothing in mind, but was surprised of the
&& !$leftType instanceof ConstantIntegerType
and understood it was maybe handled in specifyTypesForCountFuncCall, so it would have been clearer to group them.
|
Good idea. I had implemented count()-1 in the past. Will re-use |
Summary
When accessing a list with a non-negative integer index after checking
$index < count($list), PHPStan incorrectly reported "Offset int<1, max> might not exist on non-empty-list." This fix teaches the TypeSpecifier that the offset is guaranteed to be valid in that context.Changes
src/Analyser/TypeSpecifier.php(around line 337) that creates a dim fetch type specification when:$index < count($list)(strict less-than, not<=)int<0, max>orpositive-int)specifyTypesForCountFuncCall)tests/PHPStan/Rules/Arrays/data/bug-13770.phpandtests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.phpRoot cause
The TypeSpecifier already narrowed the array to
non-empty-listand the index to an integer range when processing$index < count($list), but it did not create a type specification for the actual array dim fetch expression$list[$index]. TheNonexistentOffsetInArrayDimFetchRulechecks$scope->hasExpressionType($node)before reporting errors — by adding the dim fetch specification, the rule correctly recognizes the offset as valid.Constant integer left operands (like
0 < count($list)) are excluded becausespecifyTypesForCountFuncCallalready handles those cases with precise offset tracking, and adding a redundant dim fetch specification caused unwantedhasOffsetValuepropagation through array operations likearray_map.Test
The regression test covers four scenarios:
positive-int < count($list)— no error expected (the fix)int<0, max> < count($list)— no error expected (the fix)positive-int <= count($list)— error expected (off-by-one, not safe)int < count($list)— error expected (negative index possible)Fixes phpstan/phpstan#13770