From 33ff84d9dc06afe13cad43d0c6c97816e67aec84 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 22 Dec 2024 08:32:57 +0100 Subject: [PATCH 1/7] Improve loose comparison on union type --- src/Type/UnionType.php | 16 ++++++- .../Analyser/nsrt/loose-comparisons.php | 45 +++++++++++++++++++ .../ConstantLooseComparisonRuleTest.php | 2 - 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/Type/UnionType.php b/src/Type/UnionType.php index c69888cf30..2a5f37b7fb 100644 --- a/src/Type/UnionType.php +++ b/src/Type/UnionType.php @@ -676,7 +676,21 @@ public function isScalar(): TrinaryLogic public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType { - return new BooleanType(); + $lastResult = null; + foreach ($this->types as $innerType) { + $result = $innerType->looseCompare($type, $phpVersion); + if ($lastResult === null) { + $lastResult = $result; + continue; + } + if ($lastResult->equals($result)) { + continue; + } + + return new BooleanType(); + } + + return $lastResult ?? new BooleanType(); } public function isOffsetAccessible(): TrinaryLogic diff --git a/tests/PHPStan/Analyser/nsrt/loose-comparisons.php b/tests/PHPStan/Analyser/nsrt/loose-comparisons.php index cc3eba83f3..9c76790f25 100644 --- a/tests/PHPStan/Analyser/nsrt/loose-comparisons.php +++ b/tests/PHPStan/Analyser/nsrt/loose-comparisons.php @@ -653,4 +653,49 @@ public function sayInt( } + /** + * @param true|1|"1" $looseOne + * @param false|0|"0" $looseZero + * @param false|1 $constMix + */ + public function sayConstUnion( + $looseOne, + $looseZero, + $constMix + ): void + { + assertType('true', $looseOne == 1); + assertType('false', $looseOne == 0); + assertType('true', $looseOne == true); + assertType('false', $looseOne == false); + assertType('true', $looseOne == "1"); + assertType('false', $looseOne == "0"); + assertType('false', $looseOne == []); + + assertType('false', $looseZero == 1); + assertType('true', $looseZero == 0); + assertType('false', $looseZero == true); + assertType('true', $looseZero == false); + assertType('false', $looseZero == "1"); + assertType('true', $looseZero == "0"); + assertType('bool', $looseZero == []); + + assertType('bool', $constMix == 0); + assertType('bool', $constMix == 1); + assertType('bool', $constMix == true); + assertType('bool', $constMix == false); + assertType('bool', $constMix == "1"); + assertType('bool', $constMix == "0"); + assertType('bool', $constMix == []); + + assertType('true', $looseOne == $looseOne); + assertType('true', $looseZero == $looseZero); + assertType('false', $looseOne == $looseZero); + assertType('false', $looseZero == $looseOne); + assertType('bool', $looseOne == $constMix); + assertType('bool', $constMix == $looseOne); + assertType('bool', $looseZero == $constMix); + assertType('bool', $constMix == $looseZero); + } + } diff --git a/tests/PHPStan/Rules/Comparison/ConstantLooseComparisonRuleTest.php b/tests/PHPStan/Rules/Comparison/ConstantLooseComparisonRuleTest.php index c4a819d1fc..40513f3304 100644 --- a/tests/PHPStan/Rules/Comparison/ConstantLooseComparisonRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ConstantLooseComparisonRuleTest.php @@ -181,12 +181,10 @@ public function testBug11694(): void [ "Loose comparison using == between '13foo' and int<10, 20> will always evaluate to false.", 29, - 'Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.', ], [ "Loose comparison using == between int<10, 20> and '13foo' will always evaluate to false.", 30, - 'Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.', ], ]); } From 5a94e38e749d644945b4059ec0c3e92e52294cb8 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 22 Dec 2024 09:22:00 +0100 Subject: [PATCH 2/7] Improve loose comparison on intersection type --- src/TrinaryLogic.php | 12 +++++++ .../Accessory/AccessoryNonEmptyStringType.php | 4 +++ src/Type/IntersectionType.php | 4 ++- src/Type/UnionType.php | 18 ++-------- .../Analyser/nsrt/loose-comparisons.php | 36 +++++++++++++++++++ 5 files changed, 58 insertions(+), 16 deletions(-) diff --git a/src/TrinaryLogic.php b/src/TrinaryLogic.php index a587099844..f466ae149a 100644 --- a/src/TrinaryLogic.php +++ b/src/TrinaryLogic.php @@ -47,6 +47,18 @@ public static function createFromBoolean(bool $value): self return self::$registry[$yesNo] ??= new self($yesNo); } + public static function createFromBooleanType(BooleanType $type): self + { + if ($type->isTrue()->yes()) { + return self::createYes(); + } + if ($type->isFalse()->yes()) { + return self::createNo(); + } + + return self::createMaybe(); + } + private static function create(int $value): self { self::$registry[$value] ??= new self($value); diff --git a/src/Type/Accessory/AccessoryNonEmptyStringType.php b/src/Type/Accessory/AccessoryNonEmptyStringType.php index 08f4790001..f31e3108b4 100644 --- a/src/Type/Accessory/AccessoryNonEmptyStringType.php +++ b/src/Type/Accessory/AccessoryNonEmptyStringType.php @@ -11,6 +11,7 @@ use PHPStan\Type\BooleanType; use PHPStan\Type\CompoundType; use PHPStan\Type\Constant\ConstantArrayType; +use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\ErrorType; @@ -322,6 +323,9 @@ public function isScalar(): TrinaryLogic public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType { + if ($type->isString()->yes() && $type->isNonEmptyString()->no()) { + return new ConstantBooleanType(false); + } return new BooleanType(); } diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index 255f56dd7a..85cc03c364 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -716,7 +716,9 @@ public function isScalar(): TrinaryLogic public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType { - return new BooleanType(); + return $this->intersectResults( + static fn (Type $innerType): TrinaryLogic => TrinaryLogic::createFromBooleanType($innerType->looseCompare($type, $phpVersion)) + )->toBooleanType(); } public function isOffsetAccessible(): TrinaryLogic diff --git a/src/Type/UnionType.php b/src/Type/UnionType.php index 2a5f37b7fb..36c6a4ba8b 100644 --- a/src/Type/UnionType.php +++ b/src/Type/UnionType.php @@ -676,21 +676,9 @@ public function isScalar(): TrinaryLogic public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType { - $lastResult = null; - foreach ($this->types as $innerType) { - $result = $innerType->looseCompare($type, $phpVersion); - if ($lastResult === null) { - $lastResult = $result; - continue; - } - if ($lastResult->equals($result)) { - continue; - } - - return new BooleanType(); - } - - return $lastResult ?? new BooleanType(); + return $this->unionResults( + static fn (Type $innerType): TrinaryLogic => TrinaryLogic::createFromBooleanType($innerType->looseCompare($type, $phpVersion)) + )->toBooleanType(); } public function isOffsetAccessible(): TrinaryLogic diff --git a/tests/PHPStan/Analyser/nsrt/loose-comparisons.php b/tests/PHPStan/Analyser/nsrt/loose-comparisons.php index 9c76790f25..059689d91b 100644 --- a/tests/PHPStan/Analyser/nsrt/loose-comparisons.php +++ b/tests/PHPStan/Analyser/nsrt/loose-comparisons.php @@ -698,4 +698,40 @@ public function sayConstUnion( assertType('bool', $constMix == $looseZero); } + /** + * @param uppercase-string $upper + * @param lowercase-string $lower + */ + public function sayIntersection( + string $upper, + string $lower, + string $s, + ): void + { + assertType('bool', '' == $upper); + if ($upper != '') { + assertType('false', '' == $upper); + } + assertType('bool', '0' == $upper); + assertType('bool', 'a' == $upper); // should be false + assertType('bool', 'abc' == $upper); // should be false + assertType('bool', 'aBc' == $upper); + assertType('bool', strtoupper($s) == $upper); + assertType('bool', strtolower($s) == $upper); // should be false + assertType('bool', $upper == $lower); // should be false + + assertType('bool', '' == $lower); + if ($lower != '') { + assertType('false', '' == $lower); + } + assertType('bool', '0' == $lower); + assertType('bool', 'A' == $lower); // should be false + assertType('bool', 'ABC' == $lower); // should be false + assertType('bool', 'AbC' == $lower); + assertType('bool', strtoupper($s) == $lower); // should be false + assertType('bool', strtolower($s) == $lower); + assertType('bool', $lower == $upper); // should be false + } + + } From 07c6a0dc57dcd9bb2b7702f80570009a00ffa727 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 22 Dec 2024 10:19:28 +0100 Subject: [PATCH 3/7] Improve loose comparison on array type --- src/Type/Accessory/NonEmptyArrayType.php | 5 +++++ src/Type/ArrayType.php | 4 ++++ .../Analyser/nsrt/loose-comparisons.php | 18 +++++++++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Type/Accessory/NonEmptyArrayType.php b/src/Type/Accessory/NonEmptyArrayType.php index e99bb4cbaa..a46eefc807 100644 --- a/src/Type/Accessory/NonEmptyArrayType.php +++ b/src/Type/Accessory/NonEmptyArrayType.php @@ -9,6 +9,7 @@ use PHPStan\Type\AcceptsResult; use PHPStan\Type\BooleanType; use PHPStan\Type\CompoundType; +use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\Constant\ConstantFloatType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\ErrorType; @@ -402,6 +403,10 @@ public function isScalar(): TrinaryLogic public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType { + if ($type->isArray()->yes() && $type->isIterableAtLeastOnce()->no()) { + return new ConstantBooleanType(false); + } + return new BooleanType(); } diff --git a/src/Type/ArrayType.php b/src/Type/ArrayType.php index 402c985b0e..9570359eaf 100644 --- a/src/Type/ArrayType.php +++ b/src/Type/ArrayType.php @@ -249,6 +249,10 @@ public function isConstantValue(): TrinaryLogic public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType { + if ($type->isInteger()->yes()) { + return new ConstantBooleanType(false); + } + return new BooleanType(); } diff --git a/tests/PHPStan/Analyser/nsrt/loose-comparisons.php b/tests/PHPStan/Analyser/nsrt/loose-comparisons.php index 059689d91b..cce6ac1f5f 100644 --- a/tests/PHPStan/Analyser/nsrt/loose-comparisons.php +++ b/tests/PHPStan/Analyser/nsrt/loose-comparisons.php @@ -701,11 +701,19 @@ public function sayConstUnion( /** * @param uppercase-string $upper * @param lowercase-string $lower + * @param array{} $emptyArr + * @param non-empty-array $nonEmptyArr + * @param int<10, 20> $intRange */ public function sayIntersection( string $upper, string $lower, string $s, + array $emptyArr, + array $nonEmptyArr, + array $arr, + int $i, + int $intRange, ): void { assertType('bool', '' == $upper); @@ -731,7 +739,15 @@ public function sayIntersection( assertType('bool', strtoupper($s) == $lower); // should be false assertType('bool', strtolower($s) == $lower); assertType('bool', $lower == $upper); // should be false - } + assertType('false', $arr == $i); + assertType('false', $nonEmptyArr == $i); + assertType('false', $arr == $intRange); + assertType('false', $nonEmptyArr == $intRange); + assertType('bool', $emptyArr == $nonEmptyArr); // should be false + assertType('false', $nonEmptyArr == $emptyArr); + assertType('bool', $arr == $nonEmptyArr); + assertType('bool', $nonEmptyArr == $arr); + } } From b5ecab0d3a037738b1fee0df5d9cc487bd7de484 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 22 Dec 2024 11:48:46 +0100 Subject: [PATCH 4/7] Improve loose comparison on uppercase/lowercase string Co-Authored-By: Vincent Langlet --- .../AccessoryLowercaseStringType.php | 5 +++ .../AccessoryUppercaseStringType.php | 5 +++ .../Analyser/nsrt/loose-comparisons.php | 35 ++++++++++--------- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/Type/Accessory/AccessoryLowercaseStringType.php b/src/Type/Accessory/AccessoryLowercaseStringType.php index c4b8b0b364..96965310b8 100644 --- a/src/Type/Accessory/AccessoryLowercaseStringType.php +++ b/src/Type/Accessory/AccessoryLowercaseStringType.php @@ -11,6 +11,7 @@ use PHPStan\Type\BooleanType; use PHPStan\Type\CompoundType; use PHPStan\Type\Constant\ConstantArrayType; +use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\ErrorType; use PHPStan\Type\FloatType; @@ -326,6 +327,10 @@ public function isScalar(): TrinaryLogic public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType { + if ($type->isString()->yes() && $type->isLowercaseString()->no()) { + return new ConstantBooleanType(false); + } + return new BooleanType(); } diff --git a/src/Type/Accessory/AccessoryUppercaseStringType.php b/src/Type/Accessory/AccessoryUppercaseStringType.php index f9ae03d0ac..b1fe88c26b 100644 --- a/src/Type/Accessory/AccessoryUppercaseStringType.php +++ b/src/Type/Accessory/AccessoryUppercaseStringType.php @@ -11,6 +11,7 @@ use PHPStan\Type\BooleanType; use PHPStan\Type\CompoundType; use PHPStan\Type\Constant\ConstantArrayType; +use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\ErrorType; use PHPStan\Type\FloatType; @@ -326,6 +327,10 @@ public function isScalar(): TrinaryLogic public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType { + if ($type->isString()->yes() && $type->isUppercaseString()->no()) { + return new ConstantBooleanType(false); + } + return new BooleanType(); } diff --git a/tests/PHPStan/Analyser/nsrt/loose-comparisons.php b/tests/PHPStan/Analyser/nsrt/loose-comparisons.php index cce6ac1f5f..0b7ff22038 100644 --- a/tests/PHPStan/Analyser/nsrt/loose-comparisons.php +++ b/tests/PHPStan/Analyser/nsrt/loose-comparisons.php @@ -717,28 +717,21 @@ public function sayIntersection( ): void { assertType('bool', '' == $upper); - if ($upper != '') { - assertType('false', '' == $upper); - } assertType('bool', '0' == $upper); - assertType('bool', 'a' == $upper); // should be false - assertType('bool', 'abc' == $upper); // should be false - assertType('bool', 'aBc' == $upper); + assertType('false', 'a' == $upper); + assertType('false', 'abc' == $upper); + assertType('false', 'aBc' == $upper); assertType('bool', strtoupper($s) == $upper); - assertType('bool', strtolower($s) == $upper); // should be false - assertType('bool', $upper == $lower); // should be false + assertType('bool', strtolower($s) == $upper); + assertType('bool', $upper == $lower); - assertType('bool', '' == $lower); - if ($lower != '') { - assertType('false', '' == $lower); - } assertType('bool', '0' == $lower); - assertType('bool', 'A' == $lower); // should be false - assertType('bool', 'ABC' == $lower); // should be false - assertType('bool', 'AbC' == $lower); - assertType('bool', strtoupper($s) == $lower); // should be false + assertType('false', 'A' == $lower); + assertType('false', 'ABC' == $lower); + assertType('false', 'AbC' == $lower); + assertType('bool', strtoupper($s) == $lower); assertType('bool', strtolower($s) == $lower); - assertType('bool', $lower == $upper); // should be false + assertType('bool', $lower == $upper); assertType('false', $arr == $i); assertType('false', $nonEmptyArr == $i); @@ -748,6 +741,14 @@ public function sayIntersection( assertType('false', $nonEmptyArr == $emptyArr); assertType('bool', $arr == $nonEmptyArr); assertType('bool', $nonEmptyArr == $arr); + + assertType('bool', '' == $lower); + if ($lower != '') { + assertType('false', '' == $lower); + } + if ($upper != '') { + assertType('false', '' == $upper); + } } } From 684c5999e3b320cca25f25cb4c5ad4de6bb28e10 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 22 Dec 2024 12:03:03 +0100 Subject: [PATCH 5/7] Added regression test --- .../Comparison/ConstantLooseComparisonRuleTest.php | 11 +++++++++++ tests/PHPStan/Rules/Comparison/data/bug-8800.php | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/PHPStan/Rules/Comparison/data/bug-8800.php diff --git a/tests/PHPStan/Rules/Comparison/ConstantLooseComparisonRuleTest.php b/tests/PHPStan/Rules/Comparison/ConstantLooseComparisonRuleTest.php index 40513f3304..6e56f9dfcd 100644 --- a/tests/PHPStan/Rules/Comparison/ConstantLooseComparisonRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ConstantLooseComparisonRuleTest.php @@ -225,4 +225,15 @@ public function testBug11694(): void $this->analyse([__DIR__ . '/data/bug-11694.php'], $expectedErrors); } + public function testBug8800(): void + { + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-8800.php'], [ + [ + 'Loose comparison using == between 0|1|false and 2 will always evaluate to false.', + 9, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Comparison/data/bug-8800.php b/tests/PHPStan/Rules/Comparison/data/bug-8800.php new file mode 100644 index 0000000000..c8656f5da2 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-8800.php @@ -0,0 +1,11 @@ + Date: Sun, 22 Dec 2024 15:09:32 +0100 Subject: [PATCH 6/7] Improve loose comparison on numeric string --- src/Type/Accessory/AccessoryLowercaseStringType.php | 6 +++++- src/Type/Accessory/AccessoryUppercaseStringType.php | 6 +++++- tests/PHPStan/Analyser/nsrt/loose-comparisons.php | 6 ++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Type/Accessory/AccessoryLowercaseStringType.php b/src/Type/Accessory/AccessoryLowercaseStringType.php index 96965310b8..97edeb39ba 100644 --- a/src/Type/Accessory/AccessoryLowercaseStringType.php +++ b/src/Type/Accessory/AccessoryLowercaseStringType.php @@ -327,7 +327,11 @@ public function isScalar(): TrinaryLogic public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType { - if ($type->isString()->yes() && $type->isLowercaseString()->no()) { + if ( + $type->isString()->yes() + && $type->isLowercaseString()->no() + && ($type->isNumericString()->no() || $this->isNumericString()->no()) + ) { return new ConstantBooleanType(false); } diff --git a/src/Type/Accessory/AccessoryUppercaseStringType.php b/src/Type/Accessory/AccessoryUppercaseStringType.php index b1fe88c26b..a034eea321 100644 --- a/src/Type/Accessory/AccessoryUppercaseStringType.php +++ b/src/Type/Accessory/AccessoryUppercaseStringType.php @@ -327,7 +327,11 @@ public function isScalar(): TrinaryLogic public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType { - if ($type->isString()->yes() && $type->isUppercaseString()->no()) { + if ( + $type->isString()->yes() + && $type->isUppercaseString()->no() + && ($type->isNumericString()->no() || $this->isNumericString()->no()) + ) { return new ConstantBooleanType(false); } diff --git a/tests/PHPStan/Analyser/nsrt/loose-comparisons.php b/tests/PHPStan/Analyser/nsrt/loose-comparisons.php index 0b7ff22038..16c414170b 100644 --- a/tests/PHPStan/Analyser/nsrt/loose-comparisons.php +++ b/tests/PHPStan/Analyser/nsrt/loose-comparisons.php @@ -716,11 +716,16 @@ public function sayIntersection( int $intRange, ): void { + // https://3v4l.org/q8OP2 + assertType('true', '1e2' == '1E2'); + assertType('false', '1e2' === '1E2'); + assertType('bool', '' == $upper); assertType('bool', '0' == $upper); assertType('false', 'a' == $upper); assertType('false', 'abc' == $upper); assertType('false', 'aBc' == $upper); + assertType('bool', '1e2' == $upper); assertType('bool', strtoupper($s) == $upper); assertType('bool', strtolower($s) == $upper); assertType('bool', $upper == $lower); @@ -729,6 +734,7 @@ public function sayIntersection( assertType('false', 'A' == $lower); assertType('false', 'ABC' == $lower); assertType('false', 'AbC' == $lower); + assertType('bool', '1E2' == $lower); assertType('bool', strtoupper($s) == $lower); assertType('bool', strtolower($s) == $lower); assertType('bool', $lower == $upper); From 04091a13e183e15640de6bdc5b2540138e957732 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 23 Dec 2024 20:00:32 +0100 Subject: [PATCH 7/7] Add BooleanType->toTrinaryLogic() --- src/TrinaryLogic.php | 12 ------------ src/Type/BooleanType.php | 12 ++++++++++++ src/Type/IntersectionType.php | 2 +- src/Type/UnionType.php | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/TrinaryLogic.php b/src/TrinaryLogic.php index f466ae149a..a587099844 100644 --- a/src/TrinaryLogic.php +++ b/src/TrinaryLogic.php @@ -47,18 +47,6 @@ public static function createFromBoolean(bool $value): self return self::$registry[$yesNo] ??= new self($yesNo); } - public static function createFromBooleanType(BooleanType $type): self - { - if ($type->isTrue()->yes()) { - return self::createYes(); - } - if ($type->isFalse()->yes()) { - return self::createNo(); - } - - return self::createMaybe(); - } - private static function create(int $value): self { self::$registry[$value] ??= new self($value); diff --git a/src/Type/BooleanType.php b/src/Type/BooleanType.php index df059481e6..0b0eb798ec 100644 --- a/src/Type/BooleanType.php +++ b/src/Type/BooleanType.php @@ -163,4 +163,16 @@ public function toPhpDocNode(): TypeNode return new IdentifierTypeNode('bool'); } + public function toTrinaryLogic(): TrinaryLogic + { + if ($this->isTrue()->yes()) { + return TrinaryLogic::createYes(); + } + if ($this->isFalse()->yes()) { + return TrinaryLogic::createNo(); + } + + return TrinaryLogic::createMaybe(); + } + } diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index 85cc03c364..b79f6ed7ea 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -717,7 +717,7 @@ public function isScalar(): TrinaryLogic public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType { return $this->intersectResults( - static fn (Type $innerType): TrinaryLogic => TrinaryLogic::createFromBooleanType($innerType->looseCompare($type, $phpVersion)) + static fn (Type $innerType): TrinaryLogic => $innerType->looseCompare($type, $phpVersion)->toTrinaryLogic() )->toBooleanType(); } diff --git a/src/Type/UnionType.php b/src/Type/UnionType.php index 36c6a4ba8b..9156bc09b7 100644 --- a/src/Type/UnionType.php +++ b/src/Type/UnionType.php @@ -677,7 +677,7 @@ public function isScalar(): TrinaryLogic public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType { return $this->unionResults( - static fn (Type $innerType): TrinaryLogic => TrinaryLogic::createFromBooleanType($innerType->looseCompare($type, $phpVersion)) + static fn (Type $innerType): TrinaryLogic => $innerType->looseCompare($type, $phpVersion)->toTrinaryLogic() )->toBooleanType(); }