Skip to content

Commit f1c2022

Browse files
authored
VendorEntrypointProvider: check all ancestors (#101)
1 parent ac09699 commit f1c2022

File tree

4 files changed

+80
-36
lines changed

4 files changed

+80
-36
lines changed

src/Provider/VendorEntrypointProvider.php

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace ShipMonk\PHPStan\DeadCode\Provider;
44

55
use Composer\Autoload\ClassLoader;
6-
use ReflectionException;
6+
use ReflectionClass;
77
use ReflectionMethod;
88
use function array_keys;
99
use function str_starts_with;
@@ -33,18 +33,42 @@ public function isEntrypointMethod(ReflectionMethod $method): bool
3333
return false;
3434
}
3535

36-
try {
37-
$methodPrototype = $method->getPrototype();
38-
} catch (ReflectionException $e) {
39-
return false; // hasPrototype available since PHP 8.2
40-
}
36+
$reflectionClass = $method->getDeclaringClass();
37+
$methodName = $method->getName();
38+
39+
do {
40+
if ($this->isForeignMethod($reflectionClass, $methodName)) {
41+
return true;
42+
}
43+
44+
foreach ($reflectionClass->getInterfaces() as $interface) {
45+
if ($this->isForeignMethod($interface, $methodName)) {
46+
return true;
47+
}
48+
}
49+
50+
foreach ($reflectionClass->getTraits() as $trait) {
51+
if ($this->isForeignMethod($trait, $methodName)) {
52+
return true;
53+
}
54+
}
4155

42-
return $this->isForeignMethod($methodPrototype);
56+
$reflectionClass = $reflectionClass->getParentClass();
57+
} while ($reflectionClass !== false);
58+
59+
return false;
4360
}
4461

45-
private function isForeignMethod(ReflectionMethod $methodPrototype): bool
62+
/**
63+
* @param ReflectionClass<object> $reflectionClass
64+
*/
65+
private function isForeignMethod(ReflectionClass $reflectionClass, string $methodName): bool
4666
{
47-
$filePath = $methodPrototype->getDeclaringClass()->getFileName();
67+
if (!$reflectionClass->hasMethod($methodName)) {
68+
return false;
69+
}
70+
71+
$filePath = $reflectionClass->getFileName();
4872

4973
if ($filePath === false) {
5074
return true; // php core or extension

tests/Rule/DeadMethodRuleTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public static function provideFiles(): iterable
119119
yield 'call-on-class-string' => [__DIR__ . '/data/DeadMethodRule/class-string.php'];
120120
yield 'array-map-1' => [__DIR__ . '/data/DeadMethodRule/array-map-1.php'];
121121
yield 'unknown-class' => [__DIR__ . '/data/DeadMethodRule/unknown-class.php'];
122-
yield 'provider-default' => [__DIR__ . '/data/DeadMethodRule/providers/default.php'];
122+
yield 'provider-vendor' => [__DIR__ . '/data/DeadMethodRule/providers/vendor.php'];
123123
yield 'provider-symfony' => [__DIR__ . '/data/DeadMethodRule/providers/symfony.php', 8_00_00];
124124
yield 'provider-phpunit' => [__DIR__ . '/data/DeadMethodRule/providers/phpunit.php', 8_00_00];
125125
yield 'provider-doctrine' => [__DIR__ . '/data/DeadMethodRule/providers/doctrine.php', 8_00_00];

tests/Rule/data/DeadMethodRule/providers/default.php

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Default;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Rules\Rule as RuleFromVendor;
8+
9+
interface IMyRule extends RuleFromVendor
10+
{
11+
public function getNodeType(): string;
12+
}
13+
14+
class MyRule implements IMyRule {
15+
16+
public function getNodeType(): string
17+
{
18+
return '';
19+
}
20+
21+
public function processNode(Node $node, Scope $scope): array
22+
{
23+
return [];
24+
}
25+
26+
public function unused(): array // error: Unused Default\MyRule::unused
27+
{
28+
return [];
29+
}
30+
31+
}
32+
33+
class MyRuleDirect implements RuleFromVendor
34+
{
35+
36+
public function getNodeType(): string
37+
{
38+
return '';
39+
}
40+
41+
public function processNode(Node $node, Scope $scope): array
42+
{
43+
return [];
44+
}
45+
46+
}

0 commit comments

Comments
 (0)