Skip to content

Fix phpstan/phpstan#14464: Narrow list types when count() result is stored in variable#5462

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

Fix phpstan/phpstan#14464: Narrow list types when count() result is stored in variable#5462
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-zflwklz

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Fixes phpstan/phpstan#14464

When count() or sizeof() is called on a list or constant array and the result is stored in a variable, comparing that variable to a specific integer now narrows the array type to the corresponding fixed-size tuple shape.

/** @param list<string> $list */
function example(array $list): void {
    $n = count($list);
    if ($n === 3) {
        // Before: list<string>
        // After:  array{string, string, string}
        $list[0]; // no error
        $list[1]; // no error
        $list[2]; // no error
    }
}

How it works

AssignHandler already creates ConditionalExpressionHolder entries for falsey scalar values when a variable is assigned. This PR extends that mechanism: when the assigned expression is count($array) or sizeof($array) and the argument is a list or constant array, it iterates over possible array sizes and creates holders that map each integer size to the narrowed array type. The narrowing itself reuses TypeSpecifier::specifyTypesForCountFuncCall, which already handles the direct count($list) === N case.

Supported patterns:

  • $n = count($list); if ($n === 3) (strict equality)
  • $n = count($list); if ($n == 3) (loose equality)
  • $n = sizeof($list); if ($n === 2) (sizeof alias)
  • Works with list<T>, constant array unions, and preg_split results
  • Non-list arrays (array<string, int>) correctly remain non-narrowed since keys are unknown

…tored in variable

When the result of count() or sizeof() on a list or constant array is
assigned to a variable, create ConditionalExpressionHolders so that
comparing that variable to specific integers narrows the array type
to the corresponding fixed-size shape.

Previously, `$n = count($list); if ($n === 3)` did not narrow $list,
while the direct `if (count($list) === 3)` did. This was because
AssignHandler only created holders for falsey scalar values, not for
count-specific integer comparisons.

The fix iterates over possible array sizes and creates holders that
map each size to the narrowed array type produced by TypeSpecifier's
existing specifyTypesForCountFuncCall logic.
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.

1 participant