From 90e77d8acdadad45ba002f379b4ab3474d2ffd12 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Tue, 14 Jul 2026 17:44:09 +0200 Subject: [PATCH] [TypeDeclaration] Skip trait methods on ParamTypeByMethodCallType rules Trait methods can be used in classes that implement an interface contract. Adding a param type inferred from a local call would break the signature when the trait is composed into such a class. --- .../Fixture/skip_trait.php.inc | 15 +++++++++++++++ .../AbstractParamTypeByMethodCallTypeRector.php | 7 +++++++ 2 files changed, 22 insertions(+) create mode 100644 rules-tests/TypeDeclaration/Rector/ClassMethod/ArrayParamTypeByMethodCallTypeRector/Fixture/skip_trait.php.inc diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ArrayParamTypeByMethodCallTypeRector/Fixture/skip_trait.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ArrayParamTypeByMethodCallTypeRector/Fixture/skip_trait.php.inc new file mode 100644 index 00000000000..6c66c3b3c95 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ArrayParamTypeByMethodCallTypeRector/Fixture/skip_trait.php.inc @@ -0,0 +1,15 @@ +use($value); + } + + private function use(array $value): void + { + } +} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/AbstractParamTypeByMethodCallTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/AbstractParamTypeByMethodCallTypeRector.php index bfae44424e3..ac46f5b8946 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/AbstractParamTypeByMethodCallTypeRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/AbstractParamTypeByMethodCallTypeRector.php @@ -13,6 +13,7 @@ use PhpParser\Node\Param; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; +use PHPStan\Analyser\Scope; use PHPStan\Type\Type; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; @@ -99,6 +100,12 @@ abstract protected function isMatchingParamType(Type $type): bool; private function shouldSkipClassMethod(ClassMethod $classMethod): bool { + // trait method can be used in a class that requires a contract type, adding a param type would break it + $scope = $classMethod->getAttribute(AttributeKey::SCOPE); + if ($scope instanceof Scope && $scope->isInTrait()) { + return true; + } + // user-guarded class: adding a param type here would break its child classes if ($this->parentClassMethodTypeOverrideGuard->isTypeGuardedClass($classMethod)) { return true;