diff --git a/src/Type/ArrayType.php b/src/Type/ArrayType.php index 72d21e2c3e..ce5a953a2e 100644 --- a/src/Type/ArrayType.php +++ b/src/Type/ArrayType.php @@ -322,24 +322,30 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni $offsetType = $offsetType->toArrayKey(); } - if ( - ($offsetType instanceof ConstantStringType || $offsetType instanceof ConstantIntegerType) - && $offsetType->isSuperTypeOf($this->keyType)->yes() - ) { - $builder = ConstantArrayTypeBuilder::createEmpty(); - $builder->setOffsetValueType($offsetType, $valueType); - return $builder->getArray(); - } + if ($offsetType instanceof ConstantStringType || $offsetType instanceof ConstantIntegerType) { + if ($offsetType->isSuperTypeOf($this->keyType)->yes()) { + $builder = ConstantArrayTypeBuilder::createEmpty(); + $builder->setOffsetValueType($offsetType, $valueType); + return $builder->getArray(); + } - $array = new self( - TypeCombinator::union($this->keyType, $offsetType), - $unionValues ? TypeCombinator::union($this->itemType, $valueType) : $valueType, - ); - if ($offsetType instanceof ConstantIntegerType || $offsetType instanceof ConstantStringType) { - return TypeCombinator::intersect($array, new HasOffsetValueType($offsetType, $valueType), new NonEmptyArrayType()); + return TypeCombinator::intersect( + new self( + TypeCombinator::union($this->keyType, $offsetType), + TypeCombinator::union($this->itemType, $valueType), + ), + new HasOffsetValueType($offsetType, $valueType), + new NonEmptyArrayType(), + ); } - return TypeCombinator::intersect($array, new NonEmptyArrayType()); + return TypeCombinator::intersect( + new self( + TypeCombinator::union($this->keyType, $offsetType), + $unionValues ? TypeCombinator::union($this->itemType, $valueType) : $valueType, + ), + new NonEmptyArrayType(), + ); } public function unsetOffset(Type $offsetType): Type diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 1c49aef0e7..dab3ba5e3a 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -1084,6 +1084,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-8174.php'); yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/bug-8169.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7519.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8087.php'); } /** diff --git a/tests/PHPStan/Analyser/data/bug-8087.php b/tests/PHPStan/Analyser/data/bug-8087.php new file mode 100644 index 0000000000..1dc74349ea --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-8087.php @@ -0,0 +1,27 @@ + $data + **/ + public function sayHello(array $data): bool + { + \PHPStan\dumpType($data); + assertType('array', $data); + + $data['uses'] = ['']; + + assertType("non-empty-array&hasOffsetValue('uses', array{''})", $data); + + $data['uses'][] = ''; + + assertType("non-empty-array&hasOffsetValue('uses', array{'', ''})", $data); + + return count($data['foo']) > 0; + } +}