77use PhpParser \NodeTraverser ;
88use PhpParser \NodeVisitor \NameResolver ;
99use PhpParser \Token ;
10+ use PHPStan \Analyser \FileAnalyserResult ;
1011use PHPStan \Analyser \Ignore \IgnoreLexer ;
1112use PHPStan \Analyser \Ignore \IgnoreParseException ;
1213use PHPStan \DependencyInjection \Container ;
1314use PHPStan \File \FileReader ;
1415use PHPStan \ShouldNotHappenException ;
1516use function array_filter ;
17+ use function array_key_last ;
1618use function array_map ;
19+ use function array_values ;
1720use function count ;
1821use function implode ;
1922use function in_array ;
3033use const T_DOC_COMMENT ;
3134use const T_WHITESPACE ;
3235
36+ /**
37+ * @phpstan-import-type Identifier from FileAnalyserResult
38+ */
3339final class RichParser implements Parser
3440{
3541
@@ -111,7 +117,7 @@ public function parseString(string $sourceCode): array
111117
112118 /**
113119 * @param Token[] $tokens
114- * @return array{lines: array<int, non-empty-list<string >|null>, errors: array<int, non-empty-list<string>>}
120+ * @return array{lines: array<int, non-empty-list<Identifier >|null>, errors: array<int, non-empty-list<string>>}
115121 */
116122 private function getLinesToIgnore (array $ tokens ): array
117123 {
@@ -268,33 +274,29 @@ private function getLinesToIgnoreForTokenByIgnoreComment(
268274 }
269275
270276 /**
271- * @return non-empty-list<string >
277+ * @return non-empty-list<Identifier >
272278 * @throws IgnoreParseException
273279 */
274280 private function parseIdentifiers (string $ text , int $ ignorePos ): array
275281 {
276282 $ text = substr ($ text , $ ignorePos + strlen ('@phpstan-ignore ' ));
277- $ originalTokens = $ this ->ignoreLexer ->tokenize ($ text );
278- $ tokens = [];
279-
280- foreach ($ originalTokens as $ originalToken ) {
281- if ($ originalToken [IgnoreLexer::TYPE_OFFSET ] === IgnoreLexer::TOKEN_WHITESPACE ) {
282- continue ;
283- }
284- $ tokens [] = $ originalToken ;
285- }
283+ $ tokens = $ this ->ignoreLexer ->tokenize ($ text );
286284
287285 $ c = count ($ tokens );
288286
289287 $ identifiers = [];
288+ $ comment = null ;
290289 $ openParenthesisCount = 0 ;
291290 $ expected = [IgnoreLexer::TOKEN_IDENTIFIER ];
291+ $ lastTokenTypeLabel = '@phpstan-ignore ' ;
292292
293293 for ($ i = 0 ; $ i < $ c ; $ i ++) {
294- $ lastTokenTypeLabel = isset ($ tokenType ) ? $ this ->ignoreLexer ->getLabel ($ tokenType ) : '@phpstan-ignore ' ;
294+ if (isset ($ tokenType ) && $ tokenType !== IgnoreLexer::TOKEN_WHITESPACE ) {
295+ $ lastTokenTypeLabel = $ this ->ignoreLexer ->getLabel ($ tokenType );
296+ }
295297 [IgnoreLexer::VALUE_OFFSET => $ content , IgnoreLexer::TYPE_OFFSET => $ tokenType , IgnoreLexer::LINE_OFFSET => $ tokenLine ] = $ tokens [$ i ];
296298
297- if ($ expected !== null && !in_array ($ tokenType , $ expected , true )) {
299+ if ($ expected !== null && !in_array ($ tokenType , [... $ expected, IgnoreLexer:: TOKEN_WHITESPACE ] , true )) {
298300 $ tokenTypeLabel = $ this ->ignoreLexer ->getLabel ($ tokenType );
299301 $ otherTokenContent = $ tokenType === IgnoreLexer::TOKEN_OTHER ? sprintf (" '%s' " , $ content ) : '' ;
300302 $ expectedLabels = implode (' or ' , array_map (fn ($ token ) => $ this ->ignoreLexer ->getLabel ($ token ), $ expected ));
@@ -303,6 +305,9 @@ private function parseIdentifiers(string $text, int $ignorePos): array
303305 }
304306
305307 if ($ tokenType === IgnoreLexer::TOKEN_OPEN_PARENTHESIS ) {
308+ if ($ openParenthesisCount > 0 ) {
309+ $ comment .= $ content ;
310+ }
306311 $ openParenthesisCount ++;
307312 $ expected = null ;
308313 continue ;
@@ -311,17 +316,25 @@ private function parseIdentifiers(string $text, int $ignorePos): array
311316 if ($ tokenType === IgnoreLexer::TOKEN_CLOSE_PARENTHESIS ) {
312317 $ openParenthesisCount --;
313318 if ($ openParenthesisCount === 0 ) {
319+ $ key = array_key_last ($ identifiers );
320+ if ($ key !== null ) {
321+ $ identifiers [$ key ]['comment ' ] = $ comment ;
322+ $ comment = null ;
323+ }
314324 $ expected = [IgnoreLexer::TOKEN_COMMA , IgnoreLexer::TOKEN_END ];
325+ } else {
326+ $ comment .= $ content ;
315327 }
316328 continue ;
317329 }
318330
319331 if ($ openParenthesisCount > 0 ) {
332+ $ comment .= $ content ;
320333 continue ; // waiting for comment end
321334 }
322335
323336 if ($ tokenType === IgnoreLexer::TOKEN_IDENTIFIER ) {
324- $ identifiers [] = $ content ;
337+ $ identifiers [] = [ ' name ' => $ content, ' comment ' => null ] ;
325338 $ expected = [IgnoreLexer::TOKEN_COMMA , IgnoreLexer::TOKEN_END , IgnoreLexer::TOKEN_OPEN_PARENTHESIS ];
326339 continue ;
327340 }
@@ -340,7 +353,7 @@ private function parseIdentifiers(string $text, int $ignorePos): array
340353 throw new IgnoreParseException ('Missing identifier ' , 1 );
341354 }
342355
343- return $ identifiers ;
356+ return array_values ( $ identifiers) ;
344357 }
345358
346359}
0 commit comments