From 399d220716b8f55370338c3348fe4c6c484a61fa Mon Sep 17 00:00:00 2001 From: Jeffrey Angenent <1571879+devfrey@users.noreply.github.com> Date: Sat, 23 May 2026 13:02:22 +0200 Subject: [PATCH] Report deprecation when calling methods of a deprecated trait --- ...strictedDeprecatedMethodUsageExtension.php | 42 +++++++++++++++++++ .../CallToDeprecatedMethodRuleTest.php | 8 ++++ .../CallToDeprecatedStaticMethodRuleTest.php | 8 ++++ .../call-to-deprecated-method-definition.php | 36 ++++++++++++++++ .../data/call-to-deprecated-method.php | 14 +++++++ ...to-deprecated-static-method-definition.php | 32 ++++++++++++++ .../data/call-to-deprecated-static-method.php | 12 ++++++ 7 files changed, 152 insertions(+) diff --git a/src/Rules/Deprecations/RestrictedDeprecatedMethodUsageExtension.php b/src/Rules/Deprecations/RestrictedDeprecatedMethodUsageExtension.php index d8bb0357..67039f4b 100644 --- a/src/Rules/Deprecations/RestrictedDeprecatedMethodUsageExtension.php +++ b/src/Rules/Deprecations/RestrictedDeprecatedMethodUsageExtension.php @@ -3,6 +3,7 @@ namespace PHPStan\Rules\Deprecations; use PHPStan\Analyser\Scope; +use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\ExtendedMethodReflection; use PHPStan\Rules\RestrictedUsage\RestrictedMethodUsageExtension; use PHPStan\Rules\RestrictedUsage\RestrictedUsage; @@ -63,6 +64,34 @@ public function isRestrictedMethodUsage( ); } + $deprecatedDeclaringTrait = $this->findDeprecatedDeclaringTrait( + $methodReflection->getDeclaringClass(), + $methodReflection->getName(), + ); + if ($deprecatedDeclaringTrait !== null) { + $traitDescription = $deprecatedDeclaringTrait->getDeprecatedDescription(); + if ($traitDescription === null) { + return RestrictedUsage::create( + sprintf( + 'Call to method %s() of deprecated trait %s.', + $methodReflection->getName(), + $deprecatedDeclaringTrait->getName(), + ), + sprintf('%s.deprecatedTrait', $methodReflection->isStatic() ? 'staticMethod' : 'method'), + ); + } + + return RestrictedUsage::create( + sprintf( + "Call to method %s() of deprecated trait %s:\n%s", + $methodReflection->getName(), + $deprecatedDeclaringTrait->getName(), + $traitDescription, + ), + sprintf('%s.deprecatedTrait', $methodReflection->isStatic() ? 'staticMethod' : 'method'), + ); + } + if (!$methodReflection->isDeprecated()->yes()) { return null; } @@ -113,4 +142,17 @@ public function isRestrictedMethodUsage( ); } + private function findDeprecatedDeclaringTrait(ClassReflection $declaringClass, string $methodName): ?ClassReflection + { + foreach ($declaringClass->getTraits() as $trait) { + if (!$trait->hasNativeMethod($methodName) || !$trait->isDeprecated()) { + continue; + } + + return $trait; + } + + return null; + } + } diff --git a/tests/Rules/Deprecations/CallToDeprecatedMethodRuleTest.php b/tests/Rules/Deprecations/CallToDeprecatedMethodRuleTest.php index c3f47b10..e8f760e2 100644 --- a/tests/Rules/Deprecations/CallToDeprecatedMethodRuleTest.php +++ b/tests/Rules/Deprecations/CallToDeprecatedMethodRuleTest.php @@ -47,6 +47,14 @@ public function testDeprecatedMethodCall(): void "Call to deprecated method prophesize() of class CheckDeprecatedMethodCall\\UsingDeprecatedMethodFromTrait:\nUse TraitReplacingDeprecatedMethod::prophesize()", 64, ], + [ + 'Call to method methodFromDeprecatedTrait() of deprecated trait CheckDeprecatedMethodCall\DeprecatedTrait.', + 79, + ], + [ + "Call to method methodFromDeprecatedTraitWithDescription() of deprecated trait CheckDeprecatedMethodCall\\DeprecatedTraitWithDescription:\nUse something else.", + 80, + ], ], ); } diff --git a/tests/Rules/Deprecations/CallToDeprecatedStaticMethodRuleTest.php b/tests/Rules/Deprecations/CallToDeprecatedStaticMethodRuleTest.php index f7446647..7d911927 100644 --- a/tests/Rules/Deprecations/CallToDeprecatedStaticMethodRuleTest.php +++ b/tests/Rules/Deprecations/CallToDeprecatedStaticMethodRuleTest.php @@ -71,6 +71,14 @@ public function testDeprecatedStaticMethodCall(): void 'Call to method doDeprecatedBar() of deprecated class CheckDeprecatedStaticMethodCall\DeprecatedBar.', 81, ], + [ + 'Call to method staticMethodFromDeprecatedTrait() of deprecated trait CheckDeprecatedStaticMethodCall\DeprecatedTrait.', + 83, + ], + [ + "Call to method staticMethodFromDeprecatedTraitWithDescription() of deprecated trait CheckDeprecatedStaticMethodCall\\DeprecatedTraitWithDescription:\nUse something else.", + 84, + ], ], ); } diff --git a/tests/Rules/Deprecations/data/call-to-deprecated-method-definition.php b/tests/Rules/Deprecations/data/call-to-deprecated-method-definition.php index 77ce2d1e..19d8347f 100644 --- a/tests/Rules/Deprecations/data/call-to-deprecated-method-definition.php +++ b/tests/Rules/Deprecations/data/call-to-deprecated-method-definition.php @@ -94,3 +94,39 @@ protected function prophesize(): void } } +/** + * @deprecated + */ +trait DeprecatedTrait +{ + + public function methodFromDeprecatedTrait(): void + { + } + + public static function staticMethodFromDeprecatedTrait(): void + { + } + +} + +/** + * @deprecated Use something else. + */ +trait DeprecatedTraitWithDescription +{ + + public function methodFromDeprecatedTraitWithDescription(): void + { + } + +} + +class ClassUsingDeprecatedTrait +{ + + use DeprecatedTrait; + use DeprecatedTraitWithDescription; + +} + diff --git a/tests/Rules/Deprecations/data/call-to-deprecated-method.php b/tests/Rules/Deprecations/data/call-to-deprecated-method.php index 8dd78b22..145cc3af 100644 --- a/tests/Rules/Deprecations/data/call-to-deprecated-method.php +++ b/tests/Rules/Deprecations/data/call-to-deprecated-method.php @@ -74,3 +74,17 @@ public function callProphesize(): void $this->prophesize(); } } + +$obj = new ClassUsingDeprecatedTrait(); +$obj->methodFromDeprecatedTrait(); +$obj->methodFromDeprecatedTraitWithDescription(); + +/** + * @deprecated + */ +function deprecated_scope_for_deprecated_trait(): void +{ + $obj = new ClassUsingDeprecatedTrait(); + $obj->methodFromDeprecatedTrait(); + $obj->methodFromDeprecatedTraitWithDescription(); +} diff --git a/tests/Rules/Deprecations/data/call-to-deprecated-static-method-definition.php b/tests/Rules/Deprecations/data/call-to-deprecated-static-method-definition.php index 017c13a2..7f2aa731 100644 --- a/tests/Rules/Deprecations/data/call-to-deprecated-static-method-definition.php +++ b/tests/Rules/Deprecations/data/call-to-deprecated-static-method-definition.php @@ -66,3 +66,35 @@ class DeprecatedBaz extends Foo { } + +/** + * @deprecated + */ +trait DeprecatedTrait +{ + + public static function staticMethodFromDeprecatedTrait(): void + { + } + +} + +/** + * @deprecated Use something else. + */ +trait DeprecatedTraitWithDescription +{ + + public static function staticMethodFromDeprecatedTraitWithDescription(): void + { + } + +} + +class ClassUsingDeprecatedTrait +{ + + use DeprecatedTrait; + use DeprecatedTraitWithDescription; + +} diff --git a/tests/Rules/Deprecations/data/call-to-deprecated-static-method.php b/tests/Rules/Deprecations/data/call-to-deprecated-static-method.php index b03eac3e..40bd8d20 100644 --- a/tests/Rules/Deprecations/data/call-to-deprecated-static-method.php +++ b/tests/Rules/Deprecations/data/call-to-deprecated-static-method.php @@ -79,3 +79,15 @@ public static function foo() } DeprecatedBar::doDeprecatedBar(); + +ClassUsingDeprecatedTrait::staticMethodFromDeprecatedTrait(); +ClassUsingDeprecatedTrait::staticMethodFromDeprecatedTraitWithDescription(); + +/** + * @deprecated + */ +function deprecated_scope_for_deprecated_trait(): void +{ + ClassUsingDeprecatedTrait::staticMethodFromDeprecatedTrait(); + ClassUsingDeprecatedTrait::staticMethodFromDeprecatedTraitWithDescription(); +}