Skip to content

Commit ae2b317

Browse files
committed
Fix condition of fall-through case not used for exhaustive checks
1 parent 21923d3 commit ae2b317

File tree

3 files changed

+39
-3
lines changed

3 files changed

+39
-3
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1449,9 +1449,11 @@ private function processStmtNode(
14491449
$exitPointsForOuterLoop = [];
14501450
$throwPoints = $condResult->getThrowPoints();
14511451
$impurePoints = $condResult->getImpurePoints();
1452+
$fullCondExpr = null;
14521453
foreach ($stmt->cases as $caseNode) {
14531454
if ($caseNode->cond !== null) {
14541455
$condExpr = new BinaryOp\Equal($stmt->cond, $caseNode->cond);
1456+
$fullCondExpr = $fullCondExpr === null ? $condExpr : new BooleanOr($fullCondExpr, $condExpr);
14551457
$caseResult = $this->processExprNode($stmt, $caseNode->cond, $scopeForBranches, $nodeCallback, ExpressionContext::createDeep());
14561458
$scopeForBranches = $caseResult->getScope();
14571459
$hasYield = $hasYield || $caseResult->hasYield();
@@ -1460,6 +1462,7 @@ private function processStmtNode(
14601462
$branchScope = $caseResult->getTruthyScope()->filterByTruthyValue($condExpr);
14611463
} else {
14621464
$hasDefaultCase = true;
1465+
$fullCondExpr = null;
14631466
$branchScope = $scopeForBranches;
14641467
}
14651468

@@ -1481,8 +1484,9 @@ private function processStmtNode(
14811484
if ($branchScopeResult->isAlwaysTerminating()) {
14821485
$alwaysTerminating = $alwaysTerminating && $branchFinalScopeResult->isAlwaysTerminating();
14831486
$prevScope = null;
1484-
if (isset($condExpr)) {
1485-
$scopeForBranches = $scopeForBranches->filterByFalseyValue($condExpr);
1487+
if (isset($fullCondExpr)) {
1488+
$scopeForBranches = $scopeForBranches->filterByFalseyValue($fullCondExpr);
1489+
$fullCondExpr = null;
14861490
}
14871491
if (!$branchFinalScopeResult->isAlwaysTerminating()) {
14881492
$finalScope = $branchScope->mergeWith($finalScope);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug11064;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
interface IClass {}
8+
class ClassA implements IClass {}
9+
class ClassB implements IClass {}
10+
11+
/**
12+
* @param null|'nil'|IClass $val
13+
*/
14+
function test($val): string {
15+
switch (true) {
16+
case $val === null:
17+
case $val === 'nil':
18+
assertType("'nil'|null", $val);
19+
return 'null';
20+
21+
case $val instanceof ClassA:
22+
assertType('Bug11064\\ClassA', $val);
23+
return 'class a';
24+
25+
default:
26+
assertType('Bug11064\\IClass~Bug11064\\ClassA', $val);
27+
throw new RuntimeException('unsupported class: ' . get_class($val));
28+
}
29+
}
30+

tests/PHPStan/Rules/Variables/IssetRuleTest.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,10 +277,12 @@ public function testVariableCertaintyInIsset(): void
277277
112,
278278
],
279279
[
280-
'Variable $variableInFirstCase in isset() always exists and is not nullable.',
280+
// could be Variable $variableInFirstCase in isset() always exists and is not nullable.
281+
'Variable $variableInFirstCase in isset() is never defined.',
281282
116,
282283
],
283284
[
285+
// could be Variable $variableInSecondCase in isset() always exists and is not nullable.
284286
'Variable $variableInSecondCase in isset() is never defined.',
285287
117,
286288
],

0 commit comments

Comments
 (0)