Skip to content

Commit 8b91356

Browse files
VincentLangletondrejmirtes
authored andcommitted
Improve loose comparison for integer ranges
1 parent 60e4151 commit 8b91356

File tree

5 files changed

+130
-0
lines changed

5 files changed

+130
-0
lines changed

src/Type/IntegerRangeType.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Type;
44

5+
use PHPStan\Php\PhpVersion;
56
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
67
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
78
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
@@ -689,6 +690,15 @@ public function toPhpDocNode(): TypeNode
689690
return new GenericTypeNode(new IdentifierTypeNode('int'), [$min, $max]);
690691
}
691692

693+
public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
694+
{
695+
if ($this->isSmallerThan($type)->yes() || $this->isGreaterThan($type)->yes()) {
696+
return new ConstantBooleanType(false);
697+
}
698+
699+
return parent::looseCompare($type, $phpVersion);
700+
}
701+
692702
/**
693703
* @param mixed[] $properties
694704
*/

src/Type/NullType.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,10 @@ public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
331331
return new ConstantBooleanType($this->getValue() == []); // phpcs:ignore
332332
}
333333

334+
if ($type instanceof CompoundType) {
335+
return $type->looseCompare($this, $phpVersion);
336+
}
337+
334338
return new BooleanType();
335339
}
336340

src/Type/Traits/ConstantScalarTypeTrait.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
6666
return new ConstantBooleanType($this->getValue() == []); // phpcs:ignore
6767
}
6868

69+
if ($type instanceof CompoundType) {
70+
return $type->looseCompare($this, $phpVersion);
71+
}
72+
6973
return parent::looseCompare($type, $phpVersion);
7074
}
7175

tests/PHPStan/Rules/Comparison/ConstantLooseComparisonRuleTest.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,71 @@ public function testTreatPhpDocTypesAsCertain(bool $treatPhpDocTypesAsCertain, a
171171
$this->analyse([__DIR__ . '/data/loose-comparison-treat-phpdoc-types.php'], $expectedErrors);
172172
}
173173

174+
public function testBug11694(): void
175+
{
176+
$this->checkAlwaysTrueStrictComparison = true;
177+
$this->analyse([__DIR__ . '/data/bug-11694.php'], [
178+
[
179+
'Loose comparison using == between 3 and int<10, 20> will always evaluate to false.',
180+
17,
181+
'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%</>.',
182+
],
183+
[
184+
'Loose comparison using == between int<10, 20> and 3 will always evaluate to false.',
185+
18,
186+
'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%</>.',
187+
],
188+
[
189+
'Loose comparison using == between 23 and int<10, 20> will always evaluate to false.',
190+
23,
191+
'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%</>.',
192+
],
193+
[
194+
'Loose comparison using == between int<10, 20> and 23 will always evaluate to false.',
195+
24,
196+
'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%</>.',
197+
],
198+
[
199+
'Loose comparison using == between null and int<10, 20> will always evaluate to false.',
200+
26,
201+
'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%</>.',
202+
],
203+
[
204+
'Loose comparison using == between int<10, 20> and null will always evaluate to false.',
205+
27,
206+
'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%</>.',
207+
],
208+
[
209+
'Loose comparison using == between \' 3\' and int<10, 20> will always evaluate to false.',
210+
32,
211+
'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%</>.',
212+
],
213+
[
214+
'Loose comparison using == between int<10, 20> and \' 3\' will always evaluate to false.',
215+
33,
216+
'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%</>.',
217+
],
218+
[
219+
'Loose comparison using == between \' 23\' and int<10, 20> will always evaluate to false.',
220+
38,
221+
'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%</>.',
222+
],
223+
[
224+
'Loose comparison using == between int<10, 20> and \' 23\' will always evaluate to false.',
225+
39,
226+
'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%</>.',
227+
],
228+
[
229+
'Loose comparison using == between false and int<10, 20> will always evaluate to false.',
230+
44,
231+
'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%</>.',
232+
],
233+
[
234+
'Loose comparison using == between int<10, 20> and false will always evaluate to false.',
235+
45,
236+
'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%</>.',
237+
],
238+
]);
239+
}
240+
174241
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
namespace Bug11694;
4+
5+
/**
6+
* @param int $value
7+
* @return int<10, 20>
8+
*/
9+
function test(int $value) : int {
10+
if ($value > 5) {
11+
return 10;
12+
}
13+
14+
return 20;
15+
}
16+
17+
if (3 == test(3)) {}
18+
if (test(3) == 3) {}
19+
20+
if (13 == test(3)) {}
21+
if (test(3) == 13) {}
22+
23+
if (23 == test(3)) {}
24+
if (test(3) == 23) {}
25+
26+
if (null == test(3)) {}
27+
if (test(3) == null) {}
28+
29+
if ('13foo' == test(3)) {}
30+
if (test(3) == '13foo') {}
31+
32+
if (' 3' == test(3)) {}
33+
if (test(3) == ' 3') {}
34+
35+
if (' 13' == test(3)) {}
36+
if (test(3) == ' 13') {}
37+
38+
if (' 23' == test(3)) {}
39+
if (test(3) == ' 23') {}
40+
41+
if (true == test(3)) {}
42+
if (test(3) == true) {}
43+
44+
if (false == test(3)) {}
45+
if (test(3) == false) {}

0 commit comments

Comments
 (0)