Fix phpstan/phpstan#11978: Ordering of Intersections matters for method calls, first matching wins#5408
Conversation
VincentLanglet
left a comment
There was a problem hiding this comment.
There is multiple occurence of
$this->methods[0]
in this class.
Extract this in a private method to use it.
Also add a cache on the selected method.
|
Background |
VincentLanglet
left a comment
There was a problem hiding this comment.
Add a SHORT phpdoc to comment getMethodWithMostParameters
|
Tests passed as well. All done — the PHPDoc has been added, verified with both |
| public function renderFoo(string $foo): string | ||
| { | ||
| $a = $this->view1->render($foo); | ||
| $b = $this->view2->render($foo); |
There was a problem hiding this comment.
append these line to the test:
$c = $this->view1->render($foo, $foo);
$d = $this->view2->render($foo, $foo);
$e = $this->view1->render();
$f = $this->view2->render();
and adjust expected errors in tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php
There was a problem hiding this comment.
Background make tests also completed successfully. All done — the review changes are committed and pushed.
staabm
left a comment
There was a problem hiding this comment.
needs conflict resolution and rebase. otherwise LGTM.
…depend on ordering - IntersectionTypeMethodReflection::getVariants() now picks the method with the most parameters instead of always using the first method - Added regression test in tests/PHPStan/Rules/Methods/data/bug-11978.php - The root cause was that the first method's parameter list was always used, so ViewA&ViewB would use ViewA's 0-param render() while ViewB&ViewA would use ViewB's 1-param render()
Replace all occurrences of $this->methods[0] with a call to the new getMethodWithMostParameters() private method, which caches its result. This addresses the review feedback to avoid duplicating the method selection logic and to add caching. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fda2f70 to
61b8eb8
Compare
Summary
When calling a method on an intersection type like
ViewA&ViewB, PHPStan always used the first type's method signature for parameter validation. This meant thatViewA&ViewBandViewB&ViewAcould produce different errors, violating PHP's guarantee that intersection type ordering is irrelevant.Changes
src/Reflection/Type/IntersectionTypeMethodReflection.php: IngetVariants(), instead of always using$this->methods[0]for parameters, find the method with the most parameters across all intersection members and use thattests/PHPStan/Rules/Methods/data/bug-11978.phpand test method inCallMethodsRuleTest.phpRoot cause
IntersectionTypeMethodReflection::getVariants()mapped over$this->methods[0]->getVariants()to build the combined variant, taking the parameters from the first method in the intersection. For an intersectionViewA&ViewBwhereViewA::render()has 0 parameters andViewB::render(string $foo = '')has 1 optional parameter, the first-wins approach used ViewA's 0-parameter signature, causing a false positive when callingrender($foo).The fix selects the method with the most parameters, since any concrete implementation of the intersection must satisfy all interface contracts, meaning the widest parameter list is always valid to call.
Test
Added
tests/PHPStan/Rules/Methods/data/bug-11978.phpwith two intersection types (ViewA&ViewBandViewB&ViewA) where both callrender()with one argument. Neither should produce an error.Fixes phpstan/phpstan#11978