From 4f382838b5d6eafa0ff22939f48386b0f8bfe0f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Wed, 28 Oct 2026 09:24:04 +0000 Subject: [PATCH] Invalid Doctrine tag with backslash: return InvalidTagValueNode parse() threw ParserException for an invalid @\Foo(...) tag. Other tags already get InvalidTagValueNode in parseTagValue(). Amp-Thread-ID: https://ampcode.com/threads/T-01a17930-0285-73c5-a957-4325c77aa1b2 --- src/Parser/PhpDocParser.php | 12 ++++++++- tests/PHPStan/Parser/PhpDocParserTest.php | 31 +++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php index f6734225..29fb4be9 100644 --- a/src/Parser/PhpDocParser.php +++ b/src/Parser/PhpDocParser.php @@ -149,11 +149,21 @@ private function parseChild(TokenIterator $tokens): Ast\PhpDoc\PhpDocChildNode $tagStartLine = $tokens->currentTokenLine(); $tagStartIndex = $tokens->currentTokenIndex(); + try { + $tokens->pushSavePoint(); + $tagValue = $this->parseDoctrineTagValue($tokens, $tag); + $tokens->dropSavePoint(); + + } catch (ParserException $e) { + $tokens->rollback(); + $tagValue = new Ast\PhpDoc\InvalidTagValueNode($this->parseOptionalDescription($tokens, false), $e); + } + return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocTagNode( $tag, $this->enrichWithAttributes( $tokens, - $this->parseDoctrineTagValue($tokens, $tag), + $tagValue, $tagStartLine, $tagStartIndex, ), diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php index 238d4a38..95d045c5 100644 --- a/tests/PHPStan/Parser/PhpDocParserTest.php +++ b/tests/PHPStan/Parser/PhpDocParserTest.php @@ -6386,6 +6386,37 @@ public function provideTagsWithBackslash(): Iterator ), ]), ]; + + yield [ + 'Invalid Doctrine tag with backslash keeps the next tags', + "/**\n * @\\ORM\\Table('user')\n * @param int \$a\n */", + new PhpDocNode([ + new PhpDocTagNode( + '@\\ORM\\Table', + new InvalidTagValueNode( + "('user')", + new ParserException( + "'user'", + Lexer::TOKEN_SINGLE_QUOTED_STRING, + 19, + Lexer::TOKEN_DOUBLE_QUOTED_STRING, + null, + 2, + ), + ), + ), + new PhpDocTagNode( + '@param', + new ParamTagValueNode( + new IdentifierTypeNode('int'), + false, + '$a', + '', + false, + ), + ), + ]), + ]; } public function provideSelfOutTagsData(): Iterator