Skip to content

Commit de436ea

Browse files
committed
Add scanning for traits, and config settings to disable scanning for classes and traits.
1 parent 1bc5654 commit de436ea

File tree

6 files changed

+128
-13
lines changed

6 files changed

+128
-13
lines changed

README.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,17 @@ include:
2424
2525
## Rules
2626
### UnusedClassRule
27-
This rule scans for class declarations and use statements. If a class is declared but not used within the scanned source files, an error is generated.
27+
This rule scans for class and trait declarations and use statements. If a class or trait is declared but not used within the scanned source files, an error is generated.
28+
29+
### Disabling the rule
30+
You can disable scanning classes and traits as follows:
31+
```yaml
32+
# phpstan.neon
33+
parameters:
34+
unused_classes:
35+
classes: false
36+
traits: false
37+
```
2838
2939
#### Excluding files
3040
You can exclude directories and individual files from being scanned by this rule:
@@ -33,8 +43,7 @@ You can exclude directories and individual files from being scanned by this rule
3343
# phpstan.neon
3444
parameters:
3545
unused_classes:
36-
- 'src/Entity'
37-
- 'src/Controller'
38-
- 'src/Repositories'
39-
- 'src/MyUnusedClass.php'
46+
excludePaths:
47+
- 'src/Controller'
48+
- 'src/MyUnusedClass.php'
4049
```

config/extension.neon

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
parametersSchema:
22
unused_classes: structure([
3-
excludedPaths: array()
3+
classes: bool()
4+
traits: bool()
5+
excludePaths: array()
46
])
57

68
# default parameters
79
parameters:
810
unused_classes:
9-
excludedPaths: []
11+
classes: true
12+
traits: true
13+
excludePaths: []
1014

1115
services:
1216
-
@@ -27,6 +31,10 @@ services:
2731
class: Xact\PHPStan\Collectors\DeclareClassCollector
2832
tags:
2933
- phpstan.collector
34+
-
35+
class: Xact\PHPStan\Collectors\DeclareTraitCollector
36+
tags:
37+
- phpstan.collector
3038

3139
rules:
3240
- Xact\PHPStan\Rules\UnusedClassRule

src/Collectors/DeclareClassCollector.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,12 @@ public function processNode(Node $node, Scope $scope)
5252

5353
private function isFileExcluded(Scope $scope): bool
5454
{
55-
$excludedPaths = $this->configuration->getExcludedPaths();
56-
foreach ($excludedPaths as $path) {
55+
if ($this->configuration->isUnusedClassesEnabled() === false) {
56+
return true;
57+
}
58+
59+
$excludePaths = $this->configuration->getExcludePaths();
60+
foreach ($excludePaths as $path) {
5761
$excludePath = $this->fileHelper->absolutizePath($path);
5862
if (str_starts_with($scope->getFile(), $excludePath)) {
5963
return true;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Xact\PHPStan\Collectors;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Stmt\Trait_;
9+
use PHPStan\Analyser\Scope;
10+
use PHPStan\Collectors\Collector;
11+
use PHPStan\File\FileHelper;
12+
use Xact\PHPStan\Configuration;
13+
use Xact\PHPStan\Exception\InvalidNodeTypeException;
14+
15+
class DeclareTraitCollector implements Collector
16+
{
17+
private Configuration $configuration;
18+
private FileHelper $fileHelper;
19+
20+
public function __construct(Configuration $configuration, FileHelper $fileHelper)
21+
{
22+
$this->configuration = $configuration;
23+
$this->fileHelper = $fileHelper;
24+
}
25+
26+
public function getNodeType(): string
27+
{
28+
return Trait_::class;
29+
}
30+
31+
/**
32+
* @inheritDoc
33+
*/
34+
// phpcs:ignore SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter
35+
public function processNode(Node $node, Scope $scope)
36+
{
37+
if (!$node instanceof Trait_) {
38+
throw InvalidNodeTypeException::create($node, Trait_::class);
39+
}
40+
41+
if ($node->namespacedName === null) {
42+
return null;
43+
}
44+
45+
if ($this->isFileExcluded($scope)) {
46+
return null;
47+
}
48+
49+
// returns an array with class name and line - array{string, int}
50+
return [$node->namespacedName->toString(), $node->getLine()];
51+
}
52+
53+
private function isFileExcluded(Scope $scope): bool
54+
{
55+
if ($this->configuration->isUnusedTraitsEnabled() === false) {
56+
return true;
57+
}
58+
59+
$excludePaths = $this->configuration->getExcludePaths();
60+
foreach ($excludePaths as $path) {
61+
$excludePath = $this->fileHelper->absolutizePath($path);
62+
if (str_starts_with($scope->getFile(), $excludePath)) {
63+
return true;
64+
}
65+
}
66+
return false;
67+
}
68+
}

src/Configuration.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,26 @@ public function __construct(array $parameters)
1919
$this->parameters = $parameters;
2020
}
2121

22+
public function isUnusedClassesEnabled(): bool
23+
{
24+
return (bool)($this->parameters['classes'] ?? true);
25+
}
26+
27+
public function isUnusedTraitsEnabled(): bool
28+
{
29+
return (bool)($this->parameters['traits'] ?? true);
30+
}
31+
2232
/**
2333
* @return string[]
2434
*/
25-
public function getExcludedPaths(): array
35+
public function getExcludePaths(): array
2636
{
2737
/** @var string[] */
28-
$excludedPaths = $this->parameters['excludedPaths'] ?? $this->parameters['excludedPaths'];
38+
$excludePaths = $this->parameters['excludePaths'] ?? $this->parameters['excludePaths'];
2939

30-
Assert::allFileExists($excludedPaths);
40+
Assert::allFileExists($excludePaths);
3141

32-
return $excludedPaths;
42+
return $excludePaths;
3343
}
3444
}

src/Rules/UnusedClassRule.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Xact\PHPStan\Collectors\ClassGroupUseCollector;
1313
use Xact\PHPStan\Collectors\ClassUseCollector;
1414
use Xact\PHPStan\Collectors\DeclareClassCollector;
15+
use Xact\PHPStan\Collectors\DeclareTraitCollector;
1516
use Xact\PHPStan\Exception\InvalidNodeTypeException;
1617

1718
class UnusedClassRule implements Rule
@@ -35,6 +36,7 @@ public function processNode(Node $node, Scope $scope): array
3536
}
3637

3738
$classDeclarationData = $node->get(DeclareClassCollector::class);
39+
$traitDeclarationData = $node->get(DeclareTraitCollector::class);
3840
$groupUses = $node->get(ClassGroupUseCollector::class);
3941
$normalUses = $node->get(ClassUseCollector::class);
4042

@@ -69,6 +71,20 @@ public function processNode(Node $node, Scope $scope): array
6971
}
7072
}
7173
}
74+
foreach ($traitDeclarationData as $file => $declarations) {
75+
/**
76+
* @var string $className
77+
* @var int $line
78+
*/
79+
foreach ($declarations as [$className, $line]) {
80+
if (!array_key_exists($className, $usedClasses)) {
81+
$errors[] = RuleErrorBuilder::message("Trait {$className} is never used.")
82+
->file($file)
83+
->line($line)
84+
->build();
85+
}
86+
}
87+
}
7288

7389
return $errors;
7490
}

0 commit comments

Comments
 (0)