Skip to content

Commit c9ae89c

Browse files
committed
Handle ConstantArrayType == ConstantArrayType
1 parent 6e02453 commit c9ae89c

File tree

3 files changed

+81
-57
lines changed

3 files changed

+81
-57
lines changed

src/Analyser/MutatingScope.php

Lines changed: 73 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -628,18 +628,7 @@ private function resolveType(Expr $node): Type
628628
$leftType = $this->getType($node->left);
629629
$rightType = $this->getType($node->right);
630630

631-
$stringType = new StringType();
632-
$integerType = new IntegerType();
633-
$floatType = new FloatType();
634-
if (
635-
($stringType->isSuperTypeOf($leftType)->yes() && $stringType->isSuperTypeOf($rightType)->yes())
636-
|| ($integerType->isSuperTypeOf($leftType)->yes() && $integerType->isSuperTypeOf($rightType)->yes())
637-
|| ($floatType->isSuperTypeOf($leftType)->yes() && $floatType->isSuperTypeOf($rightType)->yes())
638-
) {
639-
return $this->getType(new Expr\BinaryOp\Identical($node->left, $node->right));
640-
}
641-
642-
return new BooleanType();
631+
return $this->resolveEqualType($leftType, $rightType);
643632
}
644633

645634
if ($node instanceof Expr\BinaryOp\NotEqual) {
@@ -2335,7 +2324,7 @@ private function resolveType(Expr $node): Type
23352324
return new MixedType();
23362325
}
23372326

2338-
private function resolveIdenticalType(Type $leftType, Type $rightType): Type
2327+
private function resolveIdenticalType(Type $leftType, Type $rightType): BooleanType
23392328
{
23402329
$isSuperset = $leftType->isSuperTypeOf($rightType);
23412330
if ($isSuperset->no()) {
@@ -2350,65 +2339,93 @@ private function resolveIdenticalType(Type $leftType, Type $rightType): Type
23502339
}
23512340

23522341
if ($leftType instanceof ConstantArrayType && $rightType instanceof ConstantArrayType) {
2353-
$leftValueTypes = $leftType->getValueTypes();
2354-
$rightKeyTypes = $rightType->getKeyTypes();
2355-
$rightValueTypes = $rightType->getValueTypes();
2356-
$hasOptional = false;
2357-
foreach ($leftType->getKeyTypes() as $i => $keyType) {
2358-
if (!array_key_exists($i, $rightKeyTypes)) {
2359-
if ($leftType->isOptionalKey($i)) {
2360-
$hasOptional = true;
2361-
continue;
2362-
}
2363-
return new ConstantBooleanType(false);
2364-
}
2342+
return $this->resolveConstantArrayTypeComparison($leftType, $rightType, fn ($leftValueType, $rightValueType): BooleanType => $this->resolveIdenticalType($leftValueType, $rightValueType));
2343+
}
23652344

2366-
$rightKeyType = $rightKeyTypes[$i];
2367-
if (!$keyType->equals($rightKeyType)) {
2368-
return new ConstantBooleanType(false);
2369-
}
2345+
return new BooleanType();
2346+
}
23702347

2371-
$leftValueType = $leftValueTypes[$i];
2372-
$rightValueType = $rightValueTypes[$i];
2373-
$leftIdenticalToRight = $this->resolveIdenticalType($leftValueType, $rightValueType);
2374-
if ($leftIdenticalToRight instanceof ConstantBooleanType) {
2375-
if (!$leftIdenticalToRight->getValue()) {
2376-
return new ConstantBooleanType(false);
2377-
}
2378-
$isLeftOptional = $leftType->isOptionalKey($i);
2379-
if ($isLeftOptional || $rightType->isOptionalKey($i)) {
2380-
$hasOptional = true;
2381-
}
2382-
continue;
2383-
}
2348+
private function resolveEqualType(Type $leftType, Type $rightType): BooleanType
2349+
{
2350+
$stringType = new StringType();
2351+
$integerType = new IntegerType();
2352+
$floatType = new FloatType();
2353+
if (
2354+
($stringType->isSuperTypeOf($leftType)->yes() && $stringType->isSuperTypeOf($rightType)->yes())
2355+
|| ($integerType->isSuperTypeOf($leftType)->yes() && $integerType->isSuperTypeOf($rightType)->yes())
2356+
|| ($floatType->isSuperTypeOf($leftType)->yes() && $floatType->isSuperTypeOf($rightType)->yes())
2357+
) {
2358+
return $this->resolveIdenticalType($leftType, $rightType);
2359+
}
23842360

2385-
$hasOptional = true;
2386-
}
2361+
if ($leftType instanceof ConstantArrayType && $rightType instanceof ConstantArrayType) {
2362+
return $this->resolveConstantArrayTypeComparison($leftType, $rightType, fn ($leftValueType, $rightValueType): BooleanType => $this->resolveEqualType($leftValueType, $rightValueType));
2363+
}
23872364

2388-
if (!isset($i)) {
2389-
$i = 0;
2390-
} else {
2391-
$i++;
2392-
}
2365+
return new BooleanType();
2366+
}
23932367

2394-
$rightKeyTypesCount = count($rightKeyTypes);
2395-
for (; $i < $rightKeyTypesCount; $i++) {
2396-
if ($rightType->isOptionalKey($i)) {
2368+
/**
2369+
* @param callable(Type, Type): BooleanType $valueComparisonCallback
2370+
*/
2371+
private function resolveConstantArrayTypeComparison(ConstantArrayType $leftType, ConstantArrayType $rightType, callable $valueComparisonCallback): BooleanType
2372+
{
2373+
$leftValueTypes = $leftType->getValueTypes();
2374+
$rightKeyTypes = $rightType->getKeyTypes();
2375+
$rightValueTypes = $rightType->getValueTypes();
2376+
$hasOptional = false;
2377+
foreach ($leftType->getKeyTypes() as $i => $keyType) {
2378+
if (!array_key_exists($i, $rightKeyTypes)) {
2379+
if ($leftType->isOptionalKey($i)) {
23972380
$hasOptional = true;
23982381
continue;
23992382
}
2383+
return new ConstantBooleanType(false);
2384+
}
24002385

2386+
$rightKeyType = $rightKeyTypes[$i];
2387+
if (!$keyType->equals($rightKeyType)) {
24012388
return new ConstantBooleanType(false);
24022389
}
24032390

2404-
if ($hasOptional) {
2405-
return new BooleanType();
2391+
$leftValueType = $leftValueTypes[$i];
2392+
$rightValueType = $rightValueTypes[$i];
2393+
$leftIdenticalToRight = $valueComparisonCallback($leftValueType, $rightValueType);
2394+
if ($leftIdenticalToRight instanceof ConstantBooleanType) {
2395+
if (!$leftIdenticalToRight->getValue()) {
2396+
return new ConstantBooleanType(false);
2397+
}
2398+
$isLeftOptional = $leftType->isOptionalKey($i);
2399+
if ($isLeftOptional || $rightType->isOptionalKey($i)) {
2400+
$hasOptional = true;
2401+
}
2402+
continue;
24062403
}
24072404

2408-
return new ConstantBooleanType(true);
2405+
$hasOptional = true;
24092406
}
24102407

2411-
return new BooleanType();
2408+
if (!isset($i)) {
2409+
$i = 0;
2410+
} else {
2411+
$i++;
2412+
}
2413+
2414+
$rightKeyTypesCount = count($rightKeyTypes);
2415+
for (; $i < $rightKeyTypesCount; $i++) {
2416+
if ($rightType->isOptionalKey($i)) {
2417+
$hasOptional = true;
2418+
continue;
2419+
}
2420+
2421+
return new ConstantBooleanType(false);
2422+
}
2423+
2424+
if ($hasOptional) {
2425+
return new BooleanType();
2426+
}
2427+
2428+
return new ConstantBooleanType(true);
24122429
}
24132430

24142431
private function resolveConcatType(Expr\BinaryOp\Concat|Expr\AssignOp\Concat $node): Type

tests/PHPStan/Analyser/data/bug-6940.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Bug6940
1010
public function foo(): void
1111
{
1212
$b = [] == [];
13-
assertType('bool', $b);
13+
assertType('true', $b);
1414
}
1515

1616
}

tests/PHPStan/Analyser/data/constant-array-type-identical.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ class Foo
1111
public function doFoo(string $s): void
1212
{
1313
assertType('true', [1] === [1]);
14+
assertType('true', [1] == [1]);
15+
assertType('false', [1] != [1]);
16+
assertType('false', [1] == [2]);
17+
assertType('true', [1] != [2]);
18+
assertType('bool', [1] == ["1"]);
19+
assertType('bool', [1] != ["1"]);
20+
1421
assertType('false', [1] === [2]);
1522
assertType('false', [1] !== [1]);
1623
assertType('true', [1] !== [2]);

0 commit comments

Comments
 (0)