@@ -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