From b7973e47ccb929f3d6ddeac53973b07ea1653d62 Mon Sep 17 00:00:00 2001 From: Richard van Velzen Date: Mon, 26 Sep 2022 10:42:36 +0200 Subject: [PATCH] Improve unsetting constant offsets on array --- src/Type/ArrayType.php | 14 ++++++++++++++ tests/PHPStan/Analyser/NodeScopeResolverTest.php | 1 + .../PHPStan/Analyser/data/array-offset-unset.php | 16 ++++++++++++++++ tests/PHPStan/Analyser/data/bug-2648.php | 2 +- tests/PHPStan/Analyser/data/bug-4016.php | 4 ++-- tests/PHPStan/Analyser/data/bug-6399.php | 2 +- .../PHPStan/Analyser/data/composer-array-bug.php | 2 +- tests/PHPStan/Analyser/data/list-type.php | 2 +- tests/PHPStan/Rules/Variables/data/bug-3391.php | 2 +- 9 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 tests/PHPStan/Analyser/data/array-offset-unset.php diff --git a/src/Type/ArrayType.php b/src/Type/ArrayType.php index 17ff1f3dd6..dd408dd9e3 100644 --- a/src/Type/ArrayType.php +++ b/src/Type/ArrayType.php @@ -297,6 +297,20 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni public function unsetOffset(Type $offsetType): Type { + $offsetType = self::castToArrayKeyType($offsetType); + + if ( + ($offsetType instanceof ConstantIntegerType || $offsetType instanceof ConstantStringType) + && !$this->keyType->isSuperTypeOf($offsetType)->no() + ) { + $keyType = TypeCombinator::remove($this->keyType, $offsetType); + if ($keyType instanceof NeverType) { + return new ConstantArrayType([], []); + } + + return new self($keyType, $this->itemType); + } + return $this; } diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index c706e719f9..c41dab704e 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -1043,6 +1043,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8017.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8004.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/global-namespace.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/array-offset-unset.php'); } /** diff --git a/tests/PHPStan/Analyser/data/array-offset-unset.php b/tests/PHPStan/Analyser/data/array-offset-unset.php new file mode 100644 index 0000000000..0c9395d6ce --- /dev/null +++ b/tests/PHPStan/Analyser/data/array-offset-unset.php @@ -0,0 +1,16 @@ + $list + */ +function foo(array $list) { + assertType('array<0|1, mixed>', $list); + unset($list[0]); + assertType('array<1, mixed>', $list); + unset($list[1]); + assertType('array{}', $list); +} diff --git a/tests/PHPStan/Analyser/data/bug-2648.php b/tests/PHPStan/Analyser/data/bug-2648.php index 11087dfbaa..9acaa05026 100644 --- a/tests/PHPStan/Analyser/data/bug-2648.php +++ b/tests/PHPStan/Analyser/data/bug-2648.php @@ -15,7 +15,7 @@ public function doFoo(array $list): void if (count($list) > 1) { assertType('int<2, max>', count($list)); unset($list['fooo']); - assertType('array', $list); + assertType("array", $list); assertType('int<0, max>', count($list)); } } diff --git a/tests/PHPStan/Analyser/data/bug-4016.php b/tests/PHPStan/Analyser/data/bug-4016.php index 9a75c401fd..c6d67ad772 100644 --- a/tests/PHPStan/Analyser/data/bug-4016.php +++ b/tests/PHPStan/Analyser/data/bug-4016.php @@ -17,7 +17,7 @@ public function doFoo(array $a): void assertType('non-empty-array', $a); unset($a[0]); - assertType('array', $a); + assertType('array|int<1, max>, int>', $a); } /** @@ -30,7 +30,7 @@ public function doBar(array $a): void assertType('non-empty-array&hasOffsetValue(1, 2)', $a); unset($a[1]); - assertType('array', $a); + assertType('array|int<2, max>, int>', $a); } } diff --git a/tests/PHPStan/Analyser/data/bug-6399.php b/tests/PHPStan/Analyser/data/bug-6399.php index 16a0ae0002..50de3ae3f1 100644 --- a/tests/PHPStan/Analyser/data/bug-6399.php +++ b/tests/PHPStan/Analyser/data/bug-6399.php @@ -54,7 +54,7 @@ public function doBar(array $a): void { assertType('non-empty-array', $a); unset($a[1]); - assertType('array', $a); + assertType('array', $a); } } diff --git a/tests/PHPStan/Analyser/data/composer-array-bug.php b/tests/PHPStan/Analyser/data/composer-array-bug.php index 9d470d568f..42733805c3 100644 --- a/tests/PHPStan/Analyser/data/composer-array-bug.php +++ b/tests/PHPStan/Analyser/data/composer-array-bug.php @@ -49,7 +49,7 @@ public function doFoo(): void if (empty($this->config['authors'])) { unset($this->config['authors']); - assertType("array", $this->config); + assertType("array", $this->config); } else { assertType("array&hasOffsetValue('authors', mixed~0|0.0|''|'0'|array{}|false|null)", $this->config); } diff --git a/tests/PHPStan/Analyser/data/list-type.php b/tests/PHPStan/Analyser/data/list-type.php index c832ec7c5d..2eb24e2087 100644 --- a/tests/PHPStan/Analyser/data/list-type.php +++ b/tests/PHPStan/Analyser/data/list-type.php @@ -86,7 +86,7 @@ public function withFullListFunctionality(): void $list[] = '1'; $list[] = '2'; unset($list[0]);//break list behaviour - assertType('array', $list); + assertType('array|int<1, max>, mixed>', $list); /** @var list $list2 */ $list2 = []; diff --git a/tests/PHPStan/Rules/Variables/data/bug-3391.php b/tests/PHPStan/Rules/Variables/data/bug-3391.php index ae8f8e8b03..bfe756a020 100644 --- a/tests/PHPStan/Rules/Variables/data/bug-3391.php +++ b/tests/PHPStan/Rules/Variables/data/bug-3391.php @@ -26,7 +26,7 @@ public function test() unset($data['id']); - assertType("array&hasOffsetValue('bar', 'b')&hasOffsetValue('foo', 'a')", $data); + assertType("array&hasOffsetValue('bar', 'b')&hasOffsetValue('foo', 'a')", $data); return $data; } }