From 89e9a61d189ce94697026b2003032a8a1288859e Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Tue, 12 Mar 2024 21:27:03 +0100 Subject: [PATCH] fix bug with class-string-map --- .../Expression/Call/ArgumentAnalyzer.php | 2 +- .../Expression/Call/ArgumentsAnalyzer.php | 20 +++++++++++-------- tests/Template/ClassStringMapTest.php | 3 +++ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php index 4550c126af1..b92238a0aa7 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php @@ -501,7 +501,7 @@ private static function checkFunctionLikeTypeMatches( $invalid_string_key = false; $possibly_matches = false; foreach ($arg_value_type->getAtomicTypes() as $atomic_type) { - if (!$atomic_type->isIterable($codebase)) { + if (!$atomic_type->isIterable($codebase) && !$atomic_type instanceof TClassStringMap) { $non_iterable = true; continue; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php index d56a0556aa3..3e6ca62b096 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php @@ -767,9 +767,6 @@ public static function checkArgumentsMatch( && $arg_value_type->hasArray()) { $named_args_was_used_before_array = $named_args_was_used; - /** - * @var TArray|TKeyedArray - */ $array_type = $arg_value_type->getArray(); if ($array_type instanceof TCallableArray || @@ -821,7 +818,7 @@ public static function checkArgumentsMatch( $args_provided_min += $array_type->count ? ($array_type->count - 1) : 0; } else { - if ($array_type->type_params[1]->isNever()) { + if (isset($array_type->type_params[1]) && $array_type->type_params[1]->isNever()) { $args_provided_max--; } else { $args_provided_max = $function_param_count + 10_00_00_00_00; @@ -832,9 +829,16 @@ public static function checkArgumentsMatch( $has_unpacked_non_keyed_array = true; } - $key_types = $array_type->type_params[0]->getAtomicTypes(); + $key_types = Type::getArrayKey(); + if ($array_type instanceof Type\Atomic\TClassStringMap) { + $key_types = Type::getNonFalsyString(); + } elseif ($array_type instanceof TCallableKeyedArray) { + $key_types = Type::getInt(); + } elseif (isset($array_type->type_params[0])) { + $key_types = $array_type->type_params[0]; + } - if ($array_type->type_params[0]->isString()) { + if ($key_types->isString()) { $named_args_was_used = true; } elseif ($named_args_was_used) { IssueBuffer::maybeAdd( @@ -845,7 +849,7 @@ public static function checkArgumentsMatch( ), $statements_analyzer->getSuppressedIssues(), ); - } elseif (!$array_type->type_params[0]->isInt()) { + } elseif (!$key_types->isInt()) { // e.g. array-key or a mix of literal strings with int or just int|string IssueBuffer::maybeAdd( new PossiblyInvalidArgument( @@ -859,7 +863,7 @@ public static function checkArgumentsMatch( $named_args_was_used = true; } - foreach ($key_types as $key_type) { + foreach ($key_types->getAtomicTypes() as $key_type) { if ($function_storage && !$function_storage->allow_named_arg_calls) { continue; } diff --git a/tests/Template/ClassStringMapTest.php b/tests/Template/ClassStringMapTest.php index c5105462d18..80165b1d9d9 100644 --- a/tests/Template/ClassStringMapTest.php +++ b/tests/Template/ClassStringMapTest.php @@ -82,6 +82,9 @@ function takesVariadic(...$args): void { function foo(array $arr) : void { takesVariadic(...$arr); }', + 'assertions' => [], + 'ignored_issues' => [], + 'php_version' => '8.1', ], 'assignClassStringMapInConstruct' => [ 'code' => '