Skip to content

Fix phpstan/phpstan#13669: Offset -1|0|1|2 might not exist on non-empty-array<-1|0|1|2, int>.#5266

Closed
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-x13aibv
Closed

Fix phpstan/phpstan#13669: Offset -1|0|1|2 might not exist on non-empty-array<-1|0|1|2, int>.#5266
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-x13aibv

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

  • Fixes false positive "Offset -1|0|1|2 might not exist on non-empty-array<-1|0|1|2, int>" when all offsets were explicitly set in a constant array shape before a loop that modifies values via ++$arr[$key][$offset]
  • Two-part fix: (1) preserve constant array shape in AssignHandler's descending phase by using the scope's tracked type when it has all required offsets, and (2) preserve constant array shape through loop generalization by not invalidating child expressions when both scopes track them as constant arrays with matching keys

Test plan

  • Added regression test testBug13669 in NonexistentOffsetInArrayDimFetchRuleTest that verifies no false positive is reported
  • All 11629 tests pass (0 failures)
  • PHPStan self-analysis passes with no errors
  • CS check passes
  • Existing tests for similar cases (pr-4390, bug-10438) continue to pass

Fixes phpstan/phpstan#13669

… loop

When a constant array shape (e.g. array{-1: 0, 0: 0, 2: 0, 1: 0}) was
assigned to a nested property dim fetch like $this->prop[$key], and then
an inner loop modified it via ++$this->prop[$key][$offset], the constant
shape was lost during loop processing. This caused false positive
"Offset might not exist" errors even though all offsets were explicitly
set.

Two changes fix this:

1. In AssignHandler's descending phase, when the scope tracks a more
   precise type for an intermediate dim fetch and that type has all the
   offsets needed by the next level, use the scope's tracked type instead
   of recomputing from the parent's general array type.

2. In MutatingScope::generalizeVariableTypeHolders, when a parent
   expression is generalized and both scopes track a child expression as
   constant arrays with matching keys, generalize the child instead of
   invalidating it. This preserves the constant array shape through loop
   generalization.

Fixes phpstan/phpstan#13669
@staabm staabm closed this Mar 21, 2026
@staabm staabm deleted the create-pull-request/patch-x13aibv branch March 21, 2026 09:01
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.

2 participants