mirror of
https://github.com/danog/psalm.git
synced 2024-12-02 09:37:59 +01:00
Fix handling of coerced callmap args
This commit is contained in:
parent
c29b3744ec
commit
9aa0aca949
@ -1508,7 +1508,7 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer
|
|||||||
) {
|
) {
|
||||||
$storage = $this->getFunctionLikeStorage($statements_analyzer);
|
$storage = $this->getFunctionLikeStorage($statements_analyzer);
|
||||||
|
|
||||||
foreach ($storage->params as $i => $param) {
|
foreach ($storage->params as $param) {
|
||||||
if ($param->by_ref && isset($context->vars_in_scope['$' . $param->name]) && !$param->is_variadic) {
|
if ($param->by_ref && isset($context->vars_in_scope['$' . $param->name]) && !$param->is_variadic) {
|
||||||
$actual_type = $context->vars_in_scope['$' . $param->name];
|
$actual_type = $context->vars_in_scope['$' . $param->name];
|
||||||
$param_out_type = $param->out_type ?: $param->type;
|
$param_out_type = $param->out_type ?: $param->type;
|
||||||
|
@ -513,7 +513,6 @@ class ArgumentsAnalyzer
|
|||||||
$cased_method_id,
|
$cased_method_id,
|
||||||
$last_param,
|
$last_param,
|
||||||
$function_params,
|
$function_params,
|
||||||
$function_storage,
|
|
||||||
$argument_offset,
|
$argument_offset,
|
||||||
$arg,
|
$arg,
|
||||||
$context,
|
$context,
|
||||||
@ -692,7 +691,6 @@ class ArgumentsAnalyzer
|
|||||||
* @param string|null $cased_method_id
|
* @param string|null $cased_method_id
|
||||||
* @param FunctionLikeParameter|null $last_param
|
* @param FunctionLikeParameter|null $last_param
|
||||||
* @param array<int, FunctionLikeParameter> $function_params
|
* @param array<int, FunctionLikeParameter> $function_params
|
||||||
* @param FunctionLikeStorage|null $function_storage
|
|
||||||
* @return false|null
|
* @return false|null
|
||||||
*/
|
*/
|
||||||
private static function handlePossiblyMatchingByRefParam(
|
private static function handlePossiblyMatchingByRefParam(
|
||||||
@ -702,7 +700,6 @@ class ArgumentsAnalyzer
|
|||||||
$cased_method_id,
|
$cased_method_id,
|
||||||
$last_param,
|
$last_param,
|
||||||
$function_params,
|
$function_params,
|
||||||
$function_storage,
|
|
||||||
int $argument_offset,
|
int $argument_offset,
|
||||||
PhpParser\Node\Arg $arg,
|
PhpParser\Node\Arg $arg,
|
||||||
Context $context,
|
Context $context,
|
||||||
|
@ -96,6 +96,7 @@ class InternalCallMapHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
$matching_param_count_callable = null;
|
$matching_param_count_callable = null;
|
||||||
|
$matching_coerced_param_count_callable = null;
|
||||||
|
|
||||||
foreach ($callables as $possible_callable) {
|
foreach ($callables as $possible_callable) {
|
||||||
$possible_function_params = $possible_callable->params;
|
$possible_function_params = $possible_callable->params;
|
||||||
@ -103,6 +104,7 @@ class InternalCallMapHandler
|
|||||||
assert($possible_function_params !== null);
|
assert($possible_function_params !== null);
|
||||||
|
|
||||||
$all_args_match = true;
|
$all_args_match = true;
|
||||||
|
$type_coerced = false;
|
||||||
|
|
||||||
$last_param = count($possible_function_params)
|
$last_param = count($possible_function_params)
|
||||||
? $possible_function_params[count($possible_function_params) - 1]
|
? $possible_function_params[count($possible_function_params) - 1]
|
||||||
@ -179,6 +181,10 @@ class InternalCallMapHandler
|
|||||||
true,
|
true,
|
||||||
$arg_result
|
$arg_result
|
||||||
) || $arg_result->type_coerced) {
|
) || $arg_result->type_coerced) {
|
||||||
|
if ($arg_result->type_coerced) {
|
||||||
|
$type_coerced = true;
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,9 +196,17 @@ class InternalCallMapHandler
|
|||||||
$matching_param_count_callable = $possible_callable;
|
$matching_param_count_callable = $possible_callable;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($all_args_match) {
|
if ($all_args_match && !$type_coerced) {
|
||||||
return $possible_callable;
|
return $possible_callable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($all_args_match) {
|
||||||
|
$matching_coerced_param_count_callable = $possible_callable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($matching_coerced_param_count_callable) {
|
||||||
|
return $matching_coerced_param_count_callable;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($matching_param_count_callable) {
|
if ($matching_param_count_callable) {
|
||||||
@ -274,6 +288,13 @@ class InternalCallMapHandler
|
|||||||
? Type::parseString($arg_type)
|
? Type::parseString($arg_type)
|
||||||
: Type::getMixed();
|
: Type::getMixed();
|
||||||
|
|
||||||
|
$out_type = null;
|
||||||
|
|
||||||
|
if ($arg_name[0] === 'w' && $arg_name[1] === '_') {
|
||||||
|
$out_type = $param_type;
|
||||||
|
$param_type = Type::getMixed();
|
||||||
|
}
|
||||||
|
|
||||||
$function_param = new FunctionLikeParameter(
|
$function_param = new FunctionLikeParameter(
|
||||||
$arg_name,
|
$arg_name,
|
||||||
$by_reference,
|
$by_reference,
|
||||||
@ -285,6 +306,10 @@ class InternalCallMapHandler
|
|||||||
$variadic
|
$variadic
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($out_type) {
|
||||||
|
$function_param->out_type = $out_type;
|
||||||
|
}
|
||||||
|
|
||||||
if (isset(self::$taint_sink_map[$call_map_key][$arg_offset])) {
|
if (isset(self::$taint_sink_map[$call_map_key][$arg_offset])) {
|
||||||
$function_param->sinks = self::$taint_sink_map[$call_map_key][$arg_offset];
|
$function_param->sinks = self::$taint_sink_map[$call_map_key][$arg_offset];
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,9 @@ class DateTimeZone
|
|||||||
public function __construct(string $tz) {}
|
public function __construct(string $tz) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-taint-specialize
|
||||||
|
*/
|
||||||
interface Throwable
|
interface Throwable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -56,6 +59,9 @@ interface Throwable
|
|||||||
public function getTraceAsString() : string;
|
public function getTraceAsString() : string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-taint-specialize
|
||||||
|
*/
|
||||||
class Exception implements Throwable
|
class Exception implements Throwable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -124,6 +130,9 @@ class Exception implements Throwable
|
|||||||
public final function getTraceAsString() : string {}
|
public final function getTraceAsString() : string {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-taint-specialize
|
||||||
|
*/
|
||||||
class Error implements Throwable
|
class Error implements Throwable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -1291,6 +1291,15 @@ class FunctionCallTest extends TestCase
|
|||||||
'$n' => 'int',
|
'$n' => 'int',
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
'writeArgsAllowed' => [
|
||||||
|
'<?php
|
||||||
|
/** @return false|int */
|
||||||
|
function safeMatch(string $pattern, string $subject, ?array $matches = null, int $flags = 0) {
|
||||||
|
return \preg_match($pattern, $subject, $matches, $flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
safeMatch("/a/", "b");'
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user