diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 50f99230e3..1bdd06fd61 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -761,12 +761,16 @@ private function processStmtNode( } elseif ($stmt instanceof Foreach_) { $condResult = $this->processExprNode($stmt->expr, $scope, $nodeCallback, ExpressionContext::createDeep()); $scope = $condResult->getScope(); - $bodyScope = $this->enterForeach($scope, $stmt); + $arrayComparisonExpr = new BinaryOp\NotIdentical( + $stmt->expr, + new Array_([]) + ); + $bodyScope = $this->enterForeach($scope->filterByTruthyValue($arrayComparisonExpr), $stmt); $hasYield = false; $count = 0; do { $prevScope = $bodyScope; - $bodyScope = $bodyScope->mergeWith($scope); + $bodyScope = $bodyScope->mergeWith($scope->filterByTruthyValue($arrayComparisonExpr)); $bodyScope = $this->enterForeach($bodyScope, $stmt); $bodyScopeResult = $this->processStmtNodes($stmt, $stmt->stmts, $bodyScope, static function (): void { })->filterOutLoopExitPoints(); @@ -785,7 +789,7 @@ private function processStmtNode( $count++; } while (!$alwaysTerminating && $count < self::LOOP_SCOPE_ITERATIONS); - $bodyScope = $bodyScope->mergeWith($scope); + $bodyScope = $bodyScope->mergeWith($scope->filterByTruthyValue($arrayComparisonExpr)); $bodyScope = $this->enterForeach($bodyScope, $stmt); $finalScopeResult = $this->processStmtNodes($stmt, $stmt->stmts, $bodyScope, $nodeCallback)->filterOutLoopExitPoints(); $finalScope = $finalScopeResult->getScope(); @@ -801,11 +805,7 @@ private function processStmtNode( $finalScope = $scope; } elseif ($isIterableAtLeastOnce->maybe()) { if ($this->polluteScopeWithAlwaysIterableForeach) { - $arrayComparisonExpr = new BinaryOp\NotIdentical( - $stmt->expr, - new Array_([]) - ); - $finalScope = $finalScope->filterByTruthyValue($arrayComparisonExpr)->mergeWith($scope->filterByFalseyValue($arrayComparisonExpr)); + $finalScope = $finalScope->mergeWith($scope->filterByFalseyValue($arrayComparisonExpr)); } else { $finalScope = $finalScope->mergeWith($scope); } diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index e96637b213..6d23a8725e 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -4774,7 +4774,7 @@ public function dataForeachArrayType(): array ], [ __DIR__ . '/data/foreach/foreach-with-specified-key-type.php', - 'array', + 'array&nonEmpty', '$list', ], [ @@ -6537,7 +6537,7 @@ public function dataIterable(): array '$unionBar', ], [ - 'array', + 'array&nonEmpty', '$mixedUnionIterableType', ], [ @@ -10603,6 +10603,11 @@ public function dataBug4339(): array return $this->gatherAssertTypes(__DIR__ . '/data/bug-4339.php'); } + public function dataBug4343(): array + { + return $this->gatherAssertTypes(__DIR__ . '/data/bug-4343.php'); + } + /** * @param string $file * @return array @@ -10807,6 +10812,7 @@ private function gatherAssertTypes(string $file): array * @dataProvider dataBug3986 * @dataProvider dataBug4188 * @dataProvider dataBug4339 + * @dataProvider dataBug4343 * @param string $assertType * @param string $file * @param mixed ...$args diff --git a/tests/PHPStan/Analyser/data/bug-4343.php b/tests/PHPStan/Analyser/data/bug-4343.php new file mode 100644 index 0000000000..83af0ea88c --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-4343.php @@ -0,0 +1,16 @@ + 0) { + $test = new \stdClass(); + } + + foreach ($a as $my) { + assertVariableCertainty(TrinaryLogic::createYes(), $test); + } +}; diff --git a/tests/PHPStan/Analyser/data/native-types.php b/tests/PHPStan/Analyser/data/native-types.php index 8159013f2f..e08fa84c03 100644 --- a/tests/PHPStan/Analyser/data/native-types.php +++ b/tests/PHPStan/Analyser/data/native-types.php @@ -83,8 +83,8 @@ public function doForeach(array $array): void assertNativeType('array', $array); foreach ($array as $key => $value) { - assertType('array', $array); - assertNativeType('array', $array); + assertType('array&nonEmpty', $array); + assertNativeType('array&nonEmpty', $array); assertType('string', $key); assertNativeType('(int|string)', $key); @@ -124,8 +124,8 @@ public function doForeachArrayDestructuring(array $array) assertType('array', $array); assertNativeType('array', $array); foreach ($array as $key => [$i, $s]) { - assertType('array', $array); - assertNativeType('array', $array); + assertType('array&nonEmpty', $array); + assertNativeType('array&nonEmpty', $array); assertType('string', $key); assertNativeType('(int|string)', $key); diff --git a/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php b/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php index 427a937cec..cbcb2f1c86 100644 --- a/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php +++ b/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php @@ -135,19 +135,7 @@ public function testTypesAssignedToPropertiesExpressionNames(): void 97, ], [ - 'Property PropertiesFromArrayIntoObject\Foo::$float_test (float) does not accept float|int|string.', - 110, - ], - [ - 'Property PropertiesFromArrayIntoObject\Foo::$foo (string) does not accept float|int|string.', - 110, - ], - [ - 'Property PropertiesFromArrayIntoObject\Foo::$lall (int) does not accept float|int|string.', - 110, - ], - [ - 'Property PropertiesFromArrayIntoObject\Foo::$test (int|null) does not accept float|int|string.', + 'Property PropertiesFromArrayIntoObject\Foo::$lall (int) does not accept string.', 110, ], [