diff --git a/src/Node/ClassStatementsGatherer.php b/src/Node/ClassStatementsGatherer.php index a2bb6889d5..663d8c4e0d 100644 --- a/src/Node/ClassStatementsGatherer.php +++ b/src/Node/ClassStatementsGatherer.php @@ -13,11 +13,14 @@ use PhpParser\Node\Identifier; use PHPStan\Analyser\Scope; use PHPStan\Node\Constant\ClassConstantFetch; +use PHPStan\Node\Expr\SetExistingOffsetValueTypeExpr; +use PHPStan\Node\Expr\SetOffsetValueTypeExpr; use PHPStan\Node\Property\PropertyAssign; use PHPStan\Node\Property\PropertyRead; use PHPStan\Node\Property\PropertyWrite; use PHPStan\Reflection\ClassReflection; use PHPStan\ShouldNotHappenException; +use PHPStan\Type\ObjectType; use PHPStan\Type\TypeUtils; use ReflectionProperty; use function count; @@ -200,7 +203,18 @@ private function gatherNodes(Node $node, Scope $scope): void return; } if ($node instanceof PropertyAssignNode) { - $this->propertyUsages[] = new PropertyWrite($node->getPropertyFetch(), $scope, false); + $propertyFetch = $node->getPropertyFetch(); + $assignedExpr = $node->getAssignedExpr(); + if ($assignedExpr instanceof SetOffsetValueTypeExpr || $assignedExpr instanceof SetExistingOffsetValueTypeExpr) { + $propertyType = $scope->getType($propertyFetch); + if ( + !$propertyType->isArray()->yes() + && (new ObjectType(\ArrayAccess::class))->isSuperTypeOf($propertyType)->yes() + ) { + $this->propertyUsages[] = new PropertyRead($propertyFetch, $scope); + } + } + $this->propertyUsages[] = new PropertyWrite($propertyFetch, $scope, false); $this->propertyAssigns[] = new PropertyAssign($node, $scope); return; } diff --git a/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php b/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php index 199e22d1a2..6cc5e5e230 100644 --- a/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php +++ b/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php @@ -130,16 +130,6 @@ public function testRule(): void 191, $tip, ], - [ - 'Property UnusedPrivateProperty\WriteToCollection::$collection1 is never read, only written.', - 221, - $tip, - ], - [ - 'Property UnusedPrivateProperty\WriteToCollection::$collection2 is never read, only written.', - 224, - $tip, - ], ]); $this->analyse([__DIR__ . '/data/TestExtension.php'], [ [ @@ -410,4 +400,13 @@ public function testBug9213(): void $this->analyse([__DIR__ . '/data/bug-9213.php'], []); } + #[RequiresPhp('>= 8.0')] + public function testBug6777(): void + { + $this->alwaysWrittenTags = []; + $this->alwaysReadTags = []; + + $this->analyse([__DIR__ . '/data/bug-6777.php'], []); + } + } diff --git a/tests/PHPStan/Rules/DeadCode/data/bug-6777.php b/tests/PHPStan/Rules/DeadCode/data/bug-6777.php new file mode 100644 index 0000000000..0e24d4eced --- /dev/null +++ b/tests/PHPStan/Rules/DeadCode/data/bug-6777.php @@ -0,0 +1,15 @@ += 8.0 + +declare(strict_types = 1); + +namespace Bug6777; + +class HelloWorld +{ + /** @param \ArrayObject $array */ + public function __construct(private \ArrayObject $array){} + + public function send(string $s) : void{ + $this->array[] = $s; + } +}