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 ;
1719use function count ;
1820use function implode ;
3032use const T_DOC_COMMENT ;
3133use const T_WHITESPACE ;
3234
35+ /**
36+ * @phpstan-import-type Identifier from FileAnalyserResult
37+ */
3338final class RichParser implements Parser
3439{
3540
@@ -120,7 +125,7 @@ public function parseString(string $sourceCode): array
120125
121126 /**
122127 * @param Token[] $tokens
123- * @return array{lines: array<int, non-empty-list<string >|null>, errors: array<int, non-empty-list<string>>}
128+ * @return array{lines: array<int, non-empty-list<Identifier >|null>, errors: array<int, non-empty-list<string>>}
124129 */
125130 private function getLinesToIgnore (array $ tokens ): array
126131 {
@@ -277,33 +282,29 @@ private function getLinesToIgnoreForTokenByIgnoreComment(
277282 }
278283
279284 /**
280- * @return non-empty-list<string >
285+ * @return non-empty-list<Identifier >
281286 * @throws IgnoreParseException
282287 */
283288 private function parseIdentifiers (string $ text , int $ ignorePos ): array
284289 {
285290 $ 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- }
291+ $ tokens = $ this ->ignoreLexer ->tokenize ($ text );
295292
296293 $ c = count ($ tokens );
297294
298295 $ identifiers = [];
296+ $ comment = null ;
299297 $ openParenthesisCount = 0 ;
300298 $ expected = [IgnoreLexer::TOKEN_IDENTIFIER ];
299+ $ lastTokenTypeLabel = '@phpstan-ignore ' ;
301300
302301 for ($ i = 0 ; $ i < $ c ; $ i ++) {
303- $ lastTokenTypeLabel = isset ($ tokenType ) ? $ this ->ignoreLexer ->getLabel ($ tokenType ) : '@phpstan-ignore ' ;
302+ if (isset ($ tokenType ) && $ tokenType !== IgnoreLexer::TOKEN_WHITESPACE ) {
303+ $ lastTokenTypeLabel = $ this ->ignoreLexer ->getLabel ($ tokenType );
304+ }
304305 [IgnoreLexer::VALUE_OFFSET => $ content , IgnoreLexer::TYPE_OFFSET => $ tokenType , IgnoreLexer::LINE_OFFSET => $ tokenLine ] = $ tokens [$ i ];
305306
306- if ($ expected !== null && !in_array ($ tokenType , $ expected , true )) {
307+ if ($ expected !== null && !in_array ($ tokenType , [... $ expected, IgnoreLexer:: TOKEN_WHITESPACE ] , true )) {
307308 $ tokenTypeLabel = $ this ->ignoreLexer ->getLabel ($ tokenType );
308309 $ otherTokenContent = $ tokenType === IgnoreLexer::TOKEN_OTHER ? sprintf (" '%s' " , $ content ) : '' ;
309310 $ expectedLabels = implode (' or ' , array_map (fn ($ token ) => $ this ->ignoreLexer ->getLabel ($ token ), $ expected ));
@@ -312,6 +313,9 @@ private function parseIdentifiers(string $text, int $ignorePos): array
312313 }
313314
314315 if ($ tokenType === IgnoreLexer::TOKEN_OPEN_PARENTHESIS ) {
316+ if ($ openParenthesisCount > 0 ) {
317+ $ comment .= $ content ;
318+ }
315319 $ openParenthesisCount ++;
316320 $ expected = null ;
317321 continue ;
@@ -320,17 +324,25 @@ private function parseIdentifiers(string $text, int $ignorePos): array
320324 if ($ tokenType === IgnoreLexer::TOKEN_CLOSE_PARENTHESIS ) {
321325 $ openParenthesisCount --;
322326 if ($ openParenthesisCount === 0 ) {
327+ $ key = array_key_last ($ identifiers );
328+ if ($ key !== null ) {
329+ $ identifiers [$ key ]['comment ' ] = $ comment ;
330+ $ comment = null ;
331+ }
323332 $ expected = [IgnoreLexer::TOKEN_COMMA , IgnoreLexer::TOKEN_END ];
333+ } else {
334+ $ comment .= $ content ;
324335 }
325336 continue ;
326337 }
327338
328339 if ($ openParenthesisCount > 0 ) {
340+ $ comment .= $ content ;
329341 continue ; // waiting for comment end
330342 }
331343
332344 if ($ tokenType === IgnoreLexer::TOKEN_IDENTIFIER ) {
333- $ identifiers [] = $ content ;
345+ $ identifiers [] = [ ' name ' => $ content, ' comment ' => null ] ;
334346 $ expected = [IgnoreLexer::TOKEN_COMMA , IgnoreLexer::TOKEN_END , IgnoreLexer::TOKEN_OPEN_PARENTHESIS ];
335347 continue ;
336348 }
@@ -349,6 +361,7 @@ private function parseIdentifiers(string $text, int $ignorePos): array
349361 throw new IgnoreParseException ('Missing identifier ' , 1 );
350362 }
351363
364+ /** @phpstan-ignore return.type (return type is correct, not sure why it's being changed from array shape to key-value shape) */
352365 return $ identifiers ;
353366 }
354367
0 commit comments