Skip to content

Commit 853a1e6

Browse files
committed
Fix foreach scope when iterating non-array and polluteScopeWithAlwaysIterableForeach is true
1 parent b93f46d commit 853a1e6

4 files changed

Lines changed: 48 additions & 2 deletions

File tree

src/Analyser/NodeScopeResolver.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,15 @@ private function processStmtNode(
911911
$finalScope = $scope;
912912
} elseif ($isIterableAtLeastOnce->maybe()) {
913913
if ($this->polluteScopeWithAlwaysIterableForeach) {
914-
$finalScope = $finalScope->mergeWith($scope->filterByFalseyValue($arrayComparisonExpr));
914+
$finalScope = $finalScope->mergeWith($scope->filterByTruthyValue(new BooleanOr(
915+
new BinaryOp\Identical(
916+
$stmt->expr,
917+
new Array_([]),
918+
),
919+
new FuncCall(new Name\FullyQualified('is_object'), [
920+
new Arg($stmt->expr),
921+
]),
922+
)));
915923
} else {
916924
$finalScope = $finalScope->mergeWith($scope);
917925
}

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ public function dataFileAsserts(): iterable
272272

273273
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-801.php');
274274
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-1209.php');
275+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9784.php');
275276
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-2980.php');
276277
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-3986.php');
277278

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Bug9784;
4+
5+
use Iterator;
6+
use stdClass;
7+
use function PHPStan\Testing\assertType;
8+
9+
final class DemoFile
10+
{
11+
private function complementArticleData(Iterator $articles): void
12+
{
13+
$result = [
14+
'artikel' => $articles,
15+
'farben' => null,
16+
'artikel_ids' => [],
17+
];
18+
19+
// collect article ids
20+
foreach ($result['artikel'] as $article) {
21+
$result['artikel_ids'][] = 1;
22+
}
23+
24+
assertType('array{artikel: Iterator, farben: null, artikel_ids: list<1>}', $result);
25+
assertType('list<1>', $result['artikel_ids']);
26+
27+
if ($result['artikel_ids'] !== []) {
28+
$result['farben'] = new stdClass();
29+
}
30+
31+
// $result['farben'] might be also null
32+
assertType('stdClass|null', $result['farben']);
33+
if ($result['farben'] instanceof stdClass) {
34+
echo '123';
35+
}
36+
}
37+
}

tests/PHPStan/Analyser/data/dependent-variables-type-guard-same-as-type.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function doFoo(): void
3030
assertType('int<1, max>', $itemsCounter);
3131
}
3232

33-
assertType('Generator&iterable', $associationData);
33+
assertType('Generator', $associationData);
3434

3535
assertType('int<0, max>', $itemsCounter);
3636
}

0 commit comments

Comments
 (0)