diff --git a/src/Parser/RichParser.php b/src/Parser/RichParser.php index f7ef05a228..42fbd0c13f 100644 --- a/src/Parser/RichParser.php +++ b/src/Parser/RichParser.php @@ -14,10 +14,8 @@ use PHPStan\ShouldNotHappenException; use function array_filter; use function array_pop; -use function array_values; use function count; use function implode; -use function in_array; use function is_string; use function preg_match_all; use function sprintf; @@ -278,8 +276,19 @@ private function getLinesToIgnoreForTokenByIgnoreComment( private function parseIdentifiers(string $text, int $ignorePos): array { $text = substr($text, $ignorePos + strlen('@phpstan-ignore')); - $tokens = $this->ignoreLexer->tokenize($text); - $tokens = array_values(array_filter($tokens, static fn (array $token) => !in_array($token[IgnoreLexer::TYPE_OFFSET], [IgnoreLexer::TOKEN_WHITESPACE, IgnoreLexer::TOKEN_EOL], true))); + $originalTokens = $this->ignoreLexer->tokenize($text); + $tokens = []; + + foreach ($originalTokens as $originalToken) { + if ($originalToken[IgnoreLexer::TYPE_OFFSET] === IgnoreLexer::TOKEN_WHITESPACE) { + continue; + } + if ($originalToken[IgnoreLexer::TYPE_OFFSET] === IgnoreLexer::TOKEN_EOL) { + break; + } + $tokens[] = $originalToken; + } + $c = count($tokens); $identifiers = []; @@ -321,6 +330,10 @@ private function parseIdentifiers(string $text, int $ignorePos): array $parenthesisStack[] = $tokenLine; } + if (isset($tokens[$c - 1]) && $tokens[$c - 1][IgnoreLexer::TYPE_OFFSET] === IgnoreLexer::TOKEN_COMMA) { + throw new IgnoreParseException('Unexpected trailing comma (,)', $tokens[$c - 1][IgnoreLexer::LINE_OFFSET]); + } + if (count($parenthesisStack) > 0) { throw new IgnoreParseException('Unclosed opening parenthesis "(" without closing parenthesis ")"', $parenthesisStack[count($parenthesisStack) - 1]); } diff --git a/tests/PHPStan/Parser/RichParserTest.php b/tests/PHPStan/Parser/RichParserTest.php index 08a15b480b..75c9d28ba0 100644 --- a/tests/PHPStan/Parser/RichParserTest.php +++ b/tests/PHPStan/Parser/RichParserTest.php @@ -221,18 +221,6 @@ public function dataLinesToIgnore(): iterable 3 => ['test'], ], ]; - - yield [ - ' ['return.ref', 'return.non'], - ], - ]; } /** @@ -245,8 +233,8 @@ public function testLinesToIgnore(string $code, array $expectedLines): void $parser = self::getContainer()->getService('currentPhpVersionRichParser'); $ast = $parser->parseString($code); $lines = $ast[0]->getAttribute('linesToIgnore'); - $this->assertSame($expectedLines, $lines); $this->assertNull($ast[0]->getAttribute('linesToIgnoreParseErrors')); + $this->assertSame($expectedLines, $lines); } public function dataLinesToIgnoreParseErrors(): iterable @@ -263,6 +251,50 @@ public function dataLinesToIgnoreParseErrors(): iterable ], ]; + yield [ + ' ['Unexpected trailing comma (,)'], + ], + ]; + + yield [ + ' ['Missing identifier'], + ], + ]; + + yield [ + ' ['Unexpected trailing comma (,)'], + ], + ]; + + yield [ + ' ['Unexpected trailing comma (,)'], + ], + ]; + + yield [ + ' ['First token is not an identifier'], + ], + ]; + yield [ '