From f4dbac09bc7a49628ce42f631da7ee6fba727ae4 Mon Sep 17 00:00:00 2001 From: Gert de Pagter Date: Thu, 27 Feb 2020 14:23:22 +0100 Subject: [PATCH 1/2] Check for invalid phpstan doc comments --- .../PhpDoc/InvalidPhpDocTagValueRule.php | 20 +++++++++++++++++++ .../PhpDoc/InvalidPhpDocTagValueRuleTest.php | 8 ++++++++ .../Rules/PhpDoc/data/invalid-phpdoc.php | 11 ++++++++++ 3 files changed, 39 insertions(+) diff --git a/src/Rules/PhpDoc/InvalidPhpDocTagValueRule.php b/src/Rules/PhpDoc/InvalidPhpDocTagValueRule.php index 74d43327e8..b457d8d4cf 100644 --- a/src/Rules/PhpDoc/InvalidPhpDocTagValueRule.php +++ b/src/Rules/PhpDoc/InvalidPhpDocTagValueRule.php @@ -16,6 +16,18 @@ class InvalidPhpDocTagValueRule implements \PHPStan\Rules\Rule { + private const POSSIBLE_PHPSTAN_TAGS = [ + '@phpstan-param', + '@phpstan-var', + '@phpstan-template', + '@phpstan-extends', + '@phpstan-implements', + '@phpstan-use', + '@phpstan-template', + '@phpstan-template-covariant', + '@phpstan-return', + ]; + /** @var Lexer */ private $phpDocLexer; @@ -57,6 +69,14 @@ public function processNode(Node $node, Scope $scope): array $errors = []; foreach ($phpDocNode->getTags() as $phpDocTag) { + if (strpos($phpDocTag->name, '@phpstan-') === 0 + && !in_array($phpDocTag->name, self::POSSIBLE_PHPSTAN_TAGS, true) + ) { + $errors[] = RuleErrorBuilder::message(sprintf( + 'Encountered unknown tag that had the phpstan prefix: %s', + $phpDocTag->name + ))->build(); + } if (!($phpDocTag->value instanceof InvalidTagValueNode)) { continue; } diff --git a/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleTest.php b/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleTest.php index 0d110c39fb..c6a6193efa 100644 --- a/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleTest.php +++ b/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleTest.php @@ -82,6 +82,14 @@ public function testRule(): void 'PHPDoc tag @var has invalid value ((Foo|Bar): Unexpected token "*/", expected \')\' at offset 18', 62, ], + [ + 'Encountered unknown tag that had the phpstan prefix: @phpstan-extens', + 67, + ], + [ + 'Encountered unknown tag that had the phpstan prefix: @phpstan-pararm', + 74, + ], ]); } diff --git a/tests/PHPStan/Rules/PhpDoc/data/invalid-phpdoc.php b/tests/PHPStan/Rules/PhpDoc/data/invalid-phpdoc.php index 353078efd4..f1dc4b1449 100644 --- a/tests/PHPStan/Rules/PhpDoc/data/invalid-phpdoc.php +++ b/tests/PHPStan/Rules/PhpDoc/data/invalid-phpdoc.php @@ -62,3 +62,14 @@ class Baz private $barProperty; } + +/** @phpstan-extens Baz */ +class Boo extends Baz +{ + /** + * @phpstan-template T + * @phpstan-pararm class-string $a + * @phpstan-return T + */ + function foo(string $a){} +} From f786f98c519f3eef796dcd19ac5062faaae830c6 Mon Sep 17 00:00:00 2001 From: Gert de Pagter Date: Thu, 27 Feb 2020 18:30:37 +0100 Subject: [PATCH 2/2] Extract into its own rule --- conf/config.level2.neon | 1 + src/Rules/PhpDoc/InvalidPHPStanDocTagRule.php | 85 +++++++++++++++++++ .../PhpDoc/InvalidPhpDocTagValueRule.php | 20 ----- .../PhpDoc/InvalidPHPStanDocTagRuleTest.php | 36 ++++++++ .../PhpDoc/InvalidPhpDocTagValueRuleTest.php | 8 -- .../Rules/PhpDoc/data/invalid-phpdoc.php | 11 --- .../Rules/PhpDoc/data/invalid-phpstan-doc.php | 15 ++++ 7 files changed, 137 insertions(+), 39 deletions(-) create mode 100644 src/Rules/PhpDoc/InvalidPHPStanDocTagRule.php create mode 100644 tests/PHPStan/Rules/PhpDoc/InvalidPHPStanDocTagRuleTest.php create mode 100644 tests/PHPStan/Rules/PhpDoc/data/invalid-phpstan-doc.php diff --git a/conf/config.level2.neon b/conf/config.level2.neon index 31d4874741..b44a73faa7 100644 --- a/conf/config.level2.neon +++ b/conf/config.level2.neon @@ -28,6 +28,7 @@ rules: - PHPStan\Rules\PhpDoc\IncompatiblePhpDocTypeRule - PHPStan\Rules\PhpDoc\IncompatiblePropertyPhpDocTypeRule - PHPStan\Rules\PhpDoc\InvalidPhpDocTagValueRule + - PHPStan\Rules\PhpDoc\InvalidPHPStanDocTagRule - PHPStan\Rules\PhpDoc\InvalidThrowsPhpDocValueRule - PHPStan\Rules\PhpDoc\WrongVariableNameInVarTagRule diff --git a/src/Rules/PhpDoc/InvalidPHPStanDocTagRule.php b/src/Rules/PhpDoc/InvalidPHPStanDocTagRule.php new file mode 100644 index 0000000000..b53b00cc37 --- /dev/null +++ b/src/Rules/PhpDoc/InvalidPHPStanDocTagRule.php @@ -0,0 +1,85 @@ + + */ +class InvalidPHPStanDocTagRule implements \PHPStan\Rules\Rule +{ + + private const POSSIBLE_PHPSTAN_TAGS = [ + '@phpstan-param', + '@phpstan-var', + '@phpstan-template', + '@phpstan-extends', + '@phpstan-implements', + '@phpstan-use', + '@phpstan-template', + '@phpstan-template-covariant', + '@phpstan-return', + ]; + + /** @var Lexer */ + private $phpDocLexer; + + /** @var PhpDocParser */ + private $phpDocParser; + + public function __construct(Lexer $phpDocLexer, PhpDocParser $phpDocParser) + { + $this->phpDocLexer = $phpDocLexer; + $this->phpDocParser = $phpDocParser; + } + + public function getNodeType(): string + { + return \PhpParser\Node::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if ( + !$node instanceof Node\Stmt\ClassLike + && !$node instanceof Node\FunctionLike + && !$node instanceof Node\Stmt\Foreach_ + && !$node instanceof Node\Stmt\Property + && !$node instanceof Node\Expr\Assign + && !$node instanceof Node\Expr\AssignRef + ) { + return []; + } + + $docComment = $node->getDocComment(); + if ($docComment === null) { + return []; + } + $phpDocString = $docComment->getText(); + $tokens = new TokenIterator($this->phpDocLexer->tokenize($phpDocString)); + $phpDocNode = $this->phpDocParser->parse($tokens); + + $errors = []; + foreach ($phpDocNode->getTags() as $phpDocTag) { + if (strpos($phpDocTag->name, '@phpstan-') !== 0 + || in_array($phpDocTag->name, self::POSSIBLE_PHPSTAN_TAGS, true) + ) { + continue; + } + + $errors[] = RuleErrorBuilder::message(sprintf( + 'Encountered unknown tag that had the phpstan prefix: %s', + $phpDocTag->name + ))->build(); + } + + return $errors; + } + +} diff --git a/src/Rules/PhpDoc/InvalidPhpDocTagValueRule.php b/src/Rules/PhpDoc/InvalidPhpDocTagValueRule.php index b457d8d4cf..74d43327e8 100644 --- a/src/Rules/PhpDoc/InvalidPhpDocTagValueRule.php +++ b/src/Rules/PhpDoc/InvalidPhpDocTagValueRule.php @@ -16,18 +16,6 @@ class InvalidPhpDocTagValueRule implements \PHPStan\Rules\Rule { - private const POSSIBLE_PHPSTAN_TAGS = [ - '@phpstan-param', - '@phpstan-var', - '@phpstan-template', - '@phpstan-extends', - '@phpstan-implements', - '@phpstan-use', - '@phpstan-template', - '@phpstan-template-covariant', - '@phpstan-return', - ]; - /** @var Lexer */ private $phpDocLexer; @@ -69,14 +57,6 @@ public function processNode(Node $node, Scope $scope): array $errors = []; foreach ($phpDocNode->getTags() as $phpDocTag) { - if (strpos($phpDocTag->name, '@phpstan-') === 0 - && !in_array($phpDocTag->name, self::POSSIBLE_PHPSTAN_TAGS, true) - ) { - $errors[] = RuleErrorBuilder::message(sprintf( - 'Encountered unknown tag that had the phpstan prefix: %s', - $phpDocTag->name - ))->build(); - } if (!($phpDocTag->value instanceof InvalidTagValueNode)) { continue; } diff --git a/tests/PHPStan/Rules/PhpDoc/InvalidPHPStanDocTagRuleTest.php b/tests/PHPStan/Rules/PhpDoc/InvalidPHPStanDocTagRuleTest.php new file mode 100644 index 0000000000..57cdbb0903 --- /dev/null +++ b/tests/PHPStan/Rules/PhpDoc/InvalidPHPStanDocTagRuleTest.php @@ -0,0 +1,36 @@ + + */ +class InvalidPHPStanDocTagRuleTest extends \PHPStan\Testing\RuleTestCase +{ + + protected function getRule(): \PHPStan\Rules\Rule + { + return new InvalidPHPStanDocTagRule( + self::getContainer()->getByType(Lexer::class), + self::getContainer()->getByType(PhpDocParser::class) + ); + } + + public function testRule(): void + { + $this->analyse([__DIR__ . '/data/invalid-phpstan-doc.php'], [ + [ + 'Encountered unknown tag that had the phpstan prefix: @phpstan-extens', + 7, + ], + [ + 'Encountered unknown tag that had the phpstan prefix: @phpstan-pararm', + 14, + ], + ]); + } + +} diff --git a/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleTest.php b/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleTest.php index c6a6193efa..0d110c39fb 100644 --- a/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleTest.php +++ b/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleTest.php @@ -82,14 +82,6 @@ public function testRule(): void 'PHPDoc tag @var has invalid value ((Foo|Bar): Unexpected token "*/", expected \')\' at offset 18', 62, ], - [ - 'Encountered unknown tag that had the phpstan prefix: @phpstan-extens', - 67, - ], - [ - 'Encountered unknown tag that had the phpstan prefix: @phpstan-pararm', - 74, - ], ]); } diff --git a/tests/PHPStan/Rules/PhpDoc/data/invalid-phpdoc.php b/tests/PHPStan/Rules/PhpDoc/data/invalid-phpdoc.php index f1dc4b1449..353078efd4 100644 --- a/tests/PHPStan/Rules/PhpDoc/data/invalid-phpdoc.php +++ b/tests/PHPStan/Rules/PhpDoc/data/invalid-phpdoc.php @@ -62,14 +62,3 @@ class Baz private $barProperty; } - -/** @phpstan-extens Baz */ -class Boo extends Baz -{ - /** - * @phpstan-template T - * @phpstan-pararm class-string $a - * @phpstan-return T - */ - function foo(string $a){} -} diff --git a/tests/PHPStan/Rules/PhpDoc/data/invalid-phpstan-doc.php b/tests/PHPStan/Rules/PhpDoc/data/invalid-phpstan-doc.php new file mode 100644 index 0000000000..4b1efb0805 --- /dev/null +++ b/tests/PHPStan/Rules/PhpDoc/data/invalid-phpstan-doc.php @@ -0,0 +1,15 @@ + $a + * @phpstan-return T + */ + function foo(string $a){} +}