Implement getNamedArgumentsVariants() on UnionTypeMethodReflection and IntersectionTypeMethodReflection to combine parameters by name#5727
Conversation
…` and `IntersectionTypeMethodReflection` to combine parameters by name - Add `combineAcceptorsByParameterName()` to `ParametersAcceptorSelector` that groups parameters by name instead of position, so named argument matching works when union/intersection members have the same parameters in different order - Implement `getNamedArgumentsVariants()` on `UnionTypeMethodReflection` and `IntersectionTypeMethodReflection` using the new combiner (previously returned null) - Skip the early return in `selectFromArgs()` when the acceptor has compound parameter names (from positional combining) and named arguments are present, allowing the named-arguments flow at line 539 to handle them correctly - Add `acceptorHasCompoundParameterNames()` helper to detect compound names created by `combineAcceptors()` (e.g. `other|target`) Closes phpstan/phpstan#14661
ondrejmirtes
left a comment
There was a problem hiding this comment.
I miss some test expected error failures to demonstrate that differentTypes method cannot be called without named arguments.
…erent parameter order Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Pushed. Added test cases for |
ondrejmirtes
left a comment
There was a problem hiding this comment.
No, I want to test that $obj->differentTypes('hello', 1); is wrong. Without named arguments.
…ent parameter names When calling a method on a union type where parameters at the same position have different names (e.g., A::foo(int $a, string $b) vs B::foo(string $b, int $a)), the combined positional acceptor produces compound names like $a|$b with union types int|string that accept too broadly. This adds per-member checking in CallMethodsRule for positional-only calls when compound parameter names are detected, so type errors against individual union members are reported. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Pushed. The changes address the reviewer's request by adding per-member checking for positional calls on union type methods where parameter names differ at the same position. What changed:
|
|
I don't like this direction, doesn't work well. Keeping open so that someone can pick this up. |
Summary
When calling a method with named arguments on a union type
A|Bwhere both classes have the same method with parameters in a different order, PHPStan falsely reported "Unknown parameter $target". This happened becauseUnionTypeMethodReflection::getNamedArgumentsVariants()returnednull, forcing the system to use the positional-combined acceptor fromgetVariants()which produced compound parameter names likeother|targetthat couldn't be matched by the named argument normalizer.Changes
ParametersAcceptorSelector::combineAcceptorsByParameterName()— a new public method analogous tocombineAcceptors()but groups parameters by name instead of position (src/Reflection/ParametersAcceptorSelector.php)UnionTypeMethodReflection::getNamedArgumentsVariants()usingcombineAcceptorsByParameterName()(src/Reflection/Type/UnionTypeMethodReflection.php)IntersectionTypeMethodReflection::getNamedArgumentsVariants()using the same approach (src/Reflection/Type/IntersectionTypeMethodReflection.php)ParametersAcceptorSelector::selectFromArgs()to skip when the acceptor has compound parameter names (from union type positional combining) and named arguments are present, allowing the existing named-arguments flow to handle them correctlyacceptorHasCompoundParameterNames()private helper to detect names containing|Analogous cases probed
UnionTypeMethodReflection, so the fix applies automatically. Added test inCallStaticMethodsRuleTest.IntersectionTypeMethodReflection::getNamedArgumentsVariants()with the same pattern.InstantiationRuleiterates per-class, doesn't go throughUnionTypeMethodReflection.Root cause
combineAcceptors()merges parameters by position index, producing compound names likeother|targetwhen parameters at the same position have different names across union members. NeitherArgumentsNormalizer::reorderArgs()norFunctionCallParametersCheck::processArguments()could match a named argument liketarget:against a parameter namedother|target. The fix provides a separate named-arguments-specific variant combined by parameter name.Test
tests/PHPStan/Rules/Methods/data/bug-14661.php— regression test for instance method calls onA|Bwith named arguments in different parameter order. Covers: single named arg, both args in both orders, different parameter types, and three-way union.tests/PHPStan/Rules/Methods/data/bug-14661-static.php— analogous test for static method calls onclass-string<A>|class-string<B>.Fixes phpstan/phpstan#14661