Skip to content

Comments

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

Open
phpstan-bot wants to merge 2 commits into2.1.xfrom
create-pull-request/patch-371kg31
Open

Fix phpstan/phpstan#13669: Offset -1|0|1|2 might not exist on non-empty-array<-1|0|1|2, int>.#5030
phpstan-bot wants to merge 2 commits into2.1.xfrom
create-pull-request/patch-371kg31

Conversation

@phpstan-bot
Copy link
Collaborator

@phpstan-bot phpstan-bot commented Feb 22, 2026

This PR fixes phpstan/phpstan#13669

Summary

Fixes a false positive where ++$this->mailCounts[$templateId][$mail] reported "Offset -1|0|1|2 might not exist on non-empty-array<-1|0|1|2, int>" even though all four keys were explicitly set in a constant array assignment immediately before the loop.

Root Cause

In NodeScopeResolver::produceArrayDimFetchAssignValueToWrite(), the additional expressions (intermediate array dim fetch types tracked in the scope) were computed by deriving top-down from the root property type. For a property typed as array<int, array<MailStatus::CODE_*, int>>, deriving getOffsetValueType() produces a general array<-1|0|1|2, int> — losing the constant array structure (array{-1: 0, 0: 0, 2: 0, 1: 0}) that the scope was actually tracking from the assignment.

During loop generalization, the constant array (from before the loop body) was compared against this general array (from after), producing non-empty-array<-1|0|1|2, int>. On this general array type, hasOffsetValueType(-1|0|1|2) returns maybe (never yes for ArrayType), triggering the false positive.

Fix

Before building the additional expressions, the fix computes improved intermediate types bottom-up using the scope's tracked types. For each intermediate dim fetch where the scope holds a constant array type with all required offsets, it applies setExistingOffsetValueType() with the child's offset type and value — preserving the constant array structure. This means loop generalization compares two constant arrays with matching keys, producing a constant array result where hasOffsetValueType() correctly returns yes.

The forward/reverse passes that compute the root type are left unchanged, avoiding any risk of child expression invalidation during generalization.

Closes phpstan/phpstan#10349

staabm and others added 2 commits February 22, 2026 22:04
…t keys set in loop

When a constant array with all expected keys (e.g. array{-1: 0, 0: 0, 1: 0, 2: 0})
was modified inside a foreach loop via a union offset type covering all keys,
the intermediate expression type was derived top-down from the root property type,
producing a general array (array<-1|0|1|2, int>) that lost the constant structure.
During loop generalization, this general array couldn't confirm offset existence,
causing a false "Offset ... might not exist" error.

The fix computes improved intermediate types bottom-up using the scope's tracked
constant array types and setExistingOffsetValueType, preserving the constant array
structure through loop generalization.

Closes phpstan/phpstan#13669
@staabm staabm changed the title Fix #13669: Offset -1|0|1|2 might not exist on non-empty-array<-1|0|1|2, int>. Fix phpstan/phpstan#13669: Offset -1|0|1|2 might not exist on non-empty-array<-1|0|1|2, int>. Feb 23, 2026
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