Skip to content

Commit fed13e0

Browse files
authored
Fix phpstan/phpstan#6670: Return in finally does not always overwrite return in try-catch
1 parent 4919ff4 commit fed13e0

File tree

6 files changed

+141
-1
lines changed

6 files changed

+141
-1
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2027,7 +2027,7 @@ public function processStmtNode(
20272027
$impurePoints = array_merge($impurePoints, $finallyResult->getImpurePoints());
20282028
$finallyScope = $finallyResult->getScope();
20292029
$finalScope = $finallyResult->isAlwaysTerminating() ? $finalScope : $finalScope->processFinallyScope($finallyScope, $originalFinallyScope);
2030-
if (count($finallyResult->getExitPoints()) > 0) {
2030+
if (count($finallyResult->getExitPoints()) > 0 && $finallyResult->isAlwaysTerminating()) {
20312031
$this->callNodeCallback($nodeCallback, new FinallyExitPointsNode(
20322032
$finallyResult->toPublic()->getExitPoints(),
20332033
$finallyExitPoints,

tests/PHPStan/Rules/Exceptions/OverwrittenExitPointByFinallyRuleTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,26 @@ public function testRule(): void
3838
]);
3939
}
4040

41+
public function testBug6670(): void
42+
{
43+
$this->analyse([__DIR__ . '/data/bug-6670.php'], []);
44+
}
45+
46+
public function testBug12215(): void
47+
{
48+
$this->analyse([__DIR__ . '/data/bug-12215.php'], []);
49+
}
50+
51+
public function testBug11906(): void
52+
{
53+
$this->analyse([__DIR__ . '/data/bug-11906.php'], []);
54+
}
55+
56+
public function testBug7665(): void
57+
{
58+
$this->analyse([__DIR__ . '/data/bug-7665.php'], []);
59+
}
60+
4161
public function testBug5627(): void
4262
{
4363
$this->analyse([__DIR__ . '/data/bug-5627.php'], [
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug11906;
4+
5+
function func(): void {
6+
try {
7+
throw new \LogicException('test');
8+
} catch (\LogicException $e) {
9+
// This catch-block should cause line 7 to not be treated as an exit point
10+
} finally {
11+
if (getenv('FOO')) {
12+
return;
13+
}
14+
}
15+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug12215;
4+
5+
abstract class Spinlock
6+
{
7+
private float $expireTimeout;
8+
9+
private ?float $acquireTs = null;
10+
11+
private ?string $token = null;
12+
13+
public function __construct(float $expireTimeout = \PHP_INT_MAX)
14+
{
15+
$this->expireTimeout = $expireTimeout;
16+
17+
// acquire lock
18+
$this->acquireTs = microtime(true);
19+
$this->token = random_bytes(64);
20+
}
21+
22+
protected function release(string $key): bool
23+
{
24+
try {
25+
return $this->releaseWithToken($key, $this->token);
26+
} finally {
27+
$this->token = null;
28+
29+
$elapsedTime = microtime(true) - $this->acquireTs;
30+
if ($elapsedTime >= $this->expireTimeout) {
31+
throw new \Exception('Execution outside lock exception');
32+
}
33+
}
34+
}
35+
36+
protected function release2(string $key): bool
37+
{
38+
try {
39+
return $this->releaseWithToken($key, $this->token);
40+
} finally {
41+
try {
42+
$elapsedTime = microtime(true) - $this->acquireTs;
43+
if ($elapsedTime >= $this->expireTimeout) {
44+
throw new \Exception('Execution outside lock exception');
45+
}
46+
} finally {
47+
$this->token = null;
48+
}
49+
}
50+
}
51+
52+
abstract protected function releaseWithToken(string $key, string $token): bool;
53+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug6670;
4+
5+
class HelloWorld
6+
{
7+
public function sayHello(): string
8+
{
9+
try {
10+
return 'string';
11+
} finally {
12+
try {
13+
$this->clearCache();
14+
} catch(\Exception $e) {
15+
return 'same string';
16+
}
17+
}
18+
}
19+
20+
private function clearCache(): void
21+
{
22+
// do...
23+
}
24+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug7665;
4+
5+
class HelloWorld
6+
{
7+
public function doStuff(): string
8+
{
9+
try {
10+
if(rand(0,1) === 1) {
11+
throw new \RuntimeException('Bad luck');
12+
}
13+
14+
15+
return 'yay';
16+
// other stuff
17+
} catch(\Throwable $e) {
18+
if (rand(0,1) === 1) {
19+
exit(1);
20+
}
21+
// do some stuff to reset
22+
} finally {
23+
if(rand(0,1) === 1) {
24+
exit(1);
25+
}
26+
}
27+
}
28+
}

0 commit comments

Comments
 (0)