From b55208ea6cc5b9d5e86805fb65e5e0dc10571935 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sun, 3 May 2020 14:06:53 +0200 Subject: [PATCH] Fixed ConstantArrayType::isSuperTypeOf() --- src/Type/Constant/ConstantArrayType.php | 17 +++++++--- ...rictComparisonOfDifferentTypesRuleTest.php | 8 ----- .../Rules/Methods/data/method-signature.php | 24 ++++++++++++++ .../Type/Constant/ConstantArrayTypeTest.php | 32 +++++++++++++++++++ 4 files changed, 68 insertions(+), 13 deletions(-) diff --git a/src/Type/Constant/ConstantArrayType.php b/src/Type/Constant/ConstantArrayType.php index 21ef15b5fc..b8a384474a 100644 --- a/src/Type/Constant/ConstantArrayType.php +++ b/src/Type/Constant/ConstantArrayType.php @@ -121,14 +121,21 @@ public function accepts(Type $type, bool $strictTypes): TrinaryLogic public function isSuperTypeOf(Type $type): TrinaryLogic { if ($type instanceof self) { - if (count($this->keyTypes) !== count($type->keyTypes)) { - return TrinaryLogic::createNo(); + if (count($this->keyTypes) === 0) { + if (count($type->keyTypes) > 0) { + return TrinaryLogic::createNo(); + } + + return TrinaryLogic::createYes(); } $results = []; - foreach (array_keys($this->keyTypes) as $i) { - $results[] = $this->keyTypes[$i]->isSuperTypeOf($type->keyTypes[$i]); - $results[] = $this->valueTypes[$i]->isSuperTypeOf($type->valueTypes[$i]); + foreach ($this->keyTypes as $i => $keyType) { + $hasOffset = $type->hasOffsetValueType($keyType); + if ($hasOffset->no()) { + return TrinaryLogic::createNo(); + } + $results[] = $this->valueTypes[$i]->isSuperTypeOf($type->getOffsetValueType($keyType)); } return TrinaryLogic::createYes()->and(...$results); diff --git a/tests/PHPStan/Rules/Comparison/StrictComparisonOfDifferentTypesRuleTest.php b/tests/PHPStan/Rules/Comparison/StrictComparisonOfDifferentTypesRuleTest.php index a11b4f6cfa..eee5e88e78 100644 --- a/tests/PHPStan/Rules/Comparison/StrictComparisonOfDifferentTypesRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/StrictComparisonOfDifferentTypesRuleTest.php @@ -118,10 +118,6 @@ public function testStrictComparison(): void 'Strict comparison using === between array(\'X\' => 1, \'Y\' => 2) and array(\'X\' => 2, \'Y\' => 1) will always evaluate to false.', 300, ], - [ - 'Strict comparison using === between array(\'X\' => 1, \'Y\' => 2) and array(\'Y\' => 2, \'X\' => 1) will always evaluate to false.', - 308, - ], [ 'Strict comparison using === between \'/\'|\'\\\\\' and \'//\' will always evaluate to false.', 320, @@ -300,10 +296,6 @@ public function testStrictComparisonWithoutAlwaysTrue(): void 'Strict comparison using === between array(\'X\' => 1, \'Y\' => 2) and array(\'X\' => 2, \'Y\' => 1) will always evaluate to false.', 300, ], - [ - 'Strict comparison using === between array(\'X\' => 1, \'Y\' => 2) and array(\'Y\' => 2, \'X\' => 1) will always evaluate to false.', - 308, - ], [ 'Strict comparison using === between \'/\'|\'\\\\\' and \'//\' will always evaluate to false.', 320, diff --git a/tests/PHPStan/Rules/Methods/data/method-signature.php b/tests/PHPStan/Rules/Methods/data/method-signature.php index 63bc5ca05c..c917073882 100644 --- a/tests/PHPStan/Rules/Methods/data/method-signature.php +++ b/tests/PHPStan/Rules/Methods/data/method-signature.php @@ -457,3 +457,27 @@ public function processNode(\PhpParser\Node $node): void } } + +interface ConstantArrayInterface +{ + + /** + * @return array{foo: string} + */ + public function foobar(): array; + +} + +class ConstantArrayClass implements ConstantArrayInterface +{ + /** + * @return array{foo: string, bar: string} + */ + public function foobar(): array + { + return [ + 'foo' => '', + 'bar' => '', + ]; + } +} diff --git a/tests/PHPStan/Type/Constant/ConstantArrayTypeTest.php b/tests/PHPStan/Type/Constant/ConstantArrayTypeTest.php index 0bade9f230..427510a58a 100644 --- a/tests/PHPStan/Type/Constant/ConstantArrayTypeTest.php +++ b/tests/PHPStan/Type/Constant/ConstantArrayTypeTest.php @@ -188,6 +188,38 @@ public function dataIsSuperTypeOf(): iterable new IterableType(new MixedType(false), new MixedType(true)), TrinaryLogic::createMaybe(), ]; + + yield [ + new ConstantArrayType([ + new ConstantStringType('foo'), + ], [ + new IntegerType(), + ]), + new ConstantArrayType([ + new ConstantStringType('foo'), + new ConstantStringType('bar'), + ], [ + new IntegerType(), + new IntegerType(), + ]), + TrinaryLogic::createYes(), + ]; + + yield [ + new ConstantArrayType([ + new ConstantStringType('foo'), + new ConstantStringType('bar'), + ], [ + new IntegerType(), + new IntegerType(), + ]), + new ConstantArrayType([ + new ConstantStringType('foo'), + ], [ + new IntegerType(), + ]), + TrinaryLogic::createNo(), + ]; } /**