Skip to content

Commit 58ce292

Browse files
committed
Fix traversing over trait methods
1 parent 783fd93 commit 58ce292

File tree

4 files changed

+49
-23
lines changed

4 files changed

+49
-23
lines changed

src/Hierarchy/ClassHierarchy.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ public function getClassDescendants(string $className): array
4444
: [];
4545
}
4646

47-
public function getDeclaringTraitMethodKey(string $definition): ?string
47+
public function getDeclaringTraitMethodKey(string $methodKey): ?string
4848
{
49-
return $this->declaringTraits[$definition] ?? null;
49+
return $this->declaringTraits[$methodKey] ?? null;
5050
}
5151

5252
}

src/Rule/DeadMethodRule.php

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use ShipMonk\PHPStan\DeadCode\Collector\MethodCallCollector;
1414
use ShipMonk\PHPStan\DeadCode\Crate\Call;
1515
use ShipMonk\PHPStan\DeadCode\Crate\Kind;
16+
use ShipMonk\PHPStan\DeadCode\Crate\Method;
1617
use ShipMonk\PHPStan\DeadCode\Crate\Visibility;
1718
use ShipMonk\PHPStan\DeadCode\Hierarchy\ClassHierarchy;
1819
use function array_key_exists;
@@ -66,7 +67,7 @@ class DeadMethodRule implements Rule
6667
/**
6768
* @var array<string, list<string>>
6869
*/
69-
private array $methodsToMarkAsUsedCache = [];
70+
private array $methodAlternativesCache = [];
7071

7172
private bool $reportTransitivelyDeadMethodAsSeparateError;
7273

@@ -149,17 +150,18 @@ public function processNode(
149150
foreach ($callsInFile as $calls) {
150151
foreach ($calls as $callString) {
151152
$call = Call::fromString($callString);
152-
153-
$callerKey = $call->caller === null || $this->isAnonymousClass($call->caller->className)
154-
? ''
155-
: $call->caller->toString();
156153
$isWhite = $this->isConsideredWhite($call);
157154

158-
foreach ($this->getAlternativeCalleeKeys($call) as $possibleCalleeKey) {
159-
$this->callGraph[$callerKey][] = $possibleCalleeKey;
155+
$alternativeCalleeKeys = $this->getAlternativeMethodKeys($call->callee, $call->possibleDescendantCall);
156+
$alternativeCallerKeys = $call->caller !== null ? $this->getAlternativeMethodKeys($call->caller, false) : [];
157+
158+
foreach ($alternativeCalleeKeys as $alternativeCalleeKey) {
159+
foreach ($alternativeCallerKeys as $alternativeCallerKey) {
160+
$this->callGraph[$alternativeCallerKey][] = $alternativeCalleeKey;
161+
}
160162

161163
if ($isWhite) {
162-
$whiteCallees[] = $possibleCalleeKey;
164+
$whiteCallees[] = $alternativeCalleeKey;
163165
}
164166
}
165167
}
@@ -177,8 +179,8 @@ public function processNode(
177179
foreach ($entrypoints as $entrypoint) {
178180
$call = Call::fromString($entrypoint);
179181

180-
foreach ($this->getAlternativeCalleeKeys($call) as $methodDefinition) {
181-
unset($this->blackMethods[$methodDefinition]);
182+
foreach ($this->getAlternativeMethodKeys($call->callee, $call->possibleDescendantCall) as $alternativeCalleeKey) {
183+
unset($this->blackMethods[$alternativeCalleeKey]);
182184
}
183185

184186
$this->markTransitiveCallsWhite($call->callee->toString());
@@ -276,32 +278,32 @@ private function isAnonymousClass(string $className): bool
276278
/**
277279
* @return list<string>
278280
*/
279-
private function getAlternativeCalleeKeys(Call $call): array
281+
private function getAlternativeMethodKeys(Method $method, bool $possibleDescendant): array
280282
{
281-
$calleeCacheKey = "{$call->callee->className}::{$call->callee->methodName}";
283+
$methodKey = $method->toString();
282284

283-
if (isset($this->methodsToMarkAsUsedCache[$calleeCacheKey])) {
284-
return $this->methodsToMarkAsUsedCache[$calleeCacheKey];
285+
if (isset($this->methodAlternativesCache[$methodKey])) {
286+
return $this->methodAlternativesCache[$methodKey];
285287
}
286288

287-
$result = [$this->getMethodKey($call->callee->className, $call->callee->methodName)];
289+
$result = [$methodKey];
288290

289-
if ($call->possibleDescendantCall) {
290-
foreach ($this->classHierarchy->getClassDescendants($call->callee->className) as $descendantName) {
291-
$result[] = $this->getMethodKey($descendantName, $call->callee->methodName);
291+
if ($possibleDescendant) {
292+
foreach ($this->classHierarchy->getClassDescendants($method->className) as $descendantName) {
293+
$result[] = $this->getMethodKey($descendantName, $method->methodName);
292294
}
293295
}
294296

295297
// each descendant can be a trait user
296-
foreach ($result as $methodDefinition) {
297-
$traitMethodKey = $this->classHierarchy->getDeclaringTraitMethodKey($methodDefinition);
298+
foreach ($result as $resultKey) {
299+
$traitMethodKey = $this->classHierarchy->getDeclaringTraitMethodKey($resultKey);
298300

299301
if ($traitMethodKey !== null) {
300302
$result[] = $traitMethodKey;
301303
}
302304
}
303305

304-
$this->methodsToMarkAsUsedCache[$calleeCacheKey] = $result;
306+
$this->methodAlternativesCache[$methodKey] = $result;
305307

306308
return $result;
307309
}

tests/Rule/DeadMethodRuleTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ public static function provideFiles(): iterable
168168
yield 'trait-20' => [__DIR__ . '/data/DeadMethodRule/traits-20.php'];
169169
yield 'trait-21' => [__DIR__ . '/data/DeadMethodRule/traits-21.php'];
170170
yield 'trait-22' => [__DIR__ . '/data/DeadMethodRule/traits-22.php'];
171+
yield 'trait-23' => [__DIR__ . '/data/DeadMethodRule/traits-23.php'];
171172
yield 'nullsafe' => [__DIR__ . '/data/DeadMethodRule/nullsafe.php'];
172173
yield 'dead-in-parent-1' => [__DIR__ . '/data/DeadMethodRule/dead-in-parent-1.php'];
173174
yield 'indirect-interface' => [__DIR__ . '/data/DeadMethodRule/indirect-interface.php'];
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace DeadTrait23;
4+
5+
trait MyTrait {
6+
public function traitMethod(): void {
7+
Statical::usedFromTrait();
8+
}
9+
}
10+
11+
class Statical {
12+
public static function usedFromTrait(): void {}
13+
}
14+
15+
class Tester {
16+
use MyTrait;
17+
18+
public function test(): void {
19+
$this->traitMethod();
20+
}
21+
}
22+
23+
(new Tester())->test();

0 commit comments

Comments
 (0)