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
@@ -120,7 +126,7 @@ public function parseString(string $sourceCode): array
120126
121127 /**
122128 * @param Token[] $tokens
123- * @return array{lines: array<int, non-empty-list<string >|null>, errors: array<int, non-empty-list<string>>}
129+ * @return array{lines: array<int, non-empty-list<Identifier >|null>, errors: array<int, non-empty-list<string>>}
124130 */
125131 private function getLinesToIgnore (array $ tokens ): array
126132 {
@@ -277,33 +283,29 @@ private function getLinesToIgnoreForTokenByIgnoreComment(
277283 }
278284
279285 /**
280- * @return non-empty-list<string >
286+ * @return non-empty-list<Identifier >
281287 * @throws IgnoreParseException
282288 */
283289 private function parseIdentifiers (string $ text , int $ ignorePos ): array
284290 {
285291 $ text = substr ($ text , $ ignorePos + strlen ('@phpstan-ignore ' ));
286- $ originalTokens = $ this ->ignoreLexer ->tokenize ($ text );
287- $ tokens = [];
288-
289- foreach ($ originalTokens as $ originalToken ) {
290- if ($ originalToken [IgnoreLexer::TYPE_OFFSET ] === IgnoreLexer::TOKEN_WHITESPACE ) {
291- continue ;
292- }
293- $ tokens [] = $ originalToken ;
294- }
292+ $ tokens = $ this ->ignoreLexer ->tokenize ($ text );
295293
296294 $ c = count ($ tokens );
297295
298296 $ identifiers = [];
297+ $ comment = null ;
299298 $ openParenthesisCount = 0 ;
300299 $ expected = [IgnoreLexer::TOKEN_IDENTIFIER ];
300+ $ lastTokenTypeLabel = '@phpstan-ignore ' ;
301301
302302 for ($ i = 0 ; $ i < $ c ; $ i ++) {
303- $ lastTokenTypeLabel = isset ($ tokenType ) ? $ this ->ignoreLexer ->getLabel ($ tokenType ) : '@phpstan-ignore ' ;
303+ if (isset ($ tokenType ) && $ tokenType !== IgnoreLexer::TOKEN_WHITESPACE ) {
304+ $ lastTokenTypeLabel = $ this ->ignoreLexer ->getLabel ($ tokenType );
305+ }
304306 [IgnoreLexer::VALUE_OFFSET => $ content , IgnoreLexer::TYPE_OFFSET => $ tokenType , IgnoreLexer::LINE_OFFSET => $ tokenLine ] = $ tokens [$ i ];
305307
306- if ($ expected !== null && !in_array ($ tokenType , $ expected , true )) {
308+ if ($ expected !== null && !in_array ($ tokenType , [... $ expected, IgnoreLexer:: TOKEN_WHITESPACE ] , true )) {
307309 $ tokenTypeLabel = $ this ->ignoreLexer ->getLabel ($ tokenType );
308310 $ otherTokenContent = $ tokenType === IgnoreLexer::TOKEN_OTHER ? sprintf (" '%s' " , $ content ) : '' ;
309311 $ expectedLabels = implode (' or ' , array_map (fn ($ token ) => $ this ->ignoreLexer ->getLabel ($ token ), $ expected ));
@@ -312,6 +314,9 @@ private function parseIdentifiers(string $text, int $ignorePos): array
312314 }
313315
314316 if ($ tokenType === IgnoreLexer::TOKEN_OPEN_PARENTHESIS ) {
317+ if ($ openParenthesisCount > 0 ) {
318+ $ comment .= $ content ;
319+ }
315320 $ openParenthesisCount ++;
316321 $ expected = null ;
317322 continue ;
@@ -320,17 +325,25 @@ private function parseIdentifiers(string $text, int $ignorePos): array
320325 if ($ tokenType === IgnoreLexer::TOKEN_CLOSE_PARENTHESIS ) {
321326 $ openParenthesisCount --;
322327 if ($ openParenthesisCount === 0 ) {
328+ $ key = array_key_last ($ identifiers );
329+ if ($ key !== null ) {
330+ $ identifiers [$ key ]['comment ' ] = $ comment ;
331+ $ comment = null ;
332+ }
323333 $ expected = [IgnoreLexer::TOKEN_COMMA , IgnoreLexer::TOKEN_END ];
334+ } else {
335+ $ comment .= $ content ;
324336 }
325337 continue ;
326338 }
327339
328340 if ($ openParenthesisCount > 0 ) {
341+ $ comment .= $ content ;
329342 continue ; // waiting for comment end
330343 }
331344
332345 if ($ tokenType === IgnoreLexer::TOKEN_IDENTIFIER ) {
333- $ identifiers [] = $ content ;
346+ $ identifiers [] = [ ' name ' => $ content, ' comment ' => null ] ;
334347 $ expected = [IgnoreLexer::TOKEN_COMMA , IgnoreLexer::TOKEN_END , IgnoreLexer::TOKEN_OPEN_PARENTHESIS ];
335348 continue ;
336349 }
@@ -349,7 +362,7 @@ private function parseIdentifiers(string $text, int $ignorePos): array
349362 throw new IgnoreParseException ('Missing identifier ' , 1 );
350363 }
351364
352- return $ identifiers ;
365+ return array_values ( $ identifiers) ;
353366 }
354367
355368}
0 commit comments