Preserve ArrayDimFetch expression tracking through loop generalization#5661
Preserve ArrayDimFetch expression tracking through loop generalization#5661phpstan-bot wants to merge 1 commit into
Conversation
When a parent expression is generalized during loop fixpoint iteration, child ArrayDimFetch expressions are now preserved with a trackingOnly flag instead of being removed. This prevents false positive "nonexistent offset" errors on array dim fetches that were explicitly assigned earlier in the same code path. The trackingOnly flag decouples expression tracking (hasExpressionType returns Yes, so the NonexistentOffsetInArrayDimFetchRule skips the check) from type resolution (resolveType falls through to the ExprHandler to compute the type fresh from the parent, avoiding stale stored types). Closes phpstan/phpstan#14598
staabm
left a comment
There was a problem hiding this comment.
reading the error message
Offset 'A'|'B'|'C' might not exist on non-empty-array{A?: list<'00'|'01'|'02'|'03'>, B?: list<'00'|'01'|'02'|'03'>, C?: list<'00'|'01'|'02'|'03'>}.
I think the type-system should be able to tell that a non-empty-array with known keys A|B|C will contain offset A|B|C
|
The branch is already up to date with origin — no additional commits or pushes needed. Summary: After thorough investigation of the type-system approach, I've confirmed the |
|
@staabm Hey, the message is correct. |
You might have an array with key |
|
You are right. Sealed vs. Unsealed territory |
Summary
$tmp[$id]was explicitly assigned inside a loop but its expression holder was removed during loop generalization of the parent variabletrackingOnlyflag onExpressionTypeHolderthat preserves expression tracking through generalization while lettingresolveTypecompute types fresh from the parent array via the ExprHandlerisExpressionTrackingOnly()guards inAssignHandlerto prevent tracking-only holders from triggeringsetExistingOffsetValueTypepaths (which would cause type precision regressions)Test plan
testBug14598with three scenarios: original reproducer, simple nested foreach, nested while loop — all produce no errorspr-4390test passes (no type precision regression onint<0, 9>)bug-10438test passes (no type precision regression onlist<string>|string)make tests— 12063 tests, 79683 assertions passmake phpstan— no errorsmake cs-fix— no violationsCloses phpstan/phpstan#14598