Skip to content

Commit 51a8d1a

Browse files
authored
[rule] Add MaximumIgnoredErrorCountRule enbaled in configuration (#162)
1 parent ef19257 commit 51a8d1a

File tree

6 files changed

+105
-1
lines changed

6 files changed

+105
-1
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ includes:
3838
3939
But at start, make baby steps with one rule at a time:
4040
41+
Jump to: [Symfony-specific rules](#3-symfony-specific-rules), [Doctrine-specific rules](#2-doctrine-specific-rules) or [PHPUnit-specific rules](#4-phpunit-specific-rules).
42+
43+
## Special rules
44+
45+
Tired of ever growing ignored error count in your `phpstan.neon`? Set hard limit to keep them low:
46+
47+
```yaml
48+
parameters:
49+
maximumIgnoredErrorCount: 50
50+
```
51+
4152
<br>
4253

4354
### CheckRequiredInterfaceInContractNamespaceRule

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"symfony/framework-bundle": "6.1.*",
1717
"symplify/easy-coding-standard": "^12.5",
1818
"tomasvotruba/class-leak": "^2.0",
19-
"rector/rector": "^2.0",
19+
"rector/rector": "^2.0.6",
2020
"phpstan/extension-installer": "^1.4",
2121
"symplify/phpstan-extensions": "^12.0",
2222
"tomasvotruba/unused-public": "^2.0",

config/services/services.neon

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
parameters:
2+
# related to MaximumIgnoredErrorCountRule
3+
maximumIgnoredErrorCount: 0
4+
5+
parametersSchema:
6+
# related to MaximumIgnoredErrorCountRule
7+
maximumIgnoredErrorCount: int()
8+
19
services:
210
- Symplify\PHPStanRules\NodeTraverser\SimpleCallableNodeTraverser
311
- Symplify\PHPStanRules\PhpDocParser\PhpDocNodeTraverser
@@ -16,3 +24,10 @@ services:
1624

1725
# doctrine
1826
- Symplify\PHPStanRules\Doctrine\DoctrineEntityDocumentAnalyser
27+
28+
# rules enabled by configuration
29+
-
30+
class: Symplify\PHPStanRules\Rules\MaximumIgnoredErrorCountRule
31+
tags: [phpstan.rules.rule]
32+
arguments:
33+
limit: %maximumIgnoredErrorCount%

phpstan.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ parameters:
88

99
level: 8
1010

11+
# custom configuration
12+
maximumIgnoredErrorCount: 10
13+
1114
paths:
1215
- src
1316
- config
@@ -45,3 +48,5 @@ parameters:
4548
- '#Public constant "Symplify\\PHPStanRules\\(.*?)Rule\:\:ERROR_MESSAGE" is never used#'
4649

4750
- '#Although PHPStan\\Node\\InClassNode is covered by backward compatibility promise, this instanceof assumption might break because (.*?) not guaranteed to always stay the same#'
51+
52+
- '#PHPStan\\DependencyInjection\\NeonAdapter#'

src/Enum/RuleIdentifier.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,6 @@ final class RuleIdentifier
103103
public const NO_GET_IN_CONTROLLER = 'symfony.noGetInController';
104104

105105
public const NO_GET_DOCTRINE_IN_CONTROLLER = 'symfony.noGetDoctrineInController';
106+
107+
public const MAXIMUM_IGNORED_ERROR_COUNT = 'symplify.maximumIgnoredErrorCount';
106108
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Rules;
6+
7+
use PhpParser\Node;
8+
use PHPStan\Analyser\Scope;
9+
use PHPStan\DependencyInjection\NeonAdapter;
10+
use PHPStan\Node\CollectedDataNode;
11+
use PHPStan\Rules\Rule;
12+
use PHPStan\Rules\RuleErrorBuilder;
13+
use Symplify\PHPStanRules\Enum\RuleIdentifier;
14+
15+
/**
16+
* @implements Rule<CollectedDataNode>
17+
*/
18+
final readonly class MaximumIgnoredErrorCountRule implements Rule
19+
{
20+
/**
21+
* @var string
22+
*/
23+
public const ERROR_MESSAGE = "Ignored error count %d in phpstan.neon surpassed maximum limit %d.\nInstead of ignoring more errors, fix them to keep your codebase fit.";
24+
25+
private NeonAdapter $neonAdapter;
26+
27+
public function __construct(
28+
private int $limit = 0
29+
) {
30+
$this->neonAdapter = new NeonAdapter();
31+
}
32+
33+
/**
34+
* @return class-string<Node>
35+
*/
36+
public function getNodeType(): string
37+
{
38+
// hack to run this rule just once
39+
return CollectedDataNode::class;
40+
}
41+
42+
/**
43+
* @param CollectedDataNode $node
44+
*/
45+
public function processNode(Node $node, Scope $scope): array
46+
{
47+
// not enabled yet, use "
48+
if ($this->limit === 0) {
49+
return [];
50+
}
51+
52+
$configFilePath = getcwd() . '/phpstan.neon';
53+
54+
// unable to find config
55+
if (! file_exists($configFilePath)) {
56+
return [];
57+
}
58+
59+
$phpstanNeon = $this->neonAdapter->load($configFilePath);
60+
$ignoreErrors = $phpstanNeon['parameters']['ignoreErrors'] ?? [];
61+
if (count($ignoreErrors) <= $this->limit) {
62+
return [];
63+
}
64+
65+
$identifierRuleError = RuleErrorBuilder::message(sprintf(self::ERROR_MESSAGE, count($ignoreErrors), $this->limit))
66+
->identifier(RuleIdentifier::MAXIMUM_IGNORED_ERROR_COUNT)
67+
->build();
68+
69+
return [$identifierRuleError];
70+
}
71+
}

0 commit comments

Comments
 (0)