Narrow variadic parameter array key integer type to int<0, max>#5791
Merged
staabm merged 1 commit intoJun 2, 2026
Merged
Conversation
- When named arguments are supported, the array gathered from a variadic parameter (`...$args`) is now typed `array<int<0, max>|string, ...>` instead of `array<int|string, ...>`. The integer keys produced for positional/spread arguments are always non-negative, so the key type is narrowed to `int<0, max>`. The string part stays `string` because empty strings can be passed as keys via argument unpacking. - Updated all four variadic key construction sites in `MutatingScope` (`getFunctionType`, `buildVariadicArrayTypeFromCallableParameters`, and the two parameter-scope builders for declared and native types). - Fixed the analogous (and inverted) branch in `InitializerExprTypeResolver::getFunctionType`: it returned the string-keyed array when named arguments were NOT supported and a pure list when they were — the opposite of `MutatingScope`. It now matches `MutatingScope` and applies the same `int<0, max>` narrowing. - Updated regression/expectation tests accordingly.
staabm
requested changes
Jun 2, 2026
Contributor
There was a problem hiding this comment.
could we even make it a list?
no we can't: https://3v4l.org/gFrDj#veol
staabm
approved these changes
Jun 2, 2026
VincentLanglet
approved these changes
Jun 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The array synthesized for a variadic parameter (
function f(mixed ...$args)) was typedarray<int|string, mixed>. The integer keys generated for positional and spread arguments are always non-negative, so the key type can be narrowed. This change types the array asarray<int<0, max>|string, mixed>.The string part of the key is left as
string(notnon-empty-string) because empty strings can legitimately be passed as keys via argument unpacking — as noted in the issue discussion.Changes
src/Analyser/MutatingScope.php— replacedint|stringwithint<0, max>|stringas the variadic array key in all four construction sites: the declared-type and native-type parameter-scope builders (used when entering a function/method body),getFunctionType(), andbuildVariadicArrayTypeFromCallableParameters().src/Reflection/InitializerExprTypeResolver.php— applied the same narrowing and fixed an inverted condition: the method returned the string-keyed array when named arguments were not supported and a pure list when they were, the opposite ofMutatingScope. It now matchesMutatingScope.nsrtdata files andTypesAssignedToPropertiesRuleTestto reflect the narrowed key type.Root cause
Variadic parameters collect arguments into an array whose integer keys (for positional and unpacked positional arguments) start at 0 and increase, so they are always non-negative. The code unconditionally used a plain
IntegerTypefor the key instead ofIntegerRangeType::createAllGreaterThanOrEqualTo(0). The sameint|stringkey was constructed in several parallel locations; all were updated.InitializerExprTypeResolveradditionally had its two branches swapped relative toMutatingScope, which was fixed at the same time.Test
tests/PHPStan/Analyser/nsrt/variadic-parameter-php8.php— updated existing assertions and added amixed ...$argscase reproducing the issue; now assertsarray<int<0, max>|string, ...>.bug-2600-php8.php,bug-2600-php-version-scope.php,call-user-func-php8.php,bug-9240.php,bug-10671.php,bug-4902-php8.php,bug-5698-php8.php,anonymous-function.php,typehints.php, andTypesAssignedToPropertiesRuleTest::testBug11275. The PHP 7 variadic case correctly remainslist<...>.Fixes phpstan/phpstan#14744