diff --git a/src/Reflection/Type/IntersectionTypeUnresolvedPropertyPrototypeReflection.php b/src/Reflection/Type/IntersectionTypeUnresolvedPropertyPrototypeReflection.php index 51e6ccaf469..a0c28b66f2d 100644 --- a/src/Reflection/Type/IntersectionTypeUnresolvedPropertyPrototypeReflection.php +++ b/src/Reflection/Type/IntersectionTypeUnresolvedPropertyPrototypeReflection.php @@ -18,7 +18,6 @@ final class IntersectionTypeUnresolvedPropertyPrototypeReflection implements Unr * @param UnresolvedPropertyPrototypeReflection[] $propertyPrototypes */ public function __construct( - private string $propertyName, private array $propertyPrototypes, ) { @@ -30,7 +29,7 @@ public function doNotResolveTemplateTypeMapToBounds(): UnresolvedPropertyPrototy return $this->cachedDoNotResolveTemplateTypeMapToBounds; } - return $this->cachedDoNotResolveTemplateTypeMapToBounds = new self($this->propertyName, array_map(static fn (UnresolvedPropertyPrototypeReflection $prototype): UnresolvedPropertyPrototypeReflection => $prototype->doNotResolveTemplateTypeMapToBounds(), $this->propertyPrototypes)); + return $this->cachedDoNotResolveTemplateTypeMapToBounds = new self(array_map(static fn (UnresolvedPropertyPrototypeReflection $prototype): UnresolvedPropertyPrototypeReflection => $prototype->doNotResolveTemplateTypeMapToBounds(), $this->propertyPrototypes)); } public function getNakedProperty(): ExtendedPropertyReflection @@ -50,7 +49,7 @@ public function getTransformedProperty(): ExtendedPropertyReflection public function withFechedOnType(Type $type): UnresolvedPropertyPrototypeReflection { - return new self($this->propertyName, array_map(static fn (UnresolvedPropertyPrototypeReflection $prototype): UnresolvedPropertyPrototypeReflection => $prototype->withFechedOnType($type), $this->propertyPrototypes)); + return new self(array_map(static fn (UnresolvedPropertyPrototypeReflection $prototype): UnresolvedPropertyPrototypeReflection => $prototype->withFechedOnType($type), $this->propertyPrototypes)); } } diff --git a/src/Reflection/Type/UnionTypeUnresolvedPropertyPrototypeReflection.php b/src/Reflection/Type/UnionTypeUnresolvedPropertyPrototypeReflection.php index b28625e3c33..f5d6b7dd9b2 100644 --- a/src/Reflection/Type/UnionTypeUnresolvedPropertyPrototypeReflection.php +++ b/src/Reflection/Type/UnionTypeUnresolvedPropertyPrototypeReflection.php @@ -18,7 +18,6 @@ final class UnionTypeUnresolvedPropertyPrototypeReflection implements Unresolved * @param UnresolvedPropertyPrototypeReflection[] $propertyPrototypes */ public function __construct( - private string $propertyName, private array $propertyPrototypes, ) { @@ -29,7 +28,7 @@ public function doNotResolveTemplateTypeMapToBounds(): UnresolvedPropertyPrototy if ($this->cachedDoNotResolveTemplateTypeMapToBounds !== null) { return $this->cachedDoNotResolveTemplateTypeMapToBounds; } - return $this->cachedDoNotResolveTemplateTypeMapToBounds = new self($this->propertyName, array_map(static fn (UnresolvedPropertyPrototypeReflection $prototype): UnresolvedPropertyPrototypeReflection => $prototype->doNotResolveTemplateTypeMapToBounds(), $this->propertyPrototypes)); + return $this->cachedDoNotResolveTemplateTypeMapToBounds = new self(array_map(static fn (UnresolvedPropertyPrototypeReflection $prototype): UnresolvedPropertyPrototypeReflection => $prototype->doNotResolveTemplateTypeMapToBounds(), $this->propertyPrototypes)); } public function getNakedProperty(): ExtendedPropertyReflection @@ -50,7 +49,7 @@ public function getTransformedProperty(): ExtendedPropertyReflection public function withFechedOnType(Type $type): UnresolvedPropertyPrototypeReflection { - return new self($this->propertyName, array_map(static fn (UnresolvedPropertyPrototypeReflection $prototype): UnresolvedPropertyPrototypeReflection => $prototype->withFechedOnType($type), $this->propertyPrototypes)); + return new self(array_map(static fn (UnresolvedPropertyPrototypeReflection $prototype): UnresolvedPropertyPrototypeReflection => $prototype->withFechedOnType($type), $this->propertyPrototypes)); } } diff --git a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php index 127cae01cd9..25c70705792 100644 --- a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php +++ b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php @@ -44,15 +44,10 @@ final class ImpossibleCheckTypeHelper { - /** - * @param string[] $universalObjectCratesClasses - */ public function __construct( private ReflectionProvider $reflectionProvider, private TypeSpecifier $typeSpecifier, #[AutowiredParameter] - private array $universalObjectCratesClasses, - #[AutowiredParameter] private bool $treatPhpDocTypesAsCertain, ) { @@ -417,7 +412,6 @@ public function doNotTreatPhpDocTypesAsCertain(): self return new self( $this->reflectionProvider, $this->typeSpecifier, - $this->universalObjectCratesClasses, false, ); } diff --git a/src/Rules/DeadCode/UnusedPrivatePropertyRule.php b/src/Rules/DeadCode/UnusedPrivatePropertyRule.php index 7d2ccc73280..1e797b06cbf 100644 --- a/src/Rules/DeadCode/UnusedPrivatePropertyRule.php +++ b/src/Rules/DeadCode/UnusedPrivatePropertyRule.php @@ -7,7 +7,9 @@ use PHPStan\DependencyInjection\AutowiredParameter; use PHPStan\DependencyInjection\RegisteredRule; use PHPStan\Node\ClassPropertiesNode; +use PHPStan\Node\ClassPropertyNode; use PHPStan\Node\Property\PropertyRead; +use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\Php\PhpMethodFromParserNodeReflection; use PHPStan\Rules\Properties\ReadWritePropertiesExtensionProvider; use PHPStan\Rules\Rule; @@ -120,6 +122,7 @@ public function processNode(Node $node, Scope $scope): array 'node' => $property, 'onlyReadable' => $property->isReadable() && !$property->isWritable(), 'onlyWritable' => $property->isWritable() && !$property->isReadable(), + 'hasTrueRead' => $alwaysRead, ]; } @@ -194,6 +197,7 @@ public function processNode(Node $node, Scope $scope): array if (!$classType->isSuperTypeOf($fetchedOnType)->no()) { if ($usage instanceof PropertyRead) { $properties[$propertyName]['read'] = true; + $properties[$propertyName]['hasTrueRead'] = true; } else { $properties[$propertyName]['written'] = true; } @@ -204,6 +208,7 @@ public function processNode(Node $node, Scope $scope): array if (!$classType->isSuperTypeOf($fetchedOnType)->no()) { if ($usage instanceof PropertyRead) { $properties[$propertyName]['read'] = true; + $properties[$propertyName]['hasTrueRead'] = true; } else { $properties[$propertyName]['written'] = true; } @@ -213,12 +218,25 @@ public function processNode(Node $node, Scope $scope): array if ($usage instanceof PropertyRead) { $properties[$propertyName]['read'] = true; + if (!$this->isPropertySelfWrite($usageScope, $propertyName, $propertyNode, $classReflection->getName())) { + $properties[$propertyName]['hasTrueRead'] = true; + } } else { $properties[$propertyName]['written'] = true; } } } + foreach ($properties as $propertyName => $data) { + if (!$data['read'] || $data['hasTrueRead']) { + continue; + } + if (!$data['node']->isPromoted()) { + continue; + } + $properties[$propertyName]['read'] = false; + } + [$uninitializedProperties] = $node->getUninitializedProperties($scope, []); $errors = []; @@ -270,4 +288,42 @@ public function processNode(Node $node, Scope $scope): array return $errors; } + private function isPropertySelfWrite( + Scope $usageScope, + string $propertyName, + ClassPropertyNode $propertyNode, + string $className, + ): bool + { + if (!$propertyNode->isPromoted()) { + return false; + } + + $callStack = $usageScope->getFunctionCallStackWithParameters(); + if ($callStack === []) { + return false; + } + + $lastCall = $callStack[count($callStack) - 1]; + [$calleeReflection, $parameterReflection] = $lastCall; + + if (!$calleeReflection instanceof MethodReflection) { + return false; + } + + if ($calleeReflection->getName() !== '__construct') { + return false; + } + + if ($calleeReflection->getDeclaringClass()->getName() !== $className) { + return false; + } + + if ($parameterReflection === null) { + return false; + } + + return $parameterReflection->getName() === $propertyName; + } + } diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index f5dceab6006..158b9183303 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -615,7 +615,7 @@ public function getUnresolvedPropertyPrototype(string $propertyName, ClassMember return $propertyPrototypes[0]; } - return new IntersectionTypeUnresolvedPropertyPrototypeReflection($propertyName, $propertyPrototypes); + return new IntersectionTypeUnresolvedPropertyPrototypeReflection($propertyPrototypes); } public function hasInstanceProperty(string $propertyName): TrinaryLogic @@ -648,7 +648,7 @@ public function getUnresolvedInstancePropertyPrototype(string $propertyName, Cla return $propertyPrototypes[0]; } - return new IntersectionTypeUnresolvedPropertyPrototypeReflection($propertyName, $propertyPrototypes); + return new IntersectionTypeUnresolvedPropertyPrototypeReflection($propertyPrototypes); } public function hasStaticProperty(string $propertyName): TrinaryLogic @@ -681,7 +681,7 @@ public function getUnresolvedStaticPropertyPrototype(string $propertyName, Class return $propertyPrototypes[0]; } - return new IntersectionTypeUnresolvedPropertyPrototypeReflection($propertyName, $propertyPrototypes); + return new IntersectionTypeUnresolvedPropertyPrototypeReflection($propertyPrototypes); } public function canCallMethods(): TrinaryLogic diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index e129e52f73f..ddd98803841 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -214,7 +214,7 @@ public function getUnresolvedPropertyPrototype(string $propertyName, ClassMember return $properties[0]; } - return new UnionTypeUnresolvedPropertyPrototypeReflection($propertyName, $properties); + return new UnionTypeUnresolvedPropertyPrototypeReflection($properties); } } } @@ -320,7 +320,7 @@ public function getUnresolvedInstancePropertyPrototype(string $propertyName, Cla return $properties[0]; } - return new UnionTypeUnresolvedPropertyPrototypeReflection($propertyName, $properties); + return new UnionTypeUnresolvedPropertyPrototypeReflection($properties); } } } diff --git a/src/Type/Php/TypeSpecifyingFunctionsDynamicReturnTypeExtension.php b/src/Type/Php/TypeSpecifyingFunctionsDynamicReturnTypeExtension.php index 8ed3c6b6056..fdc77b5e4a5 100644 --- a/src/Type/Php/TypeSpecifyingFunctionsDynamicReturnTypeExtension.php +++ b/src/Type/Php/TypeSpecifyingFunctionsDynamicReturnTypeExtension.php @@ -25,15 +25,10 @@ final class TypeSpecifyingFunctionsDynamicReturnTypeExtension implements Dynamic private ?ImpossibleCheckTypeHelper $helper = null; - /** - * @param string[] $universalObjectCratesClasses - */ public function __construct( private ReflectionProvider $reflectionProvider, #[AutowiredParameter] private bool $treatPhpDocTypesAsCertain, - #[AutowiredParameter] - private array $universalObjectCratesClasses, ) { } @@ -76,7 +71,7 @@ public function getTypeFromFunctionCall( private function getHelper(): ImpossibleCheckTypeHelper { - return $this->helper ??= new ImpossibleCheckTypeHelper($this->reflectionProvider, $this->typeSpecifier, $this->universalObjectCratesClasses, $this->treatPhpDocTypesAsCertain); + return $this->helper ??= new ImpossibleCheckTypeHelper($this->reflectionProvider, $this->typeSpecifier, $this->treatPhpDocTypesAsCertain); } } diff --git a/src/Type/UnionType.php b/src/Type/UnionType.php index ae1f5572eb2..a29b089a0b6 100644 --- a/src/Type/UnionType.php +++ b/src/Type/UnionType.php @@ -521,7 +521,7 @@ public function getUnresolvedPropertyPrototype(string $propertyName, ClassMember return $propertyPrototypes[0]; } - return new UnionTypeUnresolvedPropertyPrototypeReflection($propertyName, $propertyPrototypes); + return new UnionTypeUnresolvedPropertyPrototypeReflection($propertyPrototypes); } public function hasInstanceProperty(string $propertyName): TrinaryLogic @@ -554,7 +554,7 @@ public function getUnresolvedInstancePropertyPrototype(string $propertyName, Cla return $propertyPrototypes[0]; } - return new UnionTypeUnresolvedPropertyPrototypeReflection($propertyName, $propertyPrototypes); + return new UnionTypeUnresolvedPropertyPrototypeReflection($propertyPrototypes); } public function hasStaticProperty(string $propertyName): TrinaryLogic @@ -587,7 +587,7 @@ public function getUnresolvedStaticPropertyPrototype(string $propertyName, Class return $propertyPrototypes[0]; } - return new UnionTypeUnresolvedPropertyPrototypeReflection($propertyName, $propertyPrototypes); + return new UnionTypeUnresolvedPropertyPrototypeReflection($propertyPrototypes); } public function canCallMethods(): TrinaryLogic diff --git a/tests/PHPStan/Rules/Comparison/BooleanAndConstantConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/BooleanAndConstantConditionRuleTest.php index b332d01adb8..114c61812b2 100644 --- a/tests/PHPStan/Rules/Comparison/BooleanAndConstantConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/BooleanAndConstantConditionRuleTest.php @@ -23,7 +23,6 @@ protected function getRule(): Rule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [], $this->treatPhpDocTypesAsCertain, ), $this->treatPhpDocTypesAsCertain, diff --git a/tests/PHPStan/Rules/Comparison/BooleanNotConstantConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/BooleanNotConstantConditionRuleTest.php index 3cf3f2a8bbc..f86e67ef693 100644 --- a/tests/PHPStan/Rules/Comparison/BooleanNotConstantConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/BooleanNotConstantConditionRuleTest.php @@ -23,7 +23,6 @@ protected function getRule(): Rule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [], $this->treatPhpDocTypesAsCertain, ), $this->treatPhpDocTypesAsCertain, diff --git a/tests/PHPStan/Rules/Comparison/BooleanOrConstantConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/BooleanOrConstantConditionRuleTest.php index 75b53752284..e2d84d9e7a6 100644 --- a/tests/PHPStan/Rules/Comparison/BooleanOrConstantConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/BooleanOrConstantConditionRuleTest.php @@ -24,7 +24,6 @@ protected function getRule(): Rule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [], $this->treatPhpDocTypesAsCertain, ), $this->treatPhpDocTypesAsCertain, diff --git a/tests/PHPStan/Rules/Comparison/DoWhileLoopConstantConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/DoWhileLoopConstantConditionRuleTest.php index 97b3d705baf..3bd0c71136a 100644 --- a/tests/PHPStan/Rules/Comparison/DoWhileLoopConstantConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/DoWhileLoopConstantConditionRuleTest.php @@ -18,7 +18,6 @@ protected function getRule(): Rule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [], $this->shouldTreatPhpDocTypesAsCertain(), ), $this->shouldTreatPhpDocTypesAsCertain(), diff --git a/tests/PHPStan/Rules/Comparison/ElseIfConstantConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/ElseIfConstantConditionRuleTest.php index 220b7e115c1..92f14d6dd33 100644 --- a/tests/PHPStan/Rules/Comparison/ElseIfConstantConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ElseIfConstantConditionRuleTest.php @@ -24,7 +24,6 @@ protected function getRule(): Rule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [], $this->treatPhpDocTypesAsCertain, ), $this->treatPhpDocTypesAsCertain, diff --git a/tests/PHPStan/Rules/Comparison/IfConstantConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/IfConstantConditionRuleTest.php index 22f9d9577c0..0861db6f447 100644 --- a/tests/PHPStan/Rules/Comparison/IfConstantConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/IfConstantConditionRuleTest.php @@ -21,7 +21,6 @@ protected function getRule(): Rule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [], $this->treatPhpDocTypesAsCertain, ), $this->treatPhpDocTypesAsCertain, diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php index 2c63aec4647..2e50b4c511c 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php @@ -6,7 +6,6 @@ use PHPStan\Testing\RuleTestCase; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\RequiresPhp; -use stdClass; use function array_filter; use function array_map; use function array_values; @@ -28,7 +27,6 @@ protected function getRule(): Rule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [stdClass::class], $this->treatPhpDocTypesAsCertain, ), new PossiblyImpureTipHelper(true), diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeGenericOverwriteRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeGenericOverwriteRuleTest.php index 0190415a989..bb98ecf94bb 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeGenericOverwriteRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeGenericOverwriteRuleTest.php @@ -17,7 +17,6 @@ public function getRule(): Rule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [], true, ), new PossiblyImpureTipHelper(true), diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeMethodCallRuleEqualsTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeMethodCallRuleEqualsTest.php index 178b4148958..f48b76d5389 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeMethodCallRuleEqualsTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeMethodCallRuleEqualsTest.php @@ -17,7 +17,6 @@ public function getRule(): Rule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [], true, ), new PossiblyImpureTipHelper(true), diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeMethodCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeMethodCallRuleTest.php index 532239185d6..8648a231d1b 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeMethodCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeMethodCallRuleTest.php @@ -23,7 +23,6 @@ public function getRule(): Rule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [], $this->treatPhpDocTypesAsCertain, ), new PossiblyImpureTipHelper(true), diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeStaticMethodCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeStaticMethodCallRuleTest.php index 9022834bf99..1f18b019e47 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeStaticMethodCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeStaticMethodCallRuleTest.php @@ -23,7 +23,6 @@ public function getRule(): Rule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [], $this->treatPhpDocTypesAsCertain, ), new PossiblyImpureTipHelper(true), diff --git a/tests/PHPStan/Rules/Comparison/LogicalXorConstantConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/LogicalXorConstantConditionRuleTest.php index 14b97daacaa..5ca7530b60a 100644 --- a/tests/PHPStan/Rules/Comparison/LogicalXorConstantConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/LogicalXorConstantConditionRuleTest.php @@ -18,7 +18,6 @@ protected function getRule(): TRule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [], $this->shouldTreatPhpDocTypesAsCertain(), ), $this->shouldTreatPhpDocTypesAsCertain(), diff --git a/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php b/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php index 26b415ce9a5..7ab3586dfec 100644 --- a/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php @@ -21,7 +21,6 @@ protected function getRule(): Rule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [], $this->treatPhpDocTypesAsCertain, ), $this->treatPhpDocTypesAsCertain, diff --git a/tests/PHPStan/Rules/Comparison/TernaryOperatorConstantConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/TernaryOperatorConstantConditionRuleTest.php index 300484a89b7..4cf6c399ea7 100644 --- a/tests/PHPStan/Rules/Comparison/TernaryOperatorConstantConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/TernaryOperatorConstantConditionRuleTest.php @@ -20,7 +20,6 @@ protected function getRule(): Rule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [], $this->treatPhpDocTypesAsCertain, ), $this->treatPhpDocTypesAsCertain, diff --git a/tests/PHPStan/Rules/Comparison/WhileLoopAlwaysFalseConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/WhileLoopAlwaysFalseConditionRuleTest.php index e84fdb4d566..10c0fef19ad 100644 --- a/tests/PHPStan/Rules/Comparison/WhileLoopAlwaysFalseConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/WhileLoopAlwaysFalseConditionRuleTest.php @@ -18,7 +18,6 @@ protected function getRule(): Rule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [], $this->shouldTreatPhpDocTypesAsCertain(), ), $this->shouldTreatPhpDocTypesAsCertain(), diff --git a/tests/PHPStan/Rules/Comparison/WhileLoopAlwaysTrueConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/WhileLoopAlwaysTrueConditionRuleTest.php index 7cf64f83b88..f365fb0a9e5 100644 --- a/tests/PHPStan/Rules/Comparison/WhileLoopAlwaysTrueConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/WhileLoopAlwaysTrueConditionRuleTest.php @@ -19,7 +19,6 @@ protected function getRule(): Rule new ImpossibleCheckTypeHelper( self::createReflectionProvider(), $this->getTypeSpecifier(), - [], $this->shouldTreatPhpDocTypesAsCertain(), ), $this->shouldTreatPhpDocTypesAsCertain(), diff --git a/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php b/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php index 0214ecaaadd..cc98e1a27a8 100644 --- a/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php +++ b/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php @@ -409,4 +409,61 @@ public function testBug6777(): void $this->analyse([__DIR__ . '/data/bug-6777.php'], []); } + #[RequiresPhp('>= 8.0.0')] + public function testBug14573(): void + { + $this->alwaysWrittenTags = []; + $this->alwaysReadTags = []; + + $tip = 'See: https://phpstan.org/developing-extensions/always-read-written-properties'; + + $this->analyse([__DIR__ . '/data/bug-14573.php'], [ + [ + 'Property Bug14573\Foo::$test is never read, only written.', + 8, + $tip, + ], + [ + 'Property Bug14573\Bar::$b is never read, only written.', + 22, + $tip, + ], + [ + 'Property Bug14573\Baz::$a is never read, only written.', + 41, + $tip, + ], + [ + 'Property Bug14573\Baz::$b is never read, only written.', + 41, + $tip, + ], + [ + 'Property Bug14573\WithNewStatic::$test is never read, only written.', + 88, + $tip, + ], + [ + 'Property Bug14573\WithNewClassName::$test is never read, only written.', + 102, + $tip, + ], + [ + 'Property Bug14573\WithNamedArgument::$a is never read, only written.', + 135, + $tip, + ], + [ + 'Property Bug14573\WithNamedArgument::$b is never read, only written.', + 135, + $tip, + ], + [ + 'Property Bug14573\MultipleReadsOnlySelfWrite::$test is never read, only written.', + 177, + $tip, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/DeadCode/data/bug-14573.php b/tests/PHPStan/Rules/DeadCode/data/bug-14573.php new file mode 100644 index 00000000000..5ecde4ad57b --- /dev/null +++ b/tests/PHPStan/Rules/DeadCode/data/bug-14573.php @@ -0,0 +1,229 @@ += 8.0 + +namespace Bug14573; + +class Foo +{ + + public function __construct(private int $test) + { + } + + public function doFoo(): self + { + return new self($this->test); + } + +} + +class Bar +{ + + public function __construct(private int $a, private int $b) + { + } + + public function doFoo(): self + { + return new self($this->a, $this->b); + } + + public function getA(): int + { + return $this->a; + } + +} + +class Baz +{ + + public function __construct(private int $a, private int $b) + { + } + + public function doFoo(): self + { + return new self($this->a, $this->b); + } + +} + +class Quux +{ + + public function __construct(private int $a, private int $b) + { + } + + public function doFoo(): self + { + return new self($this->b, $this->a); + } + +} + +class WithAdditionalRead +{ + + public function __construct(private int $test) + { + } + + public function doFoo(): self + { + return new self($this->test); + } + + public function getTest(): int + { + return $this->test; + } + +} + +class WithNewStatic +{ + + public function __construct(private int $test) + { + } + + public function doFoo(): static + { + return new static($this->test); + } + +} + +class WithNewClassName +{ + + public function __construct(private int $test) + { + } + + public function doFoo(): self + { + return new WithNewClassName($this->test); + } + +} + +class DifferentPropertyPassedToSameParam +{ + + public function __construct(private int $a, private int $b) + { + } + + public function doFoo(): self + { + return new self($this->b, $this->a); + } + + public function getA(): int + { + return $this->a; + } + +} + +class WithNamedArgument +{ + + public function __construct(private int $a, private int $b) + { + } + + public function doFoo(): self + { + return new self(b: $this->b, a: $this->a); + } + +} + +class WithNamedArgumentSwapped +{ + + public function __construct(private int $a, private int $b) + { + } + + public function doFoo(): self + { + return new self(b: $this->a, a: $this->b); + } + +} + +class PassedToOtherClassConstructor +{ + + public function __construct(private int $test) + { + } + + public function doFoo(): Foo + { + return new Foo($this->test); + } + +} + +class MultipleReadsOnlySelfWrite +{ + + public function __construct(private int $test) + { + } + + public function doFoo(): self + { + return new self($this->test); + } + + public function doBar(): self + { + return new self($this->test); + } + +} + +class MixedSelfWriteAndTrueRead +{ + + public function __construct(private int $test) + { + } + + public function doFoo(): self + { + return new self($this->test); + } + + public function getTest(): int + { + return $this->test; + } + +} + +class SelfWriteInNonConstructorCall +{ + + public function __construct(private int $test) + { + } + + public function doFoo(int $test): self + { + $this->doBar($this->test); + return new self($this->test); + } + + private function doBar(int $test): void + { + } + +}