From 51d9652b706180071ba6853fe19a3934a34a5c06 Mon Sep 17 00:00:00 2001 From: AndrolGenhald Date: Sat, 11 Dec 2021 11:45:06 -0600 Subject: [PATCH] Class property issue suppression fixes. Fix @psalm-suppress and @psalm-allow-private-mutation being ignored if nothing else is in the docblock. Fix @psalm-suppress not allowing extra text after the issue name. Fix PossiblyUnusedProperty and UnusedProperty suppression not working at the property level. Fix MissingPropertyType suppression not working at the property level. --- src/Psalm/Internal/Analyzer/ClassAnalyzer.php | 2 +- .../Internal/Analyzer/CommentAnalyzer.php | 8 ++- src/Psalm/Internal/Codebase/ClassLikes.php | 4 +- tests/IssueSuppressionTest.php | 57 ++++++++++++++++++- 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php index 4aada9533..fe9b7c018 100644 --- a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php @@ -1581,7 +1581,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer new CodeLocation($source, $stmt->props[0]->name), $property_id ), - $this->source->getSuppressedIssues() + $this->source->getSuppressedIssues() + $property_storage->suppressed_issues ); } diff --git a/src/Psalm/Internal/Analyzer/CommentAnalyzer.php b/src/Psalm/Internal/Analyzer/CommentAnalyzer.php index 72179a451..d57cf1781 100644 --- a/src/Psalm/Internal/Analyzer/CommentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/CommentAnalyzer.php @@ -194,8 +194,10 @@ class CommentAnalyzer || isset($parsed_docblock->tags['readonly']) || isset($parsed_docblock->tags['psalm-readonly']) || isset($parsed_docblock->tags['psalm-readonly-allow-private-mutation']) + || isset($parsed_docblock->tags['psalm-allow-private-mutation']) || isset($parsed_docblock->tags['psalm-taint-escape']) || isset($parsed_docblock->tags['psalm-internal']) + || isset($parsed_docblock->tags['psalm-suppress']) || $parsed_docblock->description) ) { $var_comment = new VarDocblockComment(); @@ -245,7 +247,11 @@ class CommentAnalyzer } if (isset($parsed_docblock->tags['psalm-suppress'])) { - $var_comment->suppressed_issues = $parsed_docblock->tags['psalm-suppress']; + foreach ($parsed_docblock->tags['psalm-suppress'] as $offset => $suppress_entry) { + foreach (DocComment::parseSuppressList($suppress_entry) as $issue_offset => $suppressed_issue) { + $var_comment->suppressed_issues[$issue_offset + $offset] = $suppressed_issue; + } + } } } diff --git a/src/Psalm/Internal/Codebase/ClassLikes.php b/src/Psalm/Internal/Codebase/ClassLikes.php index 4d4e86050..a92600506 100644 --- a/src/Psalm/Internal/Codebase/ClassLikes.php +++ b/src/Psalm/Internal/Codebase/ClassLikes.php @@ -2159,7 +2159,7 @@ class ClassLikes } } elseif (IssueBuffer::accepts( $issue, - $classlike_storage->suppressed_issues + $classlike_storage->suppressed_issues + $property_storage->suppressed_issues )) { // fall through } @@ -2189,7 +2189,7 @@ class ClassLikes } } elseif (IssueBuffer::accepts( $issue, - $classlike_storage->suppressed_issues + $classlike_storage->suppressed_issues + $property_storage->suppressed_issues )) { // fall through } diff --git a/tests/IssueSuppressionTest.php b/tests/IssueSuppressionTest.php index eac4301fc..ef8d19d6b 100644 --- a/tests/IssueSuppressionTest.php +++ b/tests/IssueSuppressionTest.php @@ -4,6 +4,7 @@ namespace Psalm\Tests; use Psalm\Config; use Psalm\Context; use Psalm\Exception\CodeException; +use Psalm\IssueBuffer; use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait; use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait; @@ -197,6 +198,50 @@ class IssueSuppressionTest extends TestCase $this->analyzeFile(getcwd() . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'somefile.php', $context); } + public function testPossiblyUnusedPropertySuppressedOnClass(): void + { + $this->project_analyzer->getCodebase()->find_unused_code = "always"; + + $file_path = getcwd() . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'somefile.php'; + $this->addFile( + $file_path, + 'analyzeFile($file_path, new Context(), false); + $this->project_analyzer->consolidateAnalyzedData(); + IssueBuffer::processUnusedSuppressions($this->project_analyzer->getCodebase()->file_provider); + } + + public function testPossiblyUnusedPropertySuppressedOnProperty(): void + { + $this->project_analyzer->getCodebase()->find_unused_code = "always"; + + $file_path = getcwd() . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'somefile.php'; + $this->addFile( + $file_path, + 'analyzeFile($file_path, new Context(), false); + $this->project_analyzer->consolidateAnalyzedData(); + IssueBuffer::processUnusedSuppressions($this->project_analyzer->getCodebase()->file_provider); + } + /** * @return iterable,error_levels?:string[]}> */ @@ -315,7 +360,17 @@ class IssueSuppressionTest extends TestCase } } ', - ] + ], + 'missingPropertyTypeAtPropertyLevel' => [ + '