Skip to content

Commit 6b2473a

Browse files
authored
Fix missing detection of dead code in closures (#4148)
1 parent 9bde094 commit 6b2473a

File tree

3 files changed

+28
-2
lines changed

3 files changed

+28
-2
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4639,6 +4639,9 @@ private function processClosureNode(
46394639
throw new ShouldNotHappenException();
46404640
}
46414641

4642+
$returnType = $closureType->getReturnType();
4643+
$isAlwaysTerminating = ($returnType instanceof NeverType && $returnType->isExplicit());
4644+
46424645
$nodeCallback(new InClosureNode($closureType, $expr), $closureScope);
46434646

46444647
$executionEnds = [];
@@ -4690,7 +4693,7 @@ private function processClosureNode(
46904693
array_merge($statementResult->getImpurePoints(), $closureImpurePoints),
46914694
), $closureScope);
46924695

4693-
return new ProcessClosureResult($scope, $statementResult->getThrowPoints(), $statementResult->getImpurePoints(), $invalidateExpressions);
4696+
return new ProcessClosureResult($scope, $statementResult->getThrowPoints(), $statementResult->getImpurePoints(), $invalidateExpressions, $isAlwaysTerminating);
46944697
}
46954698

46964699
$count = 0;
@@ -4736,7 +4739,7 @@ private function processClosureNode(
47364739
array_merge($statementResult->getImpurePoints(), $closureImpurePoints),
47374740
), $closureScope);
47384741

4739-
return new ProcessClosureResult($scope->processClosureScope($closureResultScope, null, $byRefUses), $statementResult->getThrowPoints(), $statementResult->getImpurePoints(), $invalidateExpressions);
4742+
return new ProcessClosureResult($scope->processClosureScope($closureResultScope, null, $byRefUses), $statementResult->getThrowPoints(), $statementResult->getImpurePoints(), $invalidateExpressions, $isAlwaysTerminating);
47404743
}
47414744

47424745
/**
@@ -5180,6 +5183,7 @@ private function processArgs(
51805183
if ($callCallbackImmediately) {
51815184
$throwPoints = array_merge($throwPoints, array_map(static fn (ThrowPoint $throwPoint) => $throwPoint->isExplicit() ? ThrowPoint::createExplicit($scope, $throwPoint->getType(), $arg->value, $throwPoint->canContainAnyThrowable()) : ThrowPoint::createImplicit($scope, $arg->value), $closureResult->getThrowPoints()));
51825185
$impurePoints = array_merge($impurePoints, $closureResult->getImpurePoints());
5186+
$isAlwaysTerminating = $isAlwaysTerminating || $closureResult->isAlwaysTerminating();
51835187
}
51845188

51855189
$uses = [];

src/Analyser/ProcessClosureResult.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public function __construct(
1717
private array $throwPoints,
1818
private array $impurePoints,
1919
private array $invalidateExpressions,
20+
private bool $isAlwaysTerminating,
2021
)
2122
{
2223
}
@@ -50,4 +51,9 @@ public function getInvalidateExpressions(): array
5051
return $this->invalidateExpressions;
5152
}
5253

54+
public function isAlwaysTerminating(): bool
55+
{
56+
return $this->isAlwaysTerminating;
57+
}
58+
5359
}

tests/PHPStan/Analyser/ExpressionResultTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,22 @@ public static function dataIsAlwaysTerminating(): array
113113
'call_user_func(fn() => exit());',
114114
true,
115115
],
116+
[
117+
'(function() { exit(); })();',
118+
true,
119+
],
120+
[
121+
'function () {};',
122+
false,
123+
],
124+
[
125+
'call_user_func(function() { exit(); });',
126+
true,
127+
],
128+
[
129+
'usort($arr, static function($a, $b):int { return $a <=> $b; });',
130+
false,
131+
],
116132
[
117133
'var_dump(1+exit());',
118134
true,

0 commit comments

Comments
 (0)