Skip to content

Commit e99fc4f

Browse files
committed
Support #[Deprecated] attribute in property hooks
1 parent 793c95d commit e99fc4f

File tree

6 files changed

+131
-7
lines changed

6 files changed

+131
-7
lines changed

src/Analyser/MutatingScope.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2999,6 +2999,8 @@ public function enterPropertyHook(
29992999
?Type $phpDocPropertyType,
30003000
array $phpDocParameterTypes,
30013001
?Type $throwType,
3002+
?string $deprecatedDescription,
3003+
bool $isDeprecated,
30023004
?string $phpDocComment,
30033005
): self
30043006
{
@@ -3054,8 +3056,8 @@ public function enterPropertyHook(
30543056
$realReturnType,
30553057
$phpDocReturnType,
30563058
$throwType,
3057-
null,
3058-
false,
3059+
$deprecatedDescription,
3060+
$isDeprecated,
30593061
false,
30603062
false,
30613063
false,

src/Analyser/NodeScopeResolver.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1985,7 +1985,7 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
19851985
/**
19861986
* @return array{bool, string|null}
19871987
*/
1988-
private function getDeprecatedAttribute(Scope $scope, Node\Stmt\Function_|Node\Stmt\ClassMethod $stmt): array
1988+
private function getDeprecatedAttribute(Scope $scope, Node\Stmt\Function_|Node\Stmt\ClassMethod|Node\PropertyHook $stmt): array
19891989
{
19901990
$initializerExprContext = InitializerExprContext::fromStubParameter(
19911991
null,
@@ -4684,13 +4684,17 @@ private function processPropertyHooks(
46844684
$this->processParamNode($stmt, $param, $scope, $nodeCallback);
46854685
}
46864686

4687+
[$isDeprecated, $deprecatedDescription] = $this->getDeprecatedAttribute($scope, $hook);
4688+
46874689
$hookScope = $scope->enterPropertyHook(
46884690
$hook,
46894691
$propertyName,
46904692
$nativeTypeNode,
46914693
$phpDocType,
46924694
$phpDocParameterTypes,
46934695
$phpDocThrowType,
4696+
$deprecatedDescription,
4697+
$isDeprecated,
46944698
$phpDocComment,
46954699
);
46964700
$hookReflection = $hookScope->getFunction();

src/Reflection/InitializerExprContext.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Reflection;
44

5+
use PhpParser\Node\PropertyHook;
56
use PhpParser\Node\Stmt\ClassMethod;
67
use PhpParser\Node\Stmt\Function_;
78
use PHPStan\Analyser\Scope;
@@ -115,7 +116,7 @@ public static function fromReflectionParameter(ReflectionParameter $parameter):
115116
public static function fromStubParameter(
116117
?string $className,
117118
string $stubFile,
118-
ClassMethod|Function_ $function,
119+
ClassMethod|Function_|PropertyHook $function,
119120
): self
120121
{
121122
$namespace = null;

tests/PHPStan/Reflection/Annotations/DeprecatedAnnotationsTest.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,4 +342,54 @@ public function testDeprecatedAttributeAboveEnumCase(string $className, string $
342342
$this->assertSame($deprecatedDescription, $case->getDeprecatedDescription());
343343
}
344344

345+
public function dataDeprecatedAttributeAbovePropertyHook(): iterable
346+
{
347+
yield [
348+
'DeprecatedAttributePropertyHooks\\Foo',
349+
'i',
350+
'get',
351+
TrinaryLogic::createNo(),
352+
null,
353+
];
354+
yield [
355+
'DeprecatedAttributePropertyHooks\\Foo',
356+
'j',
357+
'get',
358+
TrinaryLogic::createYes(),
359+
null,
360+
];
361+
yield [
362+
'DeprecatedAttributePropertyHooks\\Foo',
363+
'k',
364+
'get',
365+
TrinaryLogic::createYes(),
366+
'msg',
367+
];
368+
yield [
369+
'DeprecatedAttributePropertyHooks\\Foo',
370+
'l',
371+
'get',
372+
TrinaryLogic::createYes(),
373+
'msg2',
374+
];
375+
}
376+
377+
/**
378+
* @dataProvider dataDeprecatedAttributeAbovePropertyHook
379+
* @param 'get'|'set' $hookName
380+
*/
381+
public function testDeprecatedAttributeAbovePropertyHook(string $className, string $propertyName, string $hookName, TrinaryLogic $isDeprecated, ?string $deprecatedDescription): void
382+
{
383+
if (PHP_VERSION_ID < 80400) {
384+
$this->markTestSkipped('Test requires PHP 8.4.');
385+
}
386+
387+
$reflectionProvider = $this->createReflectionProvider();
388+
$class = $reflectionProvider->getClass($className);
389+
$property = $class->getNativeProperty($propertyName);
390+
$hook = $property->getHook($hookName);
391+
$this->assertSame($isDeprecated->describe(), $hook->isDeprecated()->describe());
392+
$this->assertSame($deprecatedDescription, $hook->getDeprecatedDescription());
393+
}
394+
345395
}

tests/PHPStan/Reflection/Annotations/DeprecatedAttributePhpFunctionFromParserReflectionRuleTest.php

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
use PHPStan\Analyser\Scope;
77
use PHPStan\Node\InClassMethodNode;
88
use PHPStan\Node\InFunctionNode;
9+
use PHPStan\Node\InPropertyHookNode;
910
use PHPStan\Rules\Rule;
1011
use PHPStan\Rules\RuleErrorBuilder;
1112
use PHPStan\Testing\RuleTestCase;
1213
use function sprintf;
14+
use const PHP_VERSION_ID;
1315

1416
/**
1517
* @extends RuleTestCase<Rule>
@@ -18,15 +20,15 @@ class DeprecatedAttributePhpFunctionFromParserReflectionRuleTest extends RuleTes
1820
{
1921

2022
/**
21-
* @return Rule<Node\Stmt>
23+
* @return Rule<Node>
2224
*/
2325
protected function getRule(): Rule
2426
{
25-
return new /** @implements Rule<Node\Stmt> */ class implements Rule {
27+
return new /** @implements Rule<Node> */ class implements Rule {
2628

2729
public function getNodeType(): string
2830
{
29-
return Node\Stmt::class;
31+
return Node::class;
3032
}
3133

3234
public function processNode(Node $node, Scope $scope): array
@@ -35,6 +37,8 @@ public function processNode(Node $node, Scope $scope): array
3537
$reflection = $node->getFunctionReflection();
3638
} elseif ($node instanceof InClassMethodNode) {
3739
$reflection = $node->getMethodReflection();
40+
} elseif ($node instanceof InPropertyHookNode) {
41+
$reflection = $node->getHookReflection();
3842
} else {
3943
return [];
4044
}
@@ -108,4 +112,30 @@ public function testMethodRule(): void
108112
]);
109113
}
110114

115+
public function testPropertyHookRule(): void
116+
{
117+
if (PHP_VERSION_ID < 80400) {
118+
$this->markTestSkipped('Test requires PHP 8.4.');
119+
}
120+
121+
$this->analyse([__DIR__ . '/data/deprecated-attribute-property-hooks.php'], [
122+
[
123+
'Not deprecated',
124+
11,
125+
],
126+
[
127+
'Deprecated',
128+
17,
129+
],
130+
[
131+
'Deprecated: msg',
132+
24,
133+
],
134+
[
135+
'Deprecated: msg2',
136+
31,
137+
],
138+
]);
139+
}
140+
111141
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php // lint >= 8.4
2+
3+
namespace DeprecatedAttributePropertyHooks;
4+
5+
use Deprecated;
6+
7+
class Foo
8+
{
9+
10+
public int $i {
11+
get {
12+
return 1;
13+
}
14+
}
15+
16+
public int $j {
17+
#[Deprecated]
18+
get {
19+
return 1;
20+
}
21+
}
22+
23+
public int $k {
24+
#[Deprecated('msg')]
25+
get {
26+
return 1;
27+
}
28+
}
29+
30+
public int $l {
31+
#[Deprecated(since: '1.0', message: 'msg2')]
32+
get {
33+
return 1;
34+
}
35+
}
36+
37+
}

0 commit comments

Comments
 (0)