-
Notifications
You must be signed in to change notification settings - Fork 14
Open
Labels
enhancementNew feature or requestNew feature or request
Description
I recently worked on a rule to detect unused interfaces, abstract classes and traits from the source code, highly inspired from your plugin (using collectors).
In case you might be interested, here is a shot:
namespace App\Utils\PHPStan\Rules;
use App\Utils\PHPStan\Collector\DeadCode\ClassCollector;
use App\Utils\PHPStan\Collector\DeadCode\PossiblyUnusedClassCollector;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\CollectedDataNode;
use PHPStan\Rules\IdentifierRuleError;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
/**
* Checks unimplemented classes.
*/
final class DeadClassRule implements Rule
{
/**
* @var array<string, IdentifierRuleError>
*/
private array $errors = [];
#[\Override]
public function getNodeType(): string
{
return CollectedDataNode::class;
}
/**
* @param CollectedDataNode $node
*/
#[\Override]
public function processNode(Node $node, Scope $scope): array
{
if ($node->isOnlyFilesAnalysis()) {
return [];
}
$classDeclarationData = $node->get(ClassCollector::class);
$possiblyUnusedClasses = array_flip(array_map(static fn (array $values): string => $values[0], $node->get(PossiblyUnusedClassCollector::class)));
foreach ($classDeclarationData as $classesInFile) {
foreach ($classesInFile as $classPairs) {
foreach ($classPairs as $ancestor => $descendant) {
if (\array_key_exists($ancestor, $possiblyUnusedClasses)) {
// ancestor is used, remove it from collection
unset($possiblyUnusedClasses[$ancestor]);
}
}
}
}
foreach ($possiblyUnusedClasses as $className => $file) {
$this->errors[$className] = RuleErrorBuilder::message("Unused {$className}")
->file($file)
->line((new \ReflectionClass($className))->getStartLine())
->identifier('shipmonk.deadMethod')
->build();
}
return array_values($this->errors);
}
}
#########################################
namespace App\Utils\PHPStan\Collector\DeadCode;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Enum_;
use PHPStan\Analyser\Scope;
use PHPStan\Collectors\Collector;
/**
* @implements Collector<ClassLike, string>
*/
class PossiblyUnusedClassCollector implements Collector
{
#[\Override]
public function getNodeType(): string
{
return ClassLike::class;
}
/**
* @param ClassLike $node
*/
#[\Override]
public function processNode(Node $node, Scope $scope): ?string
{
// can't determine if a class is unused due to framework (except for abstract classes)
if ($node instanceof Class_ && !$node->isAbstract()) {
return null;
}
// can't determine if an enum is unused
if ($node instanceof Enum_) {
return null;
}
// node should be an interface, a trait or an abstract class
return $node->namespacedName?->toString();
}
}
#########################################
namespace App\Utils\PHPStan\Collector\DeadCode;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Collectors\Collector;
use PHPStan\Node\InClassNode;
/**
* @implements Collector<InClassNode, <string, string>>
*/
class ClassCollector implements Collector
{
#[\Override]
public function getNodeType(): string
{
return InClassNode::class;
}
/**
* @param InClassNode $node
*
* @return array<string, string>
*/
#[\Override]
public function processNode(Node $node, Scope $scope): array
{
$pairs = [];
$origin = $node->getClassReflection();
foreach ($origin->getAncestors() as $ancestor) {
// ignore self
if ($ancestor === $origin) {
continue;
}
// ignore ancestors from PHP global namespace
if ($ancestor->isInternal()) {
continue;
}
// ignore ancestors from vendor
if (str_contains((string) $ancestor->getFileName(), '/vendor/')) {
continue;
}
$pairs[$ancestor->getName()] = $ancestor->getFileName();
}
return $pairs;
}
}
I'm also wondering if I missed a use-case which could lead to an error or a false-alert. Any opinion about it?
Of course, if you're interested about it, I would be happy to contribute or help.
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request