Skip to content

Commit 5a94e38

Browse files
committed
Improve loose comparison on intersection type
1 parent 33ff84d commit 5a94e38

File tree

5 files changed

+58
-16
lines changed

5 files changed

+58
-16
lines changed

src/TrinaryLogic.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ public static function createFromBoolean(bool $value): self
4747
return self::$registry[$yesNo] ??= new self($yesNo);
4848
}
4949

50+
public static function createFromBooleanType(BooleanType $type): self
51+
{
52+
if ($type->isTrue()->yes()) {
53+
return self::createYes();
54+
}
55+
if ($type->isFalse()->yes()) {
56+
return self::createNo();
57+
}
58+
59+
return self::createMaybe();
60+
}
61+
5062
private static function create(int $value): self
5163
{
5264
self::$registry[$value] ??= new self($value);

src/Type/Accessory/AccessoryNonEmptyStringType.php

Lines changed: 4 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;
@@ -322,6 +323,9 @@ public function isScalar(): TrinaryLogic
322323

323324
public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
324325
{
326+
if ($type->isString()->yes() && $type->isNonEmptyString()->no()) {
327+
return new ConstantBooleanType(false);
328+
}
325329
return new BooleanType();
326330
}
327331

src/Type/IntersectionType.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,9 @@ public function isScalar(): TrinaryLogic
716716

717717
public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
718718
{
719-
return new BooleanType();
719+
return $this->intersectResults(
720+
static fn (Type $innerType): TrinaryLogic => TrinaryLogic::createFromBooleanType($innerType->looseCompare($type, $phpVersion))
721+
)->toBooleanType();
720722
}
721723

722724
public function isOffsetAccessible(): TrinaryLogic

src/Type/UnionType.php

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -676,21 +676,9 @@ public function isScalar(): TrinaryLogic
676676

677677
public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
678678
{
679-
$lastResult = null;
680-
foreach ($this->types as $innerType) {
681-
$result = $innerType->looseCompare($type, $phpVersion);
682-
if ($lastResult === null) {
683-
$lastResult = $result;
684-
continue;
685-
}
686-
if ($lastResult->equals($result)) {
687-
continue;
688-
}
689-
690-
return new BooleanType();
691-
}
692-
693-
return $lastResult ?? new BooleanType();
679+
return $this->unionResults(
680+
static fn (Type $innerType): TrinaryLogic => TrinaryLogic::createFromBooleanType($innerType->looseCompare($type, $phpVersion))
681+
)->toBooleanType();
694682
}
695683

696684
public function isOffsetAccessible(): TrinaryLogic

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,4 +698,40 @@ public function sayConstUnion(
698698
assertType('bool', $constMix == $looseZero);
699699
}
700700

701+
/**
702+
* @param uppercase-string $upper
703+
* @param lowercase-string $lower
704+
*/
705+
public function sayIntersection(
706+
string $upper,
707+
string $lower,
708+
string $s,
709+
): void
710+
{
711+
assertType('bool', '' == $upper);
712+
if ($upper != '') {
713+
assertType('false', '' == $upper);
714+
}
715+
assertType('bool', '0' == $upper);
716+
assertType('bool', 'a' == $upper); // should be false
717+
assertType('bool', 'abc' == $upper); // should be false
718+
assertType('bool', 'aBc' == $upper);
719+
assertType('bool', strtoupper($s) == $upper);
720+
assertType('bool', strtolower($s) == $upper); // should be false
721+
assertType('bool', $upper == $lower); // should be false
722+
723+
assertType('bool', '' == $lower);
724+
if ($lower != '') {
725+
assertType('false', '' == $lower);
726+
}
727+
assertType('bool', '0' == $lower);
728+
assertType('bool', 'A' == $lower); // should be false
729+
assertType('bool', 'ABC' == $lower); // should be false
730+
assertType('bool', 'AbC' == $lower);
731+
assertType('bool', strtoupper($s) == $lower); // should be false
732+
assertType('bool', strtolower($s) == $lower);
733+
assertType('bool', $lower == $upper); // should be false
734+
}
735+
736+
701737
}

0 commit comments

Comments
 (0)