Skip to content

Commit fbdf0da

Browse files
committed
Fix false positive about unused class elements when the class uses a trait
1 parent 4c7d382 commit fbdf0da

File tree

4 files changed

+102
-1
lines changed

4 files changed

+102
-1
lines changed

src/Type/ThisType.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,21 @@ public function changeBaseClass(ClassReflection $classReflection): StaticType
3030

3131
public function describe(VerbosityLevel $level): string
3232
{
33-
return sprintf('$this(%s)', $this->getStaticObjectType()->describe($level));
33+
$callback = fn () => sprintf('$this(%s)', $this->getStaticObjectType()->describe($level));
34+
return $level->handle(
35+
$callback,
36+
$callback,
37+
$callback,
38+
function () use ($callback): string {
39+
$base = $callback();
40+
$trait = $this->getTraitReflection();
41+
if ($trait === null) {
42+
return $base;
43+
}
44+
45+
return sprintf('%s-trait-%s', $base, $trait->getDisplayName());
46+
},
47+
);
3448
}
3549

3650
public function isSuperTypeOf(Type $type): TrinaryLogic

tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,13 @@ public function testBug7389(): void
9090
]);
9191
}
9292

93+
public function testFalsePositiveWithTraitUse(): void
94+
{
95+
if (PHP_VERSION_ID < 80100) {
96+
$this->markTestSkipped('This test needs PHP 8.1');
97+
}
98+
99+
$this->analyse([__DIR__ . '/data/unused-method-false-positive-with-trait.php'], []);
100+
}
101+
93102
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php // lint >= 8.1
2+
3+
namespace UnusedMethodFalsePositiveWithTrait;
4+
5+
use ReflectionEnum;
6+
7+
enum LocalOnlineReservationTime: string
8+
{
9+
10+
use LabeledEnumTrait;
11+
12+
case MORNING = 'morning';
13+
case AFTERNOON = 'afternoon';
14+
case EVENING = 'evening';
15+
16+
public static function getPeriodForHour(string $hour): self
17+
{
18+
$hour = self::hourToNumber($hour);
19+
20+
throw new \Exception('Internal error');
21+
}
22+
23+
private static function hourToNumber(string $hour): int
24+
{
25+
return (int) str_replace(':', '', $hour);
26+
}
27+
28+
}
29+
30+
trait LabeledEnumTrait
31+
{
32+
33+
use EnumTrait;
34+
35+
}
36+
37+
trait EnumTrait
38+
{
39+
40+
/**
41+
* @return list<static>
42+
*/
43+
public static function getDeprecatedEnums(): array
44+
{
45+
static $cache = [];
46+
if ($cache === []) {
47+
$reflection = new ReflectionEnum(self::class);
48+
$cases = $reflection->getCases();
49+
50+
foreach ($cases as $case) {
51+
$docComment = $case->getDocComment();
52+
if ($docComment === false || !str_contains($docComment, '@deprecated')) {
53+
continue;
54+
}
55+
$cache[] = self::from($case->getBackingValue());
56+
}
57+
}
58+
59+
return $cache;
60+
}
61+
62+
public function isDeprecated(): bool
63+
{
64+
return $this->equalsAny(self::getDeprecatedEnums());
65+
}
66+
67+
public function equalsAny(...$that): bool
68+
{
69+
return in_array($this, $that, true);
70+
}
71+
72+
}

tests/PHPStan/Type/ObjectTypeTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
use Throwable;
3535
use ThrowPoints\TryCatch\MyInvalidArgumentException;
3636
use Traversable;
37+
use UnusedMethodFalsePositiveWithTrait\LocalOnlineReservationTime;
3738
use function count;
3839
use function sprintf;
3940
use const PHP_VERSION_ID;
@@ -432,6 +433,11 @@ public function dataIsSuperTypeOf(): array
432433
new ObjectType(DateTime::class),
433434
TrinaryLogic::createNo(),
434435
],
436+
61 => [
437+
new ObjectType(LocalOnlineReservationTime::class),
438+
new ThisType($reflectionProvider->getClass(LocalOnlineReservationTime::class)),
439+
TrinaryLogic::createYes(),
440+
],
435441
];
436442
}
437443

0 commit comments

Comments
 (0)