Skip to content

Commit efcfbc1

Browse files
Fix loose inequality
1 parent ac91552 commit efcfbc1

File tree

5 files changed

+99
-16
lines changed

5 files changed

+99
-16
lines changed

src/Type/LooseComparisonHelper.php

+18-14
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ final class LooseComparisonHelper
99
{
1010

1111
public static function compareConstantScalars(ConstantScalarType $leftType, ConstantScalarType $rightType, PhpVersion $phpVersion): BooleanType
12+
{
13+
[$leftValue, $rightValue] = self::getConstantScalarValuesForComparison($leftType, $rightType, $phpVersion);
14+
15+
// @phpstan-ignore equal.notAllowed
16+
return new ConstantBooleanType($leftValue == $rightValue); // phpcs:ignore
17+
}
18+
19+
/**
20+
* @return array{scalar, scalar}
21+
*/
22+
public static function getConstantScalarValuesForComparison(ConstantScalarType $leftType, ConstantScalarType $rightType, PhpVersion $phpVersion): array
1223
{
1324
if ($phpVersion->castsNumbersToStringsOnLooseComparison()) {
1425
$isNumber = new UnionType([
@@ -17,34 +28,27 @@ public static function compareConstantScalars(ConstantScalarType $leftType, Cons
1728
]);
1829

1930
if ($leftType->isString()->yes() && $leftType->isNumericString()->no() && $isNumber->isSuperTypeOf($rightType)->yes()) {
20-
$stringValue = (string) $rightType->getValue();
21-
return new ConstantBooleanType($stringValue === $leftType->getValue());
31+
return [$leftType->getValue(), (string) $rightType->getValue()];
2232
}
2333
if ($rightType->isString()->yes() && $rightType->isNumericString()->no() && $isNumber->isSuperTypeOf($leftType)->yes()) {
24-
$stringValue = (string) $leftType->getValue();
25-
return new ConstantBooleanType($stringValue === $rightType->getValue());
34+
return [(string) $leftType->getValue(), $rightType->getValue()];
2635
}
2736
} else {
2837
if ($leftType->isString()->yes() && $leftType->isNumericString()->no() && $rightType->isFloat()->yes()) {
29-
$numericPart = (float) $leftType->getValue();
30-
return new ConstantBooleanType($numericPart === $rightType->getValue());
38+
return [(float) $leftType->getValue(), $rightType->getValue()];
3139
}
3240
if ($rightType->isString()->yes() && $rightType->isNumericString()->no() && $leftType->isFloat()->yes()) {
33-
$numericPart = (float) $rightType->getValue();
34-
return new ConstantBooleanType($numericPart === $leftType->getValue());
41+
return [$leftType->getValue(), (float) $rightType->getValue()];
3542
}
3643
if ($leftType->isString()->yes() && $leftType->isNumericString()->no() && $rightType->isInteger()->yes()) {
37-
$numericPart = (int) $leftType->getValue();
38-
return new ConstantBooleanType($numericPart === $rightType->getValue());
44+
return [(int) $leftType->getValue(), $rightType->getValue()];
3945
}
4046
if ($rightType->isString()->yes() && $rightType->isNumericString()->no() && $leftType->isInteger()->yes()) {
41-
$numericPart = (int) $rightType->getValue();
42-
return new ConstantBooleanType($numericPart === $leftType->getValue());
47+
return [$leftType->getValue(), (int) $rightType->getValue()];
4348
}
4449
}
4550

46-
// @phpstan-ignore equal.notAllowed
47-
return new ConstantBooleanType($leftType->getValue() == $rightType->getValue()); // phpcs:ignore
51+
return [$leftType->getValue(), $rightType->getValue()];
4852
}
4953

5054
}

src/Type/Traits/ConstantScalarTypeTrait.php

+6-2
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ public function equals(Type $type): bool
8181
public function isSmallerThan(Type $otherType, PhpVersion $phpVersion): TrinaryLogic
8282
{
8383
if ($otherType instanceof ConstantScalarType) {
84-
return TrinaryLogic::createFromBoolean($this->value < $otherType->getValue());
84+
[$leftValue, $rightValue] = LooseComparisonHelper::getConstantScalarValuesForComparison($this, $otherType, $phpVersion);
85+
86+
return TrinaryLogic::createFromBoolean($leftValue < $rightValue);
8587
}
8688

8789
if ($otherType instanceof CompoundType) {
@@ -94,7 +96,9 @@ public function isSmallerThan(Type $otherType, PhpVersion $phpVersion): TrinaryL
9496
public function isSmallerThanOrEqual(Type $otherType, PhpVersion $phpVersion): TrinaryLogic
9597
{
9698
if ($otherType instanceof ConstantScalarType) {
97-
return TrinaryLogic::createFromBoolean($this->value <= $otherType->getValue());
99+
[$leftValue, $rightValue] = LooseComparisonHelper::getConstantScalarValuesForComparison($this, $otherType, $phpVersion);
100+
101+
return TrinaryLogic::createFromBoolean($leftValue <= $rightValue);
98102
}
99103

100104
if ($otherType instanceof CompoundType) {

tests/PHPStan/Analyser/NodeScopeResolverTest.php

+7
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,17 @@ class NodeScopeResolverTest extends TypeInferenceTestCase
2525
*/
2626
private static function findTestFiles(): iterable
2727
{
28+
yield __DIR__ . '/data/bug-11732-php74.php'; return;
2829
foreach (self::findTestDataFilesFromDirectory(__DIR__ . '/nsrt') as $testFile) {
2930
yield $testFile;
3031
}
3132

33+
if (PHP_VERSION_ID < 80000) {
34+
yield __DIR__ . '/data/bug-11732-php8.php';
35+
} else {
36+
yield __DIR__ . '/data/bug-11732-php74.php';
37+
}
38+
3239
if (PHP_VERSION_ID < 80200 && PHP_VERSION_ID >= 80100) {
3340
yield __DIR__ . '/data/enum-reflection-php81.php';
3441
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug11732;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Foo {
8+
public function testPHP74(): void
9+
{
10+
assertType('false', '' < 0);
11+
assertType('true', '' <= 0);
12+
13+
assertType('false', 'foo' < 0);
14+
assertType('true', 'foo' <= 0);
15+
16+
assertType('false', '' < 0.0);
17+
assertType('true', '' <= 0.0);
18+
19+
assertType('false', 'foo' < 0.0);
20+
assertType('true', 'foo' <= 0.0);
21+
22+
assertType('true', '' < 1);
23+
assertType('true', '' <= 1);
24+
25+
assertType('true', 'foo' < 1);
26+
assertType('true', 'foo' <= 1);
27+
28+
assertType('true', '' < 1.0);
29+
assertType('true', '' <= 1.0);
30+
31+
assertType('true', 'foo' < 1.0);
32+
assertType('true', 'foo' <= 1.0);
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug11732;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Foo {
8+
public function testPHP8(): void
9+
{
10+
assertType('true', '' < 0);
11+
assertType('true', '' <= 0);
12+
13+
assertType('false', 'foo' < 0);
14+
assertType('false', 'foo' <= 0);
15+
16+
assertType('true', '' < 0.0);
17+
assertType('true', '' <= 0.0);
18+
19+
assertType('false', 'foo' < 0.0);
20+
assertType('false', 'foo' <= 0.0);
21+
22+
assertType('true', '' < 1);
23+
assertType('true', '' <= 1);
24+
25+
assertType('false', 'foo' < 1);
26+
assertType('false', 'foo' <= 1);
27+
28+
assertType('true', '' < 1.0);
29+
assertType('true', '' <= 1.0);
30+
31+
assertType('false', 'foo' < 1.0);
32+
assertType('false', 'foo' <= 1.0);
33+
}
34+
}

0 commit comments

Comments
 (0)