Skip to content

Commit

Permalink
PHPDoc tag @phpstan-ignore-next-line works for first line below the…
Browse files Browse the repository at this point in the history
… PHPDoc even in bleeding edge
  • Loading branch information
ondrejmirtes committed Jan 8, 2024
1 parent c370cbf commit 8b6260c
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 10 deletions.
35 changes: 30 additions & 5 deletions src/Parser/RichParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,17 @@
use PHPStan\File\FileReader;
use PHPStan\ShouldNotHappenException;
use function array_filter;
use function count;
use function implode;
use function is_string;
use function preg_match_all;
use function sprintf;
use function str_contains;
use function strpos;
use function substr;
use function substr_count;
use const ARRAY_FILTER_USE_KEY;
use const PREG_OFFSET_CAPTURE;
use const T_COMMENT;
use const T_DOC_COMMENT;

Expand All @@ -25,6 +30,10 @@ class RichParser implements Parser

public const VISITOR_SERVICE_TAG = 'phpstan.parser.richParserNodeVisitor';

private const PHPDOC_TAG_REGEX = '(@(?:[a-z][a-z0-9-\\\\]+:)?[a-z][a-z0-9-\\\\]*+)';

private const PHPDOC_DOCTRINE_TAG_REGEX = '(@[a-z_\\\\][a-z0-9_\:\\\\]*[a-z_][a-z0-9_]*)';

public function __construct(
private \PhpParser\Parser $parser,
private Lexer $lexer,
Expand Down Expand Up @@ -107,11 +116,27 @@ private function getLinesToIgnore(array $tokens): array
$text = $token[1];
$line = $token[2];

if ($this->enableIgnoreErrorsWithinPhpDocs) {
$lines = $lines +
$this->getLinesToIgnoreForTokenByIgnoreComment($text, $line, '@phpstan-ignore-next-line', true) +
$this->getLinesToIgnoreForTokenByIgnoreComment($text, $line, '@phpstan-ignore-line');

if ($this->enableIgnoreErrorsWithinPhpDocs && $type === T_DOC_COMMENT) {
$lines += $this->getLinesToIgnoreForTokenByIgnoreComment($text, $line, '@phpstan-ignore-line');
if (str_contains($text, '@phpstan-ignore-next-line')) {
$pattern = sprintf('~%s~si', implode('|', [self::PHPDOC_TAG_REGEX, self::PHPDOC_DOCTRINE_TAG_REGEX]));
$r = preg_match_all($pattern, $text, $pregMatches, PREG_OFFSET_CAPTURE);
if ($r !== false) {
$c = count($pregMatches[0]);
if ($c > 0) {
[$lastMatchTag, $lastMatchOffset] = $pregMatches[0][$c - 1];
if ($lastMatchTag === '@phpstan-ignore-next-line') {
// this will let us ignore errors outside of PHPDoc
// and also cut off the PHPDoc text before the last tag
$lineToIgnore = $line + 1 + substr_count($text, "\n");
$lines[$lineToIgnore] = null;
$text = substr($text, 0, $lastMatchOffset);
}
}
}

$lines += $this->getLinesToIgnoreForTokenByIgnoreComment($text, $line, '@phpstan-ignore-next-line', true);
}
} else {
if (str_contains($text, '@phpstan-ignore-next-line')) {
$line++;
Expand Down
10 changes: 5 additions & 5 deletions tests/PHPStan/Analyser/AnalyserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -467,11 +467,11 @@ public function testDoNotReportUnmatchedIgnoredErrorsFromPathWithCountIfPathWasN

public function testIgnoreNextLine(): void
{
$result = $this->runAnalyser([], true, [
$result = $this->runAnalyser([], false, [
__DIR__ . '/data/ignore-next-line.php',
], true);
$this->assertCount(3, $result);
foreach ([10, 20, 24] as $i => $line) {
$this->assertCount(5, $result);
foreach ([10, 20, 24, 31, 50] as $i => $line) {
$this->assertArrayHasKey($i, $result);
$this->assertInstanceOf(Error::class, $result[$i]);
$this->assertSame('Fail.', $result[$i]->getMessage());
Expand All @@ -484,8 +484,8 @@ public function testIgnoreNextLineUnmatched(): void
$result = $this->runAnalyser([], true, [
__DIR__ . '/data/ignore-next-line-unmatched.php',
], true);
$this->assertCount(1, $result);
foreach ([11] as $i => $line) {
$this->assertCount(2, $result);
foreach ([11, 15] as $i => $line) {
$this->assertArrayHasKey($i, $result);
$this->assertInstanceOf(Error::class, $result[$i]);
$this->assertStringContainsString('No error to ignore is reported on line', $result[$i]->getMessage());
Expand Down
6 changes: 6 additions & 0 deletions tests/PHPStan/Analyser/data/ignore-next-line-unmatched.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ public function doFoo(): void
{
/** @phpstan-ignore-next-line */
succ(); // reported as unmatched

/**
* @phpstan-ignore-next-line
* @var int
*/
succ(); // not reported as unmatched because phpstan-ignore-next-line is not last
}

}
25 changes: 25 additions & 0 deletions tests/PHPStan/Analyser/data/ignore-next-line.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,31 @@ public function doFoo(): void
if (fail()) {
fail(); // reported
}

/**
* @phpstan-ignore-next-line
*/
fail();
fail(); // reported

/**
* @noinspection PhpStrictTypeCheckingInspection
* @phpstan-ignore-next-line
*/
fail(); // not reported because the ignore tag is valid

/**
* @phpstan-ignore-next-line Some very loooooooooooooooooooooooooooooooooooooooooooon
* coooooooooooooooooooooooooooooooooooooooooooooooooomment
* on many lines.
*/
fail(); // not reported because the ignore tag is valid

/**
* @phpstan-ignore-next-line
* @var int
*/
fail(); // reported becase ignore tag in PHPDoc is not last
}

}

0 comments on commit 8b6260c

Please sign in to comment.