Skip to content

Commit e174d79

Browse files
committed
Support for #[Deprecated] attribute
1 parent 9f78100 commit e174d79

14 files changed

+555
-7
lines changed

Diff for: src/Analyser/NodeScopeResolver.php

+60
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
use PHPStan\Reflection\ExtendedParameterReflection;
135135
use PHPStan\Reflection\ExtendedParametersAcceptor;
136136
use PHPStan\Reflection\FunctionReflection;
137+
use PHPStan\Reflection\InitializerExprContext;
137138
use PHPStan\Reflection\InitializerExprTypeResolver;
138139
use PHPStan\Reflection\MethodReflection;
139140
use PHPStan\Reflection\Native\NativeMethodReflection;
@@ -525,6 +526,10 @@ private function processStmtNode(
525526
$nodeCallback($stmt->returnType, $scope);
526527
}
527528

529+
if (!$isDeprecated) {
530+
[$isDeprecated, $deprecatedDescription] = $this->getDeprecatedAttribute($scope, $stmt);
531+
}
532+
528533
$functionScope = $scope->enterFunction(
529534
$stmt,
530535
$templateTypeMap,
@@ -609,6 +614,10 @@ private function processStmtNode(
609614
$nodeCallback($stmt->returnType, $scope);
610615
}
611616

617+
if (!$isDeprecated) {
618+
[$isDeprecated, $deprecatedDescription] = $this->getDeprecatedAttribute($scope, $stmt);
619+
}
620+
612621
$methodScope = $scope->enterClassMethod(
613622
$stmt,
614623
$templateTypeMap,
@@ -1933,6 +1942,57 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
19331942
return new StatementResult($scope, $hasYield, false, [], $throwPoints, $impurePoints);
19341943
}
19351944

1945+
/**
1946+
* @return array{bool, string|null}
1947+
*/
1948+
private function getDeprecatedAttribute(Scope $scope, Node\Stmt\Function_|Node\Stmt\ClassMethod $stmt): array
1949+
{
1950+
$initializerExprContext = InitializerExprContext::fromStubParameter(
1951+
null,
1952+
$scope->getFile(),
1953+
$stmt,
1954+
);
1955+
$isDeprecated = false;
1956+
$deprecatedDescription = null;
1957+
$deprecatedDescriptionType = null;
1958+
foreach ($stmt->attrGroups as $attrGroup) {
1959+
foreach ($attrGroup->attrs as $attr) {
1960+
if ($attr->name->toString() !== 'Deprecated') {
1961+
continue;
1962+
}
1963+
$isDeprecated = true;
1964+
$arguments = $attr->args;
1965+
foreach ($arguments as $i => $arg) {
1966+
$argName = $arg->name;
1967+
if ($argName === null) {
1968+
if ($i !== 0) {
1969+
continue;
1970+
}
1971+
1972+
$deprecatedDescriptionType = $this->initializerExprTypeResolver->getType($arg->value, $initializerExprContext);
1973+
break;
1974+
}
1975+
1976+
if ($argName->toString() !== 'message') {
1977+
continue;
1978+
}
1979+
1980+
$deprecatedDescriptionType = $this->initializerExprTypeResolver->getType($arg->value, $initializerExprContext);
1981+
break;
1982+
}
1983+
}
1984+
}
1985+
1986+
if ($deprecatedDescriptionType !== null) {
1987+
$constantStrings = $deprecatedDescriptionType->getConstantStrings();
1988+
if (count($constantStrings) === 1) {
1989+
$deprecatedDescription = $constantStrings[0]->getValue();
1990+
}
1991+
}
1992+
1993+
return [$isDeprecated, $deprecatedDescription];
1994+
}
1995+
19361996
/**
19371997
* @return ThrowPoint[]|null
19381998
*/

Diff for: src/Internal/DeprecatedAttributeHelper.php

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Internal;
4+
5+
use PHPStan\BetterReflection\Reflection\Attribute\ReflectionAttributeHelper;
6+
use PHPStan\BetterReflection\Reflection\ReflectionAttribute;
7+
use function is_int;
8+
use function is_string;
9+
10+
final class DeprecatedAttributeHelper
11+
{
12+
13+
/**
14+
* @param list<ReflectionAttribute> $attributes
15+
*/
16+
public static function getDeprecatedDescription(array $attributes): ?string
17+
{
18+
$deprecated = ReflectionAttributeHelper::filterAttributesByName($attributes, 'Deprecated');
19+
foreach ($deprecated as $attr) {
20+
$arguments = $attr->getArguments();
21+
foreach ($arguments as $i => $arg) {
22+
if (!is_string($arg)) {
23+
continue;
24+
}
25+
26+
if (is_int($i)) {
27+
if ($i !== 0) {
28+
continue;
29+
}
30+
31+
return $arg;
32+
}
33+
34+
if ($i !== 'message') {
35+
continue;
36+
}
37+
38+
return $arg;
39+
}
40+
}
41+
42+
return null;
43+
}
44+
45+
}

Diff for: src/Reflection/ClassReflection.php

+2-3
Original file line numberDiff line numberDiff line change
@@ -772,9 +772,8 @@ public function getEnumCases(): array
772772
if ($case instanceof ReflectionEnumBackedCase) {
773773
$valueType = $this->initializerExprTypeResolver->getType($case->getValueExpression(), $initializerExprContext);
774774
}
775-
/** @var string $caseName */
776775
$caseName = $case->getName();
777-
$cases[$caseName] = new EnumCaseReflection($this, $caseName, $valueType);
776+
$cases[$caseName] = new EnumCaseReflection($this, $case, $valueType);
778777
}
779778

780779
return $this->enumCases = $cases;
@@ -800,7 +799,7 @@ public function getEnumCase(string $name): EnumCaseReflection
800799
$valueType = $this->initializerExprTypeResolver->getType($case->getValueExpression(), InitializerExprContext::fromClassReflection($this));
801800
}
802801

803-
return new EnumCaseReflection($this, $name, $valueType);
802+
return new EnumCaseReflection($this, $case, $valueType);
804803
}
805804

806805
public function isClass(): bool

Diff for: src/Reflection/EnumCaseReflection.php

+21-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
namespace PHPStan\Reflection;
44

5+
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionEnumBackedCase;
6+
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionEnumUnitCase;
7+
use PHPStan\Internal\DeprecatedAttributeHelper;
8+
use PHPStan\TrinaryLogic;
59
use PHPStan\Type\Type;
610

711
/**
@@ -10,7 +14,7 @@
1014
final class EnumCaseReflection
1115
{
1216

13-
public function __construct(private ClassReflection $declaringEnum, private string $name, private ?Type $backingValueType)
17+
public function __construct(private ClassReflection $declaringEnum, private ReflectionEnumUnitCase|ReflectionEnumBackedCase $reflection, private ?Type $backingValueType)
1418
{
1519
}
1620

@@ -21,12 +25,27 @@ public function getDeclaringEnum(): ClassReflection
2125

2226
public function getName(): string
2327
{
24-
return $this->name;
28+
return $this->reflection->getName();
2529
}
2630

2731
public function getBackingValueType(): ?Type
2832
{
2933
return $this->backingValueType;
3034
}
3135

36+
public function isDeprecated(): TrinaryLogic
37+
{
38+
return TrinaryLogic::createFromBoolean($this->reflection->isDeprecated());
39+
}
40+
41+
public function getDeprecatedDescription(): ?string
42+
{
43+
if ($this->reflection->isDeprecated()) {
44+
$attributes = $this->reflection->getBetterReflection()->getAttributes();
45+
return DeprecatedAttributeHelper::getDeprecatedDescription($attributes);
46+
}
47+
48+
return null;
49+
}
50+
3251
}

Diff for: src/Reflection/Php/PhpFunctionReflection.php

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionFunction;
66
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionParameter;
7+
use PHPStan\Internal\DeprecatedAttributeHelper;
78
use PHPStan\Parser\Parser;
89
use PHPStan\Parser\VariadicFunctionsVisitor;
910
use PHPStan\Reflection\Assertions;
@@ -190,6 +191,11 @@ public function getDeprecatedDescription(): ?string
190191
return $this->deprecatedDescription;
191192
}
192193

194+
if ($this->reflection->isDeprecated()) {
195+
$attributes = $this->reflection->getBetterReflection()->getAttributes();
196+
return DeprecatedAttributeHelper::getDeprecatedDescription($attributes);
197+
}
198+
193199
return null;
194200
}
195201

Diff for: src/Reflection/Php/PhpMethodReflection.php

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionMethod;
66
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionParameter;
7+
use PHPStan\Internal\DeprecatedAttributeHelper;
78
use PHPStan\Parser\Parser;
89
use PHPStan\Parser\VariadicMethodsVisitor;
910
use PHPStan\Reflection\Assertions;
@@ -352,6 +353,11 @@ public function getDeprecatedDescription(): ?string
352353
return $this->deprecatedDescription;
353354
}
354355

356+
if ($this->reflection->isDeprecated()) {
357+
$attributes = $this->reflection->getBetterReflection()->getAttributes();
358+
return DeprecatedAttributeHelper::getDeprecatedDescription($attributes);
359+
}
360+
355361
return null;
356362
}
357363

Diff for: src/Reflection/RealClassClassConstantReflection.php

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use PhpParser\Node\Expr;
66
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionClassConstant;
7+
use PHPStan\Internal\DeprecatedAttributeHelper;
78
use PHPStan\TrinaryLogic;
89
use PHPStan\Type\Type;
910
use PHPStan\Type\TypehintHelper;
@@ -111,7 +112,7 @@ public function isFinal(): bool
111112

112113
public function isDeprecated(): TrinaryLogic
113114
{
114-
return TrinaryLogic::createFromBoolean($this->isDeprecated);
115+
return TrinaryLogic::createFromBoolean($this->isDeprecated || $this->reflection->isDeprecated());
115116
}
116117

117118
public function getDeprecatedDescription(): ?string
@@ -120,6 +121,11 @@ public function getDeprecatedDescription(): ?string
120121
return $this->deprecatedDescription;
121122
}
122123

124+
if ($this->reflection->isDeprecated()) {
125+
$attributes = $this->reflection->getBetterReflection()->getAttributes();
126+
return DeprecatedAttributeHelper::getDeprecatedDescription($attributes);
127+
}
128+
123129
return null;
124130
}
125131

Diff for: src/Reflection/SignatureMap/NativeFunctionReflectionProvider.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public function findFunctionReflection(string $functionName): ?NativeFunctionRef
5757
$reflectionFunctionAdapter = new ReflectionFunction($reflectionFunction);
5858
$returnsByReference = TrinaryLogic::createFromBoolean($reflectionFunctionAdapter->returnsReference());
5959
$realFunctionName = $reflectionFunction->getName();
60+
$isDeprecated = $reflectionFunction->isDeprecated();
6061
if ($reflectionFunction->getFileName() !== null) {
6162
$fileName = $reflectionFunction->getFileName();
6263
$docComment = $reflectionFunction->getDocComment();
@@ -66,7 +67,6 @@ public function findFunctionReflection(string $functionName): ?NativeFunctionRef
6667
if ($throwsTag !== null) {
6768
$throwType = $throwsTag->getType();
6869
}
69-
$isDeprecated = $reflectionFunction->isDeprecated();
7070
}
7171
}
7272
} catch (IdentifierNotFound | InvalidIdentifierName) {

0 commit comments

Comments
 (0)