Skip to content

Commit 93aa88f

Browse files
committed
Improve loose comparison on string types
1 parent 9f78100 commit 93aa88f

File tree

5 files changed

+118
-1
lines changed

5 files changed

+118
-1
lines changed

src/Type/Accessory/AccessoryNonEmptyStringType.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,9 +323,14 @@ public function isScalar(): TrinaryLogic
323323

324324
public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
325325
{
326+
if ($type->isNull()->yes()) {
327+
return new ConstantBooleanType(false);
328+
}
329+
326330
if ($type->isString()->yes() && $type->isNonEmptyString()->no()) {
327331
return new ConstantBooleanType(false);
328332
}
333+
329334
return new BooleanType();
330335
}
331336

src/Type/Accessory/AccessoryNonFalsyStringType.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use PHPStan\Type\BooleanType;
1212
use PHPStan\Type\CompoundType;
1313
use PHPStan\Type\Constant\ConstantArrayType;
14+
use PHPStan\Type\Constant\ConstantBooleanType;
1415
use PHPStan\Type\Constant\ConstantIntegerType;
1516
use PHPStan\Type\ErrorType;
1617
use PHPStan\Type\FloatType;
@@ -322,6 +323,14 @@ public function isScalar(): TrinaryLogic
322323

323324
public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
324325
{
326+
if ($type->isNull()->yes()) {
327+
return new ConstantBooleanType(false);
328+
}
329+
330+
if ($type->isString()->yes() && $type->isNonFalsyString()->no()) {
331+
return new ConstantBooleanType(false);
332+
}
333+
325334
return new BooleanType();
326335
}
327336

src/Type/Accessory/AccessoryNumericStringType.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use PHPStan\Type\BooleanType;
1212
use PHPStan\Type\CompoundType;
1313
use PHPStan\Type\Constant\ConstantArrayType;
14+
use PHPStan\Type\Constant\ConstantBooleanType;
1415
use PHPStan\Type\Constant\ConstantIntegerType;
1516
use PHPStan\Type\Constant\ConstantStringType;
1617
use PHPStan\Type\ErrorType;
@@ -324,6 +325,14 @@ public function isScalar(): TrinaryLogic
324325

325326
public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
326327
{
328+
if ($type->isNull()->yes()) {
329+
return new ConstantBooleanType(false);
330+
}
331+
332+
if ($type->isString()->yes() && $type->isNumericString()->no()) {
333+
return new ConstantBooleanType(false);
334+
}
335+
327336
return new BooleanType();
328337
}
329338

src/Type/StringType.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use PHPStan\TrinaryLogic;
1111
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
1212
use PHPStan\Type\Constant\ConstantArrayType;
13+
use PHPStan\Type\Constant\ConstantBooleanType;
1314
use PHPStan\Type\Constant\ConstantIntegerType;
1415
use PHPStan\Type\Constant\ConstantStringType;
1516
use PHPStan\Type\Traits\MaybeCallableTypeTrait;
@@ -267,6 +268,10 @@ public function isScalar(): TrinaryLogic
267268

268269
public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
269270
{
271+
if ($type->isArray()->yes()) {
272+
return new ConstantBooleanType(false);
273+
}
274+
270275
return new BooleanType();
271276
}
272277

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

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ public function sayEmptyArray(
526526
* @param array{} $emptyArr
527527
* @param 'php' $phpStr
528528
* @param '' $emptyStr
529+
* @param non-falsy-string $nonFalsyString
529530
*/
530531
public function sayNonFalsyStr(
531532
$true,
@@ -540,12 +541,14 @@ public function sayNonFalsyStr(
540541
$null,
541542
$emptyArr,
542543
$phpStr,
543-
$emptyStr
544+
$emptyStr,
545+
$nonFalsyString
544546
): void
545547
{
546548
assertType('true', $phpStr == $true);
547549
assertType('false', $phpStr == $false);
548550
assertType('false', $phpStr == $one);
551+
assertType('false', $phpStr == $zero);
549552
assertType('false', $phpStr == $minusOne);
550553
assertType('false', $phpStr == $oneStr);
551554
assertType('false', $phpStr == $zeroStr);
@@ -555,6 +558,91 @@ public function sayNonFalsyStr(
555558
assertType('false', $phpStr == $emptyArr);
556559
assertType('true', $phpStr == $phpStr);
557560
assertType('false', $phpStr == $emptyStr);
561+
562+
assertType('bool', $nonFalsyString == $true);
563+
assertType('bool', $nonFalsyString == $false);
564+
assertType('bool', $nonFalsyString == $one);
565+
assertType('bool', $nonFalsyString == $zero);
566+
assertType('bool', $nonFalsyString == $minusOne);
567+
assertType('bool', $nonFalsyString == $oneStr);
568+
assertType('false', $nonFalsyString == $zeroStr);
569+
assertType('bool', $nonFalsyString == $minusOneStr);
570+
assertType('bool', $nonFalsyString == $plusOneStr);
571+
assertType('false', $nonFalsyString == $null);
572+
assertType('false', $nonFalsyString == $emptyArr);
573+
assertType('bool', $nonFalsyString == $phpStr);
574+
assertType('false', $nonFalsyString == $emptyStr);
575+
}
576+
577+
/**
578+
* @param true $true
579+
* @param false $false
580+
* @param 1 $one
581+
* @param 0 $zero
582+
* @param -1 $minusOne
583+
* @param '1' $oneStr
584+
* @param '0' $zeroStr
585+
* @param '-1' $minusOneStr
586+
* @param '+1' $plusOneStr
587+
* @param null $null
588+
* @param array{} $emptyArr
589+
* @param 'php' $phpStr
590+
* @param '' $emptyStr
591+
* @param numeric-string $numericStr
592+
*/
593+
public function sayStr(
594+
$true,
595+
$false,
596+
$one,
597+
$zero,
598+
$minusOne,
599+
$oneStr,
600+
$zeroStr,
601+
$minusOneStr,
602+
$plusOneStr,
603+
$null,
604+
$emptyArr,
605+
string $string,
606+
$phpStr,
607+
$emptyStr,
608+
$numericStr,
609+
?string $stringOrNull,
610+
): void
611+
{
612+
assertType('bool', $string == $true);
613+
assertType('bool', $string == $false);
614+
assertType('bool', $string == $one);
615+
assertType('bool', $string == $zero);
616+
assertType('bool', $string == $minusOne);
617+
assertType('bool', $string == $oneStr);
618+
assertType('bool', $string == $zeroStr);
619+
assertType('bool', $string == $minusOneStr);
620+
assertType('bool', $string == $plusOneStr);
621+
assertType('bool', $string == $null);
622+
assertType('bool', $string == $stringOrNull);
623+
assertType('false', $string == $emptyArr);
624+
assertType('bool', $string == $phpStr);
625+
assertType('bool', $string == $emptyStr);
626+
assertType('bool', $string == $numericStr);
627+
628+
assertType('bool', $numericStr == $true);
629+
assertType('bool', $numericStr == $false);
630+
assertType('bool', $numericStr == $one);
631+
assertType('bool', $numericStr == $zero);
632+
assertType('bool', $numericStr == $minusOne);
633+
assertType('bool', $numericStr == $oneStr);
634+
assertType('bool', $numericStr == $zeroStr);
635+
assertType('bool', $numericStr == $minusOneStr);
636+
assertType('bool', $numericStr == $plusOneStr);
637+
assertType('false', $numericStr == $null);
638+
assertType('bool', $numericStr == $stringOrNull);
639+
assertType('false', $numericStr == $emptyArr);
640+
assertType('bool', $numericStr == $string);
641+
assertType('false', $numericStr == $phpStr);
642+
assertType('false', $numericStr == $emptyStr);
643+
if (is_numeric($string)) {
644+
assertType('bool', $numericStr == $string);
645+
}
558646
}
559647

560648
/**
@@ -591,6 +679,7 @@ public function sayEmptyStr(
591679
assertType('false', $emptyStr == $true);
592680
assertType('true', $emptyStr == $false);
593681
assertType('false', $emptyStr == $one);
682+
assertType('false', $emptyStr == $zero);
594683
assertType('false', $emptyStr == $minusOne);
595684
assertType('false', $emptyStr == $oneStr);
596685
assertType('false', $emptyStr == $zeroStr);

0 commit comments

Comments
 (0)