Skip to content

Commit 7807722

Browse files
staabmondrejmirtes
authored andcommitted
Improve loose comparison on constant types
1 parent 17d6b29 commit 7807722

File tree

2 files changed

+63
-4
lines changed

2 files changed

+63
-4
lines changed

src/Type/Constant/ConstantArrayType.php

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -418,9 +418,25 @@ public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
418418

419419
public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
420420
{
421-
if ($this->isIterableAtLeastOnce()->no() && count($type->getConstantScalarValues()) === 1) {
422-
// @phpstan-ignore equal.invalid, equal.notAllowed
423-
return new ConstantBooleanType($type->getConstantScalarValues()[0] == []); // phpcs:ignore
421+
if ($type->isInteger()->yes()) {
422+
return new ConstantBooleanType(false);
423+
}
424+
425+
if ($this->isIterableAtLeastOnce()->no()) {
426+
if ($type->isIterableAtLeastOnce()->yes()) {
427+
return new ConstantBooleanType(false);
428+
}
429+
430+
$constantScalarValues = $type->getConstantScalarValues();
431+
if (count($constantScalarValues) > 0) {
432+
$results = [];
433+
foreach ($constantScalarValues as $constantScalarValue) {
434+
// @phpstan-ignore equal.invalid, equal.notAllowed
435+
$results[] = TrinaryLogic::createFromBoolean($constantScalarValue == []); // phpcs:ignore
436+
}
437+
438+
return TrinaryLogic::extremeIdentity(...$results)->toBooleanType();
439+
}
424440
}
425441

426442
return new BooleanType();

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

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,8 @@ public function sayInt(
729729
array $array,
730730
int $int,
731731
int $intRange,
732+
string $emptyStr,
733+
string $phpStr,
732734
): void
733735
{
734736
assertType('bool', $int == $true);
@@ -747,19 +749,37 @@ public function sayInt(
747749
assertType('false', $intRange == $emptyArr);
748750
assertType('false', $intRange == $array);
749751

752+
assertType('false', 5 == $emptyArr);
753+
assertType('false', $emptyArr == 5);
754+
assertType('false', 5 == $array);
755+
assertType('false', $array == 5);
756+
assertType('false', [] == 5);
757+
assertType('false', 5 == []);
758+
759+
assertType('false', 5 == $emptyStr);
760+
assertType('false', 5 == $phpStr);
761+
assertType('false', 5 == 'a');
762+
763+
assertType('false', $emptyStr == 5);
764+
assertType('false', $phpStr == 5);
765+
assertType('false', 'a' == 5);
750766
}
751767

752768
/**
753769
* @param true|1|"1" $looseOne
754770
* @param false|0|"0" $looseZero
755771
* @param false|1 $constMix
756772
* @param "abc"|"def" $constNonFalsy
773+
* @param array{abc: string, num?: int, nullable: ?string} $arrShape
774+
* @param array{} $emptyArr
757775
*/
758776
public function sayConstUnion(
759777
$looseOne,
760778
$looseZero,
761779
$constMix,
762780
$constNonFalsy,
781+
array $arrShape,
782+
array $emptyArr
763783
): void
764784
{
765785
assertType('true', $looseOne == 1);
@@ -802,13 +822,22 @@ public function sayConstUnion(
802822
assertType('false', $constNonFalsy == "1");
803823
assertType('false', $constNonFalsy == "0");
804824
assertType('false', $constNonFalsy == []);
825+
826+
assertType('false', $emptyArr == $looseOne);
827+
assertType('bool', $emptyArr == $constMix);
828+
assertType('bool', $emptyArr == $looseZero);
829+
830+
assertType('bool', $arrShape == $looseOne);
831+
assertType('bool', $arrShape == $constMix);
832+
assertType('bool', $arrShape == $looseZero);
805833
}
806834

807835
/**
808836
* @param uppercase-string $upper
809837
* @param lowercase-string $lower
810838
* @param array{} $emptyArr
811839
* @param non-empty-array $nonEmptyArr
840+
* @param array{abc: string, num?: int, nullable: ?string} $arrShape
812841
* @param int<10, 20> $intRange
813842
*/
814843
public function sayIntersection(
@@ -818,6 +847,7 @@ public function sayIntersection(
818847
array $emptyArr,
819848
array $nonEmptyArr,
820849
array $arr,
850+
array $arrShape,
821851
int $i,
822852
int $intRange,
823853
): void
@@ -849,11 +879,24 @@ public function sayIntersection(
849879
assertType('false', $nonEmptyArr == $i);
850880
assertType('false', $arr == $intRange);
851881
assertType('false', $nonEmptyArr == $intRange);
852-
assertType('bool', $emptyArr == $nonEmptyArr); // should be false
882+
assertType('false', $emptyArr == $nonEmptyArr);
853883
assertType('false', $nonEmptyArr == $emptyArr);
854884
assertType('bool', $arr == $nonEmptyArr);
855885
assertType('bool', $nonEmptyArr == $arr);
856886

887+
assertType('false', 5 == $arr);
888+
assertType('false', $arr == 5);
889+
assertType('false', 5 == $emptyArr);
890+
assertType('false', $emptyArr == 5);
891+
assertType('false', 5 == $nonEmptyArr);
892+
assertType('false', $nonEmptyArr == 5);
893+
assertType('false', 5 == $arrShape);
894+
assertType('false', $arrShape == 5);
895+
if (count($arr) > 0) {
896+
assertType('false', 5 == $arr);
897+
assertType('false', $arr == 5);
898+
}
899+
857900
assertType('bool', '' == $lower);
858901
if ($lower != '') {
859902
assertType('false', '' == $lower);

0 commit comments

Comments
 (0)