From 6029f5ea31010e548598acd963af29681a8e7e69 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 2 Mar 2024 07:53:12 +0100 Subject: [PATCH 1/6] Report uses of deprecated constants --- .../BetterReflection/BetterReflectionProvider.php | 1 + src/Reflection/Constant/RuntimeConstantReflection.php | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Reflection/BetterReflection/BetterReflectionProvider.php b/src/Reflection/BetterReflection/BetterReflectionProvider.php index 68c62e4801..9269c12d1a 100644 --- a/src/Reflection/BetterReflection/BetterReflectionProvider.php +++ b/src/Reflection/BetterReflection/BetterReflectionProvider.php @@ -365,6 +365,7 @@ public function getConstant(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAn $constantName, $constantValueType, $fileName, + $constantReflection->getDocComment(), ); } diff --git a/src/Reflection/Constant/RuntimeConstantReflection.php b/src/Reflection/Constant/RuntimeConstantReflection.php index 341ae68cc4..5786f3278a 100644 --- a/src/Reflection/Constant/RuntimeConstantReflection.php +++ b/src/Reflection/Constant/RuntimeConstantReflection.php @@ -2,6 +2,7 @@ namespace PHPStan\Reflection\Constant; +use PHPStan\BetterReflection\Reflection\Annotation\AnnotationHelper; use PHPStan\Reflection\GlobalConstantReflection; use PHPStan\TrinaryLogic; use PHPStan\Type\Type; @@ -13,6 +14,7 @@ public function __construct( private string $name, private Type $valueType, private ?string $fileName, + private ?string $docComment, ) { } @@ -34,7 +36,7 @@ public function getFileName(): ?string public function isDeprecated(): TrinaryLogic { - return TrinaryLogic::createNo(); + return TrinaryLogic::createFromBoolean(AnnotationHelper::isDeprecated($this->getDocComment())); } public function getDeprecatedDescription(): ?string @@ -42,6 +44,12 @@ public function getDeprecatedDescription(): ?string return null; } + /** @return non-empty-string|null */ + public function getDocComment(): ?string + { + return $this->docComment; + } + public function isInternal(): TrinaryLogic { return TrinaryLogic::createNo(); From 13f039de4226e85a8ddff7a380b9f4dd5d228721 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 3 Mar 2024 09:01:25 +0100 Subject: [PATCH 2/6] fix build --- src/Reflection/Constant/RuntimeConstantReflection.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Reflection/Constant/RuntimeConstantReflection.php b/src/Reflection/Constant/RuntimeConstantReflection.php index 5786f3278a..1f14702fec 100644 --- a/src/Reflection/Constant/RuntimeConstantReflection.php +++ b/src/Reflection/Constant/RuntimeConstantReflection.php @@ -10,6 +10,9 @@ class RuntimeConstantReflection implements GlobalConstantReflection { + /** + * @param non-empty-string|null $docComment + */ public function __construct( private string $name, private Type $valueType, From 40be91ff72da59c1ed975d7ef886c46994619556 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 3 Mar 2024 09:44:20 +0100 Subject: [PATCH 3/6] implement getDeprecatedDescription() --- .../BetterReflectionProvider.php | 1 + .../Constant/RuntimeConstantReflection.php | 37 +++++++++++++-- .../RuntimeConstantReflectionTest.php | 47 +++++++++++++++++++ .../Constant/data/deprecated-constant.php | 15 ++++++ 4 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 tests/PHPStan/Reflection/Constant/RuntimeConstantReflectionTest.php create mode 100644 tests/PHPStan/Reflection/Constant/data/deprecated-constant.php diff --git a/src/Reflection/BetterReflection/BetterReflectionProvider.php b/src/Reflection/BetterReflection/BetterReflectionProvider.php index 9269c12d1a..dd3d872345 100644 --- a/src/Reflection/BetterReflection/BetterReflectionProvider.php +++ b/src/Reflection/BetterReflection/BetterReflectionProvider.php @@ -365,6 +365,7 @@ public function getConstant(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAn $constantName, $constantValueType, $fileName, + $this->fileTypeMapper, $constantReflection->getDocComment(), ); } diff --git a/src/Reflection/Constant/RuntimeConstantReflection.php b/src/Reflection/Constant/RuntimeConstantReflection.php index 1f14702fec..60518597ce 100644 --- a/src/Reflection/Constant/RuntimeConstantReflection.php +++ b/src/Reflection/Constant/RuntimeConstantReflection.php @@ -2,14 +2,19 @@ namespace PHPStan\Reflection\Constant; -use PHPStan\BetterReflection\Reflection\Annotation\AnnotationHelper; +use PHPStan\PhpDoc\ResolvedPhpDocBlock; use PHPStan\Reflection\GlobalConstantReflection; use PHPStan\TrinaryLogic; +use PHPStan\Type\FileTypeMapper; use PHPStan\Type\Type; class RuntimeConstantReflection implements GlobalConstantReflection { + private false|ResolvedPhpDocBlock $resolvedPhpDocBlock = false; + + private ?string $deprecatedDescription = null; + /** * @param non-empty-string|null $docComment */ @@ -17,6 +22,7 @@ public function __construct( private string $name, private Type $valueType, private ?string $fileName, + private FileTypeMapper $fileTypeMapper, private ?string $docComment, ) { @@ -39,12 +45,24 @@ public function getFileName(): ?string public function isDeprecated(): TrinaryLogic { - return TrinaryLogic::createFromBoolean(AnnotationHelper::isDeprecated($this->getDocComment())); + $resolvedPhpDoc = $this->getResolvedPhpDoc(); + if ($resolvedPhpDoc === null) { + return TrinaryLogic::createNo(); + } + + return TrinaryLogic::createFromBoolean($resolvedPhpDoc->isDeprecated()); } public function getDeprecatedDescription(): ?string { - return null; + if ($this->deprecatedDescription === null && $this->isDeprecated()->yes()) { + $resolvedPhpDoc = $this->getResolvedPhpDoc(); + if ($resolvedPhpDoc !== null && $resolvedPhpDoc->getDeprecatedTag() !== null) { + $this->deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag()->getMessage(); + } + } + + return $this->deprecatedDescription; } /** @return non-empty-string|null */ @@ -53,6 +71,19 @@ public function getDocComment(): ?string return $this->docComment; } + private function getResolvedPhpDoc(): ?ResolvedPhpDocBlock + { + if ($this->docComment === null) { + return null; + } + + if ($this->resolvedPhpDocBlock !== false) { + return $this->resolvedPhpDocBlock; + } + + return $this->resolvedPhpDocBlock = $this->fileTypeMapper->getResolvedPhpDoc($this->getFileName(), null, null, null, $this->docComment); + } + public function isInternal(): TrinaryLogic { return TrinaryLogic::createNo(); diff --git a/tests/PHPStan/Reflection/Constant/RuntimeConstantReflectionTest.php b/tests/PHPStan/Reflection/Constant/RuntimeConstantReflectionTest.php new file mode 100644 index 0000000000..d2ed0fe85e --- /dev/null +++ b/tests/PHPStan/Reflection/Constant/RuntimeConstantReflectionTest.php @@ -0,0 +1,47 @@ +createReflectionProvider(); + + $this->assertTrue($reflectionProvider->hasConstant($constName, null)); + $this->assertSame($isDeprecated, $reflectionProvider->getConstant($constName, null)->isDeprecated()); + $this->assertSame($deprecationMessage, $reflectionProvider->getConstant($constName, null)->getDeprecatedDescription()); + } + +} diff --git a/tests/PHPStan/Reflection/Constant/data/deprecated-constant.php b/tests/PHPStan/Reflection/Constant/data/deprecated-constant.php new file mode 100644 index 0000000000..9dfc01dea9 --- /dev/null +++ b/tests/PHPStan/Reflection/Constant/data/deprecated-constant.php @@ -0,0 +1,15 @@ + Date: Mon, 4 Mar 2024 09:54:42 +0100 Subject: [PATCH 4/6] extract logic from RuntimeConstantReflection --- .../BetterReflectionProvider.php | 17 ++++++- .../Constant/RuntimeConstantReflection.php | 46 ++----------------- 2 files changed, 18 insertions(+), 45 deletions(-) diff --git a/src/Reflection/BetterReflection/BetterReflectionProvider.php b/src/Reflection/BetterReflection/BetterReflectionProvider.php index dd3d872345..807f64922d 100644 --- a/src/Reflection/BetterReflection/BetterReflectionProvider.php +++ b/src/Reflection/BetterReflection/BetterReflectionProvider.php @@ -43,6 +43,7 @@ use PHPStan\Reflection\SignatureMap\NativeFunctionReflectionProvider; use PHPStan\Reflection\SignatureMap\SignatureMapProvider; use PHPStan\ShouldNotHappenException; +use PHPStan\TrinaryLogic; use PHPStan\Type\FileTypeMapper; use PHPStan\Type\Generic\TemplateTypeMap; use PHPStan\Type\Type; @@ -360,13 +361,25 @@ public function getConstant(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAn $constantReflection = $this->reflector->reflectConstant($constantName); $fileName = $constantReflection->getFileName(); $constantValueType = $this->initializerExprTypeResolver->getType($constantReflection->getValueExpression(), InitializerExprContext::fromGlobalConstant($constantReflection)); + $docComment = $constantReflection->getDocComment(); + + $isDeprecated = TrinaryLogic::createNo(); + $deprecatedDescription = null; + if ($docComment !== null) { + $resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc($fileName, null, null, null, $docComment); + $isDeprecated = TrinaryLogic::createFromBoolean($resolvedPhpDoc->isDeprecated()); + + if ($resolvedPhpDoc->isDeprecated() && $resolvedPhpDoc->getDeprecatedTag() !== null) { + $deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag()->getMessage(); + } + } return $this->cachedConstants[$constantName] = new RuntimeConstantReflection( $constantName, $constantValueType, $fileName, - $this->fileTypeMapper, - $constantReflection->getDocComment(), + $isDeprecated, + $deprecatedDescription, ); } diff --git a/src/Reflection/Constant/RuntimeConstantReflection.php b/src/Reflection/Constant/RuntimeConstantReflection.php index 60518597ce..7bc10879f4 100644 --- a/src/Reflection/Constant/RuntimeConstantReflection.php +++ b/src/Reflection/Constant/RuntimeConstantReflection.php @@ -2,28 +2,19 @@ namespace PHPStan\Reflection\Constant; -use PHPStan\PhpDoc\ResolvedPhpDocBlock; use PHPStan\Reflection\GlobalConstantReflection; use PHPStan\TrinaryLogic; -use PHPStan\Type\FileTypeMapper; use PHPStan\Type\Type; class RuntimeConstantReflection implements GlobalConstantReflection { - private false|ResolvedPhpDocBlock $resolvedPhpDocBlock = false; - - private ?string $deprecatedDescription = null; - - /** - * @param non-empty-string|null $docComment - */ public function __construct( private string $name, private Type $valueType, private ?string $fileName, - private FileTypeMapper $fileTypeMapper, - private ?string $docComment, + private TrinaryLogic $isDeprecated, + private ?string $deprecatedDescription, ) { } @@ -45,45 +36,14 @@ public function getFileName(): ?string public function isDeprecated(): TrinaryLogic { - $resolvedPhpDoc = $this->getResolvedPhpDoc(); - if ($resolvedPhpDoc === null) { - return TrinaryLogic::createNo(); - } - - return TrinaryLogic::createFromBoolean($resolvedPhpDoc->isDeprecated()); + return $this->isDeprecated; } public function getDeprecatedDescription(): ?string { - if ($this->deprecatedDescription === null && $this->isDeprecated()->yes()) { - $resolvedPhpDoc = $this->getResolvedPhpDoc(); - if ($resolvedPhpDoc !== null && $resolvedPhpDoc->getDeprecatedTag() !== null) { - $this->deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag()->getMessage(); - } - } - return $this->deprecatedDescription; } - /** @return non-empty-string|null */ - public function getDocComment(): ?string - { - return $this->docComment; - } - - private function getResolvedPhpDoc(): ?ResolvedPhpDocBlock - { - if ($this->docComment === null) { - return null; - } - - if ($this->resolvedPhpDocBlock !== false) { - return $this->resolvedPhpDocBlock; - } - - return $this->resolvedPhpDocBlock = $this->fileTypeMapper->getResolvedPhpDoc($this->getFileName(), null, null, null, $this->docComment); - } - public function isInternal(): TrinaryLogic { return TrinaryLogic::createNo(); From e51deb7dec3879a2dfa7be0acf02a3bf09fcab1a Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 4 Mar 2024 10:01:45 +0100 Subject: [PATCH 5/6] Update RuntimeConstantReflectionTest.php --- .../RuntimeConstantReflectionTest.php | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/tests/PHPStan/Reflection/Constant/RuntimeConstantReflectionTest.php b/tests/PHPStan/Reflection/Constant/RuntimeConstantReflectionTest.php index d2ed0fe85e..fed66c40f2 100644 --- a/tests/PHPStan/Reflection/Constant/RuntimeConstantReflectionTest.php +++ b/tests/PHPStan/Reflection/Constant/RuntimeConstantReflectionTest.php @@ -5,28 +5,40 @@ use PhpParser\Node\Name; use PHPStan\Testing\PHPStanTestCase; use PHPStan\TrinaryLogic; +use const PHP_VERSION_ID; class RuntimeConstantReflectionTest extends PHPStanTestCase { - public function dataDeprecatedConstants(): array + public function dataDeprecatedConstants(): iterable { - return [ - [ - new Name('\DeprecatedConst\FINE'), - TrinaryLogic::createNo(), - null, - ], - [ - new Name('\DeprecatedConst\MY_CONST'), + if (PHP_VERSION_ID >= 80100) { + yield [ + new Name('\FILTER_SANITIZE_STRING'), TrinaryLogic::createYes(), - null, - ], - [ - new Name('\DeprecatedConst\MY_CONST2'), - TrinaryLogic::createYes(), - "don't use it!", - ], + '8.1', // deprecation message used in e.g. https://github.com/JetBrains/phpstorm-stubs/blob/9608c953230b08f07b703ecfe459cc58d5421437/filter/filter.php#L478 + ]; + } + yield [ + new Name('\CURLOPT_FTP_SSL'), + TrinaryLogic::createYes(), + 'use CURLOPT_USE_SSL instead.', + ]; + + yield [ + new Name('\DeprecatedConst\FINE'), + TrinaryLogic::createNo(), + null, + ]; + yield [ + new Name('\DeprecatedConst\MY_CONST'), + TrinaryLogic::createYes(), + null, + ]; + yield [ + new Name('\DeprecatedConst\MY_CONST2'), + TrinaryLogic::createYes(), + "don't use it!", ]; } From e7b57e507232d51f2c160f68697013d9917c4613 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 5 Mar 2024 09:00:12 +0100 Subject: [PATCH 6/6] filter raw version number messages --- .../BetterReflection/BetterReflectionProvider.php | 9 ++++++++- .../Constant/RuntimeConstantReflectionTest.php | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Reflection/BetterReflection/BetterReflectionProvider.php b/src/Reflection/BetterReflection/BetterReflectionProvider.php index 807f64922d..770447f085 100644 --- a/src/Reflection/BetterReflection/BetterReflectionProvider.php +++ b/src/Reflection/BetterReflection/BetterReflectionProvider.php @@ -3,6 +3,7 @@ namespace PHPStan\Reflection\BetterReflection; use Closure; +use Nette\Utils\Strings; use PhpParser\Node; use PHPStan\Analyser\Scope; use PHPStan\BetterReflection\Identifier\Exception\InvalidIdentifierName; @@ -370,7 +371,13 @@ public function getConstant(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAn $isDeprecated = TrinaryLogic::createFromBoolean($resolvedPhpDoc->isDeprecated()); if ($resolvedPhpDoc->isDeprecated() && $resolvedPhpDoc->getDeprecatedTag() !== null) { - $deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag()->getMessage(); + $deprecatedMessage = $resolvedPhpDoc->getDeprecatedTag()->getMessage(); + + // filter raw version number messages like in + // https://github.com/JetBrains/phpstorm-stubs/blob/9608c953230b08f07b703ecfe459cc58d5421437/filter/filter.php#L478 + if (Strings::match($deprecatedMessage ?? '', '#^\d+\.\d+(\.\d+)?$#') === null) { + $deprecatedDescription = $deprecatedMessage; + } } } diff --git a/tests/PHPStan/Reflection/Constant/RuntimeConstantReflectionTest.php b/tests/PHPStan/Reflection/Constant/RuntimeConstantReflectionTest.php index fed66c40f2..c11a9ee60b 100644 --- a/tests/PHPStan/Reflection/Constant/RuntimeConstantReflectionTest.php +++ b/tests/PHPStan/Reflection/Constant/RuntimeConstantReflectionTest.php @@ -16,7 +16,7 @@ public function dataDeprecatedConstants(): iterable yield [ new Name('\FILTER_SANITIZE_STRING'), TrinaryLogic::createYes(), - '8.1', // deprecation message used in e.g. https://github.com/JetBrains/phpstorm-stubs/blob/9608c953230b08f07b703ecfe459cc58d5421437/filter/filter.php#L478 + null, ]; } yield [