|
30 | 30 | use RecursiveDirectoryIterator; |
31 | 31 | use RecursiveIteratorIterator; |
32 | 32 | use ReflectionAttribute; |
| 33 | +use ReflectionEnum; |
| 34 | +use ReflectionNamedType; |
33 | 35 | use Reflector; |
34 | 36 | use ShipMonk\PHPStan\DeadCode\Enum\AccessType; |
35 | 37 | use ShipMonk\PHPStan\DeadCode\Graph\ClassConstantRef; |
@@ -152,6 +154,7 @@ public function getUsages( |
152 | 154 | ...$this->getMethodUsagesFromReflection($node), |
153 | 155 | ...$this->getPropertyUsagesFromReflection($node), |
154 | 156 | ...$this->getConstantUsages($node->getClassReflection()), |
| 157 | + ...$this->getInvokableCommandEnumUsages($node), |
155 | 158 | ]; |
156 | 159 | } |
157 | 160 |
|
@@ -1486,6 +1489,81 @@ private function getConstantUsages(ClassReflection $classReflection): array |
1486 | 1489 | return $usages; |
1487 | 1490 | } |
1488 | 1491 |
|
| 1492 | + /** |
| 1493 | + * @return list<ClassConstantUsage> |
| 1494 | + */ |
| 1495 | + private function getInvokableCommandEnumUsages(InClassNode $node): array |
| 1496 | + { |
| 1497 | + $classReflection = $node->getClassReflection(); |
| 1498 | + $nativeReflection = $classReflection->getNativeReflection(); |
| 1499 | + |
| 1500 | + if ($nativeReflection instanceof ReflectionEnum) { |
| 1501 | + return []; |
| 1502 | + } |
| 1503 | + |
| 1504 | + $isCommand = $this->hasAttribute($nativeReflection, 'Symfony\Component\Console\Attribute\AsCommand') |
| 1505 | + || $nativeReflection->isSubclassOf('Symfony\Component\Console\Command\Command'); |
| 1506 | + |
| 1507 | + if (!$isCommand) { |
| 1508 | + return []; |
| 1509 | + } |
| 1510 | + |
| 1511 | + $invokeMethod = null; |
| 1512 | + |
| 1513 | + foreach ($nativeReflection->getMethods() as $method) { |
| 1514 | + if ($method->getName() === '__invoke') { |
| 1515 | + $invokeMethod = $method; |
| 1516 | + break; |
| 1517 | + } |
| 1518 | + } |
| 1519 | + |
| 1520 | + if ($invokeMethod === null) { |
| 1521 | + return []; |
| 1522 | + } |
| 1523 | + |
| 1524 | + $usages = []; |
| 1525 | + |
| 1526 | + foreach ($invokeMethod->getParameters() as $parameter) { |
| 1527 | + $type = $parameter->getType(); |
| 1528 | + |
| 1529 | + if (!$type instanceof ReflectionNamedType || $type->isBuiltin()) { |
| 1530 | + continue; |
| 1531 | + } |
| 1532 | + |
| 1533 | + $typeName = $type->getName(); |
| 1534 | + |
| 1535 | + if (!$this->reflectionProvider->hasClass($typeName)) { |
| 1536 | + continue; |
| 1537 | + } |
| 1538 | + |
| 1539 | + $enumReflection = $this->reflectionProvider->getClass($typeName); |
| 1540 | + |
| 1541 | + if (!$enumReflection->isBackedEnum()) { |
| 1542 | + continue; |
| 1543 | + } |
| 1544 | + |
| 1545 | + $nativeEnumReflection = $enumReflection->getNativeReflection(); |
| 1546 | + |
| 1547 | + if (!$nativeEnumReflection instanceof ReflectionEnum) { |
| 1548 | + continue; |
| 1549 | + } |
| 1550 | + |
| 1551 | + foreach ($nativeEnumReflection->getCases() as $case) { |
| 1552 | + $usages[] = new ClassConstantUsage( |
| 1553 | + UsageOrigin::createVirtual($this, VirtualUsageData::withNote('Invokable command parameter in ' . $nativeReflection->getShortName())), |
| 1554 | + new ClassConstantRef( |
| 1555 | + $typeName, |
| 1556 | + $case->getName(), |
| 1557 | + possibleDescendant: false, |
| 1558 | + isEnumCase: TrinaryLogic::createYes(), |
| 1559 | + ), |
| 1560 | + ); |
| 1561 | + } |
| 1562 | + } |
| 1563 | + |
| 1564 | + return $usages; |
| 1565 | + } |
| 1566 | + |
1489 | 1567 | private function getContainerXmlPath(Container $container): ?string |
1490 | 1568 | { |
1491 | 1569 | try { |
|
0 commit comments