From 6bbca56b8067637dbb90c70e6d75ef1f8731a323 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 20 Aug 2021 13:16:41 +0200 Subject: [PATCH 1/6] (mb_)?str_split() always returns non-empty-array --- resources/functionMap.php | 2 +- resources/functionMap_php74delta.php | 2 +- resources/functionMap_php80delta.php | 4 +-- .../Analyser/LegacyNodeScopeResolverTest.php | 26 +++++++++---------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/resources/functionMap.php b/resources/functionMap.php index 704dc36f96..634830aac8 100644 --- a/resources/functionMap.php +++ b/resources/functionMap.php @@ -11696,7 +11696,7 @@ 'str_replace' => ['string|array', 'search'=>'string|array', 'replace'=>'string|array', 'subject'=>'string|array', '&w_replace_count='=>'int'], 'str_rot13' => ['string', 'str'=>'string'], 'str_shuffle' => ['string', 'str'=>'string'], -'str_split' => ['array|false', 'str'=>'string', 'split_length='=>'int'], +'str_split' => ['non-empty-array|false', 'str'=>'string', 'split_length='=>'int'], 'str_word_count' => ['array|int|false', 'string'=>'string', 'format='=>'int', 'charlist='=>'string'], 'strcasecmp' => ['int', 'str1'=>'string', 'str2'=>'string'], 'strchr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'before_needle='=>'bool'], diff --git a/resources/functionMap_php74delta.php b/resources/functionMap_php74delta.php index 73349639fe..73832288f7 100644 --- a/resources/functionMap_php74delta.php +++ b/resources/functionMap_php74delta.php @@ -39,7 +39,7 @@ 'FFI::typeof' => ['FFI\CType', '&ptr'=>'FFI\CData'], 'FFI::type' => ['FFI\CType', 'type'=>'string'], 'get_mangled_object_vars' => ['array', 'obj'=>'object'], - 'mb_str_split' => ['array|false', 'str'=>'string', 'split_length='=>'int', 'encoding='=>'string'], + 'mb_str_split' => ['non-empty-array|false', 'str'=>'string', 'split_length='=>'int', 'encoding='=>'string'], 'password_algos' => ['array'], 'password_hash' => ['string|false', 'password'=>'string', 'algo'=>'string|null', 'options='=>'array'], 'password_needs_rehash' => ['bool', 'hash'=>'string', 'algo'=>'string|null', 'options='=>'array'], diff --git a/resources/functionMap_php80delta.php b/resources/functionMap_php80delta.php index eaf5385734..f6a4efdd98 100644 --- a/resources/functionMap_php80delta.php +++ b/resources/functionMap_php80delta.php @@ -70,7 +70,7 @@ 'imagerotate' => ['false|object', 'src_im'=>'resource', 'angle'=>'float', 'bgdcolor'=>'int', 'ignoretransparent='=>'int'], 'imagescale' => ['false|object', 'im'=>'resource', 'new_width'=>'int', 'new_height='=>'int', 'method='=>'int'], 'mb_decode_numericentity' => ['string|false', 'string'=>'string', 'convmap'=>'array', 'encoding='=>'string'], - 'mb_str_split' => ['array', 'str'=>'string', 'split_length='=>'positive-int', 'encoding='=>'string'], + 'mb_str_split' => ['non-empty-array', 'str'=>'string', 'split_length='=>'positive-int', 'encoding='=>'string'], 'mktime' => ['int|false', 'hour'=>'int', 'minute='=>'int', 'second='=>'int', 'month='=>'int', 'day='=>'int', 'year='=>'int'], 'odbc_exec' => ['resource|false', 'connection_id'=>'resource', 'query'=>'string'], 'parse_str' => ['void', 'encoded_string'=>'string', '&w_result'=>'array'], @@ -84,7 +84,7 @@ 'socket_select' => ['int|false', '&rw_read'=>'Socket[]|null', '&rw_write'=>'Socket[]|null', '&rw_except'=>'Socket[]|null', 'seconds'=>'int|null', 'microseconds='=>'int'], 'sodium_crypto_aead_chacha20poly1305_ietf_decrypt' => ['string|false', 'confidential_message'=>'string', 'public_message'=>'string', 'nonce'=>'string', 'key'=>'string'], 'str_contains' => ['bool', 'haystack'=>'string', 'needle'=>'string'], - 'str_split' => ['array', 'str'=>'string', 'split_length='=>'positive-int'], + 'str_split' => ['non-empty-array', 'str'=>'string', 'split_length='=>'positive-int'], 'str_ends_with' => ['bool', 'haystack'=>'string', 'needle'=>'string'], 'str_starts_with' => ['bool', 'haystack'=>'string', 'needle'=>'string'], 'strchr' => ['string|false', 'haystack'=>'string', 'needle'=>'string', 'before_needle='=>'bool'], diff --git a/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php index af8dae7a3c..39573d7852 100644 --- a/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php @@ -5880,7 +5880,7 @@ public function dataFunctions(): array '$gettimeofdayBenevolent', ], [ - PHP_VERSION_ID < 80000 ? 'array|false' : 'array', + PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', '$strSplitConstantStringWithoutDefinedParameters', ], [ @@ -5904,7 +5904,7 @@ public function dataFunctions(): array '$strSplitConstantStringWithFailureSplitLength', ], [ - PHP_VERSION_ID < 80000 ? 'array|false' : 'array', + PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', '$strSplitConstantStringWithInvalidSplitLengthType', ], [ @@ -5912,7 +5912,7 @@ public function dataFunctions(): array '$strSplitConstantStringWithVariableStringAndConstantSplitLength', ], [ - PHP_VERSION_ID < 80000 ? 'array|false' : 'array', + PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', '$strSplitConstantStringWithVariableStringAndVariableSplitLength', ], // parse_url @@ -9674,7 +9674,7 @@ public function dataPhp74Functions(): array { return [ [ - PHP_VERSION_ID < 80000 ? 'array|false' : 'array', + PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithoutDefinedParameters', ], [ @@ -9698,7 +9698,7 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithFailureSplitLength', ], [ - PHP_VERSION_ID < 80000 ? 'array|false' : 'array', + PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithInvalidSplitLengthType', ], [ @@ -9706,7 +9706,7 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithVariableStringAndConstantSplitLength', ], [ - PHP_VERSION_ID < 80000 ? 'array|false' : 'array', + PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithVariableStringAndVariableSplitLength', ], [ @@ -9718,7 +9718,7 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithOneSplitLengthAndInvalidEncoding', ], [ - PHP_VERSION_ID < 80000 ? 'array|false' : 'array', + PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithOneSplitLengthAndVariableEncoding', ], [ @@ -9730,7 +9730,7 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithGreaterSplitLengthThanStringLengthAndInvalidEncoding', ], [ - PHP_VERSION_ID < 80000 ? 'array|false' : 'array', + PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithGreaterSplitLengthThanStringLengthAndVariableEncoding', ], [ @@ -9746,7 +9746,7 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithFailureSplitLengthAndVariableEncoding', ], [ - PHP_VERSION_ID < 80000 ? 'array|false' : 'array', + PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithInvalidSplitLengthTypeAndValidEncoding', ], [ @@ -9754,7 +9754,7 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithInvalidSplitLengthTypeAndInvalidEncoding', ], [ - PHP_VERSION_ID < 80000 ? 'array|false' : 'array', + PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithInvalidSplitLengthTypeAndVariableEncoding', ], [ @@ -9766,11 +9766,11 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithVariableStringAndConstantSplitLengthAndInvalidEncoding', ], [ - PHP_VERSION_ID < 80000 ? 'array|false' : 'array', + PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithVariableStringAndConstantSplitLengthAndVariableEncoding', ], [ - PHP_VERSION_ID < 80000 ? 'array|false' : 'array', + PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithVariableStringAndVariableSplitLengthAndValidEncoding', ], [ @@ -9778,7 +9778,7 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithVariableStringAndVariableSplitLengthAndInvalidEncoding', ], [ - PHP_VERSION_ID < 80000 ? 'array|false' : 'array', + PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithVariableStringAndVariableSplitLengthAndVariableEncoding', ], ]; From 7fefa4ea8b2db2965747bac544e5defd7ac44ce3 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 20 Aug 2021 13:28:38 +0200 Subject: [PATCH 2/6] fix tests --- tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php index 39573d7852..93b73f2aa9 100644 --- a/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php @@ -5880,7 +5880,7 @@ public function dataFunctions(): array '$gettimeofdayBenevolent', ], [ - PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', + PHP_VERSION_ID < 80000 ? '(array&nonEmpty)|false' : 'array&nonEmpty', '$strSplitConstantStringWithoutDefinedParameters', ], [ @@ -5904,7 +5904,7 @@ public function dataFunctions(): array '$strSplitConstantStringWithFailureSplitLength', ], [ - PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', + PHP_VERSION_ID < 80000 ? '(array&nonEmpty)|false' : 'array&nonEmpty', '$strSplitConstantStringWithInvalidSplitLengthType', ], [ @@ -5912,7 +5912,7 @@ public function dataFunctions(): array '$strSplitConstantStringWithVariableStringAndConstantSplitLength', ], [ - PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', + PHP_VERSION_ID < 80000 ? '(array&nonEmpty)|false' : 'array&nonEmpty', '$strSplitConstantStringWithVariableStringAndVariableSplitLength', ], // parse_url From dbfc9f7d4ed065618fe26fb6fe16d6d0fa28f06d Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 20 Aug 2021 13:34:21 +0200 Subject: [PATCH 3/6] remove dead code Call to function is_array() with array&nonEmpty will always evaluate to true. --- src/Type/Php/StrSplitFunctionReturnTypeExtension.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Type/Php/StrSplitFunctionReturnTypeExtension.php b/src/Type/Php/StrSplitFunctionReturnTypeExtension.php index 192fd5733c..df96648adb 100644 --- a/src/Type/Php/StrSplitFunctionReturnTypeExtension.php +++ b/src/Type/Php/StrSplitFunctionReturnTypeExtension.php @@ -101,9 +101,6 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, $items = isset($encoding) ? mb_str_split($stringValue, $splitLength, $encoding) : str_split($stringValue, $splitLength); - if (!is_array($items)) { - throw new \PHPStan\ShouldNotHappenException(); - } return self::createConstantArrayFrom($items, $scope); } From bc3c64f762c8d9305a8a6b9f956ea8d060d8b800 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 20 Aug 2021 13:46:08 +0200 Subject: [PATCH 4/6] adjust test expectations --- .../Analyser/LegacyNodeScopeResolverTest.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php index 93b73f2aa9..ef8ff856e2 100644 --- a/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php @@ -9674,7 +9674,7 @@ public function dataPhp74Functions(): array { return [ [ - PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', + PHP_VERSION_ID < 80000 ? '(array&nonEmpty)|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithoutDefinedParameters', ], [ @@ -9698,7 +9698,7 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithFailureSplitLength', ], [ - PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', + PHP_VERSION_ID < 80000 ? '(array&nonEmpty)|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithInvalidSplitLengthType', ], [ @@ -9706,7 +9706,7 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithVariableStringAndConstantSplitLength', ], [ - PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', + PHP_VERSION_ID < 80000 ? '(array&nonEmpty)|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithVariableStringAndVariableSplitLength', ], [ @@ -9718,7 +9718,7 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithOneSplitLengthAndInvalidEncoding', ], [ - PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', + PHP_VERSION_ID < 80000 ? '(array&nonEmpty)|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithOneSplitLengthAndVariableEncoding', ], [ @@ -9730,7 +9730,7 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithGreaterSplitLengthThanStringLengthAndInvalidEncoding', ], [ - PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', + PHP_VERSION_ID < 80000 ? '(array&nonEmpty)|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithGreaterSplitLengthThanStringLengthAndVariableEncoding', ], [ @@ -9746,7 +9746,7 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithFailureSplitLengthAndVariableEncoding', ], [ - PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', + PHP_VERSION_ID < 80000 ? '(array&nonEmpty)|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithInvalidSplitLengthTypeAndValidEncoding', ], [ @@ -9754,7 +9754,7 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithInvalidSplitLengthTypeAndInvalidEncoding', ], [ - PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', + PHP_VERSION_ID < 80000 ? '(array&nonEmpty)|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithInvalidSplitLengthTypeAndVariableEncoding', ], [ @@ -9766,11 +9766,11 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithVariableStringAndConstantSplitLengthAndInvalidEncoding', ], [ - PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', + PHP_VERSION_ID < 80000 ? '(array&nonEmpty)|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithVariableStringAndConstantSplitLengthAndVariableEncoding', ], [ - PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', + PHP_VERSION_ID < 80000 ? '(array&nonEmpty)|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithVariableStringAndVariableSplitLengthAndValidEncoding', ], [ @@ -9778,7 +9778,7 @@ public function dataPhp74Functions(): array '$mbStrSplitConstantStringWithVariableStringAndVariableSplitLengthAndInvalidEncoding', ], [ - PHP_VERSION_ID < 80000 ? 'array&nonEmpty|false' : 'array&nonEmpty', + PHP_VERSION_ID < 80000 ? '(array&nonEmpty)|false' : 'array&nonEmpty', '$mbStrSplitConstantStringWithVariableStringAndVariableSplitLengthAndVariableEncoding', ], ]; From 4a526b160e166ddae40e83d8a0c8973eb6168690 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 20 Aug 2021 13:53:19 +0200 Subject: [PATCH 5/6] adjusted should-not-happen condition --- src/Type/Php/StrSplitFunctionReturnTypeExtension.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Type/Php/StrSplitFunctionReturnTypeExtension.php b/src/Type/Php/StrSplitFunctionReturnTypeExtension.php index df96648adb..bd78e3993e 100644 --- a/src/Type/Php/StrSplitFunctionReturnTypeExtension.php +++ b/src/Type/Php/StrSplitFunctionReturnTypeExtension.php @@ -101,6 +101,9 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, $items = isset($encoding) ? mb_str_split($stringValue, $splitLength, $encoding) : str_split($stringValue, $splitLength); + if ($items === false) { + throw new \PHPStan\ShouldNotHappenException(); + } return self::createConstantArrayFrom($items, $scope); } From 7036765c2b1c5369f388588f81be4d8f629884ab Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 23 Aug 2021 16:39:00 +0200 Subject: [PATCH 6/6] fix php8 baseline --- build/baseline-8.0.neon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/baseline-8.0.neon b/build/baseline-8.0.neon index 17ff4b0ca8..b9be86042c 100644 --- a/build/baseline-8.0.neon +++ b/build/baseline-8.0.neon @@ -11,7 +11,7 @@ parameters: path: ../src/Type/Php/MbFunctionsReturnTypeExtension.php - - message: "#^Call to function is_array\\(\\) with array\\ will always evaluate to true\\.$#" + message: "#^Strict comparison using \\=\\=\\= between array&nonEmpty and false will always evaluate to false\\.$#" count: 1 path: ../src/Type/Php/StrSplitFunctionReturnTypeExtension.php