From 129913e45e347e8c856db214852a69381dda7873 Mon Sep 17 00:00:00 2001 From: "Eirik S. Morland" Date: Sat, 20 Nov 2021 06:27:29 +0100 Subject: [PATCH 1/3] Detect deprecated methods from the interface as well. Fix #48 --- .../CallToDeprecatedMethodRule.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Rules/Deprecations/CallToDeprecatedMethodRule.php b/src/Rules/Deprecations/CallToDeprecatedMethodRule.php index bf1549c..033b835 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,7 +48,23 @@ 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; + + foreach ($interfaces as $interface) { + try { + $interfaceMethodReflection = $interface->getMethod($methodName, $scope); + if ($interfaceMethodReflection->isDeprecated()->yes()) { + $interfaceIsDeprecated = true; + break; + } + } catch (MissingMethodFromReflectionException $e) { + // The method did not exist on the interface, which is totally fine. + } + } + + if (!$methodReflection->isDeprecated()->yes() && !$interfaceIsDeprecated) { continue; } From af13c4c09601ef7a9e35b31b6da6d23aa3c372bd Mon Sep 17 00:00:00 2001 From: "Eirik S. Morland" Date: Sat, 20 Nov 2021 06:39:24 +0100 Subject: [PATCH 2/3] Find deprecation description on interface if possible --- src/Rules/Deprecations/CallToDeprecatedMethodRule.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Rules/Deprecations/CallToDeprecatedMethodRule.php b/src/Rules/Deprecations/CallToDeprecatedMethodRule.php index 033b835..efb031c 100644 --- a/src/Rules/Deprecations/CallToDeprecatedMethodRule.php +++ b/src/Rules/Deprecations/CallToDeprecatedMethodRule.php @@ -51,12 +51,14 @@ public function processNode(Node $node, Scope $scope): array $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) { @@ -69,6 +71,10 @@ public function processNode(Node $node, Scope $scope): array } $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.', From 6be98db84b5382d2af1b9151524a8030b25f4629 Mon Sep 17 00:00:00 2001 From: "Eirik S. Morland" Date: Sat, 20 Nov 2021 06:39:33 +0100 Subject: [PATCH 3/3] Add a test --- .../CallToDeprecatedMethodRuleTest.php | 4 ++++ .../call-to-deprecated-method-definition.php | 22 +++++++++++++++++++ .../data/call-to-deprecated-method.php | 3 +++ 3 files changed, 29 insertions(+) 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 */