Skip to content

Commit 8ca2c01

Browse files
committed
Support multiple line descriptions for all tags
1 parent c830404 commit 8ca2c01

File tree

2 files changed

+75
-20
lines changed

2 files changed

+75
-20
lines changed

src/Parser/PhpDocParser.php

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,38 @@ private function parseChild(TokenIterator $tokens): Ast\PhpDoc\PhpDocChildNode
6060

6161
private function parseText(TokenIterator $tokens): Ast\PhpDoc\PhpDocTextNode
6262
{
63-
$text = $tokens->joinUntil(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END);
64-
$text = rtrim($text, " \t"); // the trimmed characters MUST match Lexer::TOKEN_HORIZONTAL_WS
63+
$text = '';
64+
while (true) {
65+
// If we received a Lexer::TOKEN_PHPDOC_EOL, exit early to prevent
66+
// them from being processed.
67+
if ($tokens->currentTokenType() === Lexer::TOKEN_PHPDOC_EOL) {
68+
break;
69+
}
70+
$text .= $tokens->joinUntil(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END);
71+
$text = rtrim($text, " \t");
72+
73+
// If we joined until TOKEN_PHPDOC_EOL, peak at the next tokens to see
74+
// if we have a multiline string to join.
75+
if ($tokens->currentTokenType() !== Lexer::TOKEN_PHPDOC_EOL) {
76+
break;
77+
}
78+
79+
// Peek at the next token to determine if it is more text that needs
80+
// to be combined.
81+
$tokens->pushSavePoint();
82+
$tokens->next();
83+
if ($tokens->currentTokenType() === Lexer::TOKEN_PHPDOC_EOL) {
84+
$tokens->next();
85+
}
86+
if ($tokens->currentTokenType() !== Lexer::TOKEN_IDENTIFIER) {
87+
$tokens->rollback();
88+
break;
89+
}
90+
91+
// There's more text on a new line, ensure spacing.
92+
$text .= ' ';
93+
}
94+
$text = trim($text, " \t");
6595

6696
return new Ast\PhpDoc\PhpDocTextNode($text);
6797
}
@@ -165,18 +195,7 @@ private function parseThrowsTagValue(TokenIterator $tokens): Ast\PhpDoc\ThrowsTa
165195

166196
private function parseDeprecatedTagValue(TokenIterator $tokens): Ast\PhpDoc\DeprecatedTagValueNode
167197
{
168-
$description = '';
169-
while ($tokens->currentTokenType() !== Lexer::TOKEN_CLOSE_PHPDOC) {
170-
$description .= $tokens->joinUntil(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END);
171-
$description = rtrim($description, " \t");
172-
if ($tokens->currentTokenType() !== Lexer::TOKEN_PHPDOC_EOL) {
173-
break;
174-
}
175-
// There's more text on a new line, ensure spacing.
176-
$description .= ' ';
177-
$tokens->next();
178-
}
179-
$description = rtrim($description, " \t");
198+
$description = $this->parseOptionalDescription($tokens);
180199
return new Ast\PhpDoc\DeprecatedTagValueNode($description);
181200
}
182201

tests/PHPStan/Parser/PhpDocParserTest.php

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,41 @@ public function provideDeprecatedTagsData(): \Iterator
992992
),
993993
]),
994994
];
995+
yield [
996+
'OK with two simple description with break',
997+
'/** @deprecated text first
998+
*
999+
* @deprecated text second
1000+
*/',
1001+
new PhpDocNode([
1002+
new PhpDocTagNode(
1003+
'@deprecated',
1004+
new DeprecatedTagValueNode('text first')
1005+
),
1006+
new PhpDocTextNode(''),
1007+
new PhpDocTagNode(
1008+
'@deprecated',
1009+
new DeprecatedTagValueNode('text second')
1010+
),
1011+
]),
1012+
];
1013+
1014+
yield [
1015+
'OK with two simple description without break',
1016+
'/** @deprecated text first
1017+
* @deprecated text second
1018+
*/',
1019+
new PhpDocNode([
1020+
new PhpDocTagNode(
1021+
'@deprecated',
1022+
new DeprecatedTagValueNode('text first')
1023+
),
1024+
new PhpDocTagNode(
1025+
'@deprecated',
1026+
new DeprecatedTagValueNode('text second')
1027+
),
1028+
]),
1029+
];
9951030

9961031
yield [
9971032
'OK with long descriptions',
@@ -1541,10 +1576,9 @@ public function provideMultiLinePhpDocData(): array
15411576
new IdentifierTypeNode('Foo'),
15421577
false,
15431578
'$foo',
1544-
'1st multi world description'
1579+
'1st multi world description some text in the middle'
15451580
)
15461581
),
1547-
new PhpDocTextNode('some text in the middle'),
15481582
new PhpDocTagNode(
15491583
'@param',
15501584
new ParamTagValueNode(
@@ -1561,15 +1595,16 @@ public function provideMultiLinePhpDocData(): array
15611595
'/**
15621596
*
15631597
*
1564-
* @param Foo $foo 1st multi world description
1598+
* @param Foo $foo 1st multi world description with empty lines
15651599
*
15661600
*
15671601
* some text in the middle
15681602
*
15691603
*
1570-
* @param Bar $bar 2nd multi world description
1604+
* @param Bar $bar 2nd multi world description with empty lines
15711605
*
15721606
*
1607+
* test
15731608
*/',
15741609
new PhpDocNode([
15751610
new PhpDocTextNode(''),
@@ -1580,7 +1615,7 @@ public function provideMultiLinePhpDocData(): array
15801615
new IdentifierTypeNode('Foo'),
15811616
false,
15821617
'$foo',
1583-
'1st multi world description'
1618+
'1st multi world description with empty lines'
15841619
)
15851620
),
15861621
new PhpDocTextNode(''),
@@ -1594,11 +1629,12 @@ public function provideMultiLinePhpDocData(): array
15941629
new IdentifierTypeNode('Bar'),
15951630
false,
15961631
'$bar',
1597-
'2nd multi world description'
1632+
'2nd multi world description with empty lines'
15981633
)
15991634
),
16001635
new PhpDocTextNode(''),
16011636
new PhpDocTextNode(''),
1637+
new PhpDocTextNode('test'),
16021638
]),
16031639
],
16041640
[

0 commit comments

Comments
 (0)