Skip to content

Commit 217fac3

Browse files
committed
While loop - condition always false
1 parent f11480b commit 217fac3

File tree

4 files changed

+154
-0
lines changed

4 files changed

+154
-0
lines changed

conf/config.level4.neon

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,13 @@ services:
137137
tags:
138138
- phpstan.rules.rule
139139

140+
-
141+
class: PHPStan\Rules\Comparison\WhileLoopAlwaysFalseConditionRule
142+
arguments:
143+
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
144+
tags:
145+
- phpstan.rules.rule
146+
140147
-
141148
class: PHPStan\Rules\TooWideTypehints\TooWideMethodReturnTypehintRule
142149
arguments:
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Comparison;
4+
5+
use PhpParser\Node\Stmt\While_;
6+
use PHPStan\Rules\RuleErrorBuilder;
7+
use PHPStan\Type\Constant\ConstantBooleanType;
8+
9+
/**
10+
* @implements \PHPStan\Rules\Rule<While_>
11+
*/
12+
class WhileLoopAlwaysFalseConditionRule implements \PHPStan\Rules\Rule
13+
{
14+
15+
private ConstantConditionRuleHelper $helper;
16+
17+
private bool $treatPhpDocTypesAsCertain;
18+
19+
public function __construct(
20+
ConstantConditionRuleHelper $helper,
21+
bool $treatPhpDocTypesAsCertain
22+
)
23+
{
24+
$this->helper = $helper;
25+
$this->treatPhpDocTypesAsCertain = $treatPhpDocTypesAsCertain;
26+
}
27+
28+
public function getNodeType(): string
29+
{
30+
return While_::class;
31+
}
32+
33+
public function processNode(
34+
\PhpParser\Node $node,
35+
\PHPStan\Analyser\Scope $scope
36+
): array
37+
{
38+
$exprType = $this->helper->getBooleanType($scope, $node->cond);
39+
if ($exprType instanceof ConstantBooleanType && !$exprType->getValue()) {
40+
$addTip = function (RuleErrorBuilder $ruleErrorBuilder) use ($scope, $node): RuleErrorBuilder {
41+
if (!$this->treatPhpDocTypesAsCertain) {
42+
return $ruleErrorBuilder;
43+
}
44+
45+
$booleanNativeType = $this->helper->getNativeBooleanType($scope, $node->cond);
46+
if ($booleanNativeType instanceof ConstantBooleanType) {
47+
return $ruleErrorBuilder;
48+
}
49+
50+
return $ruleErrorBuilder->tip('Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.');
51+
};
52+
53+
return [
54+
$addTip(RuleErrorBuilder::message('While loop condition is always false.'))->line($node->cond->getLine())
55+
->build(),
56+
];
57+
}
58+
59+
return [];
60+
}
61+
62+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Comparison;
4+
5+
/**
6+
* @extends \PHPStan\Testing\RuleTestCase<WhileLoopAlwaysFalseConditionRule>
7+
*/
8+
class WhileLoopAlwaysFalseConditionRuleTest extends \PHPStan\Testing\RuleTestCase
9+
{
10+
11+
/** @var bool */
12+
private $treatPhpDocTypesAsCertain = true;
13+
14+
protected function getRule(): \PHPStan\Rules\Rule
15+
{
16+
return new WhileLoopAlwaysFalseConditionRule(
17+
new ConstantConditionRuleHelper(
18+
new ImpossibleCheckTypeHelper(
19+
$this->createReflectionProvider(),
20+
$this->getTypeSpecifier(),
21+
[],
22+
$this->treatPhpDocTypesAsCertain
23+
),
24+
$this->treatPhpDocTypesAsCertain
25+
),
26+
$this->treatPhpDocTypesAsCertain
27+
);
28+
}
29+
30+
protected function shouldTreatPhpDocTypesAsCertain(): bool
31+
{
32+
return $this->treatPhpDocTypesAsCertain;
33+
}
34+
35+
public function testRule(): void
36+
{
37+
$this->analyse([__DIR__ . '/data/while-loop-false.php'], [
38+
[
39+
'While loop condition is always false.',
40+
10,
41+
],
42+
[
43+
'While loop condition is always false.',
44+
20,
45+
'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.',
46+
],
47+
]);
48+
}
49+
50+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace WhileLoopFalse;
4+
5+
class Foo
6+
{
7+
8+
public function doFoo(): void
9+
{
10+
while (false) {
11+
12+
}
13+
}
14+
15+
/**
16+
* @param 0 $s
17+
*/
18+
public function doBar($s): void
19+
{
20+
while ($s) {
21+
22+
}
23+
}
24+
25+
/**
26+
* @param string $s
27+
*/
28+
public function doBar2($s): void
29+
{
30+
while ($s === null) { // reported by StrictComparisonOfDifferentTypesRule
31+
32+
}
33+
}
34+
35+
}

0 commit comments

Comments
 (0)