Skip to content

Commit d1f14df

Browse files
committed
fix assignments
1 parent b8fbca7 commit d1f14df

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()
@@ -5380,12 +5386,14 @@ private function processAssignVar(
53805386
$hasYield = false;
53815387
$throwPoints = [];
53825388
$impurePoints = [];
5389+
$isAlwaysTerminating = false;
53835390
$isAssignOp = $assignedExpr instanceof Expr\AssignOp && !$enterExpressionAssign;
53845391
if ($var instanceof Variable && is_string($var->name)) {
53855392
$result = $processExprCallback($scope);
53865393
$hasYield = $result->hasYield();
53875394
$throwPoints = $result->getThrowPoints();
53885395
$impurePoints = $result->getImpurePoints();
5396+
$isAlwaysTerminating = $result->isAlwaysTerminating();
53895397
if (in_array($var->name, Scope::SUPERGLOBAL_VARIABLES, true)) {
53905398
$impurePoints[] = new ImpurePoint($scope, $var, 'superglobal', 'assign to superglobal variable', true);
53915399
}
@@ -5470,6 +5478,7 @@ private function processAssignVar(
54705478
$hasYield = $result->hasYield();
54715479
$throwPoints = $result->getThrowPoints();
54725480
$impurePoints = $result->getImpurePoints();
5481+
$isAlwaysTerminating = $result->isAlwaysTerminating();
54735482
$scope = $result->getScope();
54745483
if ($enterExpressionAssign) {
54755484
$scope = $scope->exitExpressionAssign($var);
@@ -5521,6 +5530,7 @@ private function processAssignVar(
55215530
$hasYield = $hasYield || $result->hasYield();
55225531
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
55235532
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
5533+
$isAlwaysTerminating = $isAlwaysTerminating || $result->isAlwaysTerminating();
55245534
$scope = $result->getScope();
55255535

55265536
$varType = $scope->getType($var);
@@ -5626,6 +5636,7 @@ static function (): void {
56265636
$hasYield = $objectResult->hasYield();
56275637
$throwPoints = $objectResult->getThrowPoints();
56285638
$impurePoints = $objectResult->getImpurePoints();
5639+
$isAlwaysTerminating = $objectResult->isAlwaysTerminating();
56295640
$scope = $objectResult->getScope();
56305641

56315642
$propertyName = null;
@@ -5636,6 +5647,7 @@ static function (): void {
56365647
$hasYield = $hasYield || $propertyNameResult->hasYield();
56375648
$throwPoints = array_merge($throwPoints, $propertyNameResult->getThrowPoints());
56385649
$impurePoints = array_merge($impurePoints, $propertyNameResult->getImpurePoints());
5650+
$isAlwaysTerminating = $isAlwaysTerminating || $propertyNameResult->isAlwaysTerminating();
56395651
$scope = $propertyNameResult->getScope();
56405652
}
56415653

@@ -5644,6 +5656,7 @@ static function (): void {
56445656
$hasYield = $hasYield || $result->hasYield();
56455657
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
56465658
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
5659+
$isAlwaysTerminating = $isAlwaysTerminating || $result->isAlwaysTerminating();
56475660
$scope = $result->getScope();
56485661

56495662
if ($var->name instanceof Expr && $this->phpVersion->supportsPropertyHooks()) {
@@ -5731,6 +5744,7 @@ static function (): void {
57315744
$hasYield = $propertyNameResult->hasYield();
57325745
$throwPoints = $propertyNameResult->getThrowPoints();
57335746
$impurePoints = $propertyNameResult->getImpurePoints();
5747+
$isAlwaysTerminating = $propertyNameResult->isAlwaysTerminating();
57345748
$scope = $propertyNameResult->getScope();
57355749
}
57365750

@@ -5739,6 +5753,7 @@ static function (): void {
57395753
$hasYield = $hasYield || $result->hasYield();
57405754
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
57415755
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
5756+
$isAlwaysTerminating = $isAlwaysTerminating || $result->isAlwaysTerminating();
57425757
$scope = $result->getScope();
57435758

57445759
if ($propertyName !== null) {
@@ -5783,6 +5798,7 @@ static function (): void {
57835798
$hasYield = $result->hasYield();
57845799
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
57855800
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
5801+
$isAlwaysTerminating = $isAlwaysTerminating || $result->isAlwaysTerminating();
57865802
$scope = $result->getScope();
57875803
foreach ($var->items as $i => $arrayItem) {
57885804
if ($arrayItem === null) {
@@ -5800,13 +5816,15 @@ static function (): void {
58005816
$hasYield = $hasYield || $keyResult->hasYield();
58015817
$throwPoints = array_merge($throwPoints, $keyResult->getThrowPoints());
58025818
$impurePoints = array_merge($impurePoints, $keyResult->getImpurePoints());
5819+
$isAlwaysTerminating = $isAlwaysTerminating || $keyResult->isAlwaysTerminating();
58035820
$itemScope = $keyResult->getScope();
58045821
}
58055822

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

58115829
if ($arrayItem->key === null) {
58125830
$dimExpr = new Node\Scalar\Int_($i);
@@ -5827,6 +5845,7 @@ static function (): void {
58275845
$hasYield = $hasYield || $result->hasYield();
58285846
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
58295847
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
5848+
$isAlwaysTerminating = $isAlwaysTerminating || $result->isAlwaysTerminating();
58305849
}
58315850
} elseif ($var instanceof ExistingArrayDimFetch) {
58325851
$dimFetchStack = [];
@@ -5905,7 +5924,7 @@ static function (): void {
59055924
}
59065925
}
59075926

5908-
return new ExpressionResult($scope, $hasYield, $throwPoints, $impurePoints);
5927+
return new ExpressionResult($scope, $hasYield, $throwPoints, $impurePoints, isAlwaysTerminating: $isAlwaysTerminating);
59095928
}
59105929

59115930
/**

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)