diff --git a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php index 4aada953394..fe9b7c018f2 100644 --- a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php @@ -1581,7 +1581,7 @@ private function checkForMissingPropertyType( 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 72179a45194..d57cf17817a 100644 --- a/src/Psalm/Internal/Analyzer/CommentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/CommentAnalyzer.php @@ -194,8 +194,10 @@ public static function arrayToDocblocks( || 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 @@ private static function decorateVarDocblockComment( } 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 4d4e86050c6..a9260050668 100644 --- a/src/Psalm/Internal/Codebase/ClassLikes.php +++ b/src/Psalm/Internal/Codebase/ClassLikes.php @@ -2159,7 +2159,7 @@ private function checkPropertyReferences(ClassLikeStorage $classlike_storage): v } } elseif (IssueBuffer::accepts( $issue, - $classlike_storage->suppressed_issues + $classlike_storage->suppressed_issues + $property_storage->suppressed_issues )) { // fall through } @@ -2189,7 +2189,7 @@ private function checkPropertyReferences(ClassLikeStorage $classlike_storage): v } } 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 eac4301fc32..ef8d19d6bb0 100644 --- a/tests/IssueSuppressionTest.php +++ b/tests/IssueSuppressionTest.php @@ -4,6 +4,7 @@ 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 @@ public function testUncaughtThrowInGlobalScopeSuppressedWithoutThrow(): void $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 @@ public function func(string $var) { } } ', - ] + ], + 'missingPropertyTypeAtPropertyLevel' => [ + '