diff --git a/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php b/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php index a3fd5e843f2..6ed8f7ea14c 100644 --- a/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php @@ -22,6 +22,9 @@ use function array_pop; use function array_push; use function array_reverse; +use function count; + +use const PHP_INT_MAX; /** * @internal @@ -136,7 +139,10 @@ public static function isContainedBy( } // if params are specified - if ($container_type_part instanceof TCallable && $container_type_part->params !== null && $input_type_part instanceof TCallable) { + 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) { @@ -145,14 +151,22 @@ public static function isContainedBy( } $input_required_param_count = 0; - foreach ($input_type_part->params as $index => $input_param) { - if ($input_param->is_optional === false) { - $input_required_param_count = $index + 1; + 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 > count($input_type_part->params) || count($container_type_part->params) < $input_required_param_count) { + if ($container_required_param_count > $input_all_param_count + || count($container_type_part->params) < $input_required_param_count + ) { return false; } }