From 1c36da6ddacddd1719b3e453d0e152230276065b Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Tue, 6 Feb 2024 20:52:42 +0100 Subject: [PATCH] Strip callmap prefixes from parameter names Fixes vimeo/psalm#10662 --- .../Codebase/InternalCallMapHandler.php | 11 +++++++++- .../Codebase/InternalCallMapHandlerTest.php | 20 +++++++++++++++++++ tests/MethodSignatureTest.php | 18 +++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/Psalm/Internal/Codebase/InternalCallMapHandler.php b/src/Psalm/Internal/Codebase/InternalCallMapHandler.php index e5c96b624ed..308e6c9984e 100644 --- a/src/Psalm/Internal/Codebase/InternalCallMapHandler.php +++ b/src/Psalm/Internal/Codebase/InternalCallMapHandler.php @@ -285,11 +285,20 @@ public static function getCallablesFromCallMap(string $function_id): ?array $out_type = null; - if (strlen($arg_name) > 2 && $arg_name[0] === 'w' && $arg_name[1] === '_') { + if ($by_reference && strlen($arg_name) > 2 && $arg_name[0] === 'w' && $arg_name[1] === '_') { + // strip prefix that is not actually a part of the parameter name + $arg_name = substr($arg_name, 2); $out_type = $param_type; $param_type = Type::getMixed(); } + // removes `rw_` leftover from `&rw_haystack` or `&rw_needle` or `&rw_actual_name` + // it doesn't have any specific meaning apart from `&` signifying that + // the parameter is passed by reference (handled above) + if ($by_reference && strlen($arg_name) > 3 && strpos($arg_name, 'rw_') === 0) { + $arg_name = substr($arg_name, 3); + } + $function_param = new FunctionLikeParameter( $arg_name, $by_reference, diff --git a/tests/Internal/Codebase/InternalCallMapHandlerTest.php b/tests/Internal/Codebase/InternalCallMapHandlerTest.php index 3768c83ea37..e4dba7e3081 100644 --- a/tests/Internal/Codebase/InternalCallMapHandlerTest.php +++ b/tests/Internal/Codebase/InternalCallMapHandlerTest.php @@ -312,6 +312,26 @@ public function testGetcallmapReturnsAValidCallmap(): void } } + public function testGetCallablesFromCallmapRemovesRwPrefixFromParameterNames(): void + { + $entries = InternalCallMapHandler::getCallablesFromCallMap('collator_sort'); // has &rw_array parameter as second parameter + $this->assertNotNull($entries); + $collator_sort_entry = $entries[0]; + $this->assertIsArray($collator_sort_entry->params); + $this->assertArrayHasKey(1, $collator_sort_entry->params); + $this->assertEquals('array', $collator_sort_entry->params[1]->name); + } + + public function testGetCallablesFromCallmapRemovesWPrefixFromParameterNames(): void + { + $entries = InternalCallMapHandler::getCallablesFromCallMap('curl_multi_exec'); // has &w_still_running parameter as second parameter + $this->assertNotNull($entries); + $curl_multi_exec_entry = $entries[0]; + $this->assertIsArray($curl_multi_exec_entry->params); + $this->assertArrayHasKey(1, $curl_multi_exec_entry->params); + $this->assertEquals('still_running', $curl_multi_exec_entry->params[1]->name); + } + /** * @return iterable}> */ diff --git a/tests/MethodSignatureTest.php b/tests/MethodSignatureTest.php index e973c0f7e77..c47fd247020 100644 --- a/tests/MethodSignatureTest.php +++ b/tests/MethodSignatureTest.php @@ -957,6 +957,24 @@ public function &foo(): int { } ', ], + 'callmapInheritedMethodParamsDoNotHavePrefixes' => [ + 'code' => <<<'PHP' +