Skip to content

Commit

Permalink
Check - unreachable conditions (useful for !checkAlwaysTrue* projects)
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Nov 3, 2018
1 parent 2a96f14 commit 186e4e0
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 0 deletions.
1 change: 1 addition & 0 deletions conf/config.level4.neon
Expand Up @@ -8,6 +8,7 @@ rules:
- PHPStan\Rules\Comparison\ElseIfConstantConditionRule
- PHPStan\Rules\Comparison\IfConstantConditionRule
- PHPStan\Rules\Comparison\TernaryOperatorConstantConditionRule
- PHPStan\Rules\Comparison\UnreachableIfBranchesRule

services:
-
Expand Down
47 changes: 47 additions & 0 deletions src/Rules/Comparison/UnreachableIfBranchesRule.php
@@ -0,0 +1,47 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Comparison;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\Constant\ConstantBooleanType;

class UnreachableIfBranchesRule implements \PHPStan\Rules\Rule
{

public function getNodeType(): string
{
return Node\Stmt\If_::class;
}

/**
* @param \PhpParser\Node\Stmt\If_ $node
* @param Scope $scope
* @return RuleError[]
*/
public function processNode(Node $node, Scope $scope): array
{
$errors = [];
$conditionType = $scope->getType($node->cond)->toBoolean();
$nextBranchIsDead = $conditionType instanceof ConstantBooleanType && $conditionType->getValue();

foreach ($node->elseifs as $elseif) {
if ($nextBranchIsDead) {
$errors[] = RuleErrorBuilder::message('Elseif branch is unreachable because previous condition is always true.')->line($elseif->getLine())->build();
continue;
}

$elseIfConditionType = $scope->getType($elseif->cond)->toBoolean();
$nextBranchIsDead = $elseIfConditionType instanceof ConstantBooleanType && $elseIfConditionType->getValue();
}

if ($node->else !== null && $nextBranchIsDead) {
$errors[] = RuleErrorBuilder::message('Else branch is unreachable because previous condition is always true.')->line($node->else->getLine())->build();
}

return $errors;
}

}
42 changes: 42 additions & 0 deletions tests/PHPStan/Rules/Comparison/UnreachableIfBranchesRuleTest.php
@@ -0,0 +1,42 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Comparison;

use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;

class UnreachableIfBranchesRuleTest extends RuleTestCase
{

protected function getRule(): Rule
{
return new UnreachableIfBranchesRule();
}

public function testRule(): void
{
$this->analyse([__DIR__ . '/data/unreachable-if-branches.php'], [
[
'Else branch is unreachable because previous condition is always true.',
15,
],
[
'Elseif branch is unreachable because previous condition is always true.',
25,
],
[
'Else branch is unreachable because previous condition is always true.',
27,
],
[
'Elseif branch is unreachable because previous condition is always true.',
39,
],
[
'Else branch is unreachable because previous condition is always true.',
41,
],
]);
}

}
45 changes: 45 additions & 0 deletions tests/PHPStan/Rules/Comparison/data/unreachable-if-branches.php
@@ -0,0 +1,45 @@
<?php

function () {

if (true) {

}

};

function () {

if (true) {

} else { // unreachable

}

};

function (string $str) {

if (true) {

} elseif ($str) { // unreachable

} else { // unreachable

}

};

function (string $foo, string $bar) {

if ($str) {

} elseif (true) {

} elseif ($bar) { // unreachable

} else { // unreachable

}

};

0 comments on commit 186e4e0

Please sign in to comment.