Skip to content

Commit 45d6c36

Browse files
Merge branch '2.1.x' into merge221
2 parents 76733ff + 0c6d698 commit 45d6c36

4 files changed

Lines changed: 86 additions & 5 deletions

File tree

src/Type/Accessory/AccessoryNonFalsyStringType.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@
1313
use PHPStan\Type\Constant\ConstantArrayType;
1414
use PHPStan\Type\Constant\ConstantBooleanType;
1515
use PHPStan\Type\Constant\ConstantIntegerType;
16+
use PHPStan\Type\Constant\ConstantStringType;
1617
use PHPStan\Type\ErrorType;
1718
use PHPStan\Type\FloatType;
1819
use PHPStan\Type\GeneralizePrecision;
1920
use PHPStan\Type\IntegerType;
2021
use PHPStan\Type\IntersectionType;
2122
use PHPStan\Type\IsSuperTypeOfResult;
23+
use PHPStan\Type\NullType;
2224
use PHPStan\Type\ObjectWithoutClassType;
23-
use PHPStan\Type\StaticTypeFactory;
2425
use PHPStan\Type\StringType;
2526
use PHPStan\Type\Traits\MaybeCallableTypeTrait;
2627
use PHPStan\Type\Traits\NonArrayTypeTrait;
@@ -352,8 +353,13 @@ public function isScalar(): TrinaryLogic
352353

353354
public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
354355
{
355-
$falseyTypes = StaticTypeFactory::falsey();
356-
if ($falseyTypes->isSuperTypeOf($type)->yes()) {
356+
$dominated = TypeCombinator::union(
357+
new NullType(),
358+
new ConstantBooleanType(false),
359+
new ConstantStringType(''),
360+
new ConstantArrayType([], []),
361+
);
362+
if ($dominated->isSuperTypeOf($type)->yes()) {
357363
return new ConstantBooleanType(false);
358364
}
359365

tests/PHPStan/Analyser/nsrt/loose-comparisons.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -561,10 +561,12 @@ public function sayNonFalsyStr(
561561
assertType('bool', $nonFalsyString == $true);
562562
assertType('false', $nonFalsyString == $false);
563563
assertType('bool', $nonFalsyString == $one);
564-
assertType('false', $nonFalsyString == $zero);
564+
assertType('bool', $nonFalsyString == $zero); // e.g. '0.0' == 0 is true (non-falsy numeric string compared numerically)
565+
assertType('true', '0.0' == 0);
565566
assertType('bool', $nonFalsyString == $minusOne);
566567
assertType('bool', $nonFalsyString == $oneStr);
567-
assertType('false', $nonFalsyString == $zeroStr);
568+
assertType('bool', $nonFalsyString == $zeroStr); // e.g. '0.0' == '0' is true (numeric strings compared numerically)
569+
assertType('true', '0.0' == '0');
568570
assertType('bool', $nonFalsyString == $minusOneStr);
569571
assertType('bool', $nonFalsyString == $plusOneStr);
570572
assertType('false', $nonFalsyString == $null);

tests/PHPStan/Rules/Comparison/ConstantLooseComparisonRuleTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,4 +265,34 @@ public function testInTrait(): void
265265
]);
266266
}
267267

268+
public function testBug14606(): void
269+
{
270+
$this->analyse([__DIR__ . '/data/bug-14606.php'], [
271+
[
272+
'Loose comparison using == between non-falsy-string and false will always evaluate to false.',
273+
19,
274+
'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%</>.',
275+
],
276+
[
277+
'Loose comparison using == between non-falsy-string and null will always evaluate to false.',
278+
24,
279+
'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%</>.',
280+
],
281+
[
282+
"Loose comparison using == between non-falsy-string and '' will always evaluate to false.",
283+
29,
284+
'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%</>.',
285+
],
286+
[
287+
'Loose comparison using == between non-falsy-string and array{} will always evaluate to false.',
288+
34,
289+
],
290+
[
291+
'Loose comparison using == between non-falsy-string and false|null will always evaluate to false.',
292+
42,
293+
'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%</>.',
294+
],
295+
]);
296+
}
297+
268298
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug14606;
4+
5+
function nonFalsyStringLooseCompareInt(string $x): bool {
6+
return !empty($x) && $x == 0; // may be true if $x is '0.0'
7+
}
8+
9+
function nonFalsyStringLooseCompareFloat(string $x): bool {
10+
return !empty($x) && $x == 0.0; // may be true if $x is '0.0'
11+
}
12+
13+
function nonFalsyStringLooseCompareZeroString(string $x): bool {
14+
return !empty($x) && $x == '0'; // may be true if $x is '0.0' (numeric strings compared numerically)
15+
}
16+
17+
/** @param non-falsy-string $x */
18+
function nonFalsyStringLooseCompareFalse(string $x): bool {
19+
return $x == false; // always false: (bool)non-falsy-string is true
20+
}
21+
22+
/** @param non-falsy-string $x */
23+
function nonFalsyStringLooseCompareNull(string $x): bool {
24+
return $x == null; // always false: non-falsy-string is non-empty
25+
}
26+
27+
/** @param non-falsy-string $x */
28+
function nonFalsyStringLooseCompareEmptyString(string $x): bool {
29+
return $x == ''; // always false: non-falsy-string is non-empty
30+
}
31+
32+
/** @param non-falsy-string $x */
33+
function nonFalsyStringLooseCompareEmptyArray(string $x): bool {
34+
return $x == []; // always false
35+
}
36+
37+
/**
38+
* @param non-falsy-string $x
39+
* @param null|false $nullOrFalse
40+
*/
41+
function nonFalsyStringLooseCompareNullOrFalse(string $x, $nullOrFalse): bool {
42+
return $x == $nullOrFalse; // always false
43+
}

0 commit comments

Comments
 (0)