From 8c13aea4e5054f7958febff5913f3b8599db27d8 Mon Sep 17 00:00:00 2001 From: Jean-Luc Herren Date: Tue, 13 Oct 2020 22:50:47 +0200 Subject: [PATCH] Fix intersection of iterable and array --- src/Type/IterableType.php | 5 +++ src/Type/TypeCombinator.php | 17 +++++++++ tests/PHPStan/Type/TypeCombinatorTest.php | 44 +++++++++++++++++++++-- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/Type/IterableType.php b/src/Type/IterableType.php index 3a902ba09e..45cf284c58 100644 --- a/src/Type/IterableType.php +++ b/src/Type/IterableType.php @@ -34,6 +34,11 @@ public function __construct( $this->itemType = $itemType; } + public function getKeyType(): Type + { + return $this->keyType; + } + public function getItemType(): Type { return $this->itemType; diff --git a/src/Type/TypeCombinator.php b/src/Type/TypeCombinator.php index 7c330fee06..9a84fc2978 100644 --- a/src/Type/TypeCombinator.php +++ b/src/Type/TypeCombinator.php @@ -738,11 +738,28 @@ public static function intersect(Type ...$types): Type if ($types[$i] instanceof ConstantArrayType && $types[$j] instanceof HasOffsetType) { $types[$i] = $types[$i]->makeOffsetRequired($types[$j]->getOffsetType()); array_splice($types, $j--, 1); + continue; } if ($types[$j] instanceof ConstantArrayType && $types[$i] instanceof HasOffsetType) { $types[$j] = $types[$j]->makeOffsetRequired($types[$i]->getOffsetType()); array_splice($types, $i--, 1); + continue 2; + } + + if ( + ($types[$i] instanceof ArrayType || $types[$i] instanceof IterableType) && + ($types[$j] instanceof ArrayType || $types[$j] instanceof IterableType) + ) { + $keyType = self::intersect($types[$i]->getKeyType(), $types[$j]->getKeyType()); + $itemType = self::intersect($types[$i]->getItemType(), $types[$j]->getItemType()); + if ($types[$i] instanceof IterableType && $types[$j] instanceof IterableType) { + $types[$j] = new IterableType($keyType, $itemType); + } else { + $types[$j] = new ArrayType($keyType, $itemType); + } + array_splice($types, $i--, 1); + continue 2; } continue; diff --git a/tests/PHPStan/Type/TypeCombinatorTest.php b/tests/PHPStan/Type/TypeCombinatorTest.php index 90b402791b..35aa806db8 100644 --- a/tests/PHPStan/Type/TypeCombinatorTest.php +++ b/tests/PHPStan/Type/TypeCombinatorTest.php @@ -2002,8 +2002,48 @@ public function dataIntersect(): array new ArrayType(new MixedType(), new MixedType()), new IterableType(new MixedType(), new StringType()), ], - IntersectionType::class, - 'array&iterable', // this is correct but 'array' would be better + ArrayType::class, + 'array', + ], + [ + [ + new ArrayType(new IntegerType(), new MixedType()), + new IterableType(new MixedType(), new StringType()), + ], + ArrayType::class, + 'array', + ], + [ + [ + new ArrayType(new IntegerType(), new MixedType()), + new IterableType(new StringType(), new MixedType()), + ], + NeverType::class, + '*NEVER*', + ], + [ + [ + new ArrayType(new MixedType(), new IntegerType()), + new IterableType(new MixedType(), new StringType()), + ], + NeverType::class, + '*NEVER*', + ], + [ + [ + new ArrayType(new IntegerType(), new MixedType()), + new ArrayType(new MixedType(), new StringType()), + ], + ArrayType::class, + 'array', + ], + [ + [ + new IterableType(new IntegerType(), new MixedType()), + new IterableType(new MixedType(), new StringType()), + ], + IterableType::class, + 'iterable', ], [ [