Skip to content

Fix phpstan/phpstan#14320: Since 2.1.37. Parameter of method MyAbstractClass::myFunction() should be contravariant with parameter of method MyFirstTrait::myFunction()#5244

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

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

False positive "should be contravariant with parameter" error when using nested traits (a trait that uses another trait containing an abstract method). The PHPDoc parameter type from the original trait was lost when the method was checked through the intermediate trait, causing the parameter type to fall back to the native array instead of array<string, mixed>.

Changes

  • Added a check in src/Rules/Methods/ParentMethodHelper.php to skip traits where an abstract method is inherited from a sub-trait rather than declared directly. The actual declaring trait is still processed separately in the same loop, so the contravariance check still happens with the correct PHPDoc types.
  • New regression test in tests/PHPStan/Rules/Methods/data/bug-14320.php and tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php

Root cause

ParentMethodHelper::collectParentMethods iterates all traits recursively via $class->getTraits(true). For nested traits (e.g., MyFirstTrait uses MyTrait), both traits appear in the iteration. When checking MyFirstTrait, the abstract method myFunction is found via hasMethod() (since PHP's trait composition copies methods), but the PHPDoc from the original declaring trait MyTrait is not properly resolved through the intermediate trait. This causes the parameter type to be reported as just array (native type) instead of array<string, mixed> (PHPDoc type), triggering a false contravariance error.

The fix uses BetterReflection's getDeclaringClass() (which returns the actual declaring class, unlike PHP's native reflection which returns the implementing/composing class) to skip traits that only inherited the method from a sub-trait.

Test

Added testBug14320 in MethodSignatureRuleTest with a test case reproducing the exact scenario: a trait with a PHPDoc-annotated abstract method, used by an intermediate trait, which is then used by an abstract class that implements the method with the same PHPDoc types. The test expects no errors.

Fixes phpstan/phpstan#14320

…h nested trait

- Skip traits that inherited an abstract method from a sub-trait in ParentMethodHelper::collectParentMethods
- The actual declaring trait is still checked separately in the same loop
- New regression test in tests/PHPStan/Rules/Methods/data/bug-14320.php
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.

1 participant