Skip to content

Commit ec58baf

Browse files
committed
Multiple Doctrine tags on a single line
1 parent 4aa86cc commit ec58baf

File tree

2 files changed

+422
-20
lines changed

2 files changed

+422
-20
lines changed

src/Parser/PhpDocParser.php

+103-9
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,44 @@ public function parse(TokenIterator $tokens): Ast\PhpDoc\PhpDocNode
7878

7979
$children = [];
8080

81-
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
82-
$children[] = $this->parseChild($tokens);
83-
while ($tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL) && !$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
81+
if ($this->parseDoctrineAnnotations) {
82+
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
83+
$lastChild = $this->parseChild($tokens);
84+
$children[] = $lastChild;
85+
while (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
86+
if (
87+
$lastChild instanceof Ast\PhpDoc\PhpDocTagNode
88+
&& (
89+
$lastChild->value instanceof Doctrine\DoctrineTagValueNode
90+
|| $lastChild->value instanceof Ast\PhpDoc\GenericTagValueNode
91+
)
92+
) {
93+
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
94+
if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
95+
break;
96+
}
97+
$lastChild = $this->parseChild($tokens);
98+
$children[] = $lastChild;
99+
continue;
100+
}
101+
102+
if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
103+
break;
104+
}
105+
if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
106+
break;
107+
}
108+
109+
$lastChild = $this->parseChild($tokens);
110+
$children[] = $lastChild;
111+
}
112+
}
113+
} else {
114+
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
84115
$children[] = $this->parseChild($tokens);
116+
while ($tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL) && !$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
117+
$children[] = $this->parseChild($tokens);
118+
}
85119
}
86120
}
87121

@@ -119,6 +153,7 @@ public function parse(TokenIterator $tokens): Ast\PhpDoc\PhpDocNode
119153
}
120154

121155

156+
/** @phpstan-impure */
122157
private function parseChild(TokenIterator $tokens): Ast\PhpDoc\PhpDocChildNode
123158
{
124159
if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG)) {
@@ -202,6 +237,63 @@ private function parseText(TokenIterator $tokens): Ast\PhpDoc\PhpDocTextNode
202237
}
203238

204239

240+
private function parseOptionalDescriptionAfterDoctrineTag(TokenIterator $tokens): string
241+
{
242+
$text = '';
243+
244+
while (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
245+
$text .= $tokens->getSkippedHorizontalWhiteSpaceIfAny() . $tokens->joinUntil(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END);
246+
247+
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
248+
if (!$tokens->isPrecededByHorizontalWhitespace()) {
249+
return trim($text . $this->parseText($tokens)->text, " \t");
250+
}
251+
if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG)) {
252+
$tokens->pushSavePoint();
253+
$child = $this->parseChild($tokens);
254+
if ($child instanceof Ast\PhpDoc\PhpDocTagNode) {
255+
if (
256+
$child->value instanceof Ast\PhpDoc\GenericTagValueNode
257+
|| $child->value instanceof Doctrine\DoctrineTagValueNode
258+
) {
259+
$tokens->rollback();
260+
break;
261+
}
262+
if ($child->value instanceof Ast\PhpDoc\InvalidTagValueNode) {
263+
$tokens->rollback();
264+
$tokens->pushSavePoint();
265+
$tokens->next();
266+
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
267+
$tokens->rollback();
268+
break;
269+
}
270+
$tokens->rollback();
271+
return trim($text . $this->parseText($tokens)->text, " \t");
272+
}
273+
}
274+
275+
$tokens->rollback();
276+
return trim($text . $this->parseText($tokens)->text, " \t");
277+
}
278+
break;
279+
}
280+
281+
$tokens->pushSavePoint();
282+
$tokens->next();
283+
284+
if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END)) {
285+
$tokens->rollback();
286+
break;
287+
}
288+
289+
$tokens->dropSavePoint();
290+
$text .= "\n";
291+
}
292+
293+
return trim($text, " \t");
294+
}
295+
296+
205297
public function parseTag(TokenIterator $tokens): Ast\PhpDoc\PhpDocTagNode
206298
{
207299
$tag = $tokens->currentTokenValue();
@@ -333,15 +425,17 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph
333425
break;
334426

335427
default:
336-
if (
337-
$this->parseDoctrineAnnotations
338-
&& $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)
339-
) {
340-
$tagValue = $this->parseDoctrineTagValue($tokens, $tag);
428+
if ($this->parseDoctrineAnnotations) {
429+
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
430+
$tagValue = $this->parseDoctrineTagValue($tokens, $tag);
431+
} else {
432+
$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescriptionAfterDoctrineTag($tokens));
433+
}
341434
break;
342435
}
343436

344437
$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescription($tokens));
438+
345439
break;
346440
}
347441

@@ -368,7 +462,7 @@ private function parseDoctrineTagValue(TokenIterator $tokens, string $tag): Ast\
368462
$startLine,
369463
$startIndex
370464
),
371-
$this->parseOptionalDescription($tokens)
465+
$this->parseOptionalDescriptionAfterDoctrineTag($tokens)
372466
);
373467
}
374468

0 commit comments

Comments
 (0)