diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index ac31c5bd29a..92eb36478ca 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -953,6 +953,37 @@ jobs: echo "$OUTPUT" ../bashunit assert contains 'Result cache not used because the metadata do not match: executedFilesHashes' "$OUTPUT" ../bashunit assert contains 'Foo.php:10:Method ResultCacheE2EBootstrap\Foo::doFoo() should return int but returns string.' "$OUTPUT" + - script: | + cd e2e/result-cache-trait-ignore + rm -rf tmp/cache tmp/resultCache.php + OUTPUT=$(../bashunit assert exit_code "1" "../../bin/phpstan analyse -vv --error-format raw") + echo "$OUTPUT" + ../bashunit assert contains 'Result cache is saved.' "$OUTPUT" + ../bashunit assert contains 'MyTrait.php (in context of class Repro\A):10:Ignore with identifier method.notFound has no comment.' "$OUTPUT" + ../bashunit assert contains 'MyTrait.php (in context of class Repro\B):10:Ignore with identifier method.notFound has no comment.' "$OUTPUT" + ../bashunit assert contains 'MyTrait.php (in context of class Repro\A):12:Ignoring all errors on a line is not allowed.' "$OUTPUT" + ../bashunit assert contains 'MyTrait.php (in context of class Repro\B):12:Ignoring all errors on a line is not allowed.' "$OUTPUT" + OUTPUT=$(../bashunit assert exit_code "1" "../../bin/phpstan analyse -vv --error-format raw") + echo "$OUTPUT" + ../bashunit assert contains 'Result cache restored. 0 files will be reanalysed.' "$OUTPUT" + ../bashunit assert contains 'MyTrait.php (in context of class Repro\A):10:Ignore with identifier method.notFound has no comment.' "$OUTPUT" + ../bashunit assert contains 'MyTrait.php (in context of class Repro\B):10:Ignore with identifier method.notFound has no comment.' "$OUTPUT" + ../bashunit assert contains 'MyTrait.php (in context of class Repro\A):12:Ignoring all errors on a line is not allowed.' "$OUTPUT" + ../bashunit assert contains 'MyTrait.php (in context of class Repro\B):12:Ignoring all errors on a line is not allowed.' "$OUTPUT" + cp src/A.php tmp/A.php + OUTPUT=$(../bashunit assert exit_code "1" "../../bin/phpstan analyse -vv --error-format raw --tmp-file tmp/A.php --instead-of src/A.php") + echo "$OUTPUT" + ../bashunit assert contains 'MyTrait.php (in context of class Repro\A):10:Ignore with identifier method.notFound has no comment.' "$OUTPUT" + ../bashunit assert contains 'MyTrait.php (in context of class Repro\A):12:Ignoring all errors on a line is not allowed.' "$OUTPUT" + rm -f tmp/A.php + patch -b src/A.php < removeTrait.patch + OUTPUT=$(../bashunit assert exit_code "1" "../../bin/phpstan analyse -vv --error-format raw") + echo "$OUTPUT" + ../bashunit assert contains 'Result cache restored. 1 file will be reanalysed.' "$OUTPUT" + ../bashunit assert not_contains 'in context of class Repro\A' "$OUTPUT" + ../bashunit assert contains 'MyTrait.php (in context of class Repro\B):10:Ignore with identifier method.notFound has no comment.' "$OUTPUT" + ../bashunit assert contains 'MyTrait.php (in context of class Repro\B):12:Ignoring all errors on a line is not allowed.' "$OUTPUT" + mv src/A.php.orig src/A.php - script: | cd e2e/result-cache-traits ../../bin/phpstan analyse diff --git a/e2e/result-cache-trait-ignore/phpstan.neon b/e2e/result-cache-trait-ignore/phpstan.neon new file mode 100644 index 00000000000..c6867eeee8d --- /dev/null +++ b/e2e/result-cache-trait-ignore/phpstan.neon @@ -0,0 +1,6 @@ +parameters: + level: 5 + paths: + - src + tmpDir: tmp + reportIgnoresWithoutComments: true diff --git a/e2e/result-cache-trait-ignore/removeTrait.patch b/e2e/result-cache-trait-ignore/removeTrait.patch new file mode 100644 index 00000000000..2dadb32e619 --- /dev/null +++ b/e2e/result-cache-trait-ignore/removeTrait.patch @@ -0,0 +1,8 @@ +--- src/A.php ++++ src/A.php +@@ -4,5 +4,4 @@ + + class A + { +- use MyTrait; + } diff --git a/e2e/result-cache-trait-ignore/src/A.php b/e2e/result-cache-trait-ignore/src/A.php new file mode 100644 index 00000000000..c87537d1c23 --- /dev/null +++ b/e2e/result-cache-trait-ignore/src/A.php @@ -0,0 +1,8 @@ +undefinedMethod(); + // @phpstan-ignore-next-line + $this->anotherUndefinedMethod(); + } +} diff --git a/e2e/result-cache-trait-ignore/tmp/.gitignore b/e2e/result-cache-trait-ignore/tmp/.gitignore new file mode 100644 index 00000000000..125e34294bf --- /dev/null +++ b/e2e/result-cache-trait-ignore/tmp/.gitignore @@ -0,0 +1,2 @@ +* +!.* diff --git a/src/Analyser/Error.php b/src/Analyser/Error.php index b7ba807a7fe..7a7befa78df 100644 --- a/src/Analyser/Error.php +++ b/src/Analyser/Error.php @@ -74,23 +74,20 @@ public function getFilePath(): string public function changeFilePath(string $newFilePath): self { - if ($this->traitFilePath !== null) { - throw new ShouldNotHappenException('Errors in traits not yet supported'); - } - return new self( $this->message, - $newFilePath, + $this->traitFilePath !== null ? $this->file : $newFilePath, $this->line, $this->canBeIgnored, $newFilePath, - null, + $this->traitFilePath, $this->tip, $this->nodeLine, $this->nodeType, $this->identifier, $this->metadata, $this->renameFixedFile($this->getFilePath(), $newFilePath), + $this->traitContexts, ); } diff --git a/src/Analyser/FileAnalyser.php b/src/Analyser/FileAnalyser.php index ad0dad34c3f..19518c87f0f 100644 --- a/src/Analyser/FileAnalyser.php +++ b/src/Analyser/FileAnalyser.php @@ -155,8 +155,8 @@ public function analyseFile( $ignoredFile, $line, false, - $ignoredFile, - null, + $file, + $nodeCallback->getTraitFilePaths()[$ignoredFile] ?? null, 'Use @phpstan-ignore with an identifier and a comment (in parentheses).', ))->withIdentifier('ignore.allLineErrors'); unset($linesToIgnore[$ignoredFile][$line]); @@ -174,8 +174,8 @@ public function analyseFile( $ignoredFile, $line, false, - $ignoredFile, - null, + $file, + $nodeCallback->getTraitFilePaths()[$ignoredFile] ?? null, 'Explain why this ignore is necessary in parentheses after the identifier.', ))->withIdentifier('ignore.noComment'); unset($linesToIgnore[$ignoredFile][$line]); diff --git a/src/Analyser/FileAnalyserCallback.php b/src/Analyser/FileAnalyserCallback.php index 4dac2fa0ccf..202dcd9e495 100644 --- a/src/Analyser/FileAnalyserCallback.php +++ b/src/Analyser/FileAnalyserCallback.php @@ -48,6 +48,9 @@ final class FileAnalyserCallback /** @var LinesToIgnore */ private array $unmatchedLineIgnores; + /** @var array */ + private array $traitFilePaths = []; + /** * The registries answer per node class and cache that themselves; this keeps the answer for the * classes met in this file, so that the walk does not ask them again for every node of a class. @@ -119,6 +122,7 @@ public function __invoke(Node $node, Scope $scope): void $traitFileName = $node->getTraitReflection()->getFileName(); if ($traitFileName !== null) { + $this->traitFilePaths[$fileDescription] = $traitFileName; $this->processedFiles[] = $traitFileName; } } @@ -314,6 +318,14 @@ public function getUnmatchedLineIgnores(): array return $this->unmatchedLineIgnores; } + /** + * @return array + */ + public function getTraitFilePaths(): array + { + return $this->traitFilePaths; + } + /** * @return list */