Skip to content

Resolve component names from combined |-separated parameter names for named argument lookup on union types#5726

Closed
phpstan-bot wants to merge 1 commit into
phpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-ryz8b48
Closed

Resolve component names from combined |-separated parameter names for named argument lookup on union types#5726
phpstan-bot wants to merge 1 commit into
phpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-ryz8b48

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

When calling a method on a union type (A|B) with named arguments, PHPStan incorrectly reports "Unknown parameter" if the parameter exists in all variants but at different positions. For example, A::mixedOrder($other, $target) and B::mixedOrder($target, $other) — calling $obj->mixedOrder(target: 'value') reported Unknown parameter $target even though both classes have a $target parameter.

Changes

  • Added FunctionCallParametersCheck::mapCombinedParameterNames() (src/Rules/FunctionCallParametersCheck.php) — splits |-separated combined parameter names (e.g., other|target) into individual component names and registers each in the $parametersByName and $originalParametersByName lookup maps. Uses a two-pass strategy: first maps primary names (first component of each combined name), then fills remaining components, ensuring each component name maps to a unique parameter even when names overlap across positions.

  • Added ArgumentsNormalizer::mapCombinedParameterPositions() (src/Analyser/ArgumentsNormalizer.php) — same strategy for the $argumentPositions map used during named argument reordering.

  • Analogous cases probed:

    • Static method calls: affected (same code path) — tested and confirmed fixed
    • Constructor calls: affected (same code path via InstantiationRule) — covered by the fix
    • Function calls: affected (same code path via CallToFunctionParametersRule) — covered by the fix
    • Intersection types: not affected — uses IntersectionTypeMethodReflection which selects the method with the most parameters rather than combining by position

Root cause

ParametersAcceptorSelector::combineAcceptors() merges parameters from union type method variants by position index. When different variants have different parameter names at the same position, it creates a combined name with | separator (e.g., other|target). Both FunctionCallParametersCheck::processArguments() and ArgumentsNormalizer::reorderArgs() build name-to-parameter lookup maps keyed by the full combined name — so looking up target fails because only other|target exists as a key.

Test

  • tests/PHPStan/Rules/Methods/data/bug-14661.php — regression test for instance method calls with named arguments on A|B union type where parameters have different order across variants. Tests: single named arg, both named args, both orders, different types, and unknown parameter (should still error).
  • tests/PHPStan/Rules/Methods/data/bug-14661-static.php — same test for static method calls on union types.

Fixes phpstan/phpstan#14661

…or named argument lookup on union types

- When `combineAcceptors()` merges parameters from union type method
  variants by position, parameters at the same position with different
  names get a combined name like `other|target`. Named argument lookup
  then fails because it searches for `target` but only finds
  `other|target` in the parameters-by-name map.
- Add `mapCombinedParameterNames()` to `FunctionCallParametersCheck` to
  split combined parameter names and register each component name in the
  lookup maps, using a primary-names-first strategy to ensure each
  component maps to a unique parameter.
- Add `mapCombinedParameterPositions()` to `ArgumentsNormalizer` with
  the same strategy for the argument positions map used during argument
  reordering.
- Fix covers instance method calls, static method calls, constructor
  calls, and function calls since they all funnel through the same
  `FunctionCallParametersCheck::check()` and
  `ArgumentsNormalizer::reorderArgs()` code paths.
- Intersection types use a different approach (selecting the method with
  most parameters) and are not affected.
@ondrejmirtes
Copy link
Copy Markdown
Member

This needs more thought I think, the issue is really crazy and breaks a lot of assumptions.

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.

3 participants