Skip to content

Commit fb483a7

Browse files
committed
Fix maybe-undefined variable regression in loops with unrelated assignments
- Fixed intersectConditionalExpressions to keep matching conditional expression keys instead of dropping all keys when any single key mismatches - The previous all-or-nothing behavior caused conditional expressions (e.g. for variable certainty based on method return values) to be entirely dropped when an unrelated conditional expression key was added during loop scope merging - New regression test in tests/PHPStan/Analyser/nsrt/bug-14269.php Closes phpstan/phpstan#14269
1 parent 074a379 commit fb483a7

2 files changed

Lines changed: 69 additions & 3 deletions

File tree

src/Analyser/MutatingScope.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3431,13 +3431,19 @@ private function intersectConditionalExpressions(array $otherConditionalExpressi
34313431
}
34323432

34333433
$otherHolders = $otherConditionalExpressions[$exprString];
3434-
foreach (array_keys($holders) as $key) {
3434+
$intersectedHolders = [];
3435+
foreach ($holders as $key => $holder) {
34353436
if (!array_key_exists($key, $otherHolders)) {
3436-
continue 2;
3437+
continue;
34373438
}
3439+
$intersectedHolders[$key] = $holder;
3440+
}
3441+
3442+
if (count($intersectedHolders) === 0) {
3443+
continue;
34383444
}
34393445

3440-
$newConditionalExpressions[$exprString] = $holders;
3446+
$newConditionalExpressions[$exprString] = $intersectedHolders;
34413447
}
34423448

34433449
return $newConditionalExpressions;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug14269;
4+
5+
use PHPStan\TrinaryLogic;
6+
use function PHPStan\Testing\assertVariableCertainty;
7+
8+
class Package
9+
{
10+
11+
public function isMultiDay(): bool
12+
{
13+
return true;
14+
}
15+
16+
/**
17+
* @return string[]
18+
*/
19+
public function getWorkspaces(): array
20+
{
21+
return [];
22+
}
23+
24+
/**
25+
* @return int[]|null
26+
*/
27+
public function getService(): ?array
28+
{
29+
return [];
30+
}
31+
32+
}
33+
34+
class ReturnsSelf
35+
{
36+
37+
public function subtract(): self
38+
{
39+
return $this;
40+
}
41+
42+
}
43+
44+
function doFoo(Package $package, ReturnsSelf $s): void {
45+
if (!$package->isMultiDay()) {
46+
$packageDurationInMinutes = 60;
47+
}
48+
49+
foreach ($package->getWorkspaces() as $workplace) {
50+
$availableIntervals = $s->subtract();
51+
if ($package->getService() !== null && !$package->isMultiDay()) {
52+
assertVariableCertainty(TrinaryLogic::createYes(), $packageDurationInMinutes);
53+
continue;
54+
}
55+
56+
$availableIntervals = $package->isMultiDay()
57+
? 'aaa'
58+
: 'bbb';
59+
}
60+
}

0 commit comments

Comments
 (0)