diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index f1dfb1f0da..60f2fc6f14 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -2152,6 +2152,10 @@ public function enterAnonymousFunctionWithoutReflection( } } + if ($this->shouldNotCarryForwardPropertyFetchInClosure($expr)) { + continue; + } + $expressionTypes[$exprString] = $typeHolder; } @@ -2218,6 +2222,28 @@ public function enterAnonymousFunctionWithoutReflection( ); } + private function shouldNotCarryForwardPropertyFetchInClosure(Expr $expr): bool + { + if (!$expr instanceof PropertyFetch) { + return false; + } + + if (!$expr->name instanceof Identifier) { + return false; + } + + $objectType = $this->getType($expr->var); + $propertyName = $expr->name->name; + + foreach ($objectType->getObjectClassReflections() as $classReflection) { + if ($classReflection->hasNativeProperty($propertyName)) { + return false; + } + } + + return true; + } + private function expressionTypeIsUnchangeable(ExpressionTypeHolder $typeHolder): bool { $expr = $typeHolder->getExpr(); diff --git a/tests/PHPStan/Rules/Arrays/DeadForeachRuleTest.php b/tests/PHPStan/Rules/Arrays/DeadForeachRuleTest.php index 9e272b5802..38d62a1b45 100644 --- a/tests/PHPStan/Rules/Arrays/DeadForeachRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/DeadForeachRuleTest.php @@ -55,4 +55,9 @@ public function testBug2457(): void $this->analyse([__DIR__ . '/data/bug-2457.php'], []); } + public function testBug10345(): void + { + $this->analyse([__DIR__ . '/data/bug-10345.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Arrays/data/bug-10345.php b/tests/PHPStan/Rules/Arrays/data/bug-10345.php new file mode 100644 index 0000000000..e4a6829e58 --- /dev/null +++ b/tests/PHPStan/Rules/Arrays/data/bug-10345.php @@ -0,0 +1,15 @@ +items = []; + +$func = function() use ($container): int { + foreach ($container->items as $item) {} + return 1; +}; + +$container->items[] = '1'; + +$a = $func();