From 25b01ce3a3e0602e5dd60432f480c4e829bf79a7 Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Mon, 17 Oct 2022 15:36:08 +0200 Subject: [PATCH] ensure callbacks have the required number of params Fix https://github.com/vimeo/psalm/issues/8593 --- .../Type/Comparator/UnionTypeComparator.php | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php b/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php index c997199d650..1906fc938f1 100644 --- a/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php @@ -6,6 +6,7 @@ use Psalm\Internal\Type\TypeExpander; use Psalm\Type\Atomic; use Psalm\Type\Atomic\TArrayKey; +use Psalm\Type\Atomic\TCallable; use Psalm\Type\Atomic\TClassConstant; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TIntRange; @@ -21,6 +22,10 @@ use function array_pop; use function array_push; use function array_reverse; +use function count; +use function is_array; + +use const PHP_INT_MAX; /** * @internal @@ -134,6 +139,39 @@ public static function isContainedBy( continue; } + // if params are specified + if ($container_type_part instanceof TCallable + && is_array($container_type_part->params) + && $input_type_part instanceof TCallable + ) { + $container_required_param_count = 0; + foreach ($container_type_part->params as $index => $container_param) { + if ($container_param->is_optional === false) { + $container_required_param_count = $index + 1; + } + } + + $input_required_param_count = 0; + if (!is_array($input_type_part->params)) { + // it's not declared, there can be an arbitrary number of params + $input_all_param_count = PHP_INT_MAX; + } else { + $input_all_param_count = count($input_type_part->params); + foreach ($input_type_part->params as $index => $input_param) { + if ($input_param->is_optional === false) { + $input_required_param_count = $index + 1; + } + } + } + + // too few or too many non-optional params provided in callback + if ($container_required_param_count > $input_all_param_count + || count($container_type_part->params) < $input_required_param_count + ) { + return false; + } + } + if ($union_comparison_result) { $atomic_comparison_result = new TypeComparisonResult(); } else {