Skip to content

Collect all remaining callable parameter types for variadic closure parameters instead of using only the matching index#5634

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

Collect all remaining callable parameter types for variadic closure parameters instead of using only the matching index#5634
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-e1mgzvb

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

When a closure with a variadic parameter (...$postFiles) is passed to a function expecting a callable with multiple non-variadic parameters (e.g. Closure(PhpFileArray, PhpFileArray, PhpFileArray): bool), PHPStan incorrectly inferred the variadic parameter's type as the single element type from the matching index (array{error: int, name: string}) instead of the proper variadic array type (array<int|string, array{error: int, name: string}>). This caused false positives like "Cannot access offset 'error' on int|string".

Changes

  • src/Analyser/MutatingScope.php: Added buildVariadicArrayTypeFromCallableParameters() helper method that collects element types from all callable parameters at and beyond the variadic parameter's index, unions them, and constructs the correct variadic array type (respecting PHP version for named argument support)
  • src/Analyser/MutatingScope.php: In enterAnonymousFunctionWithoutReflection() and enterArrowFunctionWithoutReflection(), when the closure parameter is variadic, use the new helper instead of directly intersecting with $callableParameters[$i]->getType()
  • src/Analyser/NodeScopeResolver.php: In createCallableParameters(), when processing immediately-invoked closures/arrow functions and the callable parameter is variadic, union all argument types from that index onward instead of only using the first argument's type

Root cause

The bug had two manifestations sharing the same root cause pattern — treating a variadic parameter's index as a 1:1 mapping to callable parameters:

  1. Passed-to-type path (enterAnonymousFunctionWithoutReflection/enterArrowFunctionWithoutReflection): When callable parameters [PhpFileArray, PhpFileArray, PhpFileArray] existed and the closure had variadic ...$postFiles at index 0, only $callableParameters[0]->getType() was used. Since array{error: int, name: string} (a constant array) is a subtype of array<int|string, mixed>, the intersection collapsed to just array{error: int, name: string} — a single file array instead of an array of file arrays.

  2. Immediately-invoked path (createCallableParameters): When (function(...$args){})(1, 'hello', 3.14) was analyzed, the variadic parameter's type was only updated with args[0]'s type (1) instead of the union of all argument types (1|'hello'|3.14).

Test

  • tests/PHPStan/Analyser/nsrt/bug-9240.php: Regression test covering:
    • Main reported case: closure with variadic param passed to typed callable expectation
    • Mixed element types in the expected callable signature
    • Non-first-position variadic parameter with typed prefix
    • Arrow function variant
    • Immediately-invoked closure with variadic parameter
    • Immediately-invoked arrow function with variadic parameter
    • Expected callable type that itself has a variadic parameter

Fixes phpstan/phpstan#9240

…arameters instead of using only the matching index

- When a closure with a variadic parameter (e.g. `...$postFiles`) is passed to
  a function expecting a callable with multiple parameters, collect ALL remaining
  callable parameter types from the variadic param's index onward and build the
  proper variadic array type, instead of using only `$callableParameters[$i]`
- Fix applies to both `enterAnonymousFunctionWithoutReflection` and
  `enterArrowFunctionWithoutReflection` in MutatingScope
- Also fix `createCallableParameters` in NodeScopeResolver: when the closure's
  declared parameter is variadic and it is immediately invoked, union all
  argument types from that index onward instead of only the first argument
- Add `buildVariadicArrayTypeFromCallableParameters` helper that correctly
  handles PHP version (named arguments support) when constructing the array type
@VincentLanglet VincentLanglet requested a review from staabm May 10, 2026 14:08
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