From a04e0be832900749b5b4ba22e2de21db8bfa09a0 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 9 May 2023 15:14:21 +0200 Subject: [PATCH] InvalidPhpDocTagValueRule - backward compatibility of the error message without bleedingEdge --- conf/bleedingEdge.neon | 1 + conf/config.level2.neon | 1 + conf/config.neon | 2 + src/PhpDoc/StubValidator.php | 1 + .../PhpDoc/InvalidPhpDocTagValueRule.php | 15 +- .../ErrorFormatter/data/unixBaseline.neon | 2 +- .../ErrorFormatter/data/windowsBaseline.neon | 2 +- ...idPhpDocTagValueRuleNoBleedingEdgeTest.php | 146 ++++++++++++++++++ .../PhpDoc/InvalidPhpDocTagValueRuleTest.php | 1 + 9 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleNoBleedingEdgeTest.php diff --git a/conf/bleedingEdge.neon b/conf/bleedingEdge.neon index 6d4d61af81..441b5a4114 100644 --- a/conf/bleedingEdge.neon +++ b/conf/bleedingEdge.neon @@ -37,3 +37,4 @@ parameters: propertyVariance: true genericPrototypeMessage: true stricterFunctionMap: true + invalidPhpDocTagLine: true diff --git a/conf/config.level2.neon b/conf/config.level2.neon index a54338be33..1d30ec755f 100644 --- a/conf/config.level2.neon +++ b/conf/config.level2.neon @@ -81,6 +81,7 @@ services: class: PHPStan\Rules\PhpDoc\InvalidPhpDocTagValueRule arguments: checkAllInvalidPhpDocs: %featureToggles.allInvalidPhpDocs% + invalidPhpDocTagLine: %featureToggles.invalidPhpDocTagLine% tags: - phpstan.rules.rule - diff --git a/conf/config.neon b/conf/config.neon index 2bdb6ef0e0..c55e49af49 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -67,6 +67,7 @@ parameters: propertyVariance: false genericPrototypeMessage: false stricterFunctionMap: false + invalidPhpDocTagLine: false fileExtensions: - php checkAdvancedIsset: false @@ -304,6 +305,7 @@ parametersSchema: propertyVariance: bool() genericPrototypeMessage: bool() stricterFunctionMap: bool() + invalidPhpDocTagLine: bool() ]) fileExtensions: listOf(string()) checkAdvancedIsset: bool() diff --git a/src/PhpDoc/StubValidator.php b/src/PhpDoc/StubValidator.php index e26d44d1df..25f8abe4b7 100644 --- a/src/PhpDoc/StubValidator.php +++ b/src/PhpDoc/StubValidator.php @@ -180,6 +180,7 @@ private function getRuleRegistry(Container $container): RuleRegistry $container->getByType(Lexer::class), $container->getByType(PhpDocParser::class), $container->getParameter('featureToggles')['allInvalidPhpDocs'], + $container->getParameter('featureToggles')['invalidPhpDocTagLine'], ), new InvalidThrowsPhpDocValueRule($fileTypeMapper), diff --git a/src/Rules/PhpDoc/InvalidPhpDocTagValueRule.php b/src/Rules/PhpDoc/InvalidPhpDocTagValueRule.php index 2e0e5d5755..e3c97da0c9 100644 --- a/src/Rules/PhpDoc/InvalidPhpDocTagValueRule.php +++ b/src/Rules/PhpDoc/InvalidPhpDocTagValueRule.php @@ -2,6 +2,7 @@ namespace PHPStan\Rules\PhpDoc; +use Nette\Utils\Strings; use PhpParser\Node; use PHPStan\Analyser\Scope; use PHPStan\Node\VirtualNode; @@ -26,6 +27,7 @@ public function __construct( private Lexer $phpDocLexer, private PhpDocParser $phpDocParser, private bool $checkAllInvalidPhpDocs, + private bool $invalidPhpDocTagLine, ) { } @@ -86,7 +88,7 @@ public function processNode(Node $node, Scope $scope): array 'PHPDoc tag %s %s has invalid value: %s', $phpDocTag->name, $phpDocTag->value->alias, - $phpDocTag->value->type->getException()->getMessage(), + $this->trimExceptionMessage($phpDocTag->value->type->getException()->getMessage()), ))->build(); continue; @@ -98,11 +100,20 @@ public function processNode(Node $node, Scope $scope): array 'PHPDoc tag %s has invalid value (%s): %s', $phpDocTag->name, $phpDocTag->value->value, - $phpDocTag->value->exception->getMessage(), + $this->trimExceptionMessage($phpDocTag->value->exception->getMessage()), ))->build(); } return $errors; } + private function trimExceptionMessage(string $message): string + { + if ($this->invalidPhpDocTagLine) { + return $message; + } + + return Strings::replace($message, '~( on line \d+)$~', ''); + } + } diff --git a/tests/PHPStan/Command/ErrorFormatter/data/unixBaseline.neon b/tests/PHPStan/Command/ErrorFormatter/data/unixBaseline.neon index d6148567e1..ca7c8f9c2c 100644 --- a/tests/PHPStan/Command/ErrorFormatter/data/unixBaseline.neon +++ b/tests/PHPStan/Command/ErrorFormatter/data/unixBaseline.neon @@ -11,7 +11,7 @@ parameters: path: WindowsNewlines.php - - message: "#^PHPDoc tag @param has invalid value \\(\\)\\: Unexpected token \"\\\\n\\\\t \\* \", expected type at offset 113 on line 4$#" + message: "#^PHPDoc tag @param has invalid value \\(\\)\\: Unexpected token \"\\\\n\\\\t \\* \", expected type at offset 113$#" count: 1 path: WindowsNewlines.php diff --git a/tests/PHPStan/Command/ErrorFormatter/data/windowsBaseline.neon b/tests/PHPStan/Command/ErrorFormatter/data/windowsBaseline.neon index c7fd53f684..3bfe998b6e 100644 --- a/tests/PHPStan/Command/ErrorFormatter/data/windowsBaseline.neon +++ b/tests/PHPStan/Command/ErrorFormatter/data/windowsBaseline.neon @@ -11,7 +11,7 @@ parameters: path: UnixNewlines.php - - message: "#^PHPDoc tag @param has invalid value \\(\\)\\: Unexpected token \"\\\\r\\\\n\\\\t \\* \", expected type at offset 110 on line 4$#" + message: "#^PHPDoc tag @param has invalid value \\(\\)\\: Unexpected token \"\\\\r\\\\n\\\\t \\* \", expected type at offset 110$#" count: 1 path: UnixNewlines.php diff --git a/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleNoBleedingEdgeTest.php b/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleNoBleedingEdgeTest.php new file mode 100644 index 0000000000..f8158edb1e --- /dev/null +++ b/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleNoBleedingEdgeTest.php @@ -0,0 +1,146 @@ + + */ +class InvalidPhpDocTagValueRuleNoBleedingEdgeTest extends RuleTestCase +{ + + private bool $checkAllInvalidPhpDocs; + + protected function getRule(): Rule + { + return new InvalidPhpDocTagValueRule( + self::getContainer()->getByType(Lexer::class), + self::getContainer()->getByType(PhpDocParser::class), + $this->checkAllInvalidPhpDocs, + false, + ); + } + + public function dataRule(): iterable + { + $errors = [ + [ + 'PHPDoc tag @param has invalid value (): Unexpected token "\n * ", expected type at offset 13', + 25, + ], + [ + 'PHPDoc tag @param has invalid value (A & B | C $paramNameA): Unexpected token "|", expected variable at offset 72', + 25, + ], + [ + 'PHPDoc tag @param has invalid value ((A & B $paramNameB): Unexpected token "$paramNameB", expected \')\' at offset 105', + 25, + ], + [ + 'PHPDoc tag @param has invalid value (~A & B $paramNameC): Unexpected token "~A", expected type at offset 127', + 25, + ], + [ + 'PHPDoc tag @var has invalid value (): Unexpected token "\n * ", expected type at offset 156', + 25, + ], + [ + 'PHPDoc tag @var has invalid value ($invalid): Unexpected token "$invalid", expected type at offset 165', + 25, + ], + [ + 'PHPDoc tag @var has invalid value ($invalid Foo): Unexpected token "$invalid", expected type at offset 182', + 25, + ], + [ + 'PHPDoc tag @return has invalid value (): Unexpected token "\n * ", expected type at offset 208', + 25, + ], + [ + 'PHPDoc tag @return has invalid value ([int, string]): Unexpected token "[", expected type at offset 220', + 25, + ], + [ + 'PHPDoc tag @return has invalid value (A & B | C): Unexpected token "|", expected TOKEN_OTHER at offset 251', + 25, + ], + [ + 'PHPDoc tag @var has invalid value (\\\Foo|\Bar $test): Unexpected token "\\\\\\\Foo|\\\Bar", expected type at offset 9', + 29, + ], + [ + 'PHPDoc tag @var has invalid value ((Foo|Bar): Unexpected token "*/", expected \')\' at offset 18', + 62, + ], + [ + 'PHPDoc tag @throws has invalid value ((\Exception): Unexpected token "*/", expected \')\' at offset 24', + 72, + ], + [ + 'PHPDoc tag @var has invalid value ((Foo|Bar): Unexpected token "*/", expected \')\' at offset 18', + 81, + ], + [ + 'PHPDoc tag @var has invalid value ((Foo&): Unexpected token "*/", expected type at offset 15', + 89, + ], + [ + 'PHPDoc tag @var has invalid value ((Foo&): Unexpected token "*/", expected type at offset 15', + 92, + ], + ]; + + yield [false, $errors]; + yield [true, array_merge($errors, [ + [ + 'PHPDoc tag @var has invalid value ((Foo&): Unexpected token "*/", expected type at offset 15', + 102, + ], + ])]; + } + + /** + * @dataProvider dataRule + * @param list $expectedErrors + */ + public function testRule(bool $checkAllInvalidPhpDocs, array $expectedErrors): void + { + $this->checkAllInvalidPhpDocs = $checkAllInvalidPhpDocs; + $this->analyse([__DIR__ . '/data/invalid-phpdoc.php'], $expectedErrors); + } + + public function testBug4731(): void + { + $this->checkAllInvalidPhpDocs = true; + $this->analyse([__DIR__ . '/data/bug-4731.php'], []); + } + + public function testBug4731WithoutFirstTag(): void + { + $this->checkAllInvalidPhpDocs = true; + $this->analyse([__DIR__ . '/data/bug-4731-no-first-tag.php'], []); + } + + public function testInvalidTypeInTypeAlias(): void + { + $this->checkAllInvalidPhpDocs = true; + $this->analyse([__DIR__ . '/data/invalid-type-type-alias.php'], [ + [ + 'PHPDoc tag @phpstan-type InvalidFoo has invalid value: Unexpected token "{", expected TOKEN_PHPDOC_EOL at offset 65', + 12, + ], + ]); + } + + public static function getAdditionalConfigFiles(): array + { + // reset bleedingEdge + return []; + } + +} diff --git a/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleTest.php b/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleTest.php index 73bcec49a2..fa70287ce6 100644 --- a/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleTest.php +++ b/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleTest.php @@ -22,6 +22,7 @@ protected function getRule(): Rule self::getContainer()->getByType(Lexer::class), self::getContainer()->getByType(PhpDocParser::class), $this->checkAllInvalidPhpDocs, + true, ); }