Skip to content

Commit f0675a7

Browse files
committed
[PHP 8.5] Support for deprecated traits
1 parent f63b423 commit f0675a7

File tree

9 files changed

+124
-0
lines changed

9 files changed

+124
-0
lines changed

src/Php/PhpVersion.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,4 +454,9 @@ public function supportsAttributesOnGlobalConstants(): bool
454454
return $this->versionId >= 80500;
455455
}
456456

457+
public function supportsDeprecatedTraits(): bool
458+
{
459+
return $this->versionId >= 80500;
460+
}
461+
457462
}

src/Reflection/ClassReflection.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,6 +1434,12 @@ private function resolveDeprecation(): void
14341434
return;
14351435
}
14361436

1437+
if ($this->isTrait() && count($this->getNativeReflection()->getAttributes('Deprecated')) > 0) {
1438+
$this->isDeprecated = true;
1439+
$this->deprecatedDescription = null;
1440+
return;
1441+
}
1442+
14371443
$this->isDeprecated = false;
14381444
$this->deprecatedDescription = null;
14391445
}

src/Rules/Classes/ClassAttributesRule.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use PHPStan\Rules\RuleErrorBuilder;
1313
use function count;
1414
use function sprintf;
15+
use function strtolower;
1516

1617
/**
1718
* @implements Rule<InClassNode>
@@ -41,6 +42,15 @@ public function processNode(Node $node, Scope $scope): array
4142
);
4243

4344
$classReflection = $node->getClassReflection();
45+
46+
if (count($classReflection->getNativeReflection()->getAttributes('Deprecated')) > 0) {
47+
$typeName = strtolower($classReflection->getClassTypeDescription());
48+
$errors[] = RuleErrorBuilder::message(sprintf('Attribute class Deprecated cannot be used with %s %s.', $typeName, $classReflection->getDisplayName()))
49+
->identifier(sprintf('%s.deprecatedAttribute', $typeName))
50+
->nonIgnorable()
51+
->build();
52+
}
53+
4454
if (
4555
$classReflection->isReadOnly()
4656
|| $classReflection->isEnum()

src/Rules/Traits/TraitAttributesRule.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\Analyser\Scope;
88
use PHPStan\DependencyInjection\RegisteredRule;
99
use PHPStan\Node\InTraitNode;
10+
use PHPStan\Php\PhpVersion;
1011
use PHPStan\Rules\AttributesCheck;
1112
use PHPStan\Rules\Rule;
1213
use PHPStan\Rules\RuleErrorBuilder;
@@ -21,6 +22,7 @@ final class TraitAttributesRule implements Rule
2122

2223
public function __construct(
2324
private AttributesCheck $attributesCheck,
25+
private PhpVersion $phpVersion,
2426
)
2527
{
2628
}
@@ -40,6 +42,15 @@ public function processNode(Node $node, Scope $scope): array
4042
'class',
4143
);
4244

45+
if (!$this->phpVersion->supportsDeprecatedTraits()) {
46+
if (count($node->getTraitReflection()->getNativeReflection()->getAttributes('Deprecated')) > 0) {
47+
$errors[] = RuleErrorBuilder::message('Attribute class Deprecated can be used with traits only on PHP 8.5 and later.')
48+
->identifier('trait.deprecatedAttribute')
49+
->nonIgnorable()
50+
->build();
51+
}
52+
}
53+
4354
if (count($node->getTraitReflection()->getNativeReflection()->getAttributes('AllowDynamicProperties')) > 0) {
4455
$errors[] = RuleErrorBuilder::message('Attribute class AllowDynamicProperties cannot be used with trait.')
4556
->identifier('trait.allowDynamicProperties')

tests/PHPStan/Reflection/ClassReflectionTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Attributes\IsAttribute2;
88
use Attributes\IsAttribute3;
99
use Attributes\IsNotAttribute;
10+
use DeprecatedAttributeOnTrait\DeprTrait;
1011
use GenericInheritance\C;
1112
use HasTraitUse\Bar;
1213
use HasTraitUse\Baz;
@@ -318,4 +319,18 @@ public function testIs(): void
318319
$this->assertFalse($classReflection->is(RuleTestCase::class));
319320
}
320321

322+
public static function dataDeprecatedAttribute(): iterable
323+
{
324+
yield [\DeprecatedAttrOnClass\Foo::class, false];
325+
yield [DeprTrait::class, true]; // @phpstan-ignore classConstant.deprecatedTrait
326+
}
327+
328+
#[DataProvider('dataDeprecatedAttribute')]
329+
public function testDeprecatedAttribute(string $className, bool $expected): void
330+
{
331+
$reflectionProvider = self::createReflectionProvider();
332+
$classReflection = $reflectionProvider->getClass($className);
333+
$this->assertSame($expected, $classReflection->isDeprecated());
334+
}
335+
321336
}

tests/PHPStan/Rules/Classes/ClassAttributesRuleTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,4 +185,22 @@ public function testBug12281(): void
185185
]);
186186
}
187187

188+
public function testDeprecatedAttribute(): void
189+
{
190+
$this->analyse([__DIR__ . '/data/deprecated-attr-on-class.php'], [
191+
[
192+
'Attribute class Deprecated cannot be used with class DeprecatedAttrOnClass\Foo.',
193+
7,
194+
],
195+
[
196+
'Attribute class Deprecated cannot be used with interface DeprecatedAttrOnClass\Bar.',
197+
13,
198+
],
199+
[
200+
'Attribute class Deprecated cannot be used with enum DeprecatedAttrOnClass\Baz.',
201+
19,
202+
],
203+
]);
204+
}
205+
188206
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php // lint >= 8.1
2+
3+
namespace DeprecatedAttrOnClass;
4+
5+
use Deprecated;
6+
7+
#[Deprecated]
8+
class Foo
9+
{
10+
11+
}
12+
13+
#[Deprecated]
14+
interface Bar
15+
{
16+
17+
}
18+
19+
#[Deprecated]
20+
enum Baz
21+
{
22+
23+
}

tests/PHPStan/Rules/Traits/TraitAttributesRuleTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Rules\Traits;
44

5+
use PHPStan\Php\PhpVersion;
56
use PHPStan\Rules\AttributesCheck;
67
use PHPStan\Rules\ClassCaseSensitivityCheck;
78
use PHPStan\Rules\ClassForbiddenNameCheck;
@@ -13,6 +14,7 @@
1314
use PHPStan\Rules\Rule;
1415
use PHPStan\Rules\RuleLevelHelper;
1516
use PHPStan\Testing\RuleTestCase;
17+
use PHPUnit\Framework\Attributes\RequiresPhp;
1618
use const PHP_VERSION_ID;
1719

1820
/**
@@ -49,6 +51,7 @@ protected function getRule(): Rule
4951
),
5052
true,
5153
),
54+
new PhpVersion(PHP_VERSION_ID),
5255
);
5356
}
5457

@@ -92,4 +95,21 @@ public function testBug12281(): void
9295
]);
9396
}
9497

98+
#[RequiresPhp('>= 8.5')]
99+
public function testBugDeprecatedAttributeAllowed(): void
100+
{
101+
$this->analyse([__DIR__ . '/data/deprecated-attr-on-trait.php'], []);
102+
}
103+
104+
#[RequiresPhp('< 8.5')]
105+
public function testBugDeprecatedAttributeNotAllowed(): void
106+
{
107+
$this->analyse([__DIR__ . '/data/deprecated-attr-on-trait.php'], [
108+
[
109+
'Attribute class Deprecated can be used with traits only on PHP 8.5 and later.',
110+
5,
111+
],
112+
]);
113+
}
114+
95115
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php // lint >= 8.5
2+
3+
namespace DeprecatedAttributeOnTrait;
4+
5+
#[\Deprecated]
6+
trait DeprTrait
7+
{
8+
9+
}
10+
11+
class Foo
12+
{
13+
14+
use DeprTrait;
15+
16+
}

0 commit comments

Comments
 (0)