|
13 | 13 | use PHPStan\File\FileReader;
|
14 | 14 | use PHPStan\ShouldNotHappenException;
|
15 | 15 | use function array_filter;
|
16 |
| -use function array_pop; |
| 16 | +use function array_map; |
17 | 17 | use function count;
|
18 | 18 | use function implode;
|
| 19 | +use function in_array; |
19 | 20 | use function is_string;
|
20 | 21 | use function preg_match_all;
|
21 | 22 | use function sprintf;
|
@@ -283,59 +284,57 @@ private function parseIdentifiers(string $text, int $ignorePos): array
|
283 | 284 | if ($originalToken[IgnoreLexer::TYPE_OFFSET] === IgnoreLexer::TOKEN_WHITESPACE) {
|
284 | 285 | continue;
|
285 | 286 | }
|
286 |
| - if ($originalToken[IgnoreLexer::TYPE_OFFSET] === IgnoreLexer::TOKEN_EOL) { |
287 |
| - break; |
288 |
| - } |
289 | 287 | $tokens[] = $originalToken;
|
290 | 288 | }
|
291 | 289 |
|
292 | 290 | $c = count($tokens);
|
293 | 291 |
|
294 | 292 | $identifiers = [];
|
295 |
| - $depth = 0; |
296 |
| - $parenthesisStack = []; |
| 293 | + $openParenthesisCount = 0; |
| 294 | + $expected = [IgnoreLexer::TOKEN_IDENTIFIER]; |
| 295 | + |
297 | 296 | for ($i = 0; $i < $c; $i++) {
|
| 297 | + $lastTokenTypeLabel = isset($tokenType) ? $this->ignoreLexer->getLabel($tokenType) : '@phpstan-ignore'; |
298 | 298 | [IgnoreLexer::VALUE_OFFSET => $content, IgnoreLexer::TYPE_OFFSET => $tokenType, IgnoreLexer::LINE_OFFSET => $tokenLine] = $tokens[$i];
|
299 |
| - if ($tokenType === IgnoreLexer::TOKEN_IDENTIFIER && $depth === 0) { |
300 |
| - $identifiers[] = $content; |
301 |
| - if (isset($tokens[$i + 1])) { |
302 |
| - if ($tokens[$i + 1][IgnoreLexer::TYPE_OFFSET] === IgnoreLexer::TOKEN_COMMA) { |
303 |
| - $i++; |
304 |
| - } |
305 |
| - } |
306 |
| - continue; |
307 |
| - } |
308 |
| - if ($i === 0) { |
309 |
| - throw new IgnoreParseException('First token is not an identifier', $tokenLine); |
| 299 | + |
| 300 | + if ($expected !== null && !in_array($tokenType, $expected, true)) { |
| 301 | + $tokenTypeLabel = $this->ignoreLexer->getLabel($tokenType); |
| 302 | + $otherTokenContent = $tokenType === IgnoreLexer::TOKEN_OTHER ? sprintf(" '%s'", $content) : ''; |
| 303 | + $expectedLabels = implode(' or ', array_map(fn ($token) => $this->ignoreLexer->getLabel($token), $expected)); |
| 304 | + |
| 305 | + throw new IgnoreParseException(sprintf('Unexpected %s%s after %s, expected %s', $tokenTypeLabel, $otherTokenContent, $lastTokenTypeLabel, $expectedLabels), $tokenLine); |
310 | 306 | }
|
311 |
| - if ($tokenType === IgnoreLexer::TOKEN_COMMA && $depth === 0) { |
312 |
| - throw new IgnoreParseException('Unexpected comma (,)', $tokenLine); |
| 307 | + |
| 308 | + if ($tokenType === IgnoreLexer::TOKEN_OPEN_PARENTHESIS) { |
| 309 | + $openParenthesisCount++; |
| 310 | + $expected = null; |
| 311 | + continue; |
313 | 312 | }
|
| 313 | + |
314 | 314 | if ($tokenType === IgnoreLexer::TOKEN_CLOSE_PARENTHESIS) {
|
315 |
| - if ($depth < 1) { |
316 |
| - throw new IgnoreParseException('Closing parenthesis ")" before opening parenthesis "("', $tokenLine); |
317 |
| - } |
| 315 | + $openParenthesisCount--; |
| 316 | + $expected = [IgnoreLexer::TOKEN_COMMA, IgnoreLexer::TOKEN_END]; |
| 317 | + continue; |
| 318 | + } |
318 | 319 |
|
319 |
| - $depth--; |
320 |
| - array_pop($parenthesisStack); |
321 |
| - if ($depth === 0) { |
322 |
| - break; |
323 |
| - } |
| 320 | + if ($openParenthesisCount > 0) { |
| 321 | + continue; // waiting for comment end |
324 | 322 | }
|
325 |
| - if ($tokenType !== IgnoreLexer::TOKEN_OPEN_PARENTHESIS) { |
| 323 | + |
| 324 | + if ($tokenType === IgnoreLexer::TOKEN_IDENTIFIER) { |
| 325 | + $identifiers[] = $content; |
| 326 | + $expected = [IgnoreLexer::TOKEN_COMMA, IgnoreLexer::TOKEN_END, IgnoreLexer::TOKEN_OPEN_PARENTHESIS]; |
326 | 327 | continue;
|
327 | 328 | }
|
328 | 329 |
|
329 |
| - $depth++; |
330 |
| - $parenthesisStack[] = $tokenLine; |
331 |
| - } |
332 |
| - |
333 |
| - if (isset($tokens[$c - 1]) && $tokens[$c - 1][IgnoreLexer::TYPE_OFFSET] === IgnoreLexer::TOKEN_COMMA) { |
334 |
| - throw new IgnoreParseException('Unexpected trailing comma (,)', $tokens[$c - 1][IgnoreLexer::LINE_OFFSET]); |
| 330 | + if ($tokenType === IgnoreLexer::TOKEN_COMMA) { |
| 331 | + $expected = [IgnoreLexer::TOKEN_IDENTIFIER]; |
| 332 | + continue; |
| 333 | + } |
335 | 334 | }
|
336 | 335 |
|
337 |
| - if (count($parenthesisStack) > 0) { |
338 |
| - throw new IgnoreParseException('Unclosed opening parenthesis "(" without closing parenthesis ")"', $parenthesisStack[count($parenthesisStack) - 1]); |
| 336 | + if ($openParenthesisCount > 0) { |
| 337 | + throw new IgnoreParseException('Unexpected end, unclosed opening parenthesis', $tokenLine ?? 1); |
339 | 338 | }
|
340 | 339 |
|
341 | 340 | if (count($identifiers) === 0) {
|
|
0 commit comments