Skip to content

Commit

Permalink
Improve loose comparison on string types
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm authored and ondrejmirtes committed Jan 2, 2025
1 parent e36bb83 commit 45a0160
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 2 deletions.
5 changes: 5 additions & 0 deletions src/Type/Accessory/AccessoryNonEmptyStringType.php
Original file line number Diff line number Diff line change
Expand Up @@ -323,9 +323,14 @@ public function isScalar(): TrinaryLogic

public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
{
if ($type->isNull()->yes()) {
return new ConstantBooleanType(false);
}

if ($type->isString()->yes() && $type->isNonEmptyString()->no()) {
return new ConstantBooleanType(false);
}

return new BooleanType();
}

Expand Down
7 changes: 7 additions & 0 deletions src/Type/Accessory/AccessoryNonFalsyStringType.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use PHPStan\Type\BooleanType;
use PHPStan\Type\CompoundType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\FloatType;
Expand All @@ -19,6 +20,7 @@
use PHPStan\Type\IntersectionType;
use PHPStan\Type\IsSuperTypeOfResult;
use PHPStan\Type\ObjectWithoutClassType;
use PHPStan\Type\StaticTypeFactory;
use PHPStan\Type\StringType;
use PHPStan\Type\Traits\MaybeCallableTypeTrait;
use PHPStan\Type\Traits\NonArrayTypeTrait;
Expand Down Expand Up @@ -322,6 +324,11 @@ public function isScalar(): TrinaryLogic

public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
{
$falseyTypes = StaticTypeFactory::falsey();
if ($falseyTypes->isSuperTypeOf($type)->yes()) {
return new ConstantBooleanType(false);
}

return new BooleanType();
}

Expand Down
9 changes: 9 additions & 0 deletions src/Type/Accessory/AccessoryNumericStringType.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use PHPStan\Type\BooleanType;
use PHPStan\Type\CompoundType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\ErrorType;
Expand Down Expand Up @@ -324,6 +325,14 @@ public function isScalar(): TrinaryLogic

public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
{
if ($type->isNull()->yes()) {
return new ConstantBooleanType(false);
}

if ($type->isString()->yes() && $type->isNumericString()->no()) {
return new ConstantBooleanType(false);
}

return new BooleanType();
}

Expand Down
5 changes: 5 additions & 0 deletions src/Type/StringType.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use PHPStan\TrinaryLogic;
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\Traits\MaybeCallableTypeTrait;
Expand Down Expand Up @@ -267,6 +268,10 @@ public function isScalar(): TrinaryLogic

public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
{
if ($type->isArray()->yes()) {
return new ConstantBooleanType(false);
}

return new BooleanType();
}

Expand Down
11 changes: 11 additions & 0 deletions tests/PHPStan/Analyser/nsrt/loose-comparisons-php7.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,15 @@ public function sayInt(
assertType('bool', $int == $phpStr);
assertType('bool', $int == 'a');
}

/**
* @param "abc"|"def" $constNonFalsy
*/
public function sayConstUnion(
$constNonFalsy,
): void
{
assertType('true', $constNonFalsy == 0);
assertType('true', "" == 0);
}
}
11 changes: 11 additions & 0 deletions tests/PHPStan/Analyser/nsrt/loose-comparisons-php8.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,15 @@ public function sayInt(
assertType('false', $intRange == 'a');
}

/**
* @param "abc"|"def" $constNonFalsy
*/
public function sayConstUnion(
$constNonFalsy,
): void
{
assertType('false', $constNonFalsy == 0);
assertType('false', "" == 0);
}

}
110 changes: 108 additions & 2 deletions tests/PHPStan/Analyser/nsrt/loose-comparisons.php
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ public function sayEmptyArray(
* @param array{} $emptyArr
* @param 'php' $phpStr
* @param '' $emptyStr
* @param non-falsy-string $nonFalsyString
*/
public function sayNonFalsyStr(
$true,
Expand All @@ -540,7 +541,8 @@ public function sayNonFalsyStr(
$null,
$emptyArr,
$phpStr,
$emptyStr
$emptyStr,
$nonFalsyString
): void
{
assertType('true', $phpStr == $true);
Expand All @@ -555,6 +557,100 @@ public function sayNonFalsyStr(
assertType('false', $phpStr == $emptyArr);
assertType('true', $phpStr == $phpStr);
assertType('false', $phpStr == $emptyStr);

assertType('bool', $nonFalsyString == $true);
assertType('false', $nonFalsyString == $false);
assertType('bool', $nonFalsyString == $one);
assertType('false', $nonFalsyString == $zero);
assertType('bool', $nonFalsyString == $minusOne);
assertType('bool', $nonFalsyString == $oneStr);
assertType('false', $nonFalsyString == $zeroStr);
assertType('bool', $nonFalsyString == $minusOneStr);
assertType('bool', $nonFalsyString == $plusOneStr);
assertType('false', $nonFalsyString == $null);
assertType('false', $nonFalsyString == $emptyArr);
assertType('bool', $nonFalsyString == $phpStr);
assertType('false', $nonFalsyString == $emptyStr);
}

/**
* @param true $true
* @param false $false
* @param 1 $one
* @param 0 $zero
* @param -1 $minusOne
* @param '1' $oneStr
* @param '0' $zeroStr
* @param '-1' $minusOneStr
* @param '+1' $plusOneStr
* @param null $null
* @param array{} $emptyArr
* @param 'php' $phpStr
* @param '' $emptyStr
* @param numeric-string $numericStr
*/
public function sayStr(
$true,
$false,
$one,
$zero,
$minusOne,
$oneStr,
$zeroStr,
$minusOneStr,
$plusOneStr,
$null,
$emptyArr,
string $string,
$phpStr,
$emptyStr,
$numericStr,
?string $stringOrNull,
): void
{
assertType('bool', $string == $true);
assertType('bool', $string == $false);
assertType('bool', $string == $one);
assertType('bool', $string == $zero);
assertType('bool', $string == $minusOne);
assertType('bool', $string == $oneStr);
assertType('bool', $string == $zeroStr);
assertType('bool', $string == $minusOneStr);
assertType('bool', $string == $plusOneStr);
assertType('bool', $string == $null);
assertType('bool', $string == $stringOrNull);
assertType('false', $string == $emptyArr);
assertType('bool', $string == $phpStr);
assertType('bool', $string == $emptyStr);
assertType('bool', $string == $numericStr);

assertType('bool', $numericStr == $true);
assertType('bool', $numericStr == $false);
assertType('bool', $numericStr == $one);
assertType('bool', $numericStr == $zero);
assertType('bool', $numericStr == $minusOne);
assertType('bool', $numericStr == $oneStr);
assertType('bool', $numericStr == $zeroStr);
assertType('bool', $numericStr == $minusOneStr);
assertType('bool', $numericStr == $plusOneStr);
assertType('false', $numericStr == $null);
assertType('bool', $numericStr == $stringOrNull);
assertType('false', $numericStr == $emptyArr);
assertType('bool', $numericStr == $string);
assertType('false', $numericStr == $phpStr);
assertType('false', $numericStr == $emptyStr);
if (is_numeric($string)) {
assertType('bool', $numericStr == $string);
}

assertType('false', "" == 1);
assertType('true', "" == null);
assertType('false', "" == true);
assertType('true', "" == false);
assertType('false', "" == "1");
assertType('false', "" == "0");
assertType('false', "" == "-1");
assertType('false', "" == []);
}

/**
Expand Down Expand Up @@ -657,11 +753,13 @@ public function sayInt(
* @param true|1|"1" $looseOne
* @param false|0|"0" $looseZero
* @param false|1 $constMix
* @param "abc"|"def" $constNonFalsy
*/
public function sayConstUnion(
$looseOne,
$looseZero,
$constMix
$constMix,
$constNonFalsy,
): void
{
assertType('true', $looseOne == 1);
Expand Down Expand Up @@ -696,6 +794,14 @@ public function sayConstUnion(
assertType('bool', $constMix == $looseOne);
assertType('bool', $looseZero == $constMix);
assertType('bool', $constMix == $looseZero);

assertType('false', $constNonFalsy == 1);
assertType('false', $constNonFalsy == null);
assertType('true', $constNonFalsy == true);
assertType('false', $constNonFalsy == false);
assertType('false', $constNonFalsy == "1");
assertType('false', $constNonFalsy == "0");
assertType('false', $constNonFalsy == []);
}

/**
Expand Down

0 comments on commit 45a0160

Please sign in to comment.