Skip to content

Commit 18d849e

Browse files
authored
Check for invalid phpstan doc comments
1 parent 9395d76 commit 18d849e

File tree

4 files changed

+137
-0
lines changed

4 files changed

+137
-0
lines changed

conf/config.level2.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ rules:
2828
- PHPStan\Rules\PhpDoc\IncompatiblePhpDocTypeRule
2929
- PHPStan\Rules\PhpDoc\IncompatiblePropertyPhpDocTypeRule
3030
- PHPStan\Rules\PhpDoc\InvalidPhpDocTagValueRule
31+
- PHPStan\Rules\PhpDoc\InvalidPHPStanDocTagRule
3132
- PHPStan\Rules\PhpDoc\InvalidThrowsPhpDocValueRule
3233
- PHPStan\Rules\PhpDoc\WrongVariableNameInVarTagRule
3334

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\PhpDoc;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\PhpDocParser\Lexer\Lexer;
8+
use PHPStan\PhpDocParser\Parser\PhpDocParser;
9+
use PHPStan\PhpDocParser\Parser\TokenIterator;
10+
use PHPStan\Rules\RuleErrorBuilder;
11+
12+
/**
13+
* @implements \PHPStan\Rules\Rule<\PhpParser\Node>
14+
*/
15+
class InvalidPHPStanDocTagRule implements \PHPStan\Rules\Rule
16+
{
17+
18+
private const POSSIBLE_PHPSTAN_TAGS = [
19+
'@phpstan-param',
20+
'@phpstan-var',
21+
'@phpstan-template',
22+
'@phpstan-extends',
23+
'@phpstan-implements',
24+
'@phpstan-use',
25+
'@phpstan-template',
26+
'@phpstan-template-covariant',
27+
'@phpstan-return',
28+
];
29+
30+
/** @var Lexer */
31+
private $phpDocLexer;
32+
33+
/** @var PhpDocParser */
34+
private $phpDocParser;
35+
36+
public function __construct(Lexer $phpDocLexer, PhpDocParser $phpDocParser)
37+
{
38+
$this->phpDocLexer = $phpDocLexer;
39+
$this->phpDocParser = $phpDocParser;
40+
}
41+
42+
public function getNodeType(): string
43+
{
44+
return \PhpParser\Node::class;
45+
}
46+
47+
public function processNode(Node $node, Scope $scope): array
48+
{
49+
if (
50+
!$node instanceof Node\Stmt\ClassLike
51+
&& !$node instanceof Node\FunctionLike
52+
&& !$node instanceof Node\Stmt\Foreach_
53+
&& !$node instanceof Node\Stmt\Property
54+
&& !$node instanceof Node\Expr\Assign
55+
&& !$node instanceof Node\Expr\AssignRef
56+
) {
57+
return [];
58+
}
59+
60+
$docComment = $node->getDocComment();
61+
if ($docComment === null) {
62+
return [];
63+
}
64+
$phpDocString = $docComment->getText();
65+
$tokens = new TokenIterator($this->phpDocLexer->tokenize($phpDocString));
66+
$phpDocNode = $this->phpDocParser->parse($tokens);
67+
68+
$errors = [];
69+
foreach ($phpDocNode->getTags() as $phpDocTag) {
70+
if (strpos($phpDocTag->name, '@phpstan-') !== 0
71+
|| in_array($phpDocTag->name, self::POSSIBLE_PHPSTAN_TAGS, true)
72+
) {
73+
continue;
74+
}
75+
76+
$errors[] = RuleErrorBuilder::message(sprintf(
77+
'Encountered unknown tag that had the phpstan prefix: %s',
78+
$phpDocTag->name
79+
))->build();
80+
}
81+
82+
return $errors;
83+
}
84+
85+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\PhpDoc;
4+
5+
use PHPStan\PhpDocParser\Lexer\Lexer;
6+
use PHPStan\PhpDocParser\Parser\PhpDocParser;
7+
8+
/**
9+
* @extends \PHPStan\Testing\RuleTestCase<InvalidPHPStanDocTagRule>
10+
*/
11+
class InvalidPHPStanDocTagRuleTest extends \PHPStan\Testing\RuleTestCase
12+
{
13+
14+
protected function getRule(): \PHPStan\Rules\Rule
15+
{
16+
return new InvalidPHPStanDocTagRule(
17+
self::getContainer()->getByType(Lexer::class),
18+
self::getContainer()->getByType(PhpDocParser::class)
19+
);
20+
}
21+
22+
public function testRule(): void
23+
{
24+
$this->analyse([__DIR__ . '/data/invalid-phpstan-doc.php'], [
25+
[
26+
'Encountered unknown tag that had the phpstan prefix: @phpstan-extens',
27+
7,
28+
],
29+
[
30+
'Encountered unknown tag that had the phpstan prefix: @phpstan-pararm',
31+
14,
32+
],
33+
]);
34+
}
35+
36+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace InvalidPHPStanDoc;
4+
5+
class Baz{}
6+
/** @phpstan-extens Baz */
7+
class Boo extends Baz
8+
{
9+
/**
10+
* @phpstan-template T
11+
* @phpstan-pararm class-string<T> $a
12+
* @phpstan-return T
13+
*/
14+
function foo(string $a){}
15+
}

0 commit comments

Comments
 (0)