diff --git a/src/Type/Accessory/AccessoryNonFalsyStringType.php b/src/Type/Accessory/AccessoryNonFalsyStringType.php index ca08260ddb..96dae9b19f 100644 --- a/src/Type/Accessory/AccessoryNonFalsyStringType.php +++ b/src/Type/Accessory/AccessoryNonFalsyStringType.php @@ -13,14 +13,15 @@ use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\Constant\ConstantIntegerType; +use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\ErrorType; use PHPStan\Type\FloatType; use PHPStan\Type\GeneralizePrecision; use PHPStan\Type\IntegerType; use PHPStan\Type\IntersectionType; use PHPStan\Type\IsSuperTypeOfResult; +use PHPStan\Type\NullType; use PHPStan\Type\ObjectWithoutClassType; -use PHPStan\Type\StaticTypeFactory; use PHPStan\Type\StringType; use PHPStan\Type\Traits\MaybeCallableTypeTrait; use PHPStan\Type\Traits\NonArrayTypeTrait; @@ -352,8 +353,13 @@ public function isScalar(): TrinaryLogic public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType { - $falseyTypes = StaticTypeFactory::falsey(); - if ($falseyTypes->isSuperTypeOf($type)->yes()) { + $dominated = TypeCombinator::union( + new NullType(), + new ConstantBooleanType(false), + new ConstantStringType(''), + new ConstantArrayType([], []), + ); + if ($dominated->isSuperTypeOf($type)->yes()) { return new ConstantBooleanType(false); } diff --git a/tests/PHPStan/Analyser/nsrt/loose-comparisons.php b/tests/PHPStan/Analyser/nsrt/loose-comparisons.php index c385548cf5..b860adc9c0 100644 --- a/tests/PHPStan/Analyser/nsrt/loose-comparisons.php +++ b/tests/PHPStan/Analyser/nsrt/loose-comparisons.php @@ -561,10 +561,12 @@ public function sayNonFalsyStr( assertType('bool', $nonFalsyString == $true); assertType('false', $nonFalsyString == $false); assertType('bool', $nonFalsyString == $one); - assertType('false', $nonFalsyString == $zero); + assertType('bool', $nonFalsyString == $zero); // e.g. '0.0' == 0 is true (non-falsy numeric string compared numerically) + assertType('true', '0.0' == 0); assertType('bool', $nonFalsyString == $minusOne); assertType('bool', $nonFalsyString == $oneStr); - assertType('false', $nonFalsyString == $zeroStr); + assertType('bool', $nonFalsyString == $zeroStr); // e.g. '0.0' == '0' is true (numeric strings compared numerically) + assertType('true', '0.0' == '0'); assertType('bool', $nonFalsyString == $minusOneStr); assertType('bool', $nonFalsyString == $plusOneStr); assertType('false', $nonFalsyString == $null); diff --git a/tests/PHPStan/Rules/Comparison/ConstantLooseComparisonRuleTest.php b/tests/PHPStan/Rules/Comparison/ConstantLooseComparisonRuleTest.php index 100ff52f17..60c19d9bcc 100644 --- a/tests/PHPStan/Rules/Comparison/ConstantLooseComparisonRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ConstantLooseComparisonRuleTest.php @@ -265,4 +265,34 @@ public function testInTrait(): void ]); } + public function testBug14606(): void + { + $this->analyse([__DIR__ . '/data/bug-14606.php'], [ + [ + 'Loose comparison using == between non-falsy-string and false will always evaluate to false.', + 19, + 'Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.', + ], + [ + 'Loose comparison using == between non-falsy-string and null will always evaluate to false.', + 24, + 'Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.', + ], + [ + "Loose comparison using == between non-falsy-string and '' will always evaluate to false.", + 29, + 'Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.', + ], + [ + 'Loose comparison using == between non-falsy-string and array{} will always evaluate to false.', + 34, + ], + [ + 'Loose comparison using == between non-falsy-string and false|null will always evaluate to false.', + 42, + 'Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.', + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Comparison/data/bug-14606.php b/tests/PHPStan/Rules/Comparison/data/bug-14606.php new file mode 100644 index 0000000000..dd8c0fbfa3 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-14606.php @@ -0,0 +1,43 @@ +