Skip to content

Fix phpstan/phpstan#14336: assign any-int in a loop should turn list into array#5267

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

Fix phpstan/phpstan#14336: assign any-int in a loop should turn list into array#5267
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-5kr4vwy

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

Fixes phpstan/phpstan#14336

When assigning to a list-typed array with an arbitrary int offset (e.g., $list[$int] = $value), PHPStan incorrectly preserved the list type. Since lists require sequential 0-based integer keys and an arbitrary int can include negative values, the type should be degraded to array<int, T>.

Root cause: Two mechanisms incorrectly preserved lists:

  1. AssignHandler used setExistingOffsetValueType for lists when the parent expression was tracked in scope (e.g., from a previous loop iteration), even when the offset type wasn't guaranteed to be a valid list index. Fix: add a check that only allows setExistingOffsetValueType for lists when the offset is within int<0, max> range.

  2. IntersectionType::setOffsetValueType unconditionally re-added AccessoryArrayListType when the list's value type was an array, overriding the correct degradation by AccessoryArrayListType::setOffsetValueType. Fix: only re-add the list marker when the offset is null (append) or non-negative.

Test expectation updates for 3 existing tests that produced more correct results:

  • bug-10089: $matrix[$size-1][8] = 3 where $size-1 can be -1 — list correctly degraded
  • bug-13629: assignment with string key now correctly includes string in key type
  • bug-14245: array_key_exists with untyped $needle — list correctly degraded since $needle type includes full int range

…arbitrary int offset

When assigning to a list-typed array variable with an arbitrary `int` offset
(e.g., `$list[$int] = $value`), PHPStan incorrectly preserved the `list` type.
Lists require sequential 0-based integer keys, so assigning with an arbitrary
`int` (which may include negative values) should degrade the type to `array<int, T>`.

Two changes fix the issue:

1. In AssignHandler::produceArrayDimFetchAssignValueToWrite, prevent
   setExistingOffsetValueType from being used for lists when the offset
   type is not guaranteed to be non-negative (int<0, max>).

2. In IntersectionType::setOffsetValueType, only re-add AccessoryArrayListType
   for list-of-arrays when the offset type is null (append) or non-negative.
@staabm staabm closed this Mar 21, 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