Skip to content

Commit 649eb70

Browse files
committed
Support treatPhpDocTypesAsCertain in NumberComparisonOperatorsConstantConditionRule
1 parent 01f7309 commit 649eb70

File tree

5 files changed

+104
-5
lines changed

5 files changed

+104
-5
lines changed

Diff for: conf/config.level4.neon

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ includes:
33

44
rules:
55
- PHPStan\Rules\Arrays\DeadForeachRule
6-
- PHPStan\Rules\Comparison\NumberComparisonOperatorsConstantConditionRule
76
- PHPStan\Rules\DeadCode\NoopRule
87
- PHPStan\Rules\DeadCode\UnreachableStatementRule
98
- PHPStan\Rules\DeadCode\UnusedPrivateConstantRule
@@ -127,6 +126,13 @@ services:
127126
tags:
128127
- phpstan.rules.rule
129128

129+
-
130+
class: PHPStan\Rules\Comparison\NumberComparisonOperatorsConstantConditionRule
131+
arguments:
132+
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%
133+
tags:
134+
- phpstan.rules.rule
135+
130136
-
131137
class: PHPStan\Rules\Comparison\StrictComparisonOfDifferentTypesRule
132138
arguments:

Diff for: src/Rules/Comparison/NumberComparisonOperatorsConstantConditionRule.php

+22-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
class NumberComparisonOperatorsConstantConditionRule implements Rule
1818
{
1919

20+
public function __construct(
21+
private bool $treatPhpDocTypesAsCertain,
22+
)
23+
{
24+
}
25+
2026
public function getNodeType(): string
2127
{
2228
return BinaryOp::class;
@@ -36,16 +42,29 @@ public function processNode(
3642
return [];
3743
}
3844

39-
$exprType = $scope->getType($node);
45+
$exprType = $this->treatPhpDocTypesAsCertain ? $scope->getType($node) : $scope->getNativeType($node);
4046
if ($exprType instanceof ConstantBooleanType) {
47+
$addTip = function (RuleErrorBuilder $ruleErrorBuilder) use ($scope, $node): RuleErrorBuilder {
48+
if (!$this->treatPhpDocTypesAsCertain) {
49+
return $ruleErrorBuilder;
50+
}
51+
52+
$booleanNativeType = $scope->getNativeType($node);
53+
if ($booleanNativeType instanceof ConstantBooleanType) {
54+
return $ruleErrorBuilder;
55+
}
56+
57+
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%</>.');
58+
};
59+
4160
return [
42-
RuleErrorBuilder::message(sprintf(
61+
$addTip(RuleErrorBuilder::message(sprintf(
4362
'Comparison operation "%s" between %s and %s is always %s.',
4463
$node->getOperatorSigil(),
4564
$scope->getType($node->left)->describe(VerbosityLevel::value()),
4665
$scope->getType($node->right)->describe(VerbosityLevel::value()),
4766
$exprType->getValue() ? 'true' : 'false',
48-
))->build(),
67+
)))->build(),
4968
];
5069
}
5170

Diff for: tests/PHPStan/Analyser/data/native-types.php

+17
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,20 @@ public function doFoo(): void
377377
}
378378

379379
}
380+
381+
class PositiveInt
382+
{
383+
384+
/**
385+
* @param positive-int $i
386+
* @return void
387+
*/
388+
public function doFoo(int $i): void
389+
{
390+
assertType('true', $i > 0);
391+
assertType('false', $i <= 0);
392+
assertNativeType('bool', $i > 0);
393+
assertNativeType('bool', $i <= 0);
394+
}
395+
396+
}

Diff for: tests/PHPStan/Rules/Comparison/NumberComparisonOperatorsConstantConditionRuleTest.php

+36-1
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
class NumberComparisonOperatorsConstantConditionRuleTest extends RuleTestCase
1313
{
1414

15+
private bool $treatPhpDocTypesAsCertain = true;
16+
1517
protected function getRule(): Rule
1618
{
17-
return new NumberComparisonOperatorsConstantConditionRule();
19+
return new NumberComparisonOperatorsConstantConditionRule($this->treatPhpDocTypesAsCertain);
1820
}
1921

2022
public function testBug8277(): void
@@ -160,4 +162,37 @@ public function testBug8643(): void
160162
$this->analyse([__DIR__ . '/data/bug-8643.php'], []);
161163
}
162164

165+
public function dataTreatPhpDocTypesAsCertain(): iterable
166+
{
167+
yield [
168+
false,
169+
[],
170+
];
171+
yield [
172+
true,
173+
[
174+
[
175+
'Comparison operation ">=" between int<1, max> and 0 is always true.',
176+
11,
177+
'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%</>.',
178+
],
179+
[
180+
'Comparison operation "<" between int<1, max> and 0 is always false.',
181+
18,
182+
'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%</>.',
183+
],
184+
],
185+
];
186+
}
187+
188+
/**
189+
* @dataProvider dataTreatPhpDocTypesAsCertain
190+
* @param list<array{0: string, 1: int, 2?: string}> $expectedErrors
191+
*/
192+
public function testTreatPhpDocTypesAsCertain(bool $treatPhpDocTypesAsCertain, array $expectedErrors): void
193+
{
194+
$this->treatPhpDocTypesAsCertain = $treatPhpDocTypesAsCertain;
195+
$this->analyse([__DIR__ . '/data/number-comparison-treat.php'], $expectedErrors);
196+
}
197+
163198
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace NumberComparisonTreatPhpDocTypesAsCertain;
4+
5+
class Foo
6+
{
7+
8+
/** @param positive-int $i */
9+
public function sayHello(int $i): void
10+
{
11+
if ($i >= 0) {
12+
}
13+
}
14+
15+
/** @param positive-int $i */
16+
public function sayHello2(int $i): void
17+
{
18+
if ($i < 0) {
19+
}
20+
}
21+
22+
}

0 commit comments

Comments
 (0)