Skip to content

Commit 3b55545

Browse files
committed
fix assignments
1 parent 3f52cfb commit 3b55545

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -927,12 +927,14 @@ private function processStmtNode(
927927
$hasYield = $result->hasYield();
928928
$throwPoints = $result->getThrowPoints();
929929
$impurePoints = $result->getImpurePoints();
930+
$isAlwaysTerminating = $result->isAlwaysTerminating();
931+
930932
if ($earlyTerminationExpr !== null) {
931933
return new StatementResult($scope, $hasYield, true, [
932934
new StatementExitPoint($stmt, $scope),
933935
], $overridingThrowPoints ?? $throwPoints, $impurePoints);
934936
}
935-
return new StatementResult($scope, $hasYield, false, [], $overridingThrowPoints ?? $throwPoints, $impurePoints);
937+
return new StatementResult($scope, $hasYield, $isAlwaysTerminating, [], $overridingThrowPoints ?? $throwPoints, $impurePoints);
936938
} elseif ($stmt instanceof Node\Stmt\Namespace_) {
937939
if ($stmt->name !== null) {
938940
$scope = $scope->enterNamespace($stmt->name->toString());
@@ -2464,20 +2466,22 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context): Exp
24642466
$hasYield = $result->hasYield();
24652467
$throwPoints = $result->getThrowPoints();
24662468
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
2469+
$isAlwaysTerminating = $result->isAlwaysTerminating();
24672470
$scope = $result->getScope();
24682471

24692472
if ($expr instanceof AssignRef) {
24702473
$scope = $scope->exitExpressionAssign($expr->expr);
24712474
}
24722475

2473-
return new ExpressionResult($scope, $hasYield, $throwPoints, $impurePoints);
2476+
return new ExpressionResult($scope, $hasYield, $throwPoints, $impurePoints, isAlwaysTerminating: $isAlwaysTerminating);
24742477
},
24752478
true,
24762479
);
24772480
$scope = $result->getScope();
24782481
$hasYield = $result->hasYield();
24792482
$throwPoints = $result->getThrowPoints();
24802483
$impurePoints = $result->getImpurePoints();
2484+
$isAlwaysTerminating = $result->isAlwaysTerminating();
24812485
$vars = $this->getAssignedVariables($expr->var);
24822486
if (count($vars) > 0) {
24832487
$varChangedScope = false;
@@ -2509,6 +2513,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context): Exp
25092513
$result->hasYield(),
25102514
$result->getThrowPoints(),
25112515
$result->getImpurePoints(),
2516+
isAlwaysTerminating: $result->isAlwaysTerminating(),
25122517
);
25132518
}
25142519

@@ -2520,6 +2525,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context): Exp
25202525
$hasYield = $result->hasYield();
25212526
$throwPoints = $result->getThrowPoints();
25222527
$impurePoints = $result->getImpurePoints();
2528+
$isAlwaysTerminating = $result->isAlwaysTerminating();
25232529
if (
25242530
($expr instanceof Expr\AssignOp\Div || $expr instanceof Expr\AssignOp\Mod) &&
25252531
!$scope->getType($expr->expr)->toNumber()->isSuperTypeOf(new ConstantIntegerType(0))->no()
@@ -5379,12 +5385,14 @@ private function processAssignVar(
53795385
$hasYield = false;
53805386
$throwPoints = [];
53815387
$impurePoints = [];
5388+
$isAlwaysTerminating = false;
53825389
$isAssignOp = $assignedExpr instanceof Expr\AssignOp && !$enterExpressionAssign;
53835390
if ($var instanceof Variable && is_string($var->name)) {
53845391
$result = $processExprCallback($scope);
53855392
$hasYield = $result->hasYield();
53865393
$throwPoints = $result->getThrowPoints();
53875394
$impurePoints = $result->getImpurePoints();
5395+
$isAlwaysTerminating = $result->isAlwaysTerminating();
53885396
if (in_array($var->name, Scope::SUPERGLOBAL_VARIABLES, true)) {
53895397
$impurePoints[] = new ImpurePoint($scope, $var, 'superglobal', 'assign to superglobal variable', true);
53905398
}
@@ -5469,6 +5477,7 @@ private function processAssignVar(
54695477
$hasYield = $result->hasYield();
54705478
$throwPoints = $result->getThrowPoints();
54715479
$impurePoints = $result->getImpurePoints();
5480+
$isAlwaysTerminating = $result->isAlwaysTerminating();
54725481
$scope = $result->getScope();
54735482
if ($enterExpressionAssign) {
54745483
$scope = $scope->exitExpressionAssign($var);
@@ -5520,6 +5529,7 @@ private function processAssignVar(
55205529
$hasYield = $hasYield || $result->hasYield();
55215530
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
55225531
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
5532+
$isAlwaysTerminating = $isAlwaysTerminating || $result->isAlwaysTerminating();
55235533
$scope = $result->getScope();
55245534

55255535
$varType = $scope->getType($var);
@@ -5625,6 +5635,7 @@ static function (): void {
56255635
$hasYield = $objectResult->hasYield();
56265636
$throwPoints = $objectResult->getThrowPoints();
56275637
$impurePoints = $objectResult->getImpurePoints();
5638+
$isAlwaysTerminating = $objectResult->isAlwaysTerminating();
56285639
$scope = $objectResult->getScope();
56295640

56305641
$propertyName = null;
@@ -5635,6 +5646,7 @@ static function (): void {
56355646
$hasYield = $hasYield || $propertyNameResult->hasYield();
56365647
$throwPoints = array_merge($throwPoints, $propertyNameResult->getThrowPoints());
56375648
$impurePoints = array_merge($impurePoints, $propertyNameResult->getImpurePoints());
5649+
$isAlwaysTerminating = $isAlwaysTerminating || $propertyNameResult->isAlwaysTerminating();
56385650
$scope = $propertyNameResult->getScope();
56395651
}
56405652

@@ -5643,6 +5655,7 @@ static function (): void {
56435655
$hasYield = $hasYield || $result->hasYield();
56445656
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
56455657
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
5658+
$isAlwaysTerminating = $isAlwaysTerminating || $result->isAlwaysTerminating();
56465659
$scope = $result->getScope();
56475660

56485661
if ($var->name instanceof Expr && $this->phpVersion->supportsPropertyHooks()) {
@@ -5730,6 +5743,7 @@ static function (): void {
57305743
$hasYield = $propertyNameResult->hasYield();
57315744
$throwPoints = $propertyNameResult->getThrowPoints();
57325745
$impurePoints = $propertyNameResult->getImpurePoints();
5746+
$isAlwaysTerminating = $propertyNameResult->isAlwaysTerminating();
57335747
$scope = $propertyNameResult->getScope();
57345748
}
57355749

@@ -5738,6 +5752,7 @@ static function (): void {
57385752
$hasYield = $hasYield || $result->hasYield();
57395753
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
57405754
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
5755+
$isAlwaysTerminating = $isAlwaysTerminating || $result->isAlwaysTerminating();
57415756
$scope = $result->getScope();
57425757

57435758
if ($propertyName !== null) {
@@ -5782,6 +5797,7 @@ static function (): void {
57825797
$hasYield = $result->hasYield();
57835798
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
57845799
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
5800+
$isAlwaysTerminating = $isAlwaysTerminating || $result->isAlwaysTerminating();
57855801
$scope = $result->getScope();
57865802
foreach ($var->items as $i => $arrayItem) {
57875803
if ($arrayItem === null) {
@@ -5799,13 +5815,15 @@ static function (): void {
57995815
$hasYield = $hasYield || $keyResult->hasYield();
58005816
$throwPoints = array_merge($throwPoints, $keyResult->getThrowPoints());
58015817
$impurePoints = array_merge($impurePoints, $keyResult->getImpurePoints());
5818+
$isAlwaysTerminating = $isAlwaysTerminating || $keyResult->isAlwaysTerminating();
58025819
$itemScope = $keyResult->getScope();
58035820
}
58045821

58055822
$valueResult = $this->processExprNode($stmt, $arrayItem->value, $itemScope, $nodeCallback, $context->enterDeep());
58065823
$hasYield = $hasYield || $valueResult->hasYield();
58075824
$throwPoints = array_merge($throwPoints, $valueResult->getThrowPoints());
58085825
$impurePoints = array_merge($impurePoints, $valueResult->getImpurePoints());
5826+
$isAlwaysTerminating = $isAlwaysTerminating || $valueResult->isAlwaysTerminating();
58095827

58105828
if ($arrayItem->key === null) {
58115829
$dimExpr = new Node\Scalar\Int_($i);
@@ -5826,6 +5844,7 @@ static function (): void {
58265844
$hasYield = $hasYield || $result->hasYield();
58275845
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
58285846
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
5847+
$isAlwaysTerminating = $isAlwaysTerminating || $result->isAlwaysTerminating();
58295848
}
58305849
} elseif ($var instanceof ExistingArrayDimFetch) {
58315850
$dimFetchStack = [];
@@ -5904,7 +5923,7 @@ static function (): void {
59045923
}
59055924
}
59065925

5907-
return new ExpressionResult($scope, $hasYield, $throwPoints, $impurePoints);
5926+
return new ExpressionResult($scope, $hasYield, $throwPoints, $impurePoints, isAlwaysTerminating: $isAlwaysTerminating);
59085927
}
59095928

59105929
/**

tests/PHPStan/Rules/DeadCode/UnreachableStatementRuleTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,4 +277,15 @@ public function testBug13232c(): void
277277
]);
278278
}
279279

280+
public function testBug13232d(): void
281+
{
282+
$this->treatPhpDocTypesAsCertain = false;
283+
$this->analyse([__DIR__ . '/data/bug-13232d.php'], [
284+
[
285+
'Unreachable statement - code above always terminates.',
286+
11,
287+
],
288+
]);
289+
}
290+
280291
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Bug13232d;
4+
5+
final class HelloWorld
6+
{
7+
public function sayHi(): void
8+
{
9+
$x = 'Hello, ' . neverReturns()
10+
. ' no way';
11+
$x .= 'this will never happen';
12+
}
13+
}
14+
function neverReturns(): never {}
15+

0 commit comments

Comments
 (0)