Skip to content

Commit

Permalink
Fixed ignore annotations sometimes not working because of AST
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Nov 6, 2021
1 parent 4a98863 commit 9474696
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 4 deletions.
1 change: 1 addition & 0 deletions conf/config.neon
Expand Up @@ -1537,6 +1537,7 @@ services:
class: PHPStan\Parser\RichParser
arguments:
parser: @currentPhpVersionPhpParser
lexer: @currentPhpVersionLexer
autowired: no

currentPhpVersionSimpleParser:
Expand Down
44 changes: 41 additions & 3 deletions src/Analyser/FileAnalyser.php
Expand Up @@ -69,9 +69,18 @@ public function analyseFile(
if (is_file($file)) {
try {
$parserNodes = $this->parser->parseFile($file);
$linesToIgnore = [];
$linesToIgnore = $this->getLinesToIgnoreFromTokens($file, $parserNodes);
$temporaryFileErrors = [];
$nodeCallback = function (\PhpParser\Node $node, Scope $scope) use (&$fileErrors, &$fileDependencies, &$exportedNodes, $file, $registry, $outerNodeCallback, $analysedFiles, &$linesToIgnore, &$temporaryFileErrors): void {
if ($node instanceof Node\Stmt\Trait_) {
foreach (array_keys($linesToIgnore[$file] ?? []) as $lineToIgnore) {
if ($lineToIgnore < $node->getStartLine() || $lineToIgnore > $node->getEndLine()) {
continue;
}

unset($linesToIgnore[$file][$lineToIgnore]);
}
}
if ($outerNodeCallback !== null) {
$outerNodeCallback($node, $scope);
}
Expand Down Expand Up @@ -163,8 +172,16 @@ public function analyseFile(
}
}

foreach ($this->getLinesToIgnore($node) as $lineToIgnore) {
$linesToIgnore[$scope->getFileDescription()][$lineToIgnore] = true;
if ($scope->isInTrait()) {
$sameTraitFile = $file === $scope->getTraitReflection()->getFileName();
foreach ($this->getLinesToIgnore($node) as $lineToIgnore) {
$linesToIgnore[$scope->getFileDescription()][$lineToIgnore] = true;
if (!$sameTraitFile) {
continue;
}

unset($linesToIgnore[$file][$lineToIgnore]);
}
}

try {
Expand Down Expand Up @@ -277,6 +294,27 @@ private function getLinesToIgnore(Node $node): array
return $lines;
}

/**
* @param string $file
* @param \PhpParser\Node[] $nodes
* @return array<string, array<int, true>>
*/
private function getLinesToIgnoreFromTokens(string $file, array $nodes): array
{
if (!isset($nodes[0])) {
return [];
}

/** @var int[] $tokenLines */
$tokenLines = $nodes[0]->getAttribute('linesToIgnore', []);
$lines = [];
foreach ($tokenLines as $tokenLine) {
$lines[$file][$tokenLine] = true;
}

return $lines;
}

private function findLineToIgnoreComment(Comment $comment): ?int
{
$text = $comment->getText();
Expand Down
46 changes: 45 additions & 1 deletion src/Parser/RichParser.php
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\Parser;

use PhpParser\ErrorHandler\Collecting;
use PhpParser\Lexer;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\NodeVisitor\NodeConnectingVisitor;
Expand All @@ -14,6 +15,8 @@ class RichParser implements Parser

private \PhpParser\Parser $parser;

private Lexer $lexer;

private NameResolver $nameResolver;

private NodeConnectingVisitor $nodeConnectingVisitor;
Expand All @@ -22,12 +25,14 @@ class RichParser implements Parser

public function __construct(
\PhpParser\Parser $parser,
Lexer $lexer,
NameResolver $nameResolver,
NodeConnectingVisitor $nodeConnectingVisitor,
StatementOrderVisitor $statementOrderVisitor
)
{
$this->parser = $parser;
$this->lexer = $lexer;
$this->nameResolver = $nameResolver;
$this->nodeConnectingVisitor = $nodeConnectingVisitor;
$this->statementOrderVisitor = $statementOrderVisitor;
Expand All @@ -54,6 +59,7 @@ public function parseString(string $sourceCode): array
{
$errorHandler = new Collecting();
$nodes = $this->parser->parse($sourceCode, $errorHandler);
$tokens = $this->lexer->getTokens();
if ($errorHandler->hasErrors()) {
throw new \PHPStan\Parser\ParserErrorsException($errorHandler->getErrors(), null);
}
Expand All @@ -67,7 +73,45 @@ public function parseString(string $sourceCode): array
$nodeTraverser->addVisitor($this->statementOrderVisitor);

/** @var array<\PhpParser\Node\Stmt> */
return $nodeTraverser->traverse($nodes);
$nodes = $nodeTraverser->traverse($nodes);
if (isset($nodes[0])) {
$nodes[0]->setAttribute('linesToIgnore', $this->getLinesToIgnore($tokens));
}

return $nodes;
}

/**
* @param mixed[] $tokens
* @return int[]
*/
private function getLinesToIgnore(array $tokens): array
{
$lines = [];
foreach ($tokens as $token) {
if (is_string($token)) {
continue;
}

$type = $token[0];
if ($type !== T_COMMENT && $type !== T_DOC_COMMENT) {
continue;
}

$text = $token[1];
$line = $token[2];
if (strpos($text, '@phpstan-ignore-next-line') !== false) {
$line++;
} elseif (strpos($text, '@phpstan-ignore-line') === false) {
continue;
}

$line += substr_count($token[1], "\n");

$lines[] = $line;
}

return $lines;
}

}
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/AnalyserTest.php
Expand Up @@ -514,6 +514,7 @@ private function createAnalyser(bool $reportUnmatchedIgnoredErrors): \PHPStan\An
$nodeScopeResolver,
new RichParser(
new \PhpParser\Parser\Php7($lexer),
$lexer,
new \PhpParser\NodeVisitor\NameResolver(),
new NodeConnectingVisitor(),
new StatementOrderVisitor()
Expand Down

0 comments on commit 9474696

Please sign in to comment.