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 committed Dec 25, 2024
1 parent 9f78100 commit 93aa88f
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 1 deletion.
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
9 changes: 9 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 Down Expand Up @@ -322,6 +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->isNonFalsyString()->no()) {
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
91 changes: 90 additions & 1 deletion 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,12 +541,14 @@ public function sayNonFalsyStr(
$null,
$emptyArr,
$phpStr,
$emptyStr
$emptyStr,
$nonFalsyString
): void
{
assertType('true', $phpStr == $true);
assertType('false', $phpStr == $false);
assertType('false', $phpStr == $one);
assertType('false', $phpStr == $zero);
assertType('false', $phpStr == $minusOne);
assertType('false', $phpStr == $oneStr);
assertType('false', $phpStr == $zeroStr);
Expand All @@ -555,6 +558,91 @@ public function sayNonFalsyStr(
assertType('false', $phpStr == $emptyArr);
assertType('true', $phpStr == $phpStr);
assertType('false', $phpStr == $emptyStr);

assertType('bool', $nonFalsyString == $true);
assertType('bool', $nonFalsyString == $false);
assertType('bool', $nonFalsyString == $one);
assertType('bool', $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);
}
}

/**
Expand Down Expand Up @@ -591,6 +679,7 @@ public function sayEmptyStr(
assertType('false', $emptyStr == $true);
assertType('true', $emptyStr == $false);
assertType('false', $emptyStr == $one);
assertType('false', $emptyStr == $zero);
assertType('false', $emptyStr == $minusOne);
assertType('false', $emptyStr == $oneStr);
assertType('false', $emptyStr == $zeroStr);
Expand Down

0 comments on commit 93aa88f

Please sign in to comment.