diff --git a/src/Rules/Deprecations/CallToDeprecatedMethodRule.php b/src/Rules/Deprecations/CallToDeprecatedMethodRule.php index bf1549c..efb031c 100644 --- a/src/Rules/Deprecations/CallToDeprecatedMethodRule.php +++ b/src/Rules/Deprecations/CallToDeprecatedMethodRule.php @@ -6,6 +6,7 @@ use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Identifier; use PHPStan\Analyser\Scope; +use PHPStan\Reflection\MissingMethodFromReflectionException; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Type\TypeUtils; @@ -47,11 +48,33 @@ public function processNode(Node $node, Scope $scope): array $classReflection = $this->reflectionProvider->getClass($referencedClass); $methodReflection = $classReflection->getMethod($methodName, $scope); - if (!$methodReflection->isDeprecated()->yes()) { + $interfaces = $classReflection->getInterfaces(); + + $interfaceIsDeprecated = false; + $deprecatedInterface = null; + + foreach ($interfaces as $interface) { + try { + $interfaceMethodReflection = $interface->getMethod($methodName, $scope); + if ($interfaceMethodReflection->isDeprecated()->yes()) { + $interfaceIsDeprecated = true; + $deprecatedInterface = $interfaceMethodReflection; + break; + } + } catch (MissingMethodFromReflectionException $e) { + // The method did not exist on the interface, which is totally fine. + } + } + + if (!$methodReflection->isDeprecated()->yes() && !$interfaceIsDeprecated) { continue; } $description = $methodReflection->getDeprecatedDescription(); + if ($description === null && $deprecatedInterface) { + // See if the deprecated interface has the description instead. + $description = $deprecatedInterface->getDeprecatedDescription(); + } if ($description === null) { return [sprintf( 'Call to deprecated method %s() of class %s.', diff --git a/tests/Rules/Deprecations/CallToDeprecatedMethodRuleTest.php b/tests/Rules/Deprecations/CallToDeprecatedMethodRuleTest.php index f660fad..acf4439 100644 --- a/tests/Rules/Deprecations/CallToDeprecatedMethodRuleTest.php +++ b/tests/Rules/Deprecations/CallToDeprecatedMethodRuleTest.php @@ -35,6 +35,10 @@ public function testDeprecatedMethodCall(): void "Call to deprecated method deprecatedWithDescription() of class CheckDeprecatedMethodCall\\Foo:\nCall a different method instead.", 15, ], + [ + "Call to deprecated method superDeprecated() of class CheckDeprecatedMethodCall\\FooClassFromInterface:\nThis is totally deprecated.", + 18, + ], ] ); } 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 5d1f05a..c44de27 100644 --- a/tests/Rules/Deprecations/data/call-to-deprecated-method-definition.php +++ b/tests/Rules/Deprecations/data/call-to-deprecated-method-definition.php @@ -65,3 +65,25 @@ public function deprecatedFoo() } } + +interface FooInterface +{ + + /** + * @deprecated This is totally deprecated. + */ + public function superDeprecated(); +} + + +class FooClassFromInterface implements FooInterface +{ + + /** + * {@inheritdoc} + */ + public function superDeprecated() + { + + } +} diff --git a/tests/Rules/Deprecations/data/call-to-deprecated-method.php b/tests/Rules/Deprecations/data/call-to-deprecated-method.php index 7b0c3d9..71b74d0 100644 --- a/tests/Rules/Deprecations/data/call-to-deprecated-method.php +++ b/tests/Rules/Deprecations/data/call-to-deprecated-method.php @@ -14,6 +14,9 @@ $foo->deprecatedFooFromTrait(); $foo->deprecatedWithDescription(); +$fooFromInterface = new FooClassFromInterface(); +$fooFromInterface->superDeprecated(); + /** * @deprecated */