Skip to content

Commit c94eef1

Browse files
committed
Merge branch 2.1.x into 2.2.x
2 parents 3b840a1 + 9df075b commit c94eef1

File tree

4 files changed

+99
-1
lines changed

4 files changed

+99
-1
lines changed

src/Analyser/ExprHandler/AssignOpHandler.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,11 @@ static function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $contex
6666
$exprResult = $nodeScopeResolver->processExprNode($stmt, $expr->expr, $scope, $storage, $nodeCallback, $context->enterDeep());
6767
if ($expr instanceof Expr\AssignOp\Coalesce) {
6868
$nodeScopeResolver->storeBeforeScope($storage, $expr, $originalScope);
69+
$isAlwaysTerminating = $exprResult->isAlwaysTerminating() && $originalScope->getType($expr->var)->isNull()->yes();
6970
return new ExpressionResult(
7071
$exprResult->getScope()->mergeWith($originalScope),
7172
$exprResult->hasYield(),
72-
$exprResult->isAlwaysTerminating(),
73+
$isAlwaysTerminating,
7374
$exprResult->getThrowPoints(),
7475
$exprResult->getImpurePoints(),
7576
);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug14369;
6+
7+
use Exception;
8+
use function PHPStan\Testing\assertType;
9+
10+
function test(string|null $test): void
11+
{
12+
$test ??= throw new Exception();
13+
14+
assertType('string', $test);
15+
}
16+
17+
function testMaybeNull(): void
18+
{
19+
if (rand(0, 1)) {
20+
$test = null;
21+
} else {
22+
$test = 'hello';
23+
}
24+
$test ??= throw new Exception();
25+
26+
assertType("'hello'", $test);
27+
}
28+
29+
function testAlwaysNull(): void
30+
{
31+
$test = null;
32+
$test ??= throw new Exception();
33+
34+
assertType('*NEVER*', $test);
35+
}

tests/PHPStan/Rules/DeadCode/UnreachableStatementRuleTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,4 +397,19 @@ public function testBug14328(): void
397397
]);
398398
}
399399

400+
public function testBug14369(): void
401+
{
402+
$this->treatPhpDocTypesAsCertain = true;
403+
$this->analyse([__DIR__ . '/data/bug-14369.php'], [
404+
[
405+
'Unreachable statement - code above always terminates.',
406+
33,
407+
],
408+
[
409+
'Unreachable statement - code above always terminates.',
410+
40,
411+
],
412+
]);
413+
}
414+
400415
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug14369DeadCode;
6+
7+
use Exception;
8+
9+
function test(string|null $test): void
10+
{
11+
$test ??= throw new Exception();
12+
13+
echo $test;
14+
}
15+
16+
function testMaybeNull(): void
17+
{
18+
if (rand(0, 1)) {
19+
$test = null;
20+
} else {
21+
$test = 'hello';
22+
}
23+
$test ??= throw new Exception();
24+
25+
echo $test;
26+
}
27+
28+
function testAlwaysNull(): void
29+
{
30+
$test = null;
31+
$test ??= throw new Exception();
32+
33+
echo $test;
34+
}
35+
36+
function testAlwaysTerminatingLhs(): void
37+
{
38+
alwaysThrows()->prop ??= throw new Exception();
39+
40+
echo 'unreachable';
41+
}
42+
43+
/** @return never */
44+
function alwaysThrows(): never
45+
{
46+
throw new Exception();
47+
}

0 commit comments

Comments
 (0)