Skip to content

Commit

Permalink
@psalm-public-api annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-worman committed Dec 22, 2022
1 parent e48ccab commit b6ec8fb
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/Psalm/DocComment.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ final class DocComment
'taint-unescape', 'self-out', 'consistent-constructor', 'stub-override',
'require-extends', 'require-implements', 'param-out', 'ignore-var',
'consistent-templates', 'if-this-is', 'this-out', 'check-type', 'check-type-exact',
'public-api',
];

/**
Expand Down
34 changes: 30 additions & 4 deletions src/Psalm/Internal/Codebase/ClassLikes.php
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,13 @@ public function consolidateAnalyzedData(Methods $methods, ?Progress $progress, b
&& !$classlike_storage->is_trait
) {
if ($find_unused_code) {
if (!$this->file_reference_provider->isClassReferenced($fq_class_name_lc)) {
if (
$classlike_storage->public_api
|| $this->file_reference_provider->isClassReferenced($fq_class_name_lc)
) {
$this->checkMethodReferences($classlike_storage, $methods);
$this->checkPropertyReferences($classlike_storage);
} else {
IssueBuffer::maybeAdd(
new UnusedClass(
'Class ' . $classlike_storage->name . ' is never used',
Expand All @@ -860,9 +866,6 @@ public function consolidateAnalyzedData(Methods $methods, ?Progress $progress, b
),
$classlike_storage->suppressed_issues,
);
} else {
$this->checkMethodReferences($classlike_storage, $methods);
$this->checkPropertyReferences($classlike_storage);
}
}

Expand Down Expand Up @@ -1690,6 +1693,18 @@ private function checkMethodReferences(ClassLikeStorage $classlike_storage, Meth
$method_id = $declaring_method_id;
}

// TODO: UnusedParam should always be checked.
if (
$classlike_storage->public_api
&& ($method_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC
|| ($method_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PROTECTED
&& !$classlike_storage->final
)
)
) {
continue;
}

if ($method_storage->location
&& !$project_analyzer->canReportIssues($method_storage->location->file_path)
&& !$codebase->analyzer->canReportIssues($method_storage->location->file_path)
Expand Down Expand Up @@ -2064,6 +2079,17 @@ private function checkPropertyReferences(ClassLikeStorage $classlike_storage): v
$codebase = $project_analyzer->getCodebase();

foreach ($classlike_storage->properties as $property_name => $property_storage) {
if (
$classlike_storage->public_api
&& ($property_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC
|| ($property_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PROTECTED
&& !$classlike_storage->final
)
)
) {
continue;
}

$referenced_property_name = strtolower($classlike_storage->name) . '::$' . $property_name;
$property_referenced = $this->file_reference_provider->isClassPropertyReferenced(
$referenced_property_name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,8 @@ public static function parse(
$info->description = $parsed_docblock->description;
}

$info->public_api = isset($parsed_docblock->tags['psalm-public-api']);

self::addMagicPropertyToInfo($comment, $info, $parsed_docblock->tags, 'property');
self::addMagicPropertyToInfo($comment, $info, $parsed_docblock->tags, 'psalm-property');
self::addMagicPropertyToInfo($comment, $info, $parsed_docblock->tags, 'property-read');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,8 @@ public function start(PhpParser\Node\Stmt\ClassLike $node): ?bool
if ($docblock_info->description) {
$storage->description = $docblock_info->description;
}

$storage->public_api = $docblock_info->public_api;
}

foreach ($node->stmts as $node_stmt) {
Expand Down
2 changes: 2 additions & 0 deletions src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,6 @@ class ClassLikeDocblockComment
public array $implementation_requirements = [];

public ?string $description = null;

public bool $public_api = false;
}
2 changes: 2 additions & 0 deletions src/Psalm/Storage/ClassLikeStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,8 @@ final class ClassLikeStorage implements HasAttributesInterface
*/
public $description;

public bool $public_api = false;

public function __construct(string $name)
{
$this->name = $name;
Expand Down
67 changes: 67 additions & 0 deletions tests/UnusedCodeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,33 @@ class A {
$a->bar[$a->foo] = "bar";
print_r($a->bar);',
],
'psalm-public-api with unused class' => [
'code' => <<<'PHP'
<?php
/** @psalm-public-api */
class A {}
PHP,
],
'psalm-public-api with unused public and protected property' => [
'code' => <<<'PHP'
<?php
/** @psalm-public-api */
class A {
public int $b = 0;
protected int $c = 0;
}
PHP,
],
'psalm-public-api with unused public and protected method' => [
'code' => <<<'PHP'
<?php
/** @psalm-public-api */
class A {
public function b(): void {}
protected function c(): void {}
}
PHP,
],
];
}

Expand Down Expand Up @@ -1749,6 +1776,46 @@ public static function f()
',
'error_message' => 'InaccessibleProperty',
],
'psalm-public-api with unused private property' => [
'code' => <<<'PHP'
<?php
/** @psalm-public-api */
class A {
private int $b = 0;
}
PHP,
'error_message' => 'UnusedProperty',
],
'psalm-public-api with final class and unused protected property' => [
'code' => <<<'PHP'
<?php
/** @psalm-public-api */
final class A {
protected int $b = 0;
}
PHP,
'error_message' => 'PossiblyUnusedProperty',
],
'psalm-public-api with unused private method' => [
'code' => <<<'PHP'
<?php
/** @psalm-public-api */
class A {
private function b(): void {}
}
PHP,
'error_message' => 'UnusedMethod',
],
'psalm-public-api with final class and unused protected method' => [
'code' => <<<'PHP'
<?php
/** @psalm-public-api */
final class A {
protected function b(): void {}
}
PHP,
'error_message' => 'PossiblyUnusedMethod',
],
];
}
}

0 comments on commit b6ec8fb

Please sign in to comment.