Skip to content

Commit

Permalink
Merge pull request #7192 from pilif/enum-case-deprecation
Browse files Browse the repository at this point in the history
  • Loading branch information
weirdan committed Dec 22, 2021
2 parents cc9111f + fdeb195 commit f2db139
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 26 deletions.
11 changes: 9 additions & 2 deletions docs/running_psalm/issues/DeprecatedConstant.md
@@ -1,6 +1,6 @@
# DeprecatedConstant

Emitted when referring to a deprecated constant:
Emitted when referring to a deprecated constant or enum case:

```php
<?php
Expand All @@ -11,6 +11,13 @@ class A {
}

echo A::FOO;

enum B {
/** @deprecated */
case B;
}

echo B::B;
```

## Why this is bad
Expand All @@ -19,4 +26,4 @@ The `@deprecated` tag is normally indicative of code that will stop working in t

## How to fix

Don’t use the deprecated constant.
Don’t use the deprecated constant or enum case
Expand Up @@ -218,6 +218,18 @@ public static function analyze(
}

$const_class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
if ($const_class_storage->is_enum) {
$case = $const_class_storage->enum_cases[(string)$stmt->name] ?? null;
if ($case && $case->deprecated) {
IssueBuffer::maybeAdd(
new DeprecatedConstant(
"Enum Case $const_id is marked as deprecated",
new CodeLocation($statements_analyzer->getSource(), $stmt)
),
$statements_analyzer->getSuppressedIssues()
);
}
}

if ($fq_class_name === $context->self
|| (
Expand Down
103 changes: 79 additions & 24 deletions src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php
Expand Up @@ -46,6 +46,7 @@
use Psalm\Issue\MissingDocblockType;
use Psalm\Issue\ParseError;
use Psalm\IssueBuffer;
use Psalm\Storage\AttributeStorage;
use Psalm\Storage\ClassConstantStorage;
use Psalm\Storage\ClassLikeStorage;
use Psalm\Storage\EnumCaseStorage;
Expand Down Expand Up @@ -1345,10 +1346,38 @@ private function visitEnumDeclaration(
$case_location = new CodeLocation($this->file_scanner, $stmt);

if (!isset($storage->enum_cases[$stmt->name->name])) {
$storage->enum_cases[$stmt->name->name] = new EnumCaseStorage(
$case = new EnumCaseStorage(
$enum_value,
$case_location
);

$attrs = $this->getAttributeStorageFromStatement(
$this->codebase,
$this->file_scanner,
$this->file_storage,
$this->aliases,
$stmt,
$this->storage->name ?? null
);

foreach ($attrs as $attribute) {
if ($attribute->fq_class_name === 'Psalm\\Deprecated'
|| $attribute->fq_class_name === 'JetBrains\\PhpStorm\\Deprecated'
) {
$case->deprecated = true;
break;
}
}

$comment = $stmt->getDocComment();
if ($comment) {
$comments = DocComment::parsePreservingLength($comment);

if (isset($comments->tags['deprecated'])) {
$case->deprecated = true;
}
}
$storage->enum_cases[$stmt->name->name] = $case;
} else {
if (IssueBuffer::accepts(
new DuplicateEnumCase(
Expand All @@ -1361,6 +1390,34 @@ private function visitEnumDeclaration(
}
}

/**
* @param PhpParser\Node\Stmt\Property|PhpParser\Node\Stmt\EnumCase $stmt
* @return list<AttributeStorage>
*/
private function getAttributeStorageFromStatement(
Codebase $codebase,
FileScanner $file_scanner,
FileStorage $file_storage,
Aliases $aliases,
PhpParser\Node\Stmt $stmt,
?string $fq_classlike_name
): array {
$storages = [];
foreach ($stmt->attrGroups as $attr_group) {
foreach ($attr_group->attrs as $attr) {
$storages[] = AttributeResolver::resolve(
$codebase,
$file_scanner,
$file_storage,
$aliases,
$attr,
$fq_classlike_name
);
}
}
return $storages;
}

private function visitPropertyDeclaration(
PhpParser\Node\Stmt\Property $stmt,
Config $config,
Expand Down Expand Up @@ -1556,33 +1613,31 @@ private function visitPropertyDeclaration(
$storage->inheritable_property_ids[$property->name->name] = $property_id;
}

foreach ($stmt->attrGroups as $attr_group) {
foreach ($attr_group->attrs as $attr) {
$attribute = AttributeResolver::resolve(
$this->codebase,
$this->file_scanner,
$this->file_storage,
$this->aliases,
$attr,
$this->storage->name ?? null
);

if ($attribute->fq_class_name === 'Psalm\\Deprecated'
|| $attribute->fq_class_name === 'JetBrains\\PhpStorm\\Deprecated'
) {
$property_storage->deprecated = true;
}
$attrs = $this->getAttributeStorageFromStatement(
$this->codebase,
$this->file_scanner,
$this->file_storage,
$this->aliases,
$stmt,
$this->storage->name ?? null
);

if ($attribute->fq_class_name === 'Psalm\\Internal' && !$property_storage->internal) {
$property_storage->internal = NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name);
}
foreach ($attrs as $attribute) {
if ($attribute->fq_class_name === 'Psalm\\Deprecated'
|| $attribute->fq_class_name === 'JetBrains\\PhpStorm\\Deprecated'
) {
$property_storage->deprecated = true;
}

if ($attribute->fq_class_name === 'Psalm\\Readonly') {
$property_storage->readonly = true;
}
if ($attribute->fq_class_name === 'Psalm\\Internal' && !$property_storage->internal) {
$property_storage->internal = NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name);
}

$property_storage->attributes[] = $attribute;
if ($attribute->fq_class_name === 'Psalm\\Readonly') {
$property_storage->readonly = true;
}

$property_storage->attributes[] = $attribute;
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/Psalm/Storage/EnumCaseStorage.php
Expand Up @@ -14,6 +14,11 @@ class EnumCaseStorage
/** @var CodeLocation */
public $stmt_location;

/**
* @var bool
*/
public $deprecated = false;

/**
* @param int|string|null $value
*/
Expand Down
16 changes: 16 additions & 0 deletions tests/DeprecatedAnnotationTest.php
Expand Up @@ -251,6 +251,22 @@ class Bar
',
'error_message' => 'DeprecatedProperty',
],
'deprecatedEnumCaseFetch' => [
'<?php
enum Foo {
case A;
/** @deprecated */
case B;
}
Foo::B;
',
'error_message' => 'DeprecatedConstant',
[],
false,
'8.1',
]
];
}
}
1 change: 1 addition & 0 deletions tests/DocumentationTest.php
Expand Up @@ -314,6 +314,7 @@ public function providerInvalidCodeParse(): array
case 'DuplicateEnumCase':
case 'DuplicateEnumCaseValue':
case 'NoEnumProperties':
case 'DeprecatedConstant':
$php_version = '8.1';
break;
}
Expand Down
16 changes: 16 additions & 0 deletions tests/EnumTest.php
Expand Up @@ -591,6 +591,22 @@ enum Status { }
false,
'8.1',
],
'deprecatedAttribute' => [
'<?php
enum Foo {
case A;
#[Psalm\Deprecated]
case B;
}
Foo::B;
',
'error_message' => 'DeprecatedConstant',
[],
false,
'8.1',
],
];
}
}

0 comments on commit f2db139

Please sign in to comment.