Skip to content

Commit 4857b6a

Browse files
Fix InvalidComparisonOperationRule
1 parent e0c4844 commit 4857b6a

File tree

3 files changed

+74
-39
lines changed

3 files changed

+74
-39
lines changed

src/Rules/Operators/InvalidComparisonOperationRule.php

Lines changed: 14 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@
1212
use PHPStan\Rules\RuleErrorBuilder;
1313
use PHPStan\Rules\RuleLevelHelper;
1414
use PHPStan\ShouldNotHappenException;
15-
use PHPStan\Type\BenevolentUnionType;
15+
use PHPStan\Type\ArrayType;
1616
use PHPStan\Type\ErrorType;
1717
use PHPStan\Type\FloatType;
1818
use PHPStan\Type\IntegerType;
19+
use PHPStan\Type\MixedType;
20+
use PHPStan\Type\NullType;
1921
use PHPStan\Type\ObjectWithoutClassType;
2022
use PHPStan\Type\Type;
21-
use PHPStan\Type\TypeCombinator;
2223
use PHPStan\Type\UnionType;
2324
use PHPStan\Type\VerbosityLevel;
2425
use function get_class;
@@ -59,7 +60,9 @@ public function processNode(Node $node, Scope $scope): array
5960
return [];
6061
}
6162

62-
if ($this->isNumberType($scope, $node->left) && $this->isNumberType($scope, $node->right)) {
63+
$isLeftNumberType = $this->isNumberType($scope, $node->left);
64+
$isRightNumberType = $this->isNumberType($scope, $node->right);
65+
if (($isLeftNumberType && $isRightNumberType) || (!$isLeftNumberType && !$isRightNumberType)) {
6366
return [];
6467
}
6568

@@ -80,10 +83,10 @@ public function processNode(Node $node, Scope $scope): array
8083
}
8184

8285
if (
83-
($this->isNumberType($scope, $node->left) && (
86+
($isLeftNumberType && (
8487
$this->isPossiblyNullableObjectType($scope, $node->right) || $this->isPossiblyNullableArrayType($scope, $node->right)
8588
))
86-
|| ($this->isNumberType($scope, $node->right) && (
89+
|| ($isRightNumberType && (
8790
$this->isPossiblyNullableObjectType($scope, $node->left) || $this->isPossiblyNullableArrayType($scope, $node->left)
8891
))
8992
) {
@@ -113,45 +116,18 @@ private function isNumberType(Scope $scope, Node\Expr $expr): bool
113116

114117
private function isPossiblyNullableObjectType(Scope $scope, Node\Expr $expr): bool
115118
{
116-
$acceptedType = new ObjectWithoutClassType();
119+
$type = $scope->getType($expr);
120+
$acceptedType = new UnionType([new ObjectWithoutClassType(), new NullType()]);
117121

118-
$type = $this->ruleLevelHelper->findTypeToCheck(
119-
$scope,
120-
$expr,
121-
'',
122-
static fn (Type $type): bool => $acceptedType->isSuperTypeOf($type)->yes(),
123-
)->getType();
124-
125-
if ($type instanceof ErrorType) {
126-
return false;
127-
}
128-
129-
if (TypeCombinator::containsNull($type) && !$type->isNull()->yes()) {
130-
$type = TypeCombinator::removeNull($type);
131-
}
132-
133-
$isSuperType = $acceptedType->isSuperTypeOf($type);
134-
if ($type instanceof BenevolentUnionType) {
135-
return !$isSuperType->no();
136-
}
137-
138-
return $isSuperType->yes();
122+
return !$type->isNull()->yes() && $acceptedType->isSuperTypeOf($type)->yes();
139123
}
140124

141125
private function isPossiblyNullableArrayType(Scope $scope, Node\Expr $expr): bool
142126
{
143-
$type = $this->ruleLevelHelper->findTypeToCheck(
144-
$scope,
145-
$expr,
146-
'',
147-
static fn (Type $type): bool => $type->isArray()->yes(),
148-
)->getType();
149-
150-
if (TypeCombinator::containsNull($type) && !$type->isNull()->yes()) {
151-
$type = TypeCombinator::removeNull($type);
152-
}
127+
$type = $scope->getType($expr);
128+
$acceptedType = new UnionType([new ArrayType(new MixedType(), new MixedType()), new NullType()]);
153129

154-
return !($type instanceof ErrorType) && $type->isArray()->yes();
130+
return !$type->isNull()->yes() && $acceptedType->isSuperTypeOf($type)->yes();
155131
}
156132

157133
/** @return list<IdentifierRuleError> */

tests/PHPStan/Rules/Operators/InvalidComparisonOperationRuleTest.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
class InvalidComparisonOperationRuleTest extends RuleTestCase
1515
{
1616

17+
private bool $checkUnion = true;
18+
1719
protected function getRule(): Rule
1820
{
1921
return new InvalidComparisonOperationRule(
20-
new RuleLevelHelper(self::createReflectionProvider(), true, false, true, false, false, false, true),
22+
new RuleLevelHelper(self::createReflectionProvider(), true, false, $this->checkUnion, false, false, false, true),
2123
$this->getContainer()->getByType(OperatorTypeSpecifyingExtensionRegistryProvider::class),
2224
true,
2325
);
@@ -168,6 +170,21 @@ public function testRuleWithNullsafeVariant(): void
168170
]);
169171
}
170172

173+
public function testBug3364(): void
174+
{
175+
$this->checkUnion = false;
176+
$this->analyse([__DIR__ . '/data/bug-3364.php'], [
177+
[
178+
'Comparison operation "!=" between array<int|string>|null and 1 results in an error.',
179+
18,
180+
],
181+
[
182+
'Comparison operation "!=" between object|null and 1 results in an error.',
183+
26,
184+
],
185+
]);
186+
}
187+
171188
public function testBug11119(): void
172189
{
173190
$this->analyse([__DIR__ . '/data/bug-11119.php'], []);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug3364;
4+
5+
class HelloWorld
6+
{
7+
/**
8+
* @param string|array<int|string>|null $value
9+
*/
10+
public function transform($value) {
11+
$value != 1;
12+
}
13+
14+
/**
15+
* @param array<int|string>|null $value
16+
*/
17+
public function transform2($value) {
18+
$value != 1;
19+
}
20+
21+
22+
/**
23+
* @param object|null $value
24+
*/
25+
public function transform3($value) {
26+
$value != 1;
27+
}
28+
29+
/**
30+
* @param array<int|string>|object $value
31+
*/
32+
public function transform4($value) {
33+
$value != 1;
34+
}
35+
36+
/**
37+
* @param null $value
38+
*/
39+
public function transform5($value) {
40+
$value != 1;
41+
}
42+
}

0 commit comments

Comments
 (0)