Skip to content

Commit a85f37a

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

File tree

2 files changed

+65
-5
lines changed

2 files changed

+65
-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: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace LooseSemantics;
66

7+
use SelfOut\a;
78
use function PHPStan\Testing\assertType;
89

910
class HelloWorld
@@ -633,6 +634,8 @@ public function sayInt(
633634
array $array,
634635
int $int,
635636
int $intRange,
637+
string $emptyStr,
638+
string $phpStr,
636639
): void
637640
{
638641
assertType('bool', $int == $true);
@@ -651,17 +654,35 @@ public function sayInt(
651654
assertType('false', $intRange == $emptyArr);
652655
assertType('false', $intRange == $array);
653656

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

656673
/**
657674
* @param true|1|"1" $looseOne
658675
* @param false|0|"0" $looseZero
659676
* @param false|1 $constMix
677+
* @param array{abc: string, num?: int, nullable: ?string} $arrShape
678+
* @param array{} $emptyArr
660679
*/
661680
public function sayConstUnion(
662681
$looseOne,
663682
$looseZero,
664-
$constMix
683+
$constMix,
684+
array $arrShape,
685+
array $emptyArr
665686
): void
666687
{
667688
assertType('true', $looseOne == 1);
@@ -696,13 +717,22 @@ public function sayConstUnion(
696717
assertType('bool', $constMix == $looseOne);
697718
assertType('bool', $looseZero == $constMix);
698719
assertType('bool', $constMix == $looseZero);
720+
721+
assertType('false', $emptyArr == $looseOne);
722+
assertType('bool', $emptyArr == $constMix);
723+
assertType('bool', $emptyArr == $looseZero);
724+
725+
assertType('bool', $arrShape == $looseOne);
726+
assertType('bool', $arrShape == $constMix);
727+
assertType('bool', $arrShape == $looseZero);
699728
}
700729

701730
/**
702731
* @param uppercase-string $upper
703732
* @param lowercase-string $lower
704733
* @param array{} $emptyArr
705734
* @param non-empty-array $nonEmptyArr
735+
* @param array{abc: string, num?: int, nullable: ?string} $arrShape
706736
* @param int<10, 20> $intRange
707737
*/
708738
public function sayIntersection(
@@ -712,6 +742,7 @@ public function sayIntersection(
712742
array $emptyArr,
713743
array $nonEmptyArr,
714744
array $arr,
745+
array $arrShape,
715746
int $i,
716747
int $intRange,
717748
): void
@@ -743,11 +774,24 @@ public function sayIntersection(
743774
assertType('false', $nonEmptyArr == $i);
744775
assertType('false', $arr == $intRange);
745776
assertType('false', $nonEmptyArr == $intRange);
746-
assertType('bool', $emptyArr == $nonEmptyArr); // should be false
777+
assertType('false', $emptyArr == $nonEmptyArr);
747778
assertType('false', $nonEmptyArr == $emptyArr);
748779
assertType('bool', $arr == $nonEmptyArr);
749780
assertType('bool', $nonEmptyArr == $arr);
750781

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

0 commit comments

Comments
 (0)