Skip to content

Commit 7490296

Browse files
committed
Reflection::getCases
1 parent 87441fe commit 7490296

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
@@ -90,8 +90,8 @@ parameters:
9090
## Generic usage providers:
9191

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

9696
#### Vendor:
9797
- 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;
@@ -63,6 +65,7 @@ private function processMethodCall(
6365

6466
$usedConstants = [];
6567
$usedMethods = [];
68+
$usedEnumCases = [];
6669

6770
foreach ($methodNames as $methodName) {
6871
foreach ($callerType->getObjectClassReflections() as $reflection) {
@@ -86,6 +89,10 @@ private function processMethodCall(
8689
...$usedMethods,
8790
...$this->extractMethodsUsedByReflection($genericClassName, $methodName, $node->getArgs(), $node, $scope),
8891
];
92+
$usedEnumCases = [
93+
...$usedEnumCases,
94+
...$this->extractEnumCasesUsedByReflection($genericClassName, $methodName, $node->getArgs(), $node, $scope),
95+
];
8996
}
9097
}
9198
}
@@ -94,6 +101,7 @@ private function processMethodCall(
94101
return [
95102
...$usedConstants,
96103
...$usedMethods,
104+
...$usedEnumCases,
97105
];
98106
}
99107

@@ -126,6 +134,35 @@ private function extractConstantsUsedByReflection(
126134
return $usedConstants;
127135
}
128136

137+
/**
138+
* @param array<Arg> $args
139+
* @return list<EnumCaseUsage>
140+
*/
141+
private function extractEnumCasesUsedByReflection(
142+
?string $genericClassName,
143+
string $methodName,
144+
array $args,
145+
Node $node,
146+
Scope $scope
147+
): array
148+
{
149+
$usedConstants = [];
150+
151+
if ($methodName === 'getCases') {
152+
$usedConstants[] = $this->createEnumCaseUsage($node, $scope, $genericClassName, null);
153+
}
154+
155+
if (($methodName === 'getCase') && count($args) === 1) {
156+
$firstArg = $args[array_key_first($args)];
157+
158+
foreach ($scope->getType($firstArg->value)->getConstantStrings() as $constantString) {
159+
$usedConstants[] = $this->createEnumCaseUsage($node, $scope, $genericClassName, $constantString->getValue());
160+
}
161+
}
162+
163+
return $usedConstants;
164+
}
165+
129166
/**
130167
* @param array<Arg> $args
131168
* @return list<ClassMethodUsage>
@@ -202,6 +239,22 @@ private function createConstantUsage(
202239
);
203240
}
204241

242+
private function createEnumCaseUsage(
243+
Node $node,
244+
Scope $scope,
245+
?string $className,
246+
?string $enumCaseName
247+
): EnumCaseUsage
248+
{
249+
return new EnumCaseUsage(
250+
UsageOrigin::createRegular($node, $scope),
251+
new EnumCaseRef(
252+
$className,
253+
$enumCaseName,
254+
),
255+
);
256+
}
257+
205258
private function createMethodUsage(
206259
Node $node,
207260
Scope $scope,

tests/Rule/DeadCodeRuleTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,7 @@ public static function provideFiles(): Traversable
756756
// providers
757757
yield 'provider-vendor' => [__DIR__ . '/data/providers/vendor.php'];
758758
yield 'provider-reflection' => [__DIR__ . '/data/providers/reflection.php', self::requiresPhp(8_01_00)];
759+
yield 'provider-reflection-enums' => [__DIR__ . '/data/providers/reflection-enums.php', self::requiresPhp(8_01_00)];
759760
yield 'provider-reflection-no-t' => [__DIR__ . '/data/providers/reflection-no-generics.php'];
760761
yield 'provider-symfony' => [__DIR__ . '/data/providers/symfony.php', self::requiresPhp(8_00_00)];
761762
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)