Skip to content

Commit 21eb267

Browse files
committed
Reflection::getCases
1 parent 4a0434a commit 21eb267

File tree

4 files changed

+85
-2
lines changed

4 files changed

+85
-2
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ parameters:
8181
## Generic usage providers:
8282

8383
#### Reflection:
84-
- Any constant or method accessed via `ReflectionClass` is detected as used
85-
- e.g. `$reflection->getConstructor()`, `$reflection->getConstant('NAME')`, `$reflection->getMethods()`, ...
84+
- Any enum, constant or method accessed via `ReflectionClass` is detected as used
85+
- e.g. `$reflection->getConstructor()`, `$reflection->getConstant('NAME')`, `$reflection->getMethods()`, `$reflection->getCases()`...
8686

8787
#### Vendor:
8888
- Any overridden method that originates in `vendor` is not reported as dead

src/Provider/ReflectionUsageProvider.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use ShipMonk\PHPStan\DeadCode\Graph\ClassMemberUsage;
1818
use ShipMonk\PHPStan\DeadCode\Graph\ClassMethodRef;
1919
use ShipMonk\PHPStan\DeadCode\Graph\ClassMethodUsage;
20+
use ShipMonk\PHPStan\DeadCode\Graph\EnumCaseRef;
21+
use ShipMonk\PHPStan\DeadCode\Graph\EnumCaseUsage;
2022
use ShipMonk\PHPStan\DeadCode\Graph\UsageOrigin;
2123
use function array_key_first;
2224
use function count;
@@ -57,6 +59,7 @@ private function processMethodCall(MethodCall $node, Scope $scope): array
5759

5860
$usedConstants = [];
5961
$usedMethods = [];
62+
$usedEnumCases = [];
6063

6164
foreach ($methodNames as $methodName) {
6265
foreach ($callerType->getObjectClassReflections() as $reflection) {
@@ -80,6 +83,10 @@ private function processMethodCall(MethodCall $node, Scope $scope): array
8083
...$usedMethods,
8184
...$this->extractMethodsUsedByReflection($genericClassName, $methodName, $node->getArgs(), $node, $scope),
8285
];
86+
$usedEnumCases = [
87+
...$usedEnumCases,
88+
...$this->extractEnumCasesUsedByReflection($genericClassName, $methodName, $node->getArgs(), $node, $scope),
89+
];
8390
}
8491
}
8592
}
@@ -88,6 +95,7 @@ private function processMethodCall(MethodCall $node, Scope $scope): array
8895
return [
8996
...$usedConstants,
9097
...$usedMethods,
98+
...$usedEnumCases,
9199
];
92100
}
93101

@@ -120,6 +128,35 @@ private function extractConstantsUsedByReflection(
120128
return $usedConstants;
121129
}
122130

131+
/**
132+
* @param array<Arg> $args
133+
* @return list<EnumCaseUsage>
134+
*/
135+
private function extractEnumCasesUsedByReflection(
136+
?string $genericClassName,
137+
string $methodName,
138+
array $args,
139+
Node $node,
140+
Scope $scope
141+
): array
142+
{
143+
$usedConstants = [];
144+
145+
if ($methodName === 'getCases') {
146+
$usedConstants[] = $this->createEnumCaseUsage($node, $scope, $genericClassName, null);
147+
}
148+
149+
if (($methodName === 'getCase') && count($args) === 1) {
150+
$firstArg = $args[array_key_first($args)];
151+
152+
foreach ($scope->getType($firstArg->value)->getConstantStrings() as $constantString) {
153+
$usedConstants[] = $this->createEnumCaseUsage($node, $scope, $genericClassName, $constantString->getValue());
154+
}
155+
}
156+
157+
return $usedConstants;
158+
}
159+
123160
/**
124161
* @param array<Arg> $args
125162
* @return list<ClassMethodUsage>
@@ -193,6 +230,22 @@ private function createConstantUsage(
193230
);
194231
}
195232

233+
private function createEnumCaseUsage(
234+
Node $node,
235+
Scope $scope,
236+
?string $className,
237+
?string $enumCaseName
238+
): EnumCaseUsage
239+
{
240+
return new EnumCaseUsage(
241+
UsageOrigin::createRegular($node, $scope),
242+
new EnumCaseRef(
243+
$className,
244+
$enumCaseName,
245+
),
246+
);
247+
}
248+
196249
private function createMethodUsage(
197250
Node $node,
198251
Scope $scope,

tests/Rule/DeadCodeRuleTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,7 @@ public static function provideFiles(): Traversable
683683
// providers
684684
yield 'provider-vendor' => [__DIR__ . '/data/providers/vendor.php'];
685685
yield 'provider-reflection' => [__DIR__ . '/data/providers/reflection.php', self::requiresPhp(8_01_00)];
686+
yield 'provider-reflection-enums' => [__DIR__ . '/data/providers/reflection-enums.php', self::requiresPhp(8_01_00)];
686687
yield 'provider-reflection-no-t' => [__DIR__ . '/data/providers/reflection-no-generics.php'];
687688
yield 'provider-symfony' => [__DIR__ . '/data/providers/symfony.php', self::requiresPhp(8_00_00)];
688689
yield 'provider-symfony-7.1' => [__DIR__ . '/data/providers/symfony-gte71.php', self::requiresPhp(8_00_00) && self::requiresPackage('symfony/dependency-injection', '>= 7.1')];
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace ReflectionEnums;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use ShipMonk\PHPStan\DeadCode\Rule\RuleTestCase;
7+
8+
enum MyEnum1: string
9+
{
10+
case One = 'one';
11+
case Two = 'two';
12+
}
13+
14+
enum MyEnum2: int
15+
{
16+
case One = 1;
17+
case Two = 2; // error: Unused ReflectionEnums\MyEnum2::Two
18+
}
19+
20+
21+
function test() {
22+
23+
$reflection1 = new \ReflectionEnum(MyEnum1::class);
24+
$reflection1->getCases();
25+
26+
$reflection2 = new \ReflectionEnum(MyEnum2::class);
27+
$reflection2->getCase('One');
28+
29+
}

0 commit comments

Comments
 (0)