Skip to content

Commit 6fcbe71

Browse files
committed
Improve loose comparison on constant types
1 parent 9f78100 commit 6fcbe71

File tree

2 files changed

+64
-5
lines changed

2 files changed

+64
-5
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: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,8 @@ public function sayInt(
633633
array $array,
634634
int $int,
635635
int $intRange,
636+
string $emptyStr,
637+
string $phpStr,
636638
): void
637639
{
638640
assertType('bool', $int == $true);
@@ -651,17 +653,35 @@ public function sayInt(
651653
assertType('false', $intRange == $emptyArr);
652654
assertType('false', $intRange == $array);
653655

656+
assertType('false', 5 == $emptyArr);
657+
assertType('false', $emptyArr == 5);
658+
assertType('false', 5 == $array);
659+
assertType('false', $array == 5);
660+
assertType('false', [] == 5);
661+
assertType('false', 5 == []);
662+
663+
assertType('false', 5 == $emptyStr);
664+
assertType('false', 5 == $phpStr);
665+
assertType('false', 5 == 'a');
666+
667+
assertType('false', $emptyStr == 5);
668+
assertType('false', $phpStr == 5);
669+
assertType('false', 'a' == 5);
654670
}
655671

656672
/**
657673
* @param true|1|"1" $looseOne
658674
* @param false|0|"0" $looseZero
659675
* @param false|1 $constMix
676+
* @param array{abc: string, num?: int, nullable: ?string} $arrShape
677+
* @param array{} $emptyArr
660678
*/
661679
public function sayConstUnion(
662680
$looseOne,
663681
$looseZero,
664-
$constMix
682+
$constMix,
683+
array $arrShape,
684+
array $emptyArr
665685
): void
666686
{
667687
assertType('true', $looseOne == 1);
@@ -696,13 +716,22 @@ public function sayConstUnion(
696716
assertType('bool', $constMix == $looseOne);
697717
assertType('bool', $looseZero == $constMix);
698718
assertType('bool', $constMix == $looseZero);
719+
720+
assertType('false', $emptyArr == $looseOne);
721+
assertType('bool', $emptyArr == $constMix);
722+
assertType('bool', $emptyArr == $looseZero);
723+
724+
assertType('bool', $arrShape == $looseOne);
725+
assertType('bool', $arrShape == $constMix);
726+
assertType('bool', $arrShape == $looseZero);
699727
}
700728

701729
/**
702730
* @param uppercase-string $upper
703731
* @param lowercase-string $lower
704732
* @param array{} $emptyArr
705733
* @param non-empty-array $nonEmptyArr
734+
* @param array{abc: string, num?: int, nullable: ?string} $arrShape
706735
* @param int<10, 20> $intRange
707736
*/
708737
public function sayIntersection(
@@ -712,6 +741,7 @@ public function sayIntersection(
712741
array $emptyArr,
713742
array $nonEmptyArr,
714743
array $arr,
744+
array $arrShape,
715745
int $i,
716746
int $intRange,
717747
): void
@@ -743,11 +773,24 @@ public function sayIntersection(
743773
assertType('false', $nonEmptyArr == $i);
744774
assertType('false', $arr == $intRange);
745775
assertType('false', $nonEmptyArr == $intRange);
746-
assertType('bool', $emptyArr == $nonEmptyArr); // should be false
776+
assertType('false', $emptyArr == $nonEmptyArr);
747777
assertType('false', $nonEmptyArr == $emptyArr);
748778
assertType('bool', $arr == $nonEmptyArr);
749779
assertType('bool', $nonEmptyArr == $arr);
750780

781+
assertType('false', 5 == $arr);
782+
assertType('false', $arr == 5);
783+
assertType('false', 5 == $emptyArr);
784+
assertType('false', $emptyArr == 5);
785+
assertType('false', 5 == $nonEmptyArr);
786+
assertType('false', $nonEmptyArr == 5);
787+
assertType('false', 5 == $arrShape);
788+
assertType('false', $arrShape == 5);
789+
if (count($arr) > 0) {
790+
assertType('false', 5 == $arr);
791+
assertType('false', $arr == 5);
792+
}
793+
751794
assertType('bool', '' == $lower);
752795
if ($lower != '') {
753796
assertType('false', '' == $lower);

0 commit comments

Comments
 (0)