From 8fb3b077fc8d408035028cafafc9e53988485778 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sat, 9 Aug 2025 12:49:39 +0200 Subject: [PATCH 1/2] Resolve array~list --- src/Type/ArrayType.php | 4 + .../nsrt/array-is-list-type-specifying.php | 4 +- .../PHPStan/Analyser/nsrt/assert-docblock.php | 8 +- tests/PHPStan/Analyser/nsrt/bug-9734.php | 89 ++++++++++++++++++- 4 files changed, 98 insertions(+), 7 deletions(-) diff --git a/src/Type/ArrayType.php b/src/Type/ArrayType.php index 80cfc6b09f..ffd213eb93 100644 --- a/src/Type/ArrayType.php +++ b/src/Type/ArrayType.php @@ -609,6 +609,10 @@ public function tryRemove(Type $typeToRemove): ?Type return TypeCombinator::intersect($this, new NonEmptyArrayType()); } + if ($typeToRemove->isSuperTypeOf(new ConstantArrayType([], []))->yes()) { + return TypeCombinator::intersect($this, new NonEmptyArrayType()); + } + if ($typeToRemove instanceof NonEmptyArrayType) { return new ConstantArrayType([], []); } diff --git a/tests/PHPStan/Analyser/nsrt/array-is-list-type-specifying.php b/tests/PHPStan/Analyser/nsrt/array-is-list-type-specifying.php index 1b788158d6..d4e1c621d4 100644 --- a/tests/PHPStan/Analyser/nsrt/array-is-list-type-specifying.php +++ b/tests/PHPStan/Analyser/nsrt/array-is-list-type-specifying.php @@ -8,7 +8,7 @@ function foo(array $foo) { if (array_is_list($foo)) { assertType('list', $foo); } else { - assertType('array', $foo); + assertType('non-empty-array', $foo); } } @@ -51,7 +51,7 @@ function foo4(array $foo) { if (array_is_list($foo)) { assertType('list', $foo); } else { - assertType('array', $foo); + assertType('non-empty-array', $foo); } $baz = []; diff --git a/tests/PHPStan/Analyser/nsrt/assert-docblock.php b/tests/PHPStan/Analyser/nsrt/assert-docblock.php index a1cbcc9b3e..459b925ca3 100644 --- a/tests/PHPStan/Analyser/nsrt/assert-docblock.php +++ b/tests/PHPStan/Analyser/nsrt/assert-docblock.php @@ -71,7 +71,7 @@ function takesArrayIfTrue(array $arr) : void { if (validateStringArrayIfTrue($arr)) { assertType('array', $arr); } else { - assertType('array', $arr); + assertType('non-empty-array', $arr); } } /** @@ -81,7 +81,7 @@ function takesArrayIfTrue1(array $arr) : void { assertType('array', $arr); if (!validateStringArrayIfTrue($arr)) { - assertType('array', $arr); + assertType('non-empty-array', $arr); } else { assertType('array', $arr); } @@ -96,7 +96,7 @@ function takesArrayIfFalse(array $arr) : void { if (!validateStringArrayIfFalse($arr)) { assertType('array', $arr); } else { - assertType('array', $arr); + assertType('non-empty-array', $arr); } } @@ -107,7 +107,7 @@ function takesArrayIfFalse1(array $arr) : void { assertType('array', $arr); if (validateStringArrayIfFalse($arr)) { - assertType('array', $arr); + assertType('non-empty-array', $arr); } else { assertType('array', $arr); } diff --git a/tests/PHPStan/Analyser/nsrt/bug-9734.php b/tests/PHPStan/Analyser/nsrt/bug-9734.php index 1628ad6859..049fc3c51b 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-9734.php +++ b/tests/PHPStan/Analyser/nsrt/bug-9734.php @@ -17,7 +17,7 @@ public function doFoo(array $a): void if (array_is_list($a)) { assertType('list', $a); } else { - assertType('array', $a); // could be non-empty-array + assertType('non-empty-array', $a); } } @@ -45,3 +45,90 @@ public function doFoo3(array $a): void } } + +class Bar +{ + /** + * @param array $value + * @return ($value is non-empty-list ? true : false) + */ + public function assertIsNonEmptyList($value): bool + { + return false; + } + + /** + * @param array $value + * @return ($value is list ? true : false) + */ + public function assertIsStringList($value): bool + { + return false; + } + + /** + * @param array $value + * @return ($value is list{string, string} ? true : false) + */ + public function assertIsConstantList($value): bool + { + return false; + } + + /** + * @param array $value + * @return ($value is list{0?: string, 1?: string} ? true : false) + */ + public function assertIsOptionalConstantList($value): bool + { + return false; + } + + /** + * @param array $value + * @return ($value is array ? true : false) + */ + public function assertIsStringArray($value): bool + { + return false; + } + + /** + * @param array $a + * @return void + */ + public function doFoo(array $a): void + { + if ($this->assertIsNonEmptyList($a)) { + assertType('non-empty-list', $a); + } else { + assertType('array', $a); + } + + if ($this->assertIsStringList($a)) { + assertType('list', $a); + } else { + assertType('non-empty-array', $a); + } + + if ($this->assertIsConstantList($a)) { + assertType('array{string, string}', $a); + } else { + assertType('array', $a); + } + + if ($this->assertIsOptionalConstantList($a)) { + assertType('list{0?: string, 1?: string}', $a); + } else { + assertType('non-empty-array', $a); + } + + if ($this->assertIsStringArray($a)) { + assertType('array', $a); + } else { + assertType('non-empty-array', $a); + } + } + + +} From 731fb7c2e06fd6d769afde510cf4cc6b433e1974 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sat, 9 Aug 2025 14:53:43 +0200 Subject: [PATCH 2/2] Add test --- tests/PHPStan/Analyser/nsrt/bug-9734.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/PHPStan/Analyser/nsrt/bug-9734.php b/tests/PHPStan/Analyser/nsrt/bug-9734.php index 049fc3c51b..52c3dd1c20 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-9734.php +++ b/tests/PHPStan/Analyser/nsrt/bug-9734.php @@ -44,6 +44,19 @@ public function doFoo3(array $a): void } } + /** + * @param mixed $a + * @return void + */ + public function doFoo4($a): void + { + if (array_is_list($a)) { + assertType('list', $a); + } else { + assertType('mixed~list', $a); + } + } + } class Bar