Skip to content

Commit

Permalink
Fix #1540 - use correct comparison for callable param types
Browse files Browse the repository at this point in the history
  • Loading branch information
muglug committed Apr 12, 2019
1 parent a9b8952 commit ea20a2b
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 73 deletions.
31 changes: 20 additions & 11 deletions src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php
Expand Up @@ -938,18 +938,21 @@ protected static function checkFunctionLikeArgumentsMatch(

$calling_class_storage = $class_storage;

$static_fq_class_name = $fq_class_name;
$self_fq_class_name = $fq_class_name;

if ($method_id && strpos($method_id, '::')) {
$declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);

if ($declaring_method_id && $declaring_method_id !== $method_id) {
list($fq_class_name) = explode('::', $declaring_method_id);
$class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
list($self_fq_class_name) = explode('::', $declaring_method_id);
$class_storage = $codebase->classlike_storage_provider->get($self_fq_class_name);
}

$appearing_method_id = $codebase->methods->getAppearingMethodId($method_id);

if ($appearing_method_id && $declaring_method_id !== $appearing_method_id) {
list($fq_class_name) = explode('::', $appearing_method_id);
list($self_fq_class_name) = explode('::', $appearing_method_id);
}
}

Expand Down Expand Up @@ -1044,7 +1047,8 @@ protected static function checkFunctionLikeArgumentsMatch(
if (self::checkFunctionLikeArgumentMatches(
$statements_analyzer,
$cased_method_id,
$fq_class_name,
$self_fq_class_name,
$static_fq_class_name,
$function_param,
$argument_offset,
$arg,
Expand Down Expand Up @@ -1158,7 +1162,8 @@ protected static function checkFunctionLikeArgumentsMatch(

/**
* @param string|null $cased_method_id
* @param string|null $fq_class_name
* @param string|null $self_fq_class_name
* @param string|null $static_fq_class_name
* @param FunctionLikeParameter|null $function_param
* @param array<string, array<string, array{Type\Union, 1?:int}>> $existing_generic_params
* @param array<string, array<string, array{Type\Union, 1?:int}>> $generic_params
Expand All @@ -1168,7 +1173,8 @@ protected static function checkFunctionLikeArgumentsMatch(
private static function checkFunctionLikeArgumentMatches(
StatementsAnalyzer $statements_analyzer,
$cased_method_id,
$fq_class_name,
$self_fq_class_name,
$static_fq_class_name,
$function_param,
int $argument_offset,
PhpParser\Node\Arg $arg,
Expand Down Expand Up @@ -1224,7 +1230,8 @@ private static function checkFunctionLikeArgumentMatches(
$statements_analyzer,
$codebase,
$cased_method_id,
$fq_class_name,
$self_fq_class_name,
$static_fq_class_name,
$function_param,
$arg->value->inferredType,
$argument_offset,
Expand Down Expand Up @@ -1375,7 +1382,8 @@ private static function handlePossiblyMatchingByRefParam(

/**
* @param string|null $cased_method_id
* @param string|null $fq_class_name
* @param string|null $self_fq_class_name
* @param string|null $static_fq_class_name
* @param array<string, array<string, array{Type\Union, 1?:int}>> $existing_generic_params
* @param array<string, array<string, array{Type\Union, 1?:int}>> $generic_params
* @param array<string, array<string, array{Type\Union}>> $template_types
Expand All @@ -1386,7 +1394,8 @@ private static function checkFunctionLikeTypeMatches(
StatementsAnalyzer $statements_analyzer,
Codebase $codebase,
$cased_method_id,
$fq_class_name,
$self_fq_class_name,
$static_fq_class_name,
$function_param,
Type\Union $arg_type,
int $argument_offset,
Expand Down Expand Up @@ -1452,8 +1461,8 @@ private static function checkFunctionLikeTypeMatches(
$fleshed_out_type = ExpressionAnalyzer::fleshOutType(
$codebase,
$param_type,
$fq_class_name ?: $context->self,
$fq_class_name ?: $context->self
$self_fq_class_name,
$static_fq_class_name
);

if ($arg->unpack) {
Expand Down
88 changes: 48 additions & 40 deletions src/Psalm/Internal/Analyzer/TypeAnalyzer.php
Expand Up @@ -1712,58 +1712,66 @@ private static function compareCallable(
return false;
}

if ($container_type_part->params !== null) {
foreach ($container_type_part->params as $i => $container_param) {
if (!isset($input_type_part->params[$i])) {
if ($container_param->is_optional) {
break;
}
if ($input_type_part->params !== null && $container_type_part->params !== null) {
foreach ($input_type_part->params as $i => $input_param) {
$container_param = null;

$type_coerced = true;
$type_coerced_from_mixed = true;
if (isset($container_type_part->params[$i])) {
$container_param = $container_type_part->params[$i];
} elseif ($container_type_part->params) {
$last_param = end($container_type_part->params);

$all_types_contain = false;
break;
if ($last_param->is_variadic) {
$container_param = $last_param;
}
}

$input_param = $input_type_part->params[$i];
if (!$container_param) {
if ($input_param->is_optional) {
break;
}

if (!self::isContainedBy(
$codebase,
$input_param->type ?: Type::getMixed(),
$container_param->type ?: Type::getMixed(),
false,
false,
$has_scalar_match,
$type_coerced,
$type_coerced_from_mixed
)
return false;
}

if ($container_param->type
&& !$container_param->type->hasMixed()
&& !self::isContainedBy(
$codebase,
$container_param->type,
$input_param->type ?: Type::getMixed(),
false,
false,
$has_scalar_match,
$type_coerced,
$type_coerced_from_mixed
)
) {
$all_types_contain = false;
}
}
}

if (isset($container_type_part->return_type)) {
if (!isset($input_type_part->return_type)) {
$type_coerced = true;
$type_coerced_from_mixed = true;
if (isset($container_type_part->return_type)) {
if (!isset($input_type_part->return_type)) {
$type_coerced = true;
$type_coerced_from_mixed = true;

$all_types_contain = false;
} else {
if (!$container_type_part->return_type->isVoid()
&& !self::isContainedBy(
$codebase,
$input_type_part->return_type,
$container_type_part->return_type,
false,
false,
$has_scalar_match,
$type_coerced,
$type_coerced_from_mixed
)
) {
$all_types_contain = false;
} else {
if (!$container_type_part->return_type->isVoid()
&& !self::isContainedBy(
$codebase,
$input_type_part->return_type,
$container_type_part->return_type,
false,
false,
$has_scalar_match,
$type_coerced,
$type_coerced_from_mixed
)
) {
$all_types_contain = false;
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Psalm/Internal/CallMap.php
Expand Up @@ -11303,7 +11303,7 @@
'RegexIterator::setPregFlags' => ['void', 'new_flags'=>'int'],
'RegexIterator::valid' => ['bool'],
'register_event_handler' => ['bool', 'event_handler_func'=>'string', 'handler_register_name'=>'string', 'event_type_mask'=>'int'],
'register_shutdown_function' => ['void', 'function'=>'callable():void', '...parameter='=>'mixed'],
'register_shutdown_function' => ['void', 'function'=>'callable(mixed...):void', '...parameter='=>'mixed'],
'register_tick_function' => ['bool', 'function'=>'callable():void', '...args='=>'mixed'],
'rename' => ['bool', 'old_name'=>'string', 'new_name'=>'string', 'context='=>'resource'],
'rename_function' => ['bool', 'original_name'=>'string', 'new_name'=>'string'],
Expand Down
24 changes: 18 additions & 6 deletions src/Psalm/Internal/Scanner/PhpStormMetaScanner.php
Expand Up @@ -90,7 +90,7 @@ public static function handleOverride(array $args, Codebase $codebase)
* @return ?Type\Union
*/
function (
StatementsAnalyzer $_statements_analyzer,
\Psalm\StatementsSource $_statements_analyzer,
string $fq_classlike_name,
string $method_name,
array $call_args,
Expand Down Expand Up @@ -143,7 +143,7 @@ function (
* @return ?Type\Union
*/
function (
StatementsAnalyzer $_statements_analyzer,
\Psalm\StatementsSource $_statements_analyzer,
string $fq_classlike_name,
string $method_name,
array $call_args,
Expand Down Expand Up @@ -176,7 +176,7 @@ function (
* @return ?Type\Union
*/
function (
StatementsAnalyzer $_statements_analyzer,
\Psalm\StatementsSource $_statements_analyzer,
string $fq_classlike_name,
string $method_name,
array $call_args,
Expand Down Expand Up @@ -229,7 +229,7 @@ function (
* @param array<PhpParser\Node\Arg> $call_args
*/
function (
StatementsAnalyzer $statements_analyzer,
\Psalm\StatementsSource $statements_analyzer,
string $function_id,
array $call_args,
Context $_context,
Expand Down Expand Up @@ -262,6 +262,10 @@ function (
}
}

if (!$statements_analyzer instanceof StatementsAnalyzer) {
throw new \UnexpectedValueException('This is bad');
}

$storage = $statements_analyzer->getCodebase()->functions->getStorage(
$statements_analyzer,
$function_id
Expand All @@ -277,7 +281,7 @@ function (
* @param array<PhpParser\Node\Arg> $call_args
*/
function (
StatementsAnalyzer $statements_analyzer,
\Psalm\StatementsSource $statements_analyzer,
string $function_id,
array $call_args,
Context $_context,
Expand All @@ -290,6 +294,10 @@ function (
return clone $call_arg_type;
}

if (!$statements_analyzer instanceof StatementsAnalyzer) {
throw new \UnexpectedValueException('This is bad');
}

$storage = $statements_analyzer->getCodebase()->functions->getStorage(
$statements_analyzer,
$function_id
Expand All @@ -305,7 +313,7 @@ function (
* @param array<PhpParser\Node\Arg> $call_args
*/
function (
StatementsAnalyzer $statements_analyzer,
\Psalm\StatementsSource $statements_analyzer,
string $function_id,
array $call_args,
Context $_context,
Expand All @@ -327,6 +335,10 @@ function (
}
}

if (!$statements_analyzer instanceof StatementsAnalyzer) {
throw new \UnexpectedValueException('This is bad');
}

$storage = $statements_analyzer->getCodebase()->functions->getStorage(
$statements_analyzer,
$function_id
Expand Down

0 comments on commit ea20a2b

Please sign in to comment.