Skip to content

Commit bf0c7ca

Browse files
committed
Enhancement: Implement PHPStan\NoPhpstanIgnoreRule
1 parent 32ae316 commit bf0c7ca

File tree

9 files changed

+285
-0
lines changed

9 files changed

+285
-0
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
88

99
For a full diff see [`2.12.0...main`][2.12.0...main].
1010

11+
### Added
12+
13+
- Added `PHPStan\NoIgnoreRule`, which reports an error when a `@phpstan-ignore`, `@phpstan-ignore-line`, or `@phpstan-ignore-next-line` tag is used to suppress an error ([#1009]), by [@localheinz]
14+
1115
### Changed
1216

1317
- Required `phpstan/phpstan:^2.1.35` ([#1010]), by [@localheinz]
@@ -740,6 +744,7 @@ For a full diff see [`362c7ea...0.1.0`][362c7ea...0.1.0].
740744
[#958]: https://github.com/ergebnis/phpstan-rules/pull/958
741745
[#972]: https://github.com/ergebnis/phpstan-rules/pull/972
742746
[#977]: https://github.com/ergebnis/phpstan-rules/pull/977
747+
[#1009]: https://github.com/ergebnis/phpstan-rules/pull/1009
743748
[#1010]: https://github.com/ergebnis/phpstan-rules/pull/1010
744749

745750
[@cosmastech]: https://github.com/cosmastech

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ This package provides the following rules for use with [`phpstan/phpstan`](https
7373
- [`Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterwithnulldefaultvaluerule)
7474
- [`Ergebnis\PHPStan\Rules\Methods\NoReturnByReferenceRule`](https://github.com/ergebnis/phpstan-rules#methodsnoreturnbyreferencerule)
7575
- [`Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule`](https://github.com/ergebnis/phpstan-rules#methodsprivateinfinalclassrule)
76+
- [`Ergebnis\PHPStan\Rules\PHPStan\NoPhpstanIgnoreRule`](https://github.com/ergebnis/phpstan-rules#phpstannophpstanignorerule)
7677
- [`Ergebnis\PHPStan\Rules\Statements\NoSwitchRule`](https://github.com/ergebnis/phpstan-rules#statementsnoswitchrule)
7778

7879

@@ -697,6 +698,23 @@ parameters:
697698
enabled: false
698699
```
699700

701+
### PHPStan
702+
703+
#### `PHPStan\NoPhpstanIgnoreRule`
704+
705+
This rule reports an error when a `@phpstan-ignore`, `@phpstan-ignore-line`, or `@phpstan-ignore-next-line` tag is used to [ignore errors reported by `phpstan/phpstan`](https://phpstan.org/user-guide/ignoring-errors#ignoring-in-code-using-phpdocs).
706+
707+
##### Disabling the rule
708+
709+
You can set the `enabled` parameter to `false` to disable this rule.
710+
711+
```neon
712+
parameters:
713+
ergebnis:
714+
noPhpstanIgnore:
715+
enabled: false
716+
```
717+
700718
## Disabling all rules
701719

702720
You can disable all rules using the `allRules` configuration parameter:

rules.neon

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ conditionalTags:
5555
phpstan.rules.rule: %ergebnis.noReturnByReference.enabled%
5656
Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule:
5757
phpstan.rules.rule: %ergebnis.privateInFinalClass.enabled%
58+
Ergebnis\PHPStan\Rules\PHPStan\NoPhpstanIgnoreRule:
59+
phpstan.rules.rule: %ergebnis.noPhpstanIgnore.enabled%
5860
Ergebnis\PHPStan\Rules\Statements\NoSwitchRule:
5961
phpstan.rules.rule: %ergebnis.noSwitch.enabled%
6062

@@ -82,6 +84,8 @@ parameters:
8284
enabled: %ergebnis.allRules%
8385
noEval:
8486
enabled: %ergebnis.allRules%
87+
noPhpstanIgnore:
88+
enabled: %ergebnis.allRules%
8589
noExtends:
8690
classesAllowedToBeExtended: []
8791
enabled: %ergebnis.allRules%
@@ -153,6 +157,9 @@ parametersSchema:
153157
noEval: structure([
154158
enabled: bool(),
155159
])
160+
noPhpstanIgnore: structure([
161+
enabled: bool(),
162+
])
156163
noIsset: structure([
157164
enabled: bool(),
158165
])
@@ -288,5 +295,8 @@ services:
288295
-
289296
class: Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule
290297

298+
-
299+
class: Ergebnis\PHPStan\Rules\PHPStan\NoPhpstanIgnoreRule
300+
291301
-
292302
class: Ergebnis\PHPStan\Rules\Statements\NoSwitchRule

src/ErrorIdentifier.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ public static function noNullableReturnTypeDeclaration(): self
110110
return new self('noNullableReturnTypeDeclaration');
111111
}
112112

113+
public static function noPhpstanIgnore(): self
114+
{
115+
return new self('noPhpstanIgnore');
116+
}
117+
113118
public static function noReturnByReference(): self
114119
{
115120
return new self('noReturnByReference');
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright (c) 2018-2026 Andreas Möller
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE.md file that was distributed with this source code.
10+
*
11+
* @see https://github.com/ergebnis/phpstan-rules
12+
*/
13+
14+
namespace Ergebnis\PHPStan\Rules\PHPStan;
15+
16+
use Ergebnis\PHPStan\Rules\ErrorIdentifier;
17+
use PhpParser\Node;
18+
use PHPStan\Analyser;
19+
use PHPStan\Node\FileNode;
20+
use PHPStan\Rules;
21+
22+
/**
23+
* @implements Rules\Rule<FileNode>
24+
*/
25+
final class NoPhpstanIgnoreRule implements Rules\Rule
26+
{
27+
public function getNodeType(): string
28+
{
29+
return FileNode::class;
30+
}
31+
32+
public function processNode(
33+
Node $node,
34+
Analyser\Scope $scope
35+
): array {
36+
$errors = [];
37+
38+
foreach ($node->getNodes() as $nodeInFile) {
39+
foreach ($nodeInFile->getComments() as $comment) {
40+
$text = $comment->getText();
41+
$commentStartLine = $comment->getStartLine();
42+
43+
foreach (\explode("\n", $text) as $index => $line) {
44+
if (0 === \preg_match('/@phpstan-ignore(-line|-next-line)?(?=\s|$|,)/', $line, $matches)) {
45+
continue;
46+
}
47+
48+
$tag = $matches[0];
49+
50+
$message = \sprintf(
51+
'Errors reported by phpstan/phpstan should not be ignored via "%s", fix the error or use the baseline instead.',
52+
$tag,
53+
);
54+
55+
$errors[] = Rules\RuleErrorBuilder::message($message)
56+
->identifier(ErrorIdentifier::noPhpstanIgnore()->toString())
57+
->line($commentStartLine + $index)
58+
->nonIgnorable()
59+
->build();
60+
}
61+
}
62+
}
63+
64+
return $errors;
65+
}
66+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
// @phpstan-ignore variable.undefined
6+
echo $undefined;
7+
8+
/* @phpstan-ignore variable.undefined */
9+
echo $undefined;
10+
11+
/** @phpstan-ignore variable.undefined */
12+
echo $undefined;
13+
14+
/*
15+
* @phpstan-ignore variable.undefined
16+
*/
17+
echo $undefined;
18+
19+
echo $undefined; // @phpstan-ignore-line
20+
21+
echo $undefined; /* @phpstan-ignore-line */
22+
23+
echo $undefined;
24+
25+
/** @phpstan-ignore-line */
26+
27+
// @phpstan-ignore-next-line
28+
echo $undefined;
29+
30+
/* @phpstan-ignore-next-line */
31+
echo $undefined;
32+
33+
/** @phpstan-ignore-next-line */
34+
echo $undefined;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
// a normal comment
6+
/* a normal block comment */
7+
8+
/** a normal doc comment */
9+
echo 'Hello!';
10+
11+
// @phpstan-type Foo = int
12+
/* @phpstan-param int $x */
13+
14+
/** @phpstan-return int */
15+
echo 'World!';
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright (c) 2018-2026 Andreas Möller
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE.md file that was distributed with this source code.
10+
*
11+
* @see https://github.com/ergebnis/phpstan-rules
12+
*/
13+
14+
namespace Ergebnis\PHPStan\Rules\Test\Integration\PHPStan;
15+
16+
use Ergebnis\PHPStan\Rules\PHPStan;
17+
use Ergebnis\PHPStan\Rules\Test;
18+
use PHPStan\Rules;
19+
use PHPStan\Testing;
20+
21+
/**
22+
* @covers \Ergebnis\PHPStan\Rules\PHPStan\NoPhpstanIgnoreRule
23+
*
24+
* @uses \Ergebnis\PHPStan\Rules\ErrorIdentifier
25+
*
26+
* @extends Testing\RuleTestCase<PHPStan\NoPhpstanIgnoreRule>
27+
*/
28+
final class NoPhpStanIgnoreRuleTest extends Testing\RuleTestCase
29+
{
30+
use Test\Util\Helper;
31+
32+
public function testNoIgnoreRule(): void
33+
{
34+
$this->analyse(
35+
self::phpFilesIn(__DIR__ . '/../../Fixture/PHPStan/NoIgnoreRule'),
36+
[
37+
[
38+
'Errors reported by phpstan/phpstan should not be ignored via "@phpstan-ignore", fix the error or use the baseline instead.',
39+
5,
40+
],
41+
[
42+
'No error with identifier variable.undefined is reported on line 6.',
43+
6,
44+
],
45+
[
46+
'Errors reported by phpstan/phpstan should not be ignored via "@phpstan-ignore", fix the error or use the baseline instead.',
47+
8,
48+
],
49+
[
50+
'No error with identifier variable.undefined is reported on line 9.',
51+
9,
52+
],
53+
[
54+
'Errors reported by phpstan/phpstan should not be ignored via "@phpstan-ignore", fix the error or use the baseline instead.',
55+
11,
56+
],
57+
[
58+
'No error with identifier variable.undefined is reported on line 12.',
59+
12,
60+
],
61+
[
62+
'Errors reported by phpstan/phpstan should not be ignored via "@phpstan-ignore", fix the error or use the baseline instead.',
63+
15,
64+
],
65+
[
66+
'No error with identifier variable.undefined is reported on line 17.',
67+
17,
68+
],
69+
[
70+
'Errors reported by phpstan/phpstan should not be ignored via "@phpstan-ignore-line", fix the error or use the baseline instead.',
71+
19,
72+
],
73+
[
74+
'No error to ignore is reported on line 19.',
75+
19,
76+
],
77+
[
78+
'Errors reported by phpstan/phpstan should not be ignored via "@phpstan-ignore-line", fix the error or use the baseline instead.',
79+
21,
80+
],
81+
[
82+
'No error to ignore is reported on line 21.',
83+
21,
84+
],
85+
[
86+
'Errors reported by phpstan/phpstan should not be ignored via "@phpstan-ignore-line", fix the error or use the baseline instead.',
87+
25,
88+
],
89+
[
90+
'No error to ignore is reported on line 25.',
91+
25,
92+
],
93+
[
94+
'Errors reported by phpstan/phpstan should not be ignored via "@phpstan-ignore-next-line", fix the error or use the baseline instead.',
95+
27,
96+
],
97+
[
98+
'No error to ignore is reported on line 28.',
99+
28,
100+
],
101+
[
102+
'Errors reported by phpstan/phpstan should not be ignored via "@phpstan-ignore-next-line", fix the error or use the baseline instead.',
103+
30,
104+
],
105+
[
106+
'No error to ignore is reported on line 31.',
107+
31,
108+
],
109+
[
110+
'Errors reported by phpstan/phpstan should not be ignored via "@phpstan-ignore-next-line", fix the error or use the baseline instead.',
111+
33,
112+
],
113+
[
114+
'No error to ignore is reported on line 34.',
115+
34,
116+
],
117+
],
118+
);
119+
}
120+
121+
protected function getRule(): Rules\Rule
122+
{
123+
return new PHPStan\NoPhpstanIgnoreRule();
124+
}
125+
}

test/Unit/ErrorIdentifierTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,13 @@ public function testNoParameterWithNullableTypeDeclarationReturnsErrorIdentifier
140140
self::assertSame('ergebnis.noParameterWithNullableTypeDeclaration', $errorIdentifier->toString());
141141
}
142142

143+
public function testNoPhpstanIgnoreReturnsErrorIdentifier(): void
144+
{
145+
$errorIdentifier = ErrorIdentifier::noPhpstanIgnore();
146+
147+
self::assertSame('ergebnis.noPhpstanIgnore', $errorIdentifier->toString());
148+
}
149+
143150
public function testNoReturnByReferenceReturnsErrorIdentifier(): void
144151
{
145152
$errorIdentifier = ErrorIdentifier::noReturnByReference();

0 commit comments

Comments
 (0)