From badbbd669c7c78349c6a9428e6b11c2fa484acdb Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Fri, 11 Apr 2025 15:25:29 +0200 Subject: [PATCH 01/15] DeprecationProviders: allow custom deprecation-marking logic --- conf/config.neon | 10 + src/Analyser/NodeScopeResolver.php | 3 + .../BetterReflectionProvider.php | 35 ++-- src/Reflection/ClassReflection.php | 49 ++++- .../ClassConstantDeprecationProvider.php | 27 +++ .../Deprecation/ClassDeprecationProvider.php | 28 +++ .../ConstantDeprecationProvider.php | 27 +++ src/Reflection/Deprecation/Deprecation.php | 35 ++++ .../Deprecation/DeprecationResolver.php | 107 ++++++++++ .../FunctionDeprecationProvider.php | 27 +++ .../Deprecation/MethodDeprecationProvider.php | 27 +++ .../PropertyDeprecationProvider.php | 27 +++ .../Php/PhpClassReflectionExtension.php | 24 ++- src/Testing/RuleTestCase.php | 2 + src/Testing/TypeInferenceTestCase.php | 2 + tests/PHPStan/Analyser/AnalyserTest.php | 2 + .../Deprecation/DeprecationProvidersTest.php | 190 ++++++++++++++++++ .../data/CustomDeprecationProvider.php | 69 +++++++ .../data/deprecation-provider.neon | 10 + .../Deprecation/data/deprecations.php | 160 +++++++++++++++ 20 files changed, 834 insertions(+), 27 deletions(-) create mode 100644 src/Reflection/Deprecation/ClassConstantDeprecationProvider.php create mode 100644 src/Reflection/Deprecation/ClassDeprecationProvider.php create mode 100644 src/Reflection/Deprecation/ConstantDeprecationProvider.php create mode 100644 src/Reflection/Deprecation/Deprecation.php create mode 100644 src/Reflection/Deprecation/DeprecationResolver.php create mode 100644 src/Reflection/Deprecation/FunctionDeprecationProvider.php create mode 100644 src/Reflection/Deprecation/MethodDeprecationProvider.php create mode 100644 src/Reflection/Deprecation/PropertyDeprecationProvider.php create mode 100644 tests/PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php create mode 100644 tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationProvider.php create mode 100644 tests/PHPStan/Reflection/Deprecation/data/deprecation-provider.neon create mode 100644 tests/PHPStan/Reflection/Deprecation/data/deprecations.php diff --git a/conf/config.neon b/conf/config.neon index 555e7ec53c..9ebeec968b 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -2157,6 +2157,16 @@ services: - class: PHPStan\Reflection\BetterReflection\SourceStubber\ReflectionSourceStubberFactory + - + class: PHPStan\Reflection\Deprecation\DeprecationResolver + arguments: + propertyDeprecationProviders: tagged(phpstan.propertyDeprecationProvider) + methodDeprecationProviders: tagged(phpstan.methodDeprecationProvider) + classConstantDeprecationProviders: tagged(phpstan.classConstantDeprecationProvider) + classDeprecationProviders: tagged(phpstan.classDeprecationProvider) + functionDeprecationProviders: tagged(phpstan.functionDeprecationProvider) + constantDeprecationProviders: tagged(phpstan.constantDeprecationProvider) + php8Lexer: class: PhpParser\Lexer\Emulative factory: @PHPStan\Parser\LexerFactory::createEmulative() diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 668c3aa9bd..de32bb8660 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -137,6 +137,7 @@ use PHPStan\Reflection\Callables\SimpleImpurePoint; use PHPStan\Reflection\Callables\SimpleThrowPoint; use PHPStan\Reflection\ClassReflection; +use PHPStan\Reflection\Deprecation\DeprecationResolver; use PHPStan\Reflection\ExtendedMethodReflection; use PHPStan\Reflection\ExtendedParameterReflection; use PHPStan\Reflection\ExtendedParametersAcceptor; @@ -255,6 +256,7 @@ public function __construct( private readonly StubPhpDocProvider $stubPhpDocProvider, private readonly PhpVersion $phpVersion, private readonly SignatureMapProvider $signatureMapProvider, + private readonly DeprecationResolver $deprecationResolver, private readonly AttributeReflectionFactory $attributeReflectionFactory, private readonly PhpDocInheritanceResolver $phpDocInheritanceResolver, private readonly FileHelper $fileHelper, @@ -2140,6 +2142,7 @@ private function createAstClassReflection(Node\Stmt\ClassLike $stmt, string $cla $this->phpDocInheritanceResolver, $this->phpVersion, $this->signatureMapProvider, + $this->deprecationResolver, $this->attributeReflectionFactory, $this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(), $this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(), diff --git a/src/Reflection/BetterReflection/BetterReflectionProvider.php b/src/Reflection/BetterReflection/BetterReflectionProvider.php index 4029d26554..2c8781c38e 100644 --- a/src/Reflection/BetterReflection/BetterReflectionProvider.php +++ b/src/Reflection/BetterReflection/BetterReflectionProvider.php @@ -36,6 +36,7 @@ use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\Constant\RuntimeConstantReflection; use PHPStan\Reflection\ConstantReflection; +use PHPStan\Reflection\Deprecation\DeprecationResolver; use PHPStan\Reflection\FunctionReflection; use PHPStan\Reflection\FunctionReflectionFactory; use PHPStan\Reflection\InitializerExprContext; @@ -85,6 +86,7 @@ public function __construct( private Reflector $reflector, private FileTypeMapper $fileTypeMapper, private PhpDocInheritanceResolver $phpDocInheritanceResolver, + private DeprecationResolver $deprecationResolver, private PhpVersion $phpVersion, private NativeFunctionReflectionProvider $nativeFunctionReflectionProvider, private StubPhpDocProvider $stubPhpDocProvider, @@ -148,6 +150,7 @@ public function getClass(string $className): ClassReflection $this->phpDocInheritanceResolver, $this->phpVersion, $this->signatureMapProvider, + $this->deprecationResolver, $this->attributeReflectionFactory, $this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(), $this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(), @@ -243,6 +246,7 @@ public function getAnonymousClassReflection(Node\Stmt\Class_ $classNode, Scope $ $this->phpDocInheritanceResolver, $this->phpVersion, $this->signatureMapProvider, + $this->deprecationResolver, $this->attributeReflectionFactory, $this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(), $this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(), @@ -305,8 +309,11 @@ private function getCustomFunction(string $functionName): PhpFunctionReflection $phpDocParameterTypes = []; $phpDocReturnTag = null; $phpDocThrowsTag = null; - $deprecatedTag = null; - $isDeprecated = false; + + $deprecation = $this->deprecationResolver->getFunctionDeprecation($reflectionFunction); + $deprecationDescription = $deprecation?->getDescription(); + $isDeprecated = $deprecation !== null; + $isInternal = false; $isPure = null; $asserts = Assertions::createEmpty(); @@ -327,8 +334,10 @@ private function getCustomFunction(string $functionName): PhpFunctionReflection $phpDocParameterTypes = array_map(static fn ($tag) => $tag->getType(), $resolvedPhpDoc->getParamTags()); $phpDocReturnTag = $resolvedPhpDoc->getReturnTag(); $phpDocThrowsTag = $resolvedPhpDoc->getThrowsTag(); - $deprecatedTag = $resolvedPhpDoc->getDeprecatedTag(); - $isDeprecated = $resolvedPhpDoc->isDeprecated(); + if (!$isDeprecated) { + $deprecationDescription = $resolvedPhpDoc->getDeprecatedTag() !== null ? $resolvedPhpDoc->getDeprecatedTag()->getMessage() : $deprecationDescription; + $isDeprecated = $resolvedPhpDoc->isDeprecated(); + } $isInternal = $resolvedPhpDoc->isInternal(); $isPure = $resolvedPhpDoc->isPure(); $asserts = Assertions::createFromResolvedPhpDocBlock($resolvedPhpDoc); @@ -347,7 +356,7 @@ private function getCustomFunction(string $functionName): PhpFunctionReflection $phpDocParameterTypes, $phpDocReturnTag !== null ? $phpDocReturnTag->getType() : null, $phpDocThrowsTag !== null ? $phpDocThrowsTag->getType() : null, - $deprecatedTag !== null ? $deprecatedTag->getMessage() : null, + $deprecationDescription, $isDeprecated, $isInternal, $reflectionFunction->getFileName() !== false ? $reflectionFunction->getFileName() : null, @@ -407,13 +416,15 @@ public function getConstant(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAn $constantValueType = $this->initializerExprTypeResolver->getType($constantReflection->getValueExpression(), InitializerExprContext::fromGlobalConstant($constantReflection)); $docComment = $constantReflection->getDocComment(); - $isDeprecated = TrinaryLogic::createNo(); - $deprecatedDescription = null; - if ($docComment !== null) { + $deprecation = $this->deprecationResolver->getConstantDeprecation($constantReflection); + $isDeprecated = $deprecation !== null; + $deprecatedDescription = $deprecation?->getDescription(); + + if ($isDeprecated === false && $docComment !== null) { $resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc($fileName, null, null, null, $docComment); - $isDeprecated = TrinaryLogic::createFromBoolean($resolvedPhpDoc->isDeprecated()); + $isDeprecated = $resolvedPhpDoc->isDeprecated(); - if ($resolvedPhpDoc->isDeprecated() && $resolvedPhpDoc->getDeprecatedTag() !== null) { + if ($isDeprecated && $resolvedPhpDoc->getDeprecatedTag() !== null) { $deprecatedMessage = $resolvedPhpDoc->getDeprecatedTag()->getMessage(); $matches = Strings::match($deprecatedMessage ?? '', '#^(\d+)\.(\d+)(?:\.(\d+))?$#'); @@ -423,7 +434,7 @@ public function getConstant(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAn $patch = $matches[3] ?? 0; $versionId = sprintf('%d%02d%02d', $major, $minor, $patch); - $isDeprecated = TrinaryLogic::createFromBoolean($this->phpVersion->getVersionId() >= $versionId); + $isDeprecated = $this->phpVersion->getVersionId() >= $versionId; } else { // filter raw version number messages like in // https://github.com/JetBrains/phpstorm-stubs/blob/9608c953230b08f07b703ecfe459cc58d5421437/filter/filter.php#L478 @@ -436,7 +447,7 @@ public function getConstant(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAn $constantName, $constantValueType, $fileName, - $isDeprecated, + TrinaryLogic::createFromBoolean($isDeprecated), $deprecatedDescription, ); } diff --git a/src/Reflection/ClassReflection.php b/src/Reflection/ClassReflection.php index 05a9f9b6a6..4b17716b96 100644 --- a/src/Reflection/ClassReflection.php +++ b/src/Reflection/ClassReflection.php @@ -26,6 +26,7 @@ use PHPStan\PhpDoc\Tag\TemplateTag; use PHPStan\PhpDoc\Tag\TypeAliasImportTag; use PHPStan\PhpDoc\Tag\TypeAliasTag; +use PHPStan\Reflection\Deprecation\DeprecationResolver; use PHPStan\Reflection\Php\PhpClassReflectionExtension; use PHPStan\Reflection\Php\PhpPropertyReflection; use PHPStan\Reflection\Php\UniversalObjectCratesClassReflectionExtension; @@ -161,6 +162,7 @@ public function __construct( private PhpDocInheritanceResolver $phpDocInheritanceResolver, private PhpVersion $phpVersion, private SignatureMapProvider $signatureMapProvider, + private DeprecationResolver $deprecationResolver, private AttributeReflectionFactory $attributeReflectionFactory, private array $propertiesClassReflectionExtensions, private array $methodsClassReflectionExtensions, @@ -1079,6 +1081,10 @@ public function getConstant(string $name): ClassConstantReflection throw new MissingConstantFromReflectionException($this->getName(), $name); } + $deprecation = $this->deprecationResolver->getClassConstantDeprecation($reflectionConstant); + $deprecatedDescription = $deprecation?->getDescription(); + $isDeprecated = $deprecation !== null; + $declaringClass = $this->reflectionProvider->getClass($reflectionConstant->getDeclaringClass()->getName()); $fileName = $declaringClass->getFileName(); $phpDocType = null; @@ -1099,8 +1105,10 @@ public function getConstant(string $name): ClassConstantReflection ); } - $deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag() !== null ? $resolvedPhpDoc->getDeprecatedTag()->getMessage() : null; - $isDeprecated = $resolvedPhpDoc->isDeprecated(); + if (!$isDeprecated) { + $deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag() !== null ? $resolvedPhpDoc->getDeprecatedTag()->getMessage() : null; + $isDeprecated = $resolvedPhpDoc->isDeprecated(); + } $isInternal = $resolvedPhpDoc->isInternal(); $isFinal = $resolvedPhpDoc->isFinal(); $varTags = $resolvedPhpDoc->getVarTags(); @@ -1210,11 +1218,8 @@ public function getTypeAliases(): array public function getDeprecatedDescription(): ?string { - if ($this->deprecatedDescription === null && $this->isDeprecated()) { - $resolvedPhpDoc = $this->getResolvedPhpDoc(); - if ($resolvedPhpDoc !== null && $resolvedPhpDoc->getDeprecatedTag() !== null) { - $this->deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag()->getMessage(); - } + if ($this->isDeprecated === null) { + $this->resolveDeprecation(); } return $this->deprecatedDescription; @@ -1223,13 +1228,36 @@ public function getDeprecatedDescription(): ?string public function isDeprecated(): bool { if ($this->isDeprecated === null) { - $resolvedPhpDoc = $this->getResolvedPhpDoc(); - $this->isDeprecated = $resolvedPhpDoc !== null && $resolvedPhpDoc->isDeprecated(); + $this->resolveDeprecation(); } return $this->isDeprecated; } + /** + * @phpstan-assert bool $this->isDeprecated + */ + private function resolveDeprecation(): void + { + $deprecation = $this->deprecationResolver->isClassDeprecated($this->reflection); + if ($deprecation !== null) { + $this->isDeprecated = true; + $this->deprecatedDescription = $deprecation->getDescription(); + return; + } + + $resolvedPhpDoc = $this->getResolvedPhpDoc(); + + if ($resolvedPhpDoc !== null && $resolvedPhpDoc->isDeprecated()) { + $this->isDeprecated = true; + $this->deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag() !== null ? $resolvedPhpDoc->getDeprecatedTag()->getMessage() : null; + return; + } + + $this->isDeprecated = false; + $this->deprecatedDescription = null; + } + public function isBuiltin(): bool { return $this->reflection->isInternal(); @@ -1559,6 +1587,7 @@ public function withTypes(array $types): self $this->phpDocInheritanceResolver, $this->phpVersion, $this->signatureMapProvider, + $this->deprecationResolver, $this->attributeReflectionFactory, $this->propertiesClassReflectionExtensions, $this->methodsClassReflectionExtensions, @@ -1590,6 +1619,7 @@ public function withVariances(array $variances): self $this->phpDocInheritanceResolver, $this->phpVersion, $this->signatureMapProvider, + $this->deprecationResolver, $this->attributeReflectionFactory, $this->propertiesClassReflectionExtensions, $this->methodsClassReflectionExtensions, @@ -1631,6 +1661,7 @@ public function asFinal(): self $this->phpDocInheritanceResolver, $this->phpVersion, $this->signatureMapProvider, + $this->deprecationResolver, $this->attributeReflectionFactory, $this->propertiesClassReflectionExtensions, $this->methodsClassReflectionExtensions, diff --git a/src/Reflection/Deprecation/ClassConstantDeprecationProvider.php b/src/Reflection/Deprecation/ClassConstantDeprecationProvider.php new file mode 100644 index 0000000000..2f5e88f1ee --- /dev/null +++ b/src/Reflection/Deprecation/ClassConstantDeprecationProvider.php @@ -0,0 +1,27 @@ +description; + } + + public function withDescription(?string $description): self + { + $clone = clone $this; + $clone->description = $description; + + return $clone; + } + +} diff --git a/src/Reflection/Deprecation/DeprecationResolver.php b/src/Reflection/Deprecation/DeprecationResolver.php new file mode 100644 index 0000000000..82422ad01b --- /dev/null +++ b/src/Reflection/Deprecation/DeprecationResolver.php @@ -0,0 +1,107 @@ + $propertyDeprecationProviders + * @param list $methodDeprecationProviders + * @param list $classConstantDeprecationProviders + * @param list $classDeprecationProviders + * @param list $functionDeprecationProviders + * @param list $constantDeprecationProviders + */ + public function __construct( + private array $propertyDeprecationProviders, + private array $methodDeprecationProviders, + private array $classConstantDeprecationProviders, + private array $classDeprecationProviders, + private array $functionDeprecationProviders, + private array $constantDeprecationProviders, + ) + { + } + + public function getPropertyDeprecation(ReflectionProperty $reflectionProperty): ?Deprecation + { + foreach ($this->propertyDeprecationProviders as $provider) { + $deprecation = $provider->getPropertyDeprecation($reflectionProperty); + if ($deprecation !== null) { + return $deprecation; + } + } + + return null; + } + + public function getMethodDeprecation(ReflectionMethod $methodReflection): ?Deprecation + { + foreach ($this->methodDeprecationProviders as $provider) { + $deprecation = $provider->getMethodDeprecation($methodReflection); + if ($deprecation !== null) { + return $deprecation; + } + } + + return null; + } + + public function getClassConstantDeprecation(ReflectionClassConstant $reflectionConstant): ?Deprecation + { + foreach ($this->classConstantDeprecationProviders as $provider) { + $deprecation = $provider->getClassConstantDeprecation($reflectionConstant); + if ($deprecation !== null) { + return $deprecation; + } + } + + return null; + } + + public function isClassDeprecated(ReflectionClass|ReflectionEnum $reflection): ?Deprecation + { + foreach ($this->classDeprecationProviders as $provider) { + $deprecation = $provider->getClassDeprecation($reflection); + if ($deprecation !== null) { + return $deprecation; + } + } + + return null; + } + + public function getFunctionDeprecation(ReflectionFunction $reflectionFunction): ?Deprecation + { + foreach ($this->functionDeprecationProviders as $provider) { + $deprecation = $provider->getFunctionDeprecation($reflectionFunction); + if ($deprecation !== null) { + return $deprecation; + } + } + + return null; + } + + public function getConstantDeprecation(ReflectionConstant $constantReflection): ?Deprecation + { + foreach ($this->constantDeprecationProviders as $provider) { + $deprecation = $provider->getConstantDeprecation($constantReflection); + if ($deprecation !== null) { + return $deprecation; + } + } + + return null; + } + +} diff --git a/src/Reflection/Deprecation/FunctionDeprecationProvider.php b/src/Reflection/Deprecation/FunctionDeprecationProvider.php new file mode 100644 index 0000000000..34e0eb06b9 --- /dev/null +++ b/src/Reflection/Deprecation/FunctionDeprecationProvider.php @@ -0,0 +1,27 @@ +deprecationResolver->getPropertyDeprecation($propertyReflection); + $deprecatedDescription = $deprecation?->getDescription(); + $isDeprecated = $deprecation !== null; $isInternal = false; $isReadOnlyByPhpDoc = $classReflection->isImmutable(); $isAllowedPrivateMutation = false; @@ -298,8 +301,11 @@ private function createProperty( $phpDocBlockClassReflection->getCallSiteVarianceMap(), TemplateTypeVariance::createInvariant(), ) : null; - $deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag() !== null ? $resolvedPhpDoc->getDeprecatedTag()->getMessage() : null; - $isDeprecated = $resolvedPhpDoc->isDeprecated(); + + if (!$isDeprecated) { + $deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag() !== null ? $resolvedPhpDoc->getDeprecatedTag()->getMessage() : null; + $isDeprecated = $resolvedPhpDoc->isDeprecated(); + } $isInternal = $resolvedPhpDoc->isInternal(); $isReadOnlyByPhpDoc = $isReadOnlyByPhpDoc || $resolvedPhpDoc->isReadOnly(); $isAllowedPrivateMutation = $resolvedPhpDoc->isAllowedPrivateMutation(); @@ -699,6 +705,10 @@ private function createMethod( public function createUserlandMethodReflection(ClassReflection $fileDeclaringClass, ClassReflection $actualDeclaringClass, ReflectionMethod $methodReflection, ?string $declaringTraitName): PhpMethodReflection { + $deprecation = $this->deprecationResolver->getMethodDeprecation($methodReflection); + $deprecatedDescription = $deprecation?->getDescription(); + $isDeprecated = $deprecation !== null; + $resolvedPhpDoc = null; $stubPhpDocPair = $this->findMethodPhpDocIncludingAncestors($fileDeclaringClass, $fileDeclaringClass, $methodReflection->getName(), array_map(static fn (ReflectionParameter $parameter): string => $parameter->getName(), $methodReflection->getParameters())); $phpDocBlockClassReflection = $fileDeclaringClass; @@ -821,8 +831,10 @@ public function createUserlandMethodReflection(ClassReflection $fileDeclaringCla ); $phpDocReturnType = $this->getPhpDocReturnType($phpDocBlockClassReflection, $resolvedPhpDoc, $nativeReturnType); $phpDocThrowType = $resolvedPhpDoc->getThrowsTag() !== null ? $resolvedPhpDoc->getThrowsTag()->getType() : null; - $deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag() !== null ? $resolvedPhpDoc->getDeprecatedTag()->getMessage() : null; - $isDeprecated = $resolvedPhpDoc->isDeprecated(); + if (!$isDeprecated) { + $deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag() !== null ? $resolvedPhpDoc->getDeprecatedTag()->getMessage() : null; + $isDeprecated = $resolvedPhpDoc->isDeprecated(); + } $isInternal = $resolvedPhpDoc->isInternal(); $isFinal = $resolvedPhpDoc->isFinal(); $isPure = $resolvedPhpDoc->isPure(); diff --git a/src/Testing/RuleTestCase.php b/src/Testing/RuleTestCase.php index cd0f52534e..085e75f269 100644 --- a/src/Testing/RuleTestCase.php +++ b/src/Testing/RuleTestCase.php @@ -24,6 +24,7 @@ use PHPStan\PhpDoc\PhpDocInheritanceResolver; use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\Reflection\AttributeReflectionFactory; +use PHPStan\Reflection\Deprecation\DeprecationResolver; use PHPStan\Reflection\InitializerExprTypeResolver; use PHPStan\Reflection\SignatureMap\SignatureMapProvider; use PHPStan\Rules\DirectRegistry as DirectRuleRegistry; @@ -94,6 +95,7 @@ private function getAnalyser(DirectRuleRegistry $ruleRegistry): Analyser self::getContainer()->getByType(StubPhpDocProvider::class), self::getContainer()->getByType(PhpVersion::class), self::getContainer()->getByType(SignatureMapProvider::class), + self::getContainer()->getByType(DeprecationResolver::class), self::getContainer()->getByType(AttributeReflectionFactory::class), self::getContainer()->getByType(PhpDocInheritanceResolver::class), self::getContainer()->getByType(FileHelper::class), diff --git a/src/Testing/TypeInferenceTestCase.php b/src/Testing/TypeInferenceTestCase.php index dc3e884c88..50e8285396 100644 --- a/src/Testing/TypeInferenceTestCase.php +++ b/src/Testing/TypeInferenceTestCase.php @@ -18,6 +18,7 @@ use PHPStan\PhpDoc\PhpDocInheritanceResolver; use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\Reflection\AttributeReflectionFactory; +use PHPStan\Reflection\Deprecation\DeprecationResolver; use PHPStan\Reflection\InitializerExprTypeResolver; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Reflection\SignatureMap\SignatureMapProvider; @@ -74,6 +75,7 @@ public static function processFile( self::getContainer()->getByType(StubPhpDocProvider::class), self::getContainer()->getByType(PhpVersion::class), self::getContainer()->getByType(SignatureMapProvider::class), + self::getContainer()->getByType(DeprecationResolver::class), self::getContainer()->getByType(AttributeReflectionFactory::class), self::getContainer()->getByType(PhpDocInheritanceResolver::class), self::getContainer()->getByType(FileHelper::class), diff --git a/tests/PHPStan/Analyser/AnalyserTest.php b/tests/PHPStan/Analyser/AnalyserTest.php index bacd85a967..d6c42cd6b7 100644 --- a/tests/PHPStan/Analyser/AnalyserTest.php +++ b/tests/PHPStan/Analyser/AnalyserTest.php @@ -22,6 +22,7 @@ use PHPStan\PhpDoc\PhpDocInheritanceResolver; use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\Reflection\AttributeReflectionFactory; +use PHPStan\Reflection\Deprecation\DeprecationResolver; use PHPStan\Reflection\InitializerExprTypeResolver; use PHPStan\Reflection\SignatureMap\SignatureMapProvider; use PHPStan\Rules\AlwaysFailRule; @@ -717,6 +718,7 @@ private function createAnalyser(): Analyser self::getContainer()->getByType(StubPhpDocProvider::class), self::getContainer()->getByType(PhpVersion::class), self::getContainer()->getByType(SignatureMapProvider::class), + self::getContainer()->getByType(DeprecationResolver::class), self::getContainer()->getByType(AttributeReflectionFactory::class), $phpDocInheritanceResolver, $fileHelper, diff --git a/tests/PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php b/tests/PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php new file mode 100644 index 0000000000..fa8b045afa --- /dev/null +++ b/tests/PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php @@ -0,0 +1,190 @@ +getClass(NotDeprecatedClass::class); + $attributeDeprecatedClass = $reflectionProvider->getClass(AttributeDeprecatedClass::class); + $phpDocDeprecatedClass = $reflectionProvider->getClass(PhpDocDeprecatedClass::class); // @phpstan-ignore classConstant.deprecatedClass + $phpDocDeprecatedClassWithMessages = $reflectionProvider->getClass(PhpDocDeprecatedClassWithMessage::class); // @phpstan-ignore classConstant.deprecatedClass + $attributeDeprecatedClassWithMessages = $reflectionProvider->getClass(AttributeDeprecatedClassWithMessage::class); + $doubleDeprecatedClass = $reflectionProvider->getClass(DoubleDeprecatedClass::class); // @phpstan-ignore classConstant.deprecatedClass + $doubleDeprecatedClassOnlyPhpDocMessage = $reflectionProvider->getClass(DoubleDeprecatedClassOnlyPhpDocMessage::class); // @phpstan-ignore classConstant.deprecatedClass + $doubleDeprecatedClassOnlyAttributeMessage = $reflectionProvider->getClass(DoubleDeprecatedClassOnlyAttributeMessage::class); // @phpstan-ignore classConstant.deprecatedClass + + $notDeprecatedFunction = $reflectionProvider->getFunction(new FullyQualified('CustomDeprecations\\notDeprecatedFunction'), null); + $phpDocDeprecatedFunction = $reflectionProvider->getFunction(new FullyQualified('CustomDeprecations\\phpDocDeprecatedFunction'), null); + $phpDocDeprecatedFunctionWithMessage = $reflectionProvider->getFunction(new FullyQualified('CustomDeprecations\\phpDocDeprecatedFunctionWithMessage'), null); + $attributeDeprecatedFunction = $reflectionProvider->getFunction(new FullyQualified('CustomDeprecations\\attributeDeprecatedFunction'), null); + $attributeDeprecatedFunctionWithMessage = $reflectionProvider->getFunction(new FullyQualified('CustomDeprecations\\attributeDeprecatedFunctionWithMessage'), null); + $doubleDeprecatedFunction = $reflectionProvider->getFunction(new FullyQualified('CustomDeprecations\\doubleDeprecatedFunction'), null); + $doubleDeprecatedFunctionOnlyAttributeMessage = $reflectionProvider->getFunction(new FullyQualified('CustomDeprecations\\doubleDeprecatedFunctionOnlyAttributeMessage'), null); + $doubleDeprecatedFunctionOnlyPhpDocMessage = $reflectionProvider->getFunction(new FullyQualified('CustomDeprecations\\doubleDeprecatedFunctionOnlyPhpDocMessage'), null); + + $scopeFactory = self::getContainer()->getByType(ScopeFactory::class); + + $scopeForNotDeprecatedClass = $scopeFactory->create(ScopeContext::create('dummy.php')->enterClass($notDeprecatedClass)); + $scopeForDeprecatedClass = $scopeFactory->create(ScopeContext::create('dummy.php')->enterClass($attributeDeprecatedClass)); + $scopeForPhpDocDeprecatedClass = $scopeFactory->create(ScopeContext::create('dummy.php')->enterClass($phpDocDeprecatedClass)); + $scopeForPhpDocDeprecatedClassWithMessages = $scopeFactory->create(ScopeContext::create('dummy.php')->enterClass($phpDocDeprecatedClassWithMessages)); + $scopeForAttributeDeprecatedClassWithMessages = $scopeFactory->create(ScopeContext::create('dummy.php')->enterClass($attributeDeprecatedClassWithMessages)); + $scopeForDoubleDeprecatedClass = $scopeFactory->create(ScopeContext::create('dummy.php')->enterClass($doubleDeprecatedClass)); + $scopeForDoubleDeprecatedClassOnlyNativeMessage = $scopeFactory->create(ScopeContext::create('dummy.php')->enterClass($doubleDeprecatedClassOnlyPhpDocMessage)); + $scopeForDoubleDeprecatedClassOnlyCustomMessage = $scopeFactory->create(ScopeContext::create('dummy.php')->enterClass($doubleDeprecatedClassOnlyAttributeMessage)); + + // class + self::assertFalse($notDeprecatedClass->isDeprecated()); + self::assertNull($notDeprecatedClass->getDeprecatedDescription()); + + self::assertTrue($attributeDeprecatedClass->isDeprecated()); + self::assertNull($attributeDeprecatedClass->getDeprecatedDescription()); + + self::assertTrue($phpDocDeprecatedClass->isDeprecated()); + self::assertNull($phpDocDeprecatedClass->getDeprecatedDescription()); + + self::assertTrue($phpDocDeprecatedClassWithMessages->isDeprecated()); + self::assertSame('phpdoc', $phpDocDeprecatedClassWithMessages->getDeprecatedDescription()); + + self::assertTrue($attributeDeprecatedClassWithMessages->isDeprecated()); + self::assertSame('attribute', $attributeDeprecatedClassWithMessages->getDeprecatedDescription()); + + self::assertTrue($doubleDeprecatedClass->isDeprecated()); + self::assertSame('attribute', $doubleDeprecatedClass->getDeprecatedDescription()); + + self::assertTrue($doubleDeprecatedClassOnlyPhpDocMessage->isDeprecated()); + self::assertNull($doubleDeprecatedClassOnlyPhpDocMessage->getDeprecatedDescription()); + + self::assertTrue($doubleDeprecatedClassOnlyAttributeMessage->isDeprecated()); + self::assertSame('attribute', $doubleDeprecatedClassOnlyAttributeMessage->getDeprecatedDescription()); + + // class constants + self::assertFalse($notDeprecatedClass->getConstant('FOO')->isDeprecated()->yes()); + self::assertNull($notDeprecatedClass->getConstant('FOO')->getDeprecatedDescription()); + + self::assertTrue($attributeDeprecatedClass->getConstant('FOO')->isDeprecated()->yes()); + self::assertNull($attributeDeprecatedClass->getConstant('FOO')->getDeprecatedDescription()); + + self::assertTrue($phpDocDeprecatedClass->getConstant('FOO')->isDeprecated()->yes()); + self::assertNull($phpDocDeprecatedClass->getConstant('FOO')->getDeprecatedDescription()); + + self::assertTrue($phpDocDeprecatedClassWithMessages->getConstant('FOO')->isDeprecated()->yes()); + self::assertSame('phpdoc', $phpDocDeprecatedClassWithMessages->getConstant('FOO')->getDeprecatedDescription()); + + self::assertTrue($attributeDeprecatedClassWithMessages->getConstant('FOO')->isDeprecated()->yes()); + self::assertSame('attribute', $attributeDeprecatedClassWithMessages->getConstant('FOO')->getDeprecatedDescription()); + + self::assertTrue($doubleDeprecatedClass->getConstant('FOO')->isDeprecated()->yes()); + self::assertSame('attribute', $doubleDeprecatedClass->getConstant('FOO')->getDeprecatedDescription()); + + self::assertTrue($doubleDeprecatedClassOnlyPhpDocMessage->getConstant('FOO')->isDeprecated()->yes()); + self::assertNull($doubleDeprecatedClassOnlyPhpDocMessage->getConstant('FOO')->getDeprecatedDescription()); + + self::assertTrue($doubleDeprecatedClassOnlyAttributeMessage->getConstant('FOO')->isDeprecated()->yes()); + self::assertSame('attribute', $doubleDeprecatedClassOnlyAttributeMessage->getConstant('FOO')->getDeprecatedDescription()); + + // properties + self::assertFalse($notDeprecatedClass->getProperty('foo', $scopeForNotDeprecatedClass)->isDeprecated()->yes()); + self::assertNull($notDeprecatedClass->getProperty('foo', $scopeForNotDeprecatedClass)->getDeprecatedDescription()); + + self::assertTrue($attributeDeprecatedClass->getProperty('foo', $scopeForDeprecatedClass)->isDeprecated()->yes()); + self::assertNull($attributeDeprecatedClass->getProperty('foo', $scopeForDeprecatedClass)->getDeprecatedDescription()); + + self::assertTrue($phpDocDeprecatedClass->getProperty('foo', $scopeForPhpDocDeprecatedClass)->isDeprecated()->yes()); + self::assertNull($phpDocDeprecatedClass->getProperty('foo', $scopeForPhpDocDeprecatedClass)->getDeprecatedDescription()); + + self::assertTrue($phpDocDeprecatedClassWithMessages->getProperty('foo', $scopeForPhpDocDeprecatedClassWithMessages)->isDeprecated()->yes()); + self::assertSame('phpdoc', $phpDocDeprecatedClassWithMessages->getProperty('foo', $scopeForPhpDocDeprecatedClassWithMessages)->getDeprecatedDescription()); + + self::assertTrue($attributeDeprecatedClassWithMessages->getProperty('foo', $scopeForAttributeDeprecatedClassWithMessages)->isDeprecated()->yes()); + self::assertSame('attribute', $attributeDeprecatedClassWithMessages->getProperty('foo', $scopeForAttributeDeprecatedClassWithMessages)->getDeprecatedDescription()); + + self::assertTrue($doubleDeprecatedClass->getProperty('foo', $scopeForDoubleDeprecatedClass)->isDeprecated()->yes()); + self::assertSame('attribute', $doubleDeprecatedClass->getProperty('foo', $scopeForDoubleDeprecatedClass)->getDeprecatedDescription()); + + self::assertTrue($doubleDeprecatedClassOnlyPhpDocMessage->getProperty('foo', $scopeForDoubleDeprecatedClassOnlyNativeMessage)->isDeprecated()->yes()); + self::assertNull($doubleDeprecatedClassOnlyPhpDocMessage->getProperty('foo', $scopeForDoubleDeprecatedClassOnlyNativeMessage)->getDeprecatedDescription()); + + self::assertTrue($doubleDeprecatedClassOnlyAttributeMessage->getProperty('foo', $scopeForDoubleDeprecatedClassOnlyCustomMessage)->isDeprecated()->yes()); + self::assertSame('attribute', $doubleDeprecatedClassOnlyAttributeMessage->getProperty('foo', $scopeForDoubleDeprecatedClassOnlyCustomMessage)->getDeprecatedDescription()); + + // methods + self::assertFalse($notDeprecatedClass->getMethod('foo', $scopeForNotDeprecatedClass)->isDeprecated()->yes()); + self::assertNull($notDeprecatedClass->getMethod('foo', $scopeForNotDeprecatedClass)->getDeprecatedDescription()); + + self::assertTrue($attributeDeprecatedClass->getMethod('foo', $scopeForDeprecatedClass)->isDeprecated()->yes()); + self::assertNull($attributeDeprecatedClass->getMethod('foo', $scopeForDeprecatedClass)->getDeprecatedDescription()); + + self::assertTrue($phpDocDeprecatedClass->getMethod('foo', $scopeForPhpDocDeprecatedClass)->isDeprecated()->yes()); + self::assertNull($phpDocDeprecatedClass->getMethod('foo', $scopeForPhpDocDeprecatedClass)->getDeprecatedDescription()); + + self::assertTrue($phpDocDeprecatedClassWithMessages->getMethod('foo', $scopeForPhpDocDeprecatedClassWithMessages)->isDeprecated()->yes()); + self::assertSame('phpdoc', $phpDocDeprecatedClassWithMessages->getMethod('foo', $scopeForPhpDocDeprecatedClassWithMessages)->getDeprecatedDescription()); + + self::assertTrue($attributeDeprecatedClassWithMessages->getMethod('foo', $scopeForAttributeDeprecatedClassWithMessages)->isDeprecated()->yes()); + self::assertSame('attribute', $attributeDeprecatedClassWithMessages->getMethod('foo', $scopeForAttributeDeprecatedClassWithMessages)->getDeprecatedDescription()); + + self::assertTrue($doubleDeprecatedClass->getMethod('foo', $scopeForDoubleDeprecatedClass)->isDeprecated()->yes()); + self::assertSame('attribute', $doubleDeprecatedClass->getMethod('foo', $scopeForDoubleDeprecatedClass)->getDeprecatedDescription()); + + self::assertTrue($doubleDeprecatedClassOnlyPhpDocMessage->getMethod('foo', $scopeForDoubleDeprecatedClassOnlyNativeMessage)->isDeprecated()->yes()); + self::assertNull($doubleDeprecatedClassOnlyPhpDocMessage->getMethod('foo', $scopeForDoubleDeprecatedClassOnlyNativeMessage)->getDeprecatedDescription()); + + self::assertTrue($doubleDeprecatedClassOnlyAttributeMessage->getMethod('foo', $scopeForDoubleDeprecatedClassOnlyCustomMessage)->isDeprecated()->yes()); + self::assertSame('attribute', $doubleDeprecatedClassOnlyAttributeMessage->getMethod('foo', $scopeForDoubleDeprecatedClassOnlyCustomMessage)->getDeprecatedDescription()); + + // functions + self::assertFalse($notDeprecatedFunction->isDeprecated()->yes()); + self::assertNull($notDeprecatedFunction->getDeprecatedDescription()); + + self::assertTrue($phpDocDeprecatedFunction->isDeprecated()->yes()); + self::assertNull($phpDocDeprecatedFunction->getDeprecatedDescription()); + + self::assertTrue($phpDocDeprecatedFunctionWithMessage->isDeprecated()->yes()); + self::assertSame('phpdoc', $phpDocDeprecatedFunctionWithMessage->getDeprecatedDescription()); + + self::assertTrue($attributeDeprecatedFunction->isDeprecated()->yes()); + self::assertNull($attributeDeprecatedFunction->getDeprecatedDescription()); + + self::assertTrue($attributeDeprecatedFunctionWithMessage->isDeprecated()->yes()); + self::assertSame('attribute', $attributeDeprecatedFunctionWithMessage->getDeprecatedDescription()); + + self::assertTrue($doubleDeprecatedFunction->isDeprecated()->yes()); + self::assertSame('attribute', $doubleDeprecatedFunction->getDeprecatedDescription()); + + self::assertTrue($doubleDeprecatedFunctionOnlyPhpDocMessage->isDeprecated()->yes()); + self::assertNull($doubleDeprecatedFunctionOnlyPhpDocMessage->getDeprecatedDescription()); + + self::assertTrue($doubleDeprecatedFunctionOnlyAttributeMessage->isDeprecated()->yes()); + self::assertSame('attribute', $doubleDeprecatedFunctionOnlyAttributeMessage->getDeprecatedDescription()); + } + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/data/deprecation-provider.neon', + ...parent::getAdditionalConfigFiles(), + ]; + } + +} diff --git a/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationProvider.php b/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationProvider.php new file mode 100644 index 0000000000..37d2325841 --- /dev/null +++ b/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationProvider.php @@ -0,0 +1,69 @@ +buildDeprecation($reflection); + } + + public function getConstantDeprecation(ReflectionConstant $reflection): ?Deprecation + { + return $this->buildDeprecation($reflection); + } + + public function getFunctionDeprecation(ReflectionFunction $reflection): ?Deprecation + { + return $this->buildDeprecation($reflection); + } + + public function getMethodDeprecation(ReflectionMethod $reflection): ?Deprecation + { + return $this->buildDeprecation($reflection); + } + + public function getPropertyDeprecation(ReflectionProperty $reflection): ?Deprecation + { + return $this->buildDeprecation($reflection); + } + + public function getClassConstantDeprecation(ReflectionClassConstant $reflection): ?Deprecation + { + return $this->buildDeprecation($reflection); + } + + private function buildDeprecation($reflection): ?Deprecation + { + foreach ($reflection->getAttributes(CustomDeprecated::class) as $attribute) { + return Deprecation::create()->withDescription($attribute->getArguments()[0] ?? $attribute->getArguments()['description'] ?? null); + } + + return null; + } + +} diff --git a/tests/PHPStan/Reflection/Deprecation/data/deprecation-provider.neon b/tests/PHPStan/Reflection/Deprecation/data/deprecation-provider.neon new file mode 100644 index 0000000000..0d9207cadd --- /dev/null +++ b/tests/PHPStan/Reflection/Deprecation/data/deprecation-provider.neon @@ -0,0 +1,10 @@ +services: + - + class: PHPStan\Tests\CustomDeprecationProvider + tags: + - phpstan.propertyDeprecationProvider + - phpstan.methodDeprecationProvider + - phpstan.classConstantDeprecationProvider + - phpstan.classDeprecationProvider + - phpstan.functionDeprecationProvider + - phpstan.constantDeprecationProvider diff --git a/tests/PHPStan/Reflection/Deprecation/data/deprecations.php b/tests/PHPStan/Reflection/Deprecation/data/deprecations.php new file mode 100644 index 0000000000..4faf00ef80 --- /dev/null +++ b/tests/PHPStan/Reflection/Deprecation/data/deprecations.php @@ -0,0 +1,160 @@ + Date: Fri, 11 Apr 2025 15:40:55 +0200 Subject: [PATCH 02/15] No null coalesce --- src/Reflection/BetterReflection/BetterReflectionProvider.php | 4 ++-- src/Reflection/ClassReflection.php | 2 +- src/Reflection/Php/PhpClassReflectionExtension.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Reflection/BetterReflection/BetterReflectionProvider.php b/src/Reflection/BetterReflection/BetterReflectionProvider.php index 2c8781c38e..b6116f3145 100644 --- a/src/Reflection/BetterReflection/BetterReflectionProvider.php +++ b/src/Reflection/BetterReflection/BetterReflectionProvider.php @@ -311,7 +311,7 @@ private function getCustomFunction(string $functionName): PhpFunctionReflection $phpDocThrowsTag = null; $deprecation = $this->deprecationResolver->getFunctionDeprecation($reflectionFunction); - $deprecationDescription = $deprecation?->getDescription(); + $deprecationDescription = $deprecation === null ? null : $deprecation->getDescription(); $isDeprecated = $deprecation !== null; $isInternal = false; @@ -418,7 +418,7 @@ public function getConstant(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAn $deprecation = $this->deprecationResolver->getConstantDeprecation($constantReflection); $isDeprecated = $deprecation !== null; - $deprecatedDescription = $deprecation?->getDescription(); + $deprecatedDescription = $deprecation === null ? null : $deprecation->getDescription(); if ($isDeprecated === false && $docComment !== null) { $resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc($fileName, null, null, null, $docComment); diff --git a/src/Reflection/ClassReflection.php b/src/Reflection/ClassReflection.php index 4b17716b96..12e1425ea7 100644 --- a/src/Reflection/ClassReflection.php +++ b/src/Reflection/ClassReflection.php @@ -1082,7 +1082,7 @@ public function getConstant(string $name): ClassConstantReflection } $deprecation = $this->deprecationResolver->getClassConstantDeprecation($reflectionConstant); - $deprecatedDescription = $deprecation?->getDescription(); + $deprecatedDescription = $deprecation === null ? null : $deprecation->getDescription(); $isDeprecated = $deprecation !== null; $declaringClass = $this->reflectionProvider->getClass($reflectionConstant->getDeclaringClass()->getName()); diff --git a/src/Reflection/Php/PhpClassReflectionExtension.php b/src/Reflection/Php/PhpClassReflectionExtension.php index 2af8f9bac6..6b660e1f02 100644 --- a/src/Reflection/Php/PhpClassReflectionExtension.php +++ b/src/Reflection/Php/PhpClassReflectionExtension.php @@ -223,7 +223,7 @@ private function createProperty( } $deprecation = $this->deprecationResolver->getPropertyDeprecation($propertyReflection); - $deprecatedDescription = $deprecation?->getDescription(); + $deprecatedDescription = $deprecation === null ? null : $deprecation->getDescription(); $isDeprecated = $deprecation !== null; $isInternal = false; $isReadOnlyByPhpDoc = $classReflection->isImmutable(); @@ -706,7 +706,7 @@ private function createMethod( public function createUserlandMethodReflection(ClassReflection $fileDeclaringClass, ClassReflection $actualDeclaringClass, ReflectionMethod $methodReflection, ?string $declaringTraitName): PhpMethodReflection { $deprecation = $this->deprecationResolver->getMethodDeprecation($methodReflection); - $deprecatedDescription = $deprecation?->getDescription(); + $deprecatedDescription = $deprecation === null ? null : $deprecation->getDescription(); $isDeprecated = $deprecation !== null; $resolvedPhpDoc = null; From b4ac3a8c987a98d703ca703b43e2dc582877adc0 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Fri, 11 Apr 2025 16:01:43 +0200 Subject: [PATCH 03/15] Try fix PHP 7.4 CI issues --- .../Deprecation/data/CustomDeprecationProvider.php | 4 +++- tests/PHPStan/Reflection/Deprecation/data/deprecations.php | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationProvider.php b/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationProvider.php index 37d2325841..bdf8445756 100644 --- a/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationProvider.php +++ b/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationProvider.php @@ -1,4 +1,6 @@ -= 8.0 + +declare(strict_types = 1); namespace PHPStan\Tests; diff --git a/tests/PHPStan/Reflection/Deprecation/data/deprecations.php b/tests/PHPStan/Reflection/Deprecation/data/deprecations.php index 4faf00ef80..ba0adcc202 100644 --- a/tests/PHPStan/Reflection/Deprecation/data/deprecations.php +++ b/tests/PHPStan/Reflection/Deprecation/data/deprecations.php @@ -5,9 +5,12 @@ #[\Attribute(\Attribute::TARGET_ALL)] class CustomDeprecated { + public ?string $description; + public function __construct( - public ?string $description = null, + ?string $description = null, ) { + $this->description = $description; } } From 24acb10d1c3c861f3824bbff5c279dee164366c1 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Fri, 11 Apr 2025 16:04:07 +0200 Subject: [PATCH 04/15] , --- tests/PHPStan/Reflection/Deprecation/data/deprecations.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Reflection/Deprecation/data/deprecations.php b/tests/PHPStan/Reflection/Deprecation/data/deprecations.php index ba0adcc202..5980892acd 100644 --- a/tests/PHPStan/Reflection/Deprecation/data/deprecations.php +++ b/tests/PHPStan/Reflection/Deprecation/data/deprecations.php @@ -8,7 +8,7 @@ class CustomDeprecated { public ?string $description; public function __construct( - ?string $description = null, + ?string $description = null ) { $this->description = $description; } From 82fc4a81807444184490d9a2b765fa47353209fb Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Fri, 11 Apr 2025 16:10:56 +0200 Subject: [PATCH 05/15] DeprecationProvidersTest: skip on PHP 7.4 --- .../Reflection/Deprecation/DeprecationProvidersTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php b/tests/PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php index fa8b045afa..2998f65f7c 100644 --- a/tests/PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php +++ b/tests/PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php @@ -20,6 +20,10 @@ class DeprecationProvidersTest extends PHPStanTestCase public function testCustomDeprecations(): void { + if (PHP_VERSION_ID < 80000) { + self::markTestSkipped('PHP 8.0+ is required as CustomDeprecationProvider uses unions.'); + } + require __DIR__ . '/data/deprecations.php'; $reflectionProvider = self::createReflectionProvider(); From 4f825cb74c5b9a00d58a1fa3871ee49e0219c34d Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Fri, 11 Apr 2025 16:11:52 +0200 Subject: [PATCH 06/15] DeprecationProvidersTest: skip on PHP 7.4 --- .../PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php b/tests/PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php index 2998f65f7c..13b053713f 100644 --- a/tests/PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php +++ b/tests/PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php @@ -14,6 +14,7 @@ use PHPStan\Analyser\ScopeContext; use PHPStan\Analyser\ScopeFactory; use PHPStan\Testing\PHPStanTestCase; +use const PHP_VERSION_ID; class DeprecationProvidersTest extends PHPStanTestCase { From be1de74385ac4f9d30cb6f9edc5ce7073ca8ff21 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Mon, 14 Apr 2025 09:28:38 +0200 Subject: [PATCH 07/15] Renames, mainly Provider -> Extension --- conf/config.neon | 9 +- src/Analyser/NodeScopeResolver.php | 4 +- .../BetterReflectionProvider.php | 4 +- src/Reflection/ClassReflection.php | 4 +- ... => ClassConstantDeprecationExtension.php} | 4 +- ...ider.php => ClassDeprecationExtension.php} | 4 +- ...r.php => ConstantDeprecationExtension.php} | 4 +- src/Reflection/Deprecation/Deprecation.php | 4 +- .../Deprecation/DeprecationProvider.php | 119 ++++++++++++++++++ .../Deprecation/DeprecationResolver.php | 107 ---------------- ...r.php => FunctionDeprecationExtension.php} | 4 +- ...der.php => MethodDeprecationExtension.php} | 4 +- ...r.php => PropertyDeprecationExtension.php} | 4 +- .../Php/PhpClassReflectionExtension.php | 4 +- src/Testing/RuleTestCase.php | 4 +- src/Testing/TypeInferenceTestCase.php | 4 +- tests/PHPStan/Analyser/AnalyserTest.php | 4 +- ...rsTest.php => DeprecationProviderTest.php} | 2 +- ...der.php => CustomDeprecationExtension.php} | 31 ++--- .../data/deprecation-provider.neon | 14 +-- 20 files changed, 173 insertions(+), 165 deletions(-) rename src/Reflection/Deprecation/{ClassConstantDeprecationProvider.php => ClassConstantDeprecationExtension.php} (84%) rename src/Reflection/Deprecation/{ClassDeprecationProvider.php => ClassDeprecationExtension.php} (87%) rename src/Reflection/Deprecation/{ConstantDeprecationProvider.php => ConstantDeprecationExtension.php} (85%) create mode 100644 src/Reflection/Deprecation/DeprecationProvider.php delete mode 100644 src/Reflection/Deprecation/DeprecationResolver.php rename src/Reflection/Deprecation/{FunctionDeprecationProvider.php => FunctionDeprecationExtension.php} (85%) rename src/Reflection/Deprecation/{MethodDeprecationProvider.php => MethodDeprecationExtension.php} (85%) rename src/Reflection/Deprecation/{PropertyDeprecationProvider.php => PropertyDeprecationExtension.php} (85%) rename tests/PHPStan/Reflection/Deprecation/{DeprecationProvidersTest.php => DeprecationProviderTest.php} (99%) rename tests/PHPStan/Reflection/Deprecation/data/{CustomDeprecationProvider.php => CustomDeprecationExtension.php} (66%) diff --git a/conf/config.neon b/conf/config.neon index 9ebeec968b..7652845316 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -2158,14 +2158,7 @@ services: class: PHPStan\Reflection\BetterReflection\SourceStubber\ReflectionSourceStubberFactory - - class: PHPStan\Reflection\Deprecation\DeprecationResolver - arguments: - propertyDeprecationProviders: tagged(phpstan.propertyDeprecationProvider) - methodDeprecationProviders: tagged(phpstan.methodDeprecationProvider) - classConstantDeprecationProviders: tagged(phpstan.classConstantDeprecationProvider) - classDeprecationProviders: tagged(phpstan.classDeprecationProvider) - functionDeprecationProviders: tagged(phpstan.functionDeprecationProvider) - constantDeprecationProviders: tagged(phpstan.constantDeprecationProvider) + class: PHPStan\Reflection\Deprecation\DeprecationProvider php8Lexer: class: PhpParser\Lexer\Emulative diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index de32bb8660..443a72f50c 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -137,7 +137,7 @@ use PHPStan\Reflection\Callables\SimpleImpurePoint; use PHPStan\Reflection\Callables\SimpleThrowPoint; use PHPStan\Reflection\ClassReflection; -use PHPStan\Reflection\Deprecation\DeprecationResolver; +use PHPStan\Reflection\Deprecation\DeprecationProvider; use PHPStan\Reflection\ExtendedMethodReflection; use PHPStan\Reflection\ExtendedParameterReflection; use PHPStan\Reflection\ExtendedParametersAcceptor; @@ -256,7 +256,7 @@ public function __construct( private readonly StubPhpDocProvider $stubPhpDocProvider, private readonly PhpVersion $phpVersion, private readonly SignatureMapProvider $signatureMapProvider, - private readonly DeprecationResolver $deprecationResolver, + private readonly DeprecationProvider $deprecationResolver, private readonly AttributeReflectionFactory $attributeReflectionFactory, private readonly PhpDocInheritanceResolver $phpDocInheritanceResolver, private readonly FileHelper $fileHelper, diff --git a/src/Reflection/BetterReflection/BetterReflectionProvider.php b/src/Reflection/BetterReflection/BetterReflectionProvider.php index b6116f3145..938220e041 100644 --- a/src/Reflection/BetterReflection/BetterReflectionProvider.php +++ b/src/Reflection/BetterReflection/BetterReflectionProvider.php @@ -36,7 +36,7 @@ use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\Constant\RuntimeConstantReflection; use PHPStan\Reflection\ConstantReflection; -use PHPStan\Reflection\Deprecation\DeprecationResolver; +use PHPStan\Reflection\Deprecation\DeprecationProvider; use PHPStan\Reflection\FunctionReflection; use PHPStan\Reflection\FunctionReflectionFactory; use PHPStan\Reflection\InitializerExprContext; @@ -86,7 +86,7 @@ public function __construct( private Reflector $reflector, private FileTypeMapper $fileTypeMapper, private PhpDocInheritanceResolver $phpDocInheritanceResolver, - private DeprecationResolver $deprecationResolver, + private DeprecationProvider $deprecationResolver, private PhpVersion $phpVersion, private NativeFunctionReflectionProvider $nativeFunctionReflectionProvider, private StubPhpDocProvider $stubPhpDocProvider, diff --git a/src/Reflection/ClassReflection.php b/src/Reflection/ClassReflection.php index 12e1425ea7..b8b44a6889 100644 --- a/src/Reflection/ClassReflection.php +++ b/src/Reflection/ClassReflection.php @@ -26,7 +26,7 @@ use PHPStan\PhpDoc\Tag\TemplateTag; use PHPStan\PhpDoc\Tag\TypeAliasImportTag; use PHPStan\PhpDoc\Tag\TypeAliasTag; -use PHPStan\Reflection\Deprecation\DeprecationResolver; +use PHPStan\Reflection\Deprecation\DeprecationProvider; use PHPStan\Reflection\Php\PhpClassReflectionExtension; use PHPStan\Reflection\Php\PhpPropertyReflection; use PHPStan\Reflection\Php\UniversalObjectCratesClassReflectionExtension; @@ -162,7 +162,7 @@ public function __construct( private PhpDocInheritanceResolver $phpDocInheritanceResolver, private PhpVersion $phpVersion, private SignatureMapProvider $signatureMapProvider, - private DeprecationResolver $deprecationResolver, + private DeprecationProvider $deprecationResolver, private AttributeReflectionFactory $attributeReflectionFactory, private array $propertiesClassReflectionExtensions, private array $methodsClassReflectionExtensions, diff --git a/src/Reflection/Deprecation/ClassConstantDeprecationProvider.php b/src/Reflection/Deprecation/ClassConstantDeprecationExtension.php similarity index 84% rename from src/Reflection/Deprecation/ClassConstantDeprecationProvider.php rename to src/Reflection/Deprecation/ClassConstantDeprecationExtension.php index 2f5e88f1ee..7a05e17e77 100644 --- a/src/Reflection/Deprecation/ClassConstantDeprecationProvider.php +++ b/src/Reflection/Deprecation/ClassConstantDeprecationExtension.php @@ -14,12 +14,12 @@ * - * class: App\PHPStan\MyProvider * tags: - * - phpstan.classConstantDeprecationProvider + * - phpstan.classConstantDeprecationExtension * ``` * * @api */ -interface ClassConstantDeprecationProvider +interface ClassConstantDeprecationExtension { public function getClassConstantDeprecation(ReflectionClassConstant $reflection): ?Deprecation; diff --git a/src/Reflection/Deprecation/ClassDeprecationProvider.php b/src/Reflection/Deprecation/ClassDeprecationExtension.php similarity index 87% rename from src/Reflection/Deprecation/ClassDeprecationProvider.php rename to src/Reflection/Deprecation/ClassDeprecationExtension.php index 672ab00b9e..277cdc67eb 100644 --- a/src/Reflection/Deprecation/ClassDeprecationProvider.php +++ b/src/Reflection/Deprecation/ClassDeprecationExtension.php @@ -15,12 +15,12 @@ * - * class: App\PHPStan\MyProvider * tags: - * - phpstan.classDeprecationProvider + * - phpstan.classDeprecationExtension * ``` * * @api */ -interface ClassDeprecationProvider +interface ClassDeprecationExtension { public function getClassDeprecation(ReflectionClass|ReflectionEnum $reflection): ?Deprecation; diff --git a/src/Reflection/Deprecation/ConstantDeprecationProvider.php b/src/Reflection/Deprecation/ConstantDeprecationExtension.php similarity index 85% rename from src/Reflection/Deprecation/ConstantDeprecationProvider.php rename to src/Reflection/Deprecation/ConstantDeprecationExtension.php index 064ab6f321..f5eaaddd92 100644 --- a/src/Reflection/Deprecation/ConstantDeprecationProvider.php +++ b/src/Reflection/Deprecation/ConstantDeprecationExtension.php @@ -14,12 +14,12 @@ * - * class: App\PHPStan\MyProvider * tags: - * - phpstan.constantDeprecationProvider + * - phpstan.constantDeprecationExtension * ``` * * @api */ -interface ConstantDeprecationProvider +interface ConstantDeprecationExtension { public function getConstantDeprecation(ReflectionConstant $reflection): ?Deprecation; diff --git a/src/Reflection/Deprecation/Deprecation.php b/src/Reflection/Deprecation/Deprecation.php index c26ab9e784..7b439a873e 100644 --- a/src/Reflection/Deprecation/Deprecation.php +++ b/src/Reflection/Deprecation/Deprecation.php @@ -24,9 +24,9 @@ public function getDescription(): ?string return $this->description; } - public function withDescription(?string $description): self + public static function createWithDescription(string $description): self { - $clone = clone $this; + $clone = new self(); $clone->description = $description; return $clone; diff --git a/src/Reflection/Deprecation/DeprecationProvider.php b/src/Reflection/Deprecation/DeprecationProvider.php new file mode 100644 index 0000000000..22d90e63d4 --- /dev/null +++ b/src/Reflection/Deprecation/DeprecationProvider.php @@ -0,0 +1,119 @@ + $propertyDeprecationExtensions */ + private array $propertyDeprecationExtensions; + + /** @var array $methodDeprecationExtensions */ + private array $methodDeprecationExtensions; + + /** @var array $classConstantDeprecationExtensions */ + private array $classConstantDeprecationExtensions; + + /** @var array $classDeprecationExtensions */ + private array $classDeprecationExtensions; + + /** @var array $functionDeprecationExtensions */ + private array $functionDeprecationExtensions; + + /** @var array $constantDeprecationExtensions */ + private array $constantDeprecationExtensions; + + public function __construct( + Container $container, + ) + { + $this->propertyDeprecationExtensions = $container->getServicesByTag('phpstan.propertyDeprecationExtension'); + $this->methodDeprecationExtensions = $container->getServicesByTag('phpstan.methodDeprecationExtension'); + $this->classConstantDeprecationExtensions = $container->getServicesByTag('phpstan.classConstantDeprecationExtension'); + $this->classDeprecationExtensions = $container->getServicesByTag('phpstan.classDeprecationExtension'); + $this->functionDeprecationExtensions = $container->getServicesByTag('phpstan.functionDeprecationExtension'); + $this->constantDeprecationExtensions = $container->getServicesByTag('phpstan.constantDeprecationExtension'); + } + + public function getPropertyDeprecation(ReflectionProperty $reflectionProperty): ?Deprecation + { + foreach ($this->propertyDeprecationExtensions as $extension) { + $deprecation = $extension->getPropertyDeprecation($reflectionProperty); + if ($deprecation !== null) { + return $deprecation; + } + } + + return null; + } + + public function getMethodDeprecation(ReflectionMethod $methodReflection): ?Deprecation + { + foreach ($this->methodDeprecationExtensions as $extension) { + $deprecation = $extension->getMethodDeprecation($methodReflection); + if ($deprecation !== null) { + return $deprecation; + } + } + + return null; + } + + public function getClassConstantDeprecation(ReflectionClassConstant $reflectionConstant): ?Deprecation + { + foreach ($this->classConstantDeprecationExtensions as $extension) { + $deprecation = $extension->getClassConstantDeprecation($reflectionConstant); + if ($deprecation !== null) { + return $deprecation; + } + } + + return null; + } + + public function isClassDeprecated(ReflectionClass|ReflectionEnum $reflection): ?Deprecation + { + foreach ($this->classDeprecationExtensions as $extension) { + $deprecation = $extension->getClassDeprecation($reflection); + if ($deprecation !== null) { + return $deprecation; + } + } + + return null; + } + + public function getFunctionDeprecation(ReflectionFunction $reflectionFunction): ?Deprecation + { + foreach ($this->functionDeprecationExtensions as $extension) { + $deprecation = $extension->getFunctionDeprecation($reflectionFunction); + if ($deprecation !== null) { + return $deprecation; + } + } + + return null; + } + + public function getConstantDeprecation(ReflectionConstant $constantReflection): ?Deprecation + { + foreach ($this->constantDeprecationExtensions as $extension) { + $deprecation = $extension->getConstantDeprecation($constantReflection); + if ($deprecation !== null) { + return $deprecation; + } + } + + return null; + } + +} diff --git a/src/Reflection/Deprecation/DeprecationResolver.php b/src/Reflection/Deprecation/DeprecationResolver.php deleted file mode 100644 index 82422ad01b..0000000000 --- a/src/Reflection/Deprecation/DeprecationResolver.php +++ /dev/null @@ -1,107 +0,0 @@ - $propertyDeprecationProviders - * @param list $methodDeprecationProviders - * @param list $classConstantDeprecationProviders - * @param list $classDeprecationProviders - * @param list $functionDeprecationProviders - * @param list $constantDeprecationProviders - */ - public function __construct( - private array $propertyDeprecationProviders, - private array $methodDeprecationProviders, - private array $classConstantDeprecationProviders, - private array $classDeprecationProviders, - private array $functionDeprecationProviders, - private array $constantDeprecationProviders, - ) - { - } - - public function getPropertyDeprecation(ReflectionProperty $reflectionProperty): ?Deprecation - { - foreach ($this->propertyDeprecationProviders as $provider) { - $deprecation = $provider->getPropertyDeprecation($reflectionProperty); - if ($deprecation !== null) { - return $deprecation; - } - } - - return null; - } - - public function getMethodDeprecation(ReflectionMethod $methodReflection): ?Deprecation - { - foreach ($this->methodDeprecationProviders as $provider) { - $deprecation = $provider->getMethodDeprecation($methodReflection); - if ($deprecation !== null) { - return $deprecation; - } - } - - return null; - } - - public function getClassConstantDeprecation(ReflectionClassConstant $reflectionConstant): ?Deprecation - { - foreach ($this->classConstantDeprecationProviders as $provider) { - $deprecation = $provider->getClassConstantDeprecation($reflectionConstant); - if ($deprecation !== null) { - return $deprecation; - } - } - - return null; - } - - public function isClassDeprecated(ReflectionClass|ReflectionEnum $reflection): ?Deprecation - { - foreach ($this->classDeprecationProviders as $provider) { - $deprecation = $provider->getClassDeprecation($reflection); - if ($deprecation !== null) { - return $deprecation; - } - } - - return null; - } - - public function getFunctionDeprecation(ReflectionFunction $reflectionFunction): ?Deprecation - { - foreach ($this->functionDeprecationProviders as $provider) { - $deprecation = $provider->getFunctionDeprecation($reflectionFunction); - if ($deprecation !== null) { - return $deprecation; - } - } - - return null; - } - - public function getConstantDeprecation(ReflectionConstant $constantReflection): ?Deprecation - { - foreach ($this->constantDeprecationProviders as $provider) { - $deprecation = $provider->getConstantDeprecation($constantReflection); - if ($deprecation !== null) { - return $deprecation; - } - } - - return null; - } - -} diff --git a/src/Reflection/Deprecation/FunctionDeprecationProvider.php b/src/Reflection/Deprecation/FunctionDeprecationExtension.php similarity index 85% rename from src/Reflection/Deprecation/FunctionDeprecationProvider.php rename to src/Reflection/Deprecation/FunctionDeprecationExtension.php index 34e0eb06b9..bd813714fb 100644 --- a/src/Reflection/Deprecation/FunctionDeprecationProvider.php +++ b/src/Reflection/Deprecation/FunctionDeprecationExtension.php @@ -14,12 +14,12 @@ * - * class: App\PHPStan\MyProvider * tags: - * - phpstan.functionDeprecationProvider + * - phpstan.functionDeprecationExtension * ``` * * @api */ -interface FunctionDeprecationProvider +interface FunctionDeprecationExtension { public function getFunctionDeprecation(ReflectionFunction $reflection): ?Deprecation; diff --git a/src/Reflection/Deprecation/MethodDeprecationProvider.php b/src/Reflection/Deprecation/MethodDeprecationExtension.php similarity index 85% rename from src/Reflection/Deprecation/MethodDeprecationProvider.php rename to src/Reflection/Deprecation/MethodDeprecationExtension.php index 4494e5aec5..137b5c8aa6 100644 --- a/src/Reflection/Deprecation/MethodDeprecationProvider.php +++ b/src/Reflection/Deprecation/MethodDeprecationExtension.php @@ -14,12 +14,12 @@ * - * class: App\PHPStan\MyProvider * tags: - * - phpstan.methodDeprecationProvider + * - phpstan.methodDeprecationExtension * ``` * * @api */ -interface MethodDeprecationProvider +interface MethodDeprecationExtension { public function getMethodDeprecation(ReflectionMethod $reflection): ?Deprecation; diff --git a/src/Reflection/Deprecation/PropertyDeprecationProvider.php b/src/Reflection/Deprecation/PropertyDeprecationExtension.php similarity index 85% rename from src/Reflection/Deprecation/PropertyDeprecationProvider.php rename to src/Reflection/Deprecation/PropertyDeprecationExtension.php index 50354efd8c..a5299c9ae7 100644 --- a/src/Reflection/Deprecation/PropertyDeprecationProvider.php +++ b/src/Reflection/Deprecation/PropertyDeprecationExtension.php @@ -14,12 +14,12 @@ * - * class: App\PHPStan\MyProvider * tags: - * - phpstan.propertyDeprecationProvider + * - phpstan.propertyDeprecationExtension * ``` * * @api */ -interface PropertyDeprecationProvider +interface PropertyDeprecationExtension { public function getPropertyDeprecation(ReflectionProperty $reflection): ?Deprecation; diff --git a/src/Reflection/Php/PhpClassReflectionExtension.php b/src/Reflection/Php/PhpClassReflectionExtension.php index 6b660e1f02..51ced61681 100644 --- a/src/Reflection/Php/PhpClassReflectionExtension.php +++ b/src/Reflection/Php/PhpClassReflectionExtension.php @@ -22,7 +22,7 @@ use PHPStan\Reflection\Assertions; use PHPStan\Reflection\AttributeReflectionFactory; use PHPStan\Reflection\ClassReflection; -use PHPStan\Reflection\Deprecation\DeprecationResolver; +use PHPStan\Reflection\Deprecation\DeprecationProvider; use PHPStan\Reflection\ExtendedFunctionVariant; use PHPStan\Reflection\ExtendedMethodReflection; use PHPStan\Reflection\ExtendedPropertyReflection; @@ -92,7 +92,7 @@ public function __construct( private NodeScopeResolver $nodeScopeResolver, private PhpMethodReflectionFactory $methodReflectionFactory, private PhpDocInheritanceResolver $phpDocInheritanceResolver, - private DeprecationResolver $deprecationResolver, + private DeprecationProvider $deprecationResolver, private AnnotationsMethodsClassReflectionExtension $annotationsMethodsClassReflectionExtension, private AnnotationsPropertiesClassReflectionExtension $annotationsPropertiesClassReflectionExtension, private SignatureMapProvider $signatureMapProvider, diff --git a/src/Testing/RuleTestCase.php b/src/Testing/RuleTestCase.php index 085e75f269..6be5ec1e62 100644 --- a/src/Testing/RuleTestCase.php +++ b/src/Testing/RuleTestCase.php @@ -24,7 +24,7 @@ use PHPStan\PhpDoc\PhpDocInheritanceResolver; use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\Reflection\AttributeReflectionFactory; -use PHPStan\Reflection\Deprecation\DeprecationResolver; +use PHPStan\Reflection\Deprecation\DeprecationProvider; use PHPStan\Reflection\InitializerExprTypeResolver; use PHPStan\Reflection\SignatureMap\SignatureMapProvider; use PHPStan\Rules\DirectRegistry as DirectRuleRegistry; @@ -95,7 +95,7 @@ private function getAnalyser(DirectRuleRegistry $ruleRegistry): Analyser self::getContainer()->getByType(StubPhpDocProvider::class), self::getContainer()->getByType(PhpVersion::class), self::getContainer()->getByType(SignatureMapProvider::class), - self::getContainer()->getByType(DeprecationResolver::class), + self::getContainer()->getByType(DeprecationProvider::class), self::getContainer()->getByType(AttributeReflectionFactory::class), self::getContainer()->getByType(PhpDocInheritanceResolver::class), self::getContainer()->getByType(FileHelper::class), diff --git a/src/Testing/TypeInferenceTestCase.php b/src/Testing/TypeInferenceTestCase.php index 50e8285396..538ad2970d 100644 --- a/src/Testing/TypeInferenceTestCase.php +++ b/src/Testing/TypeInferenceTestCase.php @@ -18,7 +18,7 @@ use PHPStan\PhpDoc\PhpDocInheritanceResolver; use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\Reflection\AttributeReflectionFactory; -use PHPStan\Reflection\Deprecation\DeprecationResolver; +use PHPStan\Reflection\Deprecation\DeprecationProvider; use PHPStan\Reflection\InitializerExprTypeResolver; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Reflection\SignatureMap\SignatureMapProvider; @@ -75,7 +75,7 @@ public static function processFile( self::getContainer()->getByType(StubPhpDocProvider::class), self::getContainer()->getByType(PhpVersion::class), self::getContainer()->getByType(SignatureMapProvider::class), - self::getContainer()->getByType(DeprecationResolver::class), + self::getContainer()->getByType(DeprecationProvider::class), self::getContainer()->getByType(AttributeReflectionFactory::class), self::getContainer()->getByType(PhpDocInheritanceResolver::class), self::getContainer()->getByType(FileHelper::class), diff --git a/tests/PHPStan/Analyser/AnalyserTest.php b/tests/PHPStan/Analyser/AnalyserTest.php index d6c42cd6b7..c810514526 100644 --- a/tests/PHPStan/Analyser/AnalyserTest.php +++ b/tests/PHPStan/Analyser/AnalyserTest.php @@ -22,7 +22,7 @@ use PHPStan\PhpDoc\PhpDocInheritanceResolver; use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\Reflection\AttributeReflectionFactory; -use PHPStan\Reflection\Deprecation\DeprecationResolver; +use PHPStan\Reflection\Deprecation\DeprecationProvider; use PHPStan\Reflection\InitializerExprTypeResolver; use PHPStan\Reflection\SignatureMap\SignatureMapProvider; use PHPStan\Rules\AlwaysFailRule; @@ -718,7 +718,7 @@ private function createAnalyser(): Analyser self::getContainer()->getByType(StubPhpDocProvider::class), self::getContainer()->getByType(PhpVersion::class), self::getContainer()->getByType(SignatureMapProvider::class), - self::getContainer()->getByType(DeprecationResolver::class), + self::getContainer()->getByType(DeprecationProvider::class), self::getContainer()->getByType(AttributeReflectionFactory::class), $phpDocInheritanceResolver, $fileHelper, diff --git a/tests/PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php b/tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php similarity index 99% rename from tests/PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php rename to tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php index 13b053713f..9af2743a98 100644 --- a/tests/PHPStan/Reflection/Deprecation/DeprecationProvidersTest.php +++ b/tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php @@ -16,7 +16,7 @@ use PHPStan\Testing\PHPStanTestCase; use const PHP_VERSION_ID; -class DeprecationProvidersTest extends PHPStanTestCase +class DeprecationProviderTest extends PHPStanTestCase { public function testCustomDeprecations(): void diff --git a/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationProvider.php b/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationExtension.php similarity index 66% rename from tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationProvider.php rename to tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationExtension.php index bdf8445756..25f16a181b 100644 --- a/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationProvider.php +++ b/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationExtension.php @@ -12,21 +12,21 @@ use PHPStan\BetterReflection\Reflection\Adapter\ReflectionMethod; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionProperty; use PHPStan\BetterReflection\Reflection\ReflectionConstant; -use PHPStan\Reflection\Deprecation\ClassConstantDeprecationProvider; -use PHPStan\Reflection\Deprecation\ClassDeprecationProvider; -use PHPStan\Reflection\Deprecation\ConstantDeprecationProvider; +use PHPStan\Reflection\Deprecation\ClassConstantDeprecationExtension; +use PHPStan\Reflection\Deprecation\ClassDeprecationExtension; +use PHPStan\Reflection\Deprecation\ConstantDeprecationExtension; use PHPStan\Reflection\Deprecation\Deprecation; -use PHPStan\Reflection\Deprecation\FunctionDeprecationProvider; -use PHPStan\Reflection\Deprecation\MethodDeprecationProvider; -use PHPStan\Reflection\Deprecation\PropertyDeprecationProvider; +use PHPStan\Reflection\Deprecation\FunctionDeprecationExtension; +use PHPStan\Reflection\Deprecation\MethodDeprecationExtension; +use PHPStan\Reflection\Deprecation\PropertyDeprecationExtension; -class CustomDeprecationProvider implements - ConstantDeprecationProvider, - ClassDeprecationProvider, - ClassConstantDeprecationProvider, - MethodDeprecationProvider, - PropertyDeprecationProvider, - FunctionDeprecationProvider +class CustomDeprecationExtension implements + ConstantDeprecationExtension, + ClassDeprecationExtension, + ClassConstantDeprecationExtension, + MethodDeprecationExtension, + PropertyDeprecationExtension, + FunctionDeprecationExtension { public function getClassDeprecation(ReflectionClass|ReflectionEnum $reflection): ?Deprecation @@ -62,7 +62,10 @@ public function getClassConstantDeprecation(ReflectionClassConstant $reflection) private function buildDeprecation($reflection): ?Deprecation { foreach ($reflection->getAttributes(CustomDeprecated::class) as $attribute) { - return Deprecation::create()->withDescription($attribute->getArguments()[0] ?? $attribute->getArguments()['description'] ?? null); + $description = $attribute->getArguments()[0] ?? $attribute->getArguments()['description'] ?? null; + return $description === null + ? Deprecation::create() + : Deprecation::createWithDescription($description); } return null; diff --git a/tests/PHPStan/Reflection/Deprecation/data/deprecation-provider.neon b/tests/PHPStan/Reflection/Deprecation/data/deprecation-provider.neon index 0d9207cadd..1e10e24a8c 100644 --- a/tests/PHPStan/Reflection/Deprecation/data/deprecation-provider.neon +++ b/tests/PHPStan/Reflection/Deprecation/data/deprecation-provider.neon @@ -1,10 +1,10 @@ services: - - class: PHPStan\Tests\CustomDeprecationProvider + class: PHPStan\Tests\CustomDeprecationExtension tags: - - phpstan.propertyDeprecationProvider - - phpstan.methodDeprecationProvider - - phpstan.classConstantDeprecationProvider - - phpstan.classDeprecationProvider - - phpstan.functionDeprecationProvider - - phpstan.constantDeprecationProvider + - phpstan.propertyDeprecationExtension + - phpstan.methodDeprecationExtension + - phpstan.classConstantDeprecationExtension + - phpstan.classDeprecationExtension + - phpstan.functionDeprecationExtension + - phpstan.constantDeprecationExtension From 2526e5cb2949f05d6af9cf35fa6a3834286eed47 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Mon, 14 Apr 2025 10:37:53 +0200 Subject: [PATCH 08/15] Support enum cases --- src/Reflection/ClassReflection.php | 19 +++++++------ .../Deprecation/DeprecationProvider.php | 18 ++++++++++++ .../EnumCaseDeprecationExtension.php | 28 +++++++++++++++++++ src/Reflection/EnumCaseReflection.php | 28 ++++++++++++++----- .../Deprecation/DeprecationProviderTest.php | 19 +++++++++++++ .../data/CustomDeprecationExtension.php | 12 ++++++-- .../data/deprecation-provider.neon | 1 + .../Deprecation/data/deprecations.php | 19 +++++++++++++ 8 files changed, 127 insertions(+), 17 deletions(-) create mode 100644 src/Reflection/Deprecation/EnumCaseDeprecationExtension.php diff --git a/src/Reflection/ClassReflection.php b/src/Reflection/ClassReflection.php index b8b44a6889..454840d12b 100644 --- a/src/Reflection/ClassReflection.php +++ b/src/Reflection/ClassReflection.php @@ -162,7 +162,7 @@ public function __construct( private PhpDocInheritanceResolver $phpDocInheritanceResolver, private PhpVersion $phpVersion, private SignatureMapProvider $signatureMapProvider, - private DeprecationProvider $deprecationResolver, + private DeprecationProvider $deprecationProvider, private AttributeReflectionFactory $attributeReflectionFactory, private array $propertiesClassReflectionExtensions, private array $methodsClassReflectionExtensions, @@ -795,7 +795,8 @@ public function getEnumCases(): array $valueType = $this->initializerExprTypeResolver->getType($case->getValueExpression(), $initializerExprContext); } $caseName = $case->getName(); - $cases[$caseName] = new EnumCaseReflection($this, $case, $valueType, $this->attributeReflectionFactory->fromNativeReflection($case->getAttributes(), InitializerExprContext::fromClass($this->getName(), $this->getFileName()))); + $attributes = $this->attributeReflectionFactory->fromNativeReflection($case->getAttributes(), InitializerExprContext::fromClass($this->getName(), $this->getFileName())); + $cases[$caseName] = new EnumCaseReflection($this, $case, $valueType, $attributes, $this->deprecationProvider); } return $this->enumCases = $cases; @@ -821,7 +822,9 @@ public function getEnumCase(string $name): EnumCaseReflection $valueType = $this->initializerExprTypeResolver->getType($case->getValueExpression(), InitializerExprContext::fromClassReflection($this)); } - return new EnumCaseReflection($this, $case, $valueType, $this->attributeReflectionFactory->fromNativeReflection($case->getAttributes(), InitializerExprContext::fromClass($this->getName(), $this->getFileName()))); + $attributes = $this->attributeReflectionFactory->fromNativeReflection($case->getAttributes(), InitializerExprContext::fromClass($this->getName(), $this->getFileName())); + + return new EnumCaseReflection($this, $case, $valueType, $attributes, $this->deprecationProvider); } public function isClass(): bool @@ -1081,7 +1084,7 @@ public function getConstant(string $name): ClassConstantReflection throw new MissingConstantFromReflectionException($this->getName(), $name); } - $deprecation = $this->deprecationResolver->getClassConstantDeprecation($reflectionConstant); + $deprecation = $this->deprecationProvider->getClassConstantDeprecation($reflectionConstant); $deprecatedDescription = $deprecation === null ? null : $deprecation->getDescription(); $isDeprecated = $deprecation !== null; @@ -1239,7 +1242,7 @@ public function isDeprecated(): bool */ private function resolveDeprecation(): void { - $deprecation = $this->deprecationResolver->isClassDeprecated($this->reflection); + $deprecation = $this->deprecationProvider->isClassDeprecated($this->reflection); if ($deprecation !== null) { $this->isDeprecated = true; $this->deprecatedDescription = $deprecation->getDescription(); @@ -1587,7 +1590,7 @@ public function withTypes(array $types): self $this->phpDocInheritanceResolver, $this->phpVersion, $this->signatureMapProvider, - $this->deprecationResolver, + $this->deprecationProvider, $this->attributeReflectionFactory, $this->propertiesClassReflectionExtensions, $this->methodsClassReflectionExtensions, @@ -1619,7 +1622,7 @@ public function withVariances(array $variances): self $this->phpDocInheritanceResolver, $this->phpVersion, $this->signatureMapProvider, - $this->deprecationResolver, + $this->deprecationProvider, $this->attributeReflectionFactory, $this->propertiesClassReflectionExtensions, $this->methodsClassReflectionExtensions, @@ -1661,7 +1664,7 @@ public function asFinal(): self $this->phpDocInheritanceResolver, $this->phpVersion, $this->signatureMapProvider, - $this->deprecationResolver, + $this->deprecationProvider, $this->attributeReflectionFactory, $this->propertiesClassReflectionExtensions, $this->methodsClassReflectionExtensions, diff --git a/src/Reflection/Deprecation/DeprecationProvider.php b/src/Reflection/Deprecation/DeprecationProvider.php index 22d90e63d4..ad286cad0d 100644 --- a/src/Reflection/Deprecation/DeprecationProvider.php +++ b/src/Reflection/Deprecation/DeprecationProvider.php @@ -5,6 +5,8 @@ use PHPStan\BetterReflection\Reflection\Adapter\ReflectionClass; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionClassConstant; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionEnum; +use PHPStan\BetterReflection\Reflection\Adapter\ReflectionEnumBackedCase; +use PHPStan\BetterReflection\Reflection\Adapter\ReflectionEnumUnitCase; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionFunction; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionMethod; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionProperty; @@ -32,6 +34,9 @@ final class DeprecationProvider /** @var array $constantDeprecationExtensions */ private array $constantDeprecationExtensions; + /** @var array $enumCaseDeprecationExtensions */ + private array $enumCaseDeprecationExtensions; + public function __construct( Container $container, ) @@ -42,6 +47,7 @@ public function __construct( $this->classDeprecationExtensions = $container->getServicesByTag('phpstan.classDeprecationExtension'); $this->functionDeprecationExtensions = $container->getServicesByTag('phpstan.functionDeprecationExtension'); $this->constantDeprecationExtensions = $container->getServicesByTag('phpstan.constantDeprecationExtension'); + $this->enumCaseDeprecationExtensions = $container->getServicesByTag('phpstan.enumCaseDeprecationExtension'); } public function getPropertyDeprecation(ReflectionProperty $reflectionProperty): ?Deprecation @@ -116,4 +122,16 @@ public function getConstantDeprecation(ReflectionConstant $constantReflection): return null; } + public function getEnumCaseDeprecation(ReflectionEnumUnitCase|ReflectionEnumBackedCase $enumCaseReflection): ?Deprecation + { + foreach ($this->enumCaseDeprecationExtensions as $extension) { + $deprecation = $extension->getEnumCaseDeprecation($enumCaseReflection); + if ($deprecation !== null) { + return $deprecation; + } + } + + return null; + } + } diff --git a/src/Reflection/Deprecation/EnumCaseDeprecationExtension.php b/src/Reflection/Deprecation/EnumCaseDeprecationExtension.php new file mode 100644 index 0000000000..2acf79925e --- /dev/null +++ b/src/Reflection/Deprecation/EnumCaseDeprecationExtension.php @@ -0,0 +1,28 @@ + $attributes */ @@ -22,8 +27,22 @@ public function __construct( private ReflectionEnumUnitCase|ReflectionEnumBackedCase $reflection, private ?Type $backingValueType, private array $attributes, + DeprecationProvider $deprecationProvider, ) { + $deprecation = $deprecationProvider->getEnumCaseDeprecation($reflection); + if ($deprecation !== null) { + $this->isDeprecated = true; + $this->deprecatedDescription = $deprecation->getDescription(); + + } elseif ($reflection->isDeprecated()) { + $attributes = $this->reflection->getBetterReflection()->getAttributes(); + $this->isDeprecated = true; + $this->deprecatedDescription = DeprecatedAttributeHelper::getDeprecatedDescription($attributes); + } else { + $this->isDeprecated = false; + $this->deprecatedDescription = null; + } } public function getDeclaringEnum(): ClassReflection @@ -43,17 +62,12 @@ public function getBackingValueType(): ?Type public function isDeprecated(): TrinaryLogic { - return TrinaryLogic::createFromBoolean($this->reflection->isDeprecated()); + return TrinaryLogic::createFromBoolean($this->isDeprecated); } public function getDeprecatedDescription(): ?string { - if ($this->reflection->isDeprecated()) { - $attributes = $this->reflection->getBetterReflection()->getAttributes(); - return DeprecatedAttributeHelper::getDeprecatedDescription($attributes); - } - - return null; + return $this->deprecatedDescription; } /** diff --git a/tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php b/tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php index 9af2743a98..d70f635159 100644 --- a/tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php +++ b/tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php @@ -7,6 +7,7 @@ use CustomDeprecations\DoubleDeprecatedClass; use CustomDeprecations\DoubleDeprecatedClassOnlyAttributeMessage; use CustomDeprecations\DoubleDeprecatedClassOnlyPhpDocMessage; +use CustomDeprecations\MyDeprecatedEnum; use CustomDeprecations\NotDeprecatedClass; use CustomDeprecations\PhpDocDeprecatedClass; use CustomDeprecations\PhpDocDeprecatedClassWithMessage; @@ -58,6 +59,24 @@ public function testCustomDeprecations(): void $scopeForDoubleDeprecatedClassOnlyNativeMessage = $scopeFactory->create(ScopeContext::create('dummy.php')->enterClass($doubleDeprecatedClassOnlyPhpDocMessage)); $scopeForDoubleDeprecatedClassOnlyCustomMessage = $scopeFactory->create(ScopeContext::create('dummy.php')->enterClass($doubleDeprecatedClassOnlyAttributeMessage)); + // enum cases + $myEnum = $reflectionProvider->getClass(MyDeprecatedEnum::class); + + self::assertTrue($myEnum->isDeprecated()); + self::assertNull($myEnum->getDeprecatedDescription()); + + self::assertTrue($myEnum->getEnumCase('CustomDeprecated')->isDeprecated()->yes()); + self::assertSame('custom', $myEnum->getEnumCase('CustomDeprecated')->getDeprecatedDescription()); + + self::assertTrue($myEnum->getEnumCase('NativeDeprecated')->isDeprecated()->yes()); + self::assertSame('native', $myEnum->getEnumCase('NativeDeprecated')->getDeprecatedDescription()); + + self::assertTrue($myEnum->getEnumCase('PhpDocDeprecated')->isDeprecated()->yes()); + self::assertNull($myEnum->getEnumCase('PhpDocDeprecated')->getDeprecatedDescription()); // this should not be null + + self::assertFalse($myEnum->getEnumCase('NotDeprecated')->isDeprecated()->yes()); + self::assertNull($myEnum->getEnumCase('NotDeprecated')->getDeprecatedDescription()); + // class self::assertFalse($notDeprecatedClass->isDeprecated()); self::assertNull($notDeprecatedClass->getDeprecatedDescription()); diff --git a/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationExtension.php b/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationExtension.php index 25f16a181b..dd0ac38c86 100644 --- a/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationExtension.php +++ b/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecationExtension.php @@ -8,6 +8,8 @@ use PHPStan\BetterReflection\Reflection\Adapter\ReflectionClass; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionClassConstant; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionEnum; +use PHPStan\BetterReflection\Reflection\Adapter\ReflectionEnumBackedCase; +use PHPStan\BetterReflection\Reflection\Adapter\ReflectionEnumUnitCase; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionFunction; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionMethod; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionProperty; @@ -16,6 +18,7 @@ use PHPStan\Reflection\Deprecation\ClassDeprecationExtension; use PHPStan\Reflection\Deprecation\ConstantDeprecationExtension; use PHPStan\Reflection\Deprecation\Deprecation; +use PHPStan\Reflection\Deprecation\EnumCaseDeprecationExtension; use PHPStan\Reflection\Deprecation\FunctionDeprecationExtension; use PHPStan\Reflection\Deprecation\MethodDeprecationExtension; use PHPStan\Reflection\Deprecation\PropertyDeprecationExtension; @@ -26,7 +29,8 @@ class CustomDeprecationExtension implements ClassConstantDeprecationExtension, MethodDeprecationExtension, PropertyDeprecationExtension, - FunctionDeprecationExtension + FunctionDeprecationExtension, + EnumCaseDeprecationExtension { public function getClassDeprecation(ReflectionClass|ReflectionEnum $reflection): ?Deprecation @@ -59,6 +63,11 @@ public function getClassConstantDeprecation(ReflectionClassConstant $reflection) return $this->buildDeprecation($reflection); } + public function getEnumCaseDeprecation(ReflectionEnumBackedCase|ReflectionEnumUnitCase $reflection): ?Deprecation + { + return $this->buildDeprecation($reflection); + } + private function buildDeprecation($reflection): ?Deprecation { foreach ($reflection->getAttributes(CustomDeprecated::class) as $attribute) { @@ -70,5 +79,4 @@ private function buildDeprecation($reflection): ?Deprecation return null; } - } diff --git a/tests/PHPStan/Reflection/Deprecation/data/deprecation-provider.neon b/tests/PHPStan/Reflection/Deprecation/data/deprecation-provider.neon index 1e10e24a8c..6b79cfe3f2 100644 --- a/tests/PHPStan/Reflection/Deprecation/data/deprecation-provider.neon +++ b/tests/PHPStan/Reflection/Deprecation/data/deprecation-provider.neon @@ -8,3 +8,4 @@ services: - phpstan.classDeprecationExtension - phpstan.functionDeprecationExtension - phpstan.constantDeprecationExtension + - phpstan.enumCaseDeprecationExtension diff --git a/tests/PHPStan/Reflection/Deprecation/data/deprecations.php b/tests/PHPStan/Reflection/Deprecation/data/deprecations.php index 5980892acd..69f5b64a11 100644 --- a/tests/PHPStan/Reflection/Deprecation/data/deprecations.php +++ b/tests/PHPStan/Reflection/Deprecation/data/deprecations.php @@ -161,3 +161,22 @@ function doubleDeprecatedFunctionOnlyAttributeMessage() {} /** @deprecated phpdoc */ #[CustomDeprecated()] function doubleDeprecatedFunctionOnlyPhpDocMessage() {} + + +#[CustomDeprecated] +enum MyDeprecatedEnum: string +{ + #[CustomDeprecated('custom')] + case CustomDeprecated = '1'; + + /** + * @deprecated phpdoc + */ + case PhpDocDeprecated = '2'; + + #[\Deprecated('native')] + case NativeDeprecated = '3'; + + case NotDeprecated = '4'; + +} From c5ae947ac651f86fa9e33f059fa0ae0f28db47dc Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Mon, 14 Apr 2025 10:39:53 +0200 Subject: [PATCH 09/15] Missed provider var renames --- src/Analyser/NodeScopeResolver.php | 4 ++-- .../BetterReflection/BetterReflectionProvider.php | 10 +++++----- src/Reflection/Php/PhpClassReflectionExtension.php | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 443a72f50c..24ee7c4bcf 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -256,7 +256,7 @@ public function __construct( private readonly StubPhpDocProvider $stubPhpDocProvider, private readonly PhpVersion $phpVersion, private readonly SignatureMapProvider $signatureMapProvider, - private readonly DeprecationProvider $deprecationResolver, + private readonly DeprecationProvider $deprecationProvider, private readonly AttributeReflectionFactory $attributeReflectionFactory, private readonly PhpDocInheritanceResolver $phpDocInheritanceResolver, private readonly FileHelper $fileHelper, @@ -2142,7 +2142,7 @@ private function createAstClassReflection(Node\Stmt\ClassLike $stmt, string $cla $this->phpDocInheritanceResolver, $this->phpVersion, $this->signatureMapProvider, - $this->deprecationResolver, + $this->deprecationProvider, $this->attributeReflectionFactory, $this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(), $this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(), diff --git a/src/Reflection/BetterReflection/BetterReflectionProvider.php b/src/Reflection/BetterReflection/BetterReflectionProvider.php index 938220e041..707aeca28a 100644 --- a/src/Reflection/BetterReflection/BetterReflectionProvider.php +++ b/src/Reflection/BetterReflection/BetterReflectionProvider.php @@ -86,7 +86,7 @@ public function __construct( private Reflector $reflector, private FileTypeMapper $fileTypeMapper, private PhpDocInheritanceResolver $phpDocInheritanceResolver, - private DeprecationProvider $deprecationResolver, + private DeprecationProvider $deprecationProvider, private PhpVersion $phpVersion, private NativeFunctionReflectionProvider $nativeFunctionReflectionProvider, private StubPhpDocProvider $stubPhpDocProvider, @@ -150,7 +150,7 @@ public function getClass(string $className): ClassReflection $this->phpDocInheritanceResolver, $this->phpVersion, $this->signatureMapProvider, - $this->deprecationResolver, + $this->deprecationProvider, $this->attributeReflectionFactory, $this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(), $this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(), @@ -246,7 +246,7 @@ public function getAnonymousClassReflection(Node\Stmt\Class_ $classNode, Scope $ $this->phpDocInheritanceResolver, $this->phpVersion, $this->signatureMapProvider, - $this->deprecationResolver, + $this->deprecationProvider, $this->attributeReflectionFactory, $this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(), $this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(), @@ -310,7 +310,7 @@ private function getCustomFunction(string $functionName): PhpFunctionReflection $phpDocReturnTag = null; $phpDocThrowsTag = null; - $deprecation = $this->deprecationResolver->getFunctionDeprecation($reflectionFunction); + $deprecation = $this->deprecationProvider->getFunctionDeprecation($reflectionFunction); $deprecationDescription = $deprecation === null ? null : $deprecation->getDescription(); $isDeprecated = $deprecation !== null; @@ -416,7 +416,7 @@ public function getConstant(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAn $constantValueType = $this->initializerExprTypeResolver->getType($constantReflection->getValueExpression(), InitializerExprContext::fromGlobalConstant($constantReflection)); $docComment = $constantReflection->getDocComment(); - $deprecation = $this->deprecationResolver->getConstantDeprecation($constantReflection); + $deprecation = $this->deprecationProvider->getConstantDeprecation($constantReflection); $isDeprecated = $deprecation !== null; $deprecatedDescription = $deprecation === null ? null : $deprecation->getDescription(); diff --git a/src/Reflection/Php/PhpClassReflectionExtension.php b/src/Reflection/Php/PhpClassReflectionExtension.php index 51ced61681..fc0dd70dbe 100644 --- a/src/Reflection/Php/PhpClassReflectionExtension.php +++ b/src/Reflection/Php/PhpClassReflectionExtension.php @@ -92,7 +92,7 @@ public function __construct( private NodeScopeResolver $nodeScopeResolver, private PhpMethodReflectionFactory $methodReflectionFactory, private PhpDocInheritanceResolver $phpDocInheritanceResolver, - private DeprecationProvider $deprecationResolver, + private DeprecationProvider $deprecationProvider, private AnnotationsMethodsClassReflectionExtension $annotationsMethodsClassReflectionExtension, private AnnotationsPropertiesClassReflectionExtension $annotationsPropertiesClassReflectionExtension, private SignatureMapProvider $signatureMapProvider, @@ -222,7 +222,7 @@ private function createProperty( } } - $deprecation = $this->deprecationResolver->getPropertyDeprecation($propertyReflection); + $deprecation = $this->deprecationProvider->getPropertyDeprecation($propertyReflection); $deprecatedDescription = $deprecation === null ? null : $deprecation->getDescription(); $isDeprecated = $deprecation !== null; $isInternal = false; @@ -705,7 +705,7 @@ private function createMethod( public function createUserlandMethodReflection(ClassReflection $fileDeclaringClass, ClassReflection $actualDeclaringClass, ReflectionMethod $methodReflection, ?string $declaringTraitName): PhpMethodReflection { - $deprecation = $this->deprecationResolver->getMethodDeprecation($methodReflection); + $deprecation = $this->deprecationProvider->getMethodDeprecation($methodReflection); $deprecatedDescription = $deprecation === null ? null : $deprecation->getDescription(); $isDeprecated = $deprecation !== null; From e1301ac453cfbea9675c8f3c3cf2d357d83ce51d Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Mon, 14 Apr 2025 10:45:49 +0200 Subject: [PATCH 10/15] Adjust enum tests for PHP8.1+ --- .../Reflection/Deprecation/DeprecationProviderTest.php | 4 ++-- tests/PHPStan/Reflection/Deprecation/data/deprecations.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php b/tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php index d70f635159..23a1773a8c 100644 --- a/tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php +++ b/tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php @@ -22,8 +22,8 @@ class DeprecationProviderTest extends PHPStanTestCase public function testCustomDeprecations(): void { - if (PHP_VERSION_ID < 80000) { - self::markTestSkipped('PHP 8.0+ is required as CustomDeprecationProvider uses unions.'); + if (PHP_VERSION_ID < 80100) { + self::markTestSkipped('PHP 8.1+ is required to test enums.'); } require __DIR__ . '/data/deprecations.php'; diff --git a/tests/PHPStan/Reflection/Deprecation/data/deprecations.php b/tests/PHPStan/Reflection/Deprecation/data/deprecations.php index 69f5b64a11..8f75fa001a 100644 --- a/tests/PHPStan/Reflection/Deprecation/data/deprecations.php +++ b/tests/PHPStan/Reflection/Deprecation/data/deprecations.php @@ -1,4 +1,4 @@ -= 8.1 namespace CustomDeprecations; From 6935ac449891e9ea9f40d33789ef9d65f177ac3b Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Mon, 14 Apr 2025 10:54:43 +0200 Subject: [PATCH 11/15] Fix naming inconsistency typo --- src/Reflection/ClassReflection.php | 2 +- src/Reflection/Deprecation/DeprecationProvider.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Reflection/ClassReflection.php b/src/Reflection/ClassReflection.php index 454840d12b..6face3bab1 100644 --- a/src/Reflection/ClassReflection.php +++ b/src/Reflection/ClassReflection.php @@ -1242,7 +1242,7 @@ public function isDeprecated(): bool */ private function resolveDeprecation(): void { - $deprecation = $this->deprecationProvider->isClassDeprecated($this->reflection); + $deprecation = $this->deprecationProvider->getClassDeprecation($this->reflection); if ($deprecation !== null) { $this->isDeprecated = true; $this->deprecatedDescription = $deprecation->getDescription(); diff --git a/src/Reflection/Deprecation/DeprecationProvider.php b/src/Reflection/Deprecation/DeprecationProvider.php index ad286cad0d..39ce0c98ec 100644 --- a/src/Reflection/Deprecation/DeprecationProvider.php +++ b/src/Reflection/Deprecation/DeprecationProvider.php @@ -86,7 +86,7 @@ public function getClassConstantDeprecation(ReflectionClassConstant $reflectionC return null; } - public function isClassDeprecated(ReflectionClass|ReflectionEnum $reflection): ?Deprecation + public function getClassDeprecation(ReflectionClass|ReflectionEnum $reflection): ?Deprecation { foreach ($this->classDeprecationExtensions as $extension) { $deprecation = $extension->getClassDeprecation($reflection); From 529f0300b4a8f701387f577c07fb80d479a00a66 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Mon, 14 Apr 2025 11:01:31 +0200 Subject: [PATCH 12/15] DeprecationProvider: lazy extensions --- .../Deprecation/DeprecationProvider.php | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/Reflection/Deprecation/DeprecationProvider.php b/src/Reflection/Deprecation/DeprecationProvider.php index 39ce0c98ec..74948c16af 100644 --- a/src/Reflection/Deprecation/DeprecationProvider.php +++ b/src/Reflection/Deprecation/DeprecationProvider.php @@ -16,42 +16,37 @@ final class DeprecationProvider { - /** @var array $propertyDeprecationExtensions */ - private array $propertyDeprecationExtensions; + /** @var ?array $propertyDeprecationExtensions */ + private ?array $propertyDeprecationExtensions = null; - /** @var array $methodDeprecationExtensions */ - private array $methodDeprecationExtensions; + /** @var ?array $methodDeprecationExtensions */ + private ?array $methodDeprecationExtensions = null; - /** @var array $classConstantDeprecationExtensions */ - private array $classConstantDeprecationExtensions; + /** @var ?array $classConstantDeprecationExtensions */ + private ?array $classConstantDeprecationExtensions = null; - /** @var array $classDeprecationExtensions */ - private array $classDeprecationExtensions; + /** @var ?array $classDeprecationExtensions */ + private ?array $classDeprecationExtensions = null; - /** @var array $functionDeprecationExtensions */ - private array $functionDeprecationExtensions; + /** @var ?array $functionDeprecationExtensions */ + private ?array $functionDeprecationExtensions = null; - /** @var array $constantDeprecationExtensions */ - private array $constantDeprecationExtensions; + /** @var ?array $constantDeprecationExtensions */ + private ?array $constantDeprecationExtensions = null; - /** @var array $enumCaseDeprecationExtensions */ - private array $enumCaseDeprecationExtensions; + /** @var ?array $enumCaseDeprecationExtensions */ + private ?array $enumCaseDeprecationExtensions = null; public function __construct( - Container $container, + private Container $container, ) { - $this->propertyDeprecationExtensions = $container->getServicesByTag('phpstan.propertyDeprecationExtension'); - $this->methodDeprecationExtensions = $container->getServicesByTag('phpstan.methodDeprecationExtension'); - $this->classConstantDeprecationExtensions = $container->getServicesByTag('phpstan.classConstantDeprecationExtension'); - $this->classDeprecationExtensions = $container->getServicesByTag('phpstan.classDeprecationExtension'); - $this->functionDeprecationExtensions = $container->getServicesByTag('phpstan.functionDeprecationExtension'); - $this->constantDeprecationExtensions = $container->getServicesByTag('phpstan.constantDeprecationExtension'); - $this->enumCaseDeprecationExtensions = $container->getServicesByTag('phpstan.enumCaseDeprecationExtension'); } public function getPropertyDeprecation(ReflectionProperty $reflectionProperty): ?Deprecation { + $this->propertyDeprecationExtensions ??= $this->container->getServicesByTag('phpstan.propertyDeprecationExtension'); + foreach ($this->propertyDeprecationExtensions as $extension) { $deprecation = $extension->getPropertyDeprecation($reflectionProperty); if ($deprecation !== null) { @@ -64,6 +59,8 @@ public function getPropertyDeprecation(ReflectionProperty $reflectionProperty): public function getMethodDeprecation(ReflectionMethod $methodReflection): ?Deprecation { + $this->methodDeprecationExtensions ??= $this->container->getServicesByTag('phpstan.methodDeprecationExtension'); + foreach ($this->methodDeprecationExtensions as $extension) { $deprecation = $extension->getMethodDeprecation($methodReflection); if ($deprecation !== null) { @@ -76,6 +73,8 @@ public function getMethodDeprecation(ReflectionMethod $methodReflection): ?Depre public function getClassConstantDeprecation(ReflectionClassConstant $reflectionConstant): ?Deprecation { + $this->classConstantDeprecationExtensions ??= $this->container->getServicesByTag('phpstan.classConstantDeprecationExtension'); + foreach ($this->classConstantDeprecationExtensions as $extension) { $deprecation = $extension->getClassConstantDeprecation($reflectionConstant); if ($deprecation !== null) { @@ -88,6 +87,8 @@ public function getClassConstantDeprecation(ReflectionClassConstant $reflectionC public function getClassDeprecation(ReflectionClass|ReflectionEnum $reflection): ?Deprecation { + $this->classDeprecationExtensions ??= $this->container->getServicesByTag('phpstan.classDeprecationExtension'); + foreach ($this->classDeprecationExtensions as $extension) { $deprecation = $extension->getClassDeprecation($reflection); if ($deprecation !== null) { @@ -100,6 +101,8 @@ public function getClassDeprecation(ReflectionClass|ReflectionEnum $reflection): public function getFunctionDeprecation(ReflectionFunction $reflectionFunction): ?Deprecation { + $this->functionDeprecationExtensions ??= $this->container->getServicesByTag('phpstan.functionDeprecationExtension'); + foreach ($this->functionDeprecationExtensions as $extension) { $deprecation = $extension->getFunctionDeprecation($reflectionFunction); if ($deprecation !== null) { @@ -112,6 +115,8 @@ public function getFunctionDeprecation(ReflectionFunction $reflectionFunction): public function getConstantDeprecation(ReflectionConstant $constantReflection): ?Deprecation { + $this->constantDeprecationExtensions ??= $this->container->getServicesByTag('phpstan.constantDeprecationExtension'); + foreach ($this->constantDeprecationExtensions as $extension) { $deprecation = $extension->getConstantDeprecation($constantReflection); if ($deprecation !== null) { @@ -124,6 +129,8 @@ public function getConstantDeprecation(ReflectionConstant $constantReflection): public function getEnumCaseDeprecation(ReflectionEnumUnitCase|ReflectionEnumBackedCase $enumCaseReflection): ?Deprecation { + $this->enumCaseDeprecationExtensions ??= $this->container->getServicesByTag('phpstan.enumCaseDeprecationExtension'); + foreach ($this->enumCaseDeprecationExtensions as $extension) { $deprecation = $extension->getEnumCaseDeprecation($enumCaseReflection); if ($deprecation !== null) { From ea589973fb54d699572365cd31ced6cb5149dc81 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Mon, 14 Apr 2025 11:38:14 +0200 Subject: [PATCH 13/15] Tags to be in const --- .../ConditionalTagsExtension.php | 12 ++++++++++++ .../ClassConstantDeprecationExtension.php | 2 ++ .../Deprecation/ClassDeprecationExtension.php | 2 ++ .../Deprecation/ConstantDeprecationExtension.php | 2 ++ src/Reflection/Deprecation/DeprecationProvider.php | 14 +++++++------- .../Deprecation/EnumCaseDeprecationExtension.php | 2 ++ .../Deprecation/FunctionDeprecationExtension.php | 2 ++ .../Deprecation/MethodDeprecationExtension.php | 2 ++ .../Deprecation/PropertyDeprecationExtension.php | 2 ++ 9 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/DependencyInjection/ConditionalTagsExtension.php b/src/DependencyInjection/ConditionalTagsExtension.php index 6e28b549d3..9610d50c9d 100644 --- a/src/DependencyInjection/ConditionalTagsExtension.php +++ b/src/DependencyInjection/ConditionalTagsExtension.php @@ -16,6 +16,12 @@ use PHPStan\Parser\RichParser; use PHPStan\PhpDoc\StubFilesExtension; use PHPStan\PhpDoc\TypeNodeResolverExtension; +use PHPStan\Reflection\Deprecation\ClassConstantDeprecationExtension; +use PHPStan\Reflection\Deprecation\ClassDeprecationExtension; +use PHPStan\Reflection\Deprecation\EnumCaseDeprecationExtension; +use PHPStan\Reflection\Deprecation\FunctionDeprecationExtension; +use PHPStan\Reflection\Deprecation\MethodDeprecationExtension; +use PHPStan\Reflection\Deprecation\PropertyDeprecationExtension; use PHPStan\Rules\Constants\AlwaysUsedClassConstantsExtensionProvider; use PHPStan\Rules\LazyRegistry; use PHPStan\Rules\Properties\ReadWritePropertiesExtensionProvider; @@ -61,6 +67,12 @@ public function getConfigSchema(): Nette\Schema\Schema LazyParameterOutTypeExtensionProvider::STATIC_METHOD_TAG => $bool, DiagnoseExtension::EXTENSION_TAG => $bool, ResultCacheMetaExtension::EXTENSION_TAG => $bool, + ClassConstantDeprecationExtension::CLASS_CONSTANT_EXTENSION_TAG => $bool, + ClassDeprecationExtension::CLASS_EXTENSION_TAG => $bool, + EnumCaseDeprecationExtension::ENUM_CASE_EXTENSION_TAG => $bool, + FunctionDeprecationExtension::FUNCTION_EXTENSION_TAG => $bool, + MethodDeprecationExtension::METHOD_EXTENSION_TAG => $bool, + PropertyDeprecationExtension::PROPERTY_EXTENSION_TAG => $bool, ])->min(1)); } diff --git a/src/Reflection/Deprecation/ClassConstantDeprecationExtension.php b/src/Reflection/Deprecation/ClassConstantDeprecationExtension.php index 7a05e17e77..39e09047ff 100644 --- a/src/Reflection/Deprecation/ClassConstantDeprecationExtension.php +++ b/src/Reflection/Deprecation/ClassConstantDeprecationExtension.php @@ -22,6 +22,8 @@ interface ClassConstantDeprecationExtension { + public const CLASS_CONSTANT_EXTENSION_TAG = 'phpstan.classConstantDeprecationExtension'; + public function getClassConstantDeprecation(ReflectionClassConstant $reflection): ?Deprecation; } diff --git a/src/Reflection/Deprecation/ClassDeprecationExtension.php b/src/Reflection/Deprecation/ClassDeprecationExtension.php index 277cdc67eb..334b53a5a3 100644 --- a/src/Reflection/Deprecation/ClassDeprecationExtension.php +++ b/src/Reflection/Deprecation/ClassDeprecationExtension.php @@ -23,6 +23,8 @@ interface ClassDeprecationExtension { + public const CLASS_EXTENSION_TAG = 'phpstan.classDeprecationExtension'; + public function getClassDeprecation(ReflectionClass|ReflectionEnum $reflection): ?Deprecation; } diff --git a/src/Reflection/Deprecation/ConstantDeprecationExtension.php b/src/Reflection/Deprecation/ConstantDeprecationExtension.php index f5eaaddd92..20dbf09016 100644 --- a/src/Reflection/Deprecation/ConstantDeprecationExtension.php +++ b/src/Reflection/Deprecation/ConstantDeprecationExtension.php @@ -22,6 +22,8 @@ interface ConstantDeprecationExtension { + public const CONSTANT_EXTENSION_TAG = 'phpstan.constantDeprecationExtension'; + public function getConstantDeprecation(ReflectionConstant $reflection): ?Deprecation; } diff --git a/src/Reflection/Deprecation/DeprecationProvider.php b/src/Reflection/Deprecation/DeprecationProvider.php index 74948c16af..9c526121e9 100644 --- a/src/Reflection/Deprecation/DeprecationProvider.php +++ b/src/Reflection/Deprecation/DeprecationProvider.php @@ -45,7 +45,7 @@ public function __construct( public function getPropertyDeprecation(ReflectionProperty $reflectionProperty): ?Deprecation { - $this->propertyDeprecationExtensions ??= $this->container->getServicesByTag('phpstan.propertyDeprecationExtension'); + $this->propertyDeprecationExtensions ??= $this->container->getServicesByTag(PropertyDeprecationExtension::PROPERTY_EXTENSION_TAG); foreach ($this->propertyDeprecationExtensions as $extension) { $deprecation = $extension->getPropertyDeprecation($reflectionProperty); @@ -59,7 +59,7 @@ public function getPropertyDeprecation(ReflectionProperty $reflectionProperty): public function getMethodDeprecation(ReflectionMethod $methodReflection): ?Deprecation { - $this->methodDeprecationExtensions ??= $this->container->getServicesByTag('phpstan.methodDeprecationExtension'); + $this->methodDeprecationExtensions ??= $this->container->getServicesByTag(MethodDeprecationExtension::METHOD_EXTENSION_TAG); foreach ($this->methodDeprecationExtensions as $extension) { $deprecation = $extension->getMethodDeprecation($methodReflection); @@ -73,7 +73,7 @@ public function getMethodDeprecation(ReflectionMethod $methodReflection): ?Depre public function getClassConstantDeprecation(ReflectionClassConstant $reflectionConstant): ?Deprecation { - $this->classConstantDeprecationExtensions ??= $this->container->getServicesByTag('phpstan.classConstantDeprecationExtension'); + $this->classConstantDeprecationExtensions ??= $this->container->getServicesByTag(ClassConstantDeprecationExtension::CLASS_CONSTANT_EXTENSION_TAG); foreach ($this->classConstantDeprecationExtensions as $extension) { $deprecation = $extension->getClassConstantDeprecation($reflectionConstant); @@ -87,7 +87,7 @@ public function getClassConstantDeprecation(ReflectionClassConstant $reflectionC public function getClassDeprecation(ReflectionClass|ReflectionEnum $reflection): ?Deprecation { - $this->classDeprecationExtensions ??= $this->container->getServicesByTag('phpstan.classDeprecationExtension'); + $this->classDeprecationExtensions ??= $this->container->getServicesByTag(ClassDeprecationExtension::CLASS_EXTENSION_TAG); foreach ($this->classDeprecationExtensions as $extension) { $deprecation = $extension->getClassDeprecation($reflection); @@ -101,7 +101,7 @@ public function getClassDeprecation(ReflectionClass|ReflectionEnum $reflection): public function getFunctionDeprecation(ReflectionFunction $reflectionFunction): ?Deprecation { - $this->functionDeprecationExtensions ??= $this->container->getServicesByTag('phpstan.functionDeprecationExtension'); + $this->functionDeprecationExtensions ??= $this->container->getServicesByTag(FunctionDeprecationExtension::FUNCTION_EXTENSION_TAG); foreach ($this->functionDeprecationExtensions as $extension) { $deprecation = $extension->getFunctionDeprecation($reflectionFunction); @@ -115,7 +115,7 @@ public function getFunctionDeprecation(ReflectionFunction $reflectionFunction): public function getConstantDeprecation(ReflectionConstant $constantReflection): ?Deprecation { - $this->constantDeprecationExtensions ??= $this->container->getServicesByTag('phpstan.constantDeprecationExtension'); + $this->constantDeprecationExtensions ??= $this->container->getServicesByTag(ConstantDeprecationExtension::CONSTANT_EXTENSION_TAG); foreach ($this->constantDeprecationExtensions as $extension) { $deprecation = $extension->getConstantDeprecation($constantReflection); @@ -129,7 +129,7 @@ public function getConstantDeprecation(ReflectionConstant $constantReflection): public function getEnumCaseDeprecation(ReflectionEnumUnitCase|ReflectionEnumBackedCase $enumCaseReflection): ?Deprecation { - $this->enumCaseDeprecationExtensions ??= $this->container->getServicesByTag('phpstan.enumCaseDeprecationExtension'); + $this->enumCaseDeprecationExtensions ??= $this->container->getServicesByTag(EnumCaseDeprecationExtension::ENUM_CASE_EXTENSION_TAG); foreach ($this->enumCaseDeprecationExtensions as $extension) { $deprecation = $extension->getEnumCaseDeprecation($enumCaseReflection); diff --git a/src/Reflection/Deprecation/EnumCaseDeprecationExtension.php b/src/Reflection/Deprecation/EnumCaseDeprecationExtension.php index 2acf79925e..af51945d8e 100644 --- a/src/Reflection/Deprecation/EnumCaseDeprecationExtension.php +++ b/src/Reflection/Deprecation/EnumCaseDeprecationExtension.php @@ -23,6 +23,8 @@ interface EnumCaseDeprecationExtension { + public const ENUM_CASE_EXTENSION_TAG = 'phpstan.enumCaseDeprecationExtension'; + public function getEnumCaseDeprecation(ReflectionEnumUnitCase|ReflectionEnumBackedCase $reflection): ?Deprecation; } diff --git a/src/Reflection/Deprecation/FunctionDeprecationExtension.php b/src/Reflection/Deprecation/FunctionDeprecationExtension.php index bd813714fb..840cab3872 100644 --- a/src/Reflection/Deprecation/FunctionDeprecationExtension.php +++ b/src/Reflection/Deprecation/FunctionDeprecationExtension.php @@ -22,6 +22,8 @@ interface FunctionDeprecationExtension { + public const FUNCTION_EXTENSION_TAG = 'phpstan.functionDeprecationExtension'; + public function getFunctionDeprecation(ReflectionFunction $reflection): ?Deprecation; } diff --git a/src/Reflection/Deprecation/MethodDeprecationExtension.php b/src/Reflection/Deprecation/MethodDeprecationExtension.php index 137b5c8aa6..e78e7ae82a 100644 --- a/src/Reflection/Deprecation/MethodDeprecationExtension.php +++ b/src/Reflection/Deprecation/MethodDeprecationExtension.php @@ -22,6 +22,8 @@ interface MethodDeprecationExtension { + public const METHOD_EXTENSION_TAG = 'phpstan.methodDeprecationExtension'; + public function getMethodDeprecation(ReflectionMethod $reflection): ?Deprecation; } diff --git a/src/Reflection/Deprecation/PropertyDeprecationExtension.php b/src/Reflection/Deprecation/PropertyDeprecationExtension.php index a5299c9ae7..67d8f5e39b 100644 --- a/src/Reflection/Deprecation/PropertyDeprecationExtension.php +++ b/src/Reflection/Deprecation/PropertyDeprecationExtension.php @@ -22,6 +22,8 @@ interface PropertyDeprecationExtension { + public const PROPERTY_EXTENSION_TAG = 'phpstan.propertyDeprecationExtension'; + public function getPropertyDeprecation(ReflectionProperty $reflection): ?Deprecation; } From 14a6864b64bb4186aa3f085b275e88c055dd9fe0 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Mon, 14 Apr 2025 12:24:05 +0200 Subject: [PATCH 14/15] Separate enum tests --- .../Deprecation/DeprecationProviderTest.php | 50 +++++++++++-------- .../Deprecation/data/CustomDeprecated.php | 15 ++++++ .../Deprecation/data/deprecations-enums.php | 33 ++++++++++++ .../Deprecation/data/deprecations.php | 31 ------------ 4 files changed, 78 insertions(+), 51 deletions(-) create mode 100644 tests/PHPStan/Reflection/Deprecation/data/CustomDeprecated.php create mode 100644 tests/PHPStan/Reflection/Deprecation/data/deprecations-enums.php diff --git a/tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php b/tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php index 23a1773a8c..c52796550d 100644 --- a/tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php +++ b/tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php @@ -22,8 +22,8 @@ class DeprecationProviderTest extends PHPStanTestCase public function testCustomDeprecations(): void { - if (PHP_VERSION_ID < 80100) { - self::markTestSkipped('PHP 8.1+ is required to test enums.'); + if (PHP_VERSION_ID < 80000) { + self::markTestSkipped('PHP 8.0+ is required as CustomDeprecationProvider uses unions.'); } require __DIR__ . '/data/deprecations.php'; @@ -59,24 +59,6 @@ public function testCustomDeprecations(): void $scopeForDoubleDeprecatedClassOnlyNativeMessage = $scopeFactory->create(ScopeContext::create('dummy.php')->enterClass($doubleDeprecatedClassOnlyPhpDocMessage)); $scopeForDoubleDeprecatedClassOnlyCustomMessage = $scopeFactory->create(ScopeContext::create('dummy.php')->enterClass($doubleDeprecatedClassOnlyAttributeMessage)); - // enum cases - $myEnum = $reflectionProvider->getClass(MyDeprecatedEnum::class); - - self::assertTrue($myEnum->isDeprecated()); - self::assertNull($myEnum->getDeprecatedDescription()); - - self::assertTrue($myEnum->getEnumCase('CustomDeprecated')->isDeprecated()->yes()); - self::assertSame('custom', $myEnum->getEnumCase('CustomDeprecated')->getDeprecatedDescription()); - - self::assertTrue($myEnum->getEnumCase('NativeDeprecated')->isDeprecated()->yes()); - self::assertSame('native', $myEnum->getEnumCase('NativeDeprecated')->getDeprecatedDescription()); - - self::assertTrue($myEnum->getEnumCase('PhpDocDeprecated')->isDeprecated()->yes()); - self::assertNull($myEnum->getEnumCase('PhpDocDeprecated')->getDeprecatedDescription()); // this should not be null - - self::assertFalse($myEnum->getEnumCase('NotDeprecated')->isDeprecated()->yes()); - self::assertNull($myEnum->getEnumCase('NotDeprecated')->getDeprecatedDescription()); - // class self::assertFalse($notDeprecatedClass->isDeprecated()); self::assertNull($notDeprecatedClass->getDeprecatedDescription()); @@ -203,6 +185,34 @@ public function testCustomDeprecations(): void self::assertSame('attribute', $doubleDeprecatedFunctionOnlyAttributeMessage->getDeprecatedDescription()); } + public function testCustomDeprecationsOfEnumCases(): void + { + if (PHP_VERSION_ID < 80100) { + self::markTestSkipped('PHP 8.1+ is required to test enums.'); + } + + require __DIR__ . '/data/deprecations-enums.php'; + + $reflectionProvider = self::createReflectionProvider(); + + $myEnum = $reflectionProvider->getClass(MyDeprecatedEnum::class); + + self::assertTrue($myEnum->isDeprecated()); + self::assertNull($myEnum->getDeprecatedDescription()); + + self::assertTrue($myEnum->getEnumCase('CustomDeprecated')->isDeprecated()->yes()); + self::assertSame('custom', $myEnum->getEnumCase('CustomDeprecated')->getDeprecatedDescription()); + + self::assertTrue($myEnum->getEnumCase('NativeDeprecated')->isDeprecated()->yes()); + self::assertSame('native', $myEnum->getEnumCase('NativeDeprecated')->getDeprecatedDescription()); + + self::assertTrue($myEnum->getEnumCase('PhpDocDeprecated')->isDeprecated()->yes()); + self::assertNull($myEnum->getEnumCase('PhpDocDeprecated')->getDeprecatedDescription()); // this should not be null + + self::assertFalse($myEnum->getEnumCase('NotDeprecated')->isDeprecated()->yes()); + self::assertNull($myEnum->getEnumCase('NotDeprecated')->getDeprecatedDescription()); + } + public static function getAdditionalConfigFiles(): array { return [ diff --git a/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecated.php b/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecated.php new file mode 100644 index 0000000000..95997bf046 --- /dev/null +++ b/tests/PHPStan/Reflection/Deprecation/data/CustomDeprecated.php @@ -0,0 +1,15 @@ += 8.1 + +namespace CustomDeprecations; + +#[\Attribute(\Attribute::TARGET_ALL)] +class CustomDeprecated { + + public ?string $description; + + public function __construct( + ?string $description = null + ) { + $this->description = $description; + } +} diff --git a/tests/PHPStan/Reflection/Deprecation/data/deprecations-enums.php b/tests/PHPStan/Reflection/Deprecation/data/deprecations-enums.php new file mode 100644 index 0000000000..f55db1ebc8 --- /dev/null +++ b/tests/PHPStan/Reflection/Deprecation/data/deprecations-enums.php @@ -0,0 +1,33 @@ += 8.1 + +namespace CustomDeprecations; + +#[\Attribute(\Attribute::TARGET_ALL)] +class CustomDeprecated { + + public ?string $description; + + public function __construct( + ?string $description = null + ) { + $this->description = $description; + } +} + +#[CustomDeprecated] +enum MyDeprecatedEnum: string +{ + #[CustomDeprecated('custom')] + case CustomDeprecated = '1'; + + /** + * @deprecated phpdoc + */ + case PhpDocDeprecated = '2'; + + #[\Deprecated('native')] + case NativeDeprecated = '3'; + + case NotDeprecated = '4'; + +} diff --git a/tests/PHPStan/Reflection/Deprecation/data/deprecations.php b/tests/PHPStan/Reflection/Deprecation/data/deprecations.php index 8f75fa001a..9df05b1b41 100644 --- a/tests/PHPStan/Reflection/Deprecation/data/deprecations.php +++ b/tests/PHPStan/Reflection/Deprecation/data/deprecations.php @@ -2,18 +2,6 @@ namespace CustomDeprecations; -#[\Attribute(\Attribute::TARGET_ALL)] -class CustomDeprecated { - - public ?string $description; - - public function __construct( - ?string $description = null - ) { - $this->description = $description; - } -} - class NotDeprecatedClass { const FOO = 'foo'; @@ -161,22 +149,3 @@ function doubleDeprecatedFunctionOnlyAttributeMessage() {} /** @deprecated phpdoc */ #[CustomDeprecated()] function doubleDeprecatedFunctionOnlyPhpDocMessage() {} - - -#[CustomDeprecated] -enum MyDeprecatedEnum: string -{ - #[CustomDeprecated('custom')] - case CustomDeprecated = '1'; - - /** - * @deprecated phpdoc - */ - case PhpDocDeprecated = '2'; - - #[\Deprecated('native')] - case NativeDeprecated = '3'; - - case NotDeprecated = '4'; - -} From 6cba377fe37650b1820387dfbb1c9be8626f748c Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Mon, 14 Apr 2025 12:30:17 +0200 Subject: [PATCH 15/15] ignore enum issue on old php --- build/enums.neon | 4 ++++ .../Deprecation/data/deprecations-enums.php | 12 ------------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/build/enums.neon b/build/enums.neon index 3ec87ab42e..44eaccbbd1 100644 --- a/build/enums.neon +++ b/build/enums.neon @@ -13,3 +13,7 @@ parameters: paths: - ../tests/PHPStan/Type/ObjectTypeTest.php - ../tests/PHPStan/Type/IntersectionTypeTest.php + - + message: '#^Class CustomDeprecations\\MyDeprecatedEnum not found\.$#' + paths: + - ../tests/PHPStan/Reflection/Deprecation/DeprecationProviderTest.php diff --git a/tests/PHPStan/Reflection/Deprecation/data/deprecations-enums.php b/tests/PHPStan/Reflection/Deprecation/data/deprecations-enums.php index f55db1ebc8..44710f9e9f 100644 --- a/tests/PHPStan/Reflection/Deprecation/data/deprecations-enums.php +++ b/tests/PHPStan/Reflection/Deprecation/data/deprecations-enums.php @@ -2,18 +2,6 @@ namespace CustomDeprecations; -#[\Attribute(\Attribute::TARGET_ALL)] -class CustomDeprecated { - - public ?string $description; - - public function __construct( - ?string $description = null - ) { - $this->description = $description; - } -} - #[CustomDeprecated] enum MyDeprecatedEnum: string {