1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Fix handling of coerced callmap args

This commit is contained in:
Brown 2020-06-24 11:51:24 -04:00
parent c29b3744ec
commit 9aa0aca949
5 changed files with 45 additions and 5 deletions

View File

@ -1508,7 +1508,7 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer
) {
$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) {
$actual_type = $context->vars_in_scope['$' . $param->name];
$param_out_type = $param->out_type ?: $param->type;

View File

@ -513,7 +513,6 @@ class ArgumentsAnalyzer
$cased_method_id,
$last_param,
$function_params,
$function_storage,
$argument_offset,
$arg,
$context,
@ -692,7 +691,6 @@ class ArgumentsAnalyzer
* @param string|null $cased_method_id
* @param FunctionLikeParameter|null $last_param
* @param array<int, FunctionLikeParameter> $function_params
* @param FunctionLikeStorage|null $function_storage
* @return false|null
*/
private static function handlePossiblyMatchingByRefParam(
@ -702,7 +700,6 @@ class ArgumentsAnalyzer
$cased_method_id,
$last_param,
$function_params,
$function_storage,
int $argument_offset,
PhpParser\Node\Arg $arg,
Context $context,

View File

@ -96,6 +96,7 @@ class InternalCallMapHandler
}
$matching_param_count_callable = null;
$matching_coerced_param_count_callable = null;
foreach ($callables as $possible_callable) {
$possible_function_params = $possible_callable->params;
@ -103,6 +104,7 @@ class InternalCallMapHandler
assert($possible_function_params !== null);
$all_args_match = true;
$type_coerced = false;
$last_param = count($possible_function_params)
? $possible_function_params[count($possible_function_params) - 1]
@ -179,6 +181,10 @@ class InternalCallMapHandler
true,
$arg_result
) || $arg_result->type_coerced) {
if ($arg_result->type_coerced) {
$type_coerced = true;
}
continue;
}
@ -190,9 +196,17 @@ class InternalCallMapHandler
$matching_param_count_callable = $possible_callable;
}
if ($all_args_match) {
if ($all_args_match && !$type_coerced) {
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) {
@ -274,6 +288,13 @@ class InternalCallMapHandler
? Type::parseString($arg_type)
: 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(
$arg_name,
$by_reference,
@ -285,6 +306,10 @@ class InternalCallMapHandler
$variadic
);
if ($out_type) {
$function_param->out_type = $out_type;
}
if (isset(self::$taint_sink_map[$call_map_key][$arg_offset])) {
$function_param->sinks = self::$taint_sink_map[$call_map_key][$arg_offset];
}

View File

@ -16,6 +16,9 @@ class DateTimeZone
public function __construct(string $tz) {}
}
/**
* @psalm-taint-specialize
*/
interface Throwable
{
/**
@ -56,6 +59,9 @@ interface Throwable
public function getTraceAsString() : string;
}
/**
* @psalm-taint-specialize
*/
class Exception implements Throwable
{
/**
@ -124,6 +130,9 @@ class Exception implements Throwable
public final function getTraceAsString() : string {}
}
/**
* @psalm-taint-specialize
*/
class Error implements Throwable
{
/**

View File

@ -1291,6 +1291,15 @@ class FunctionCallTest extends TestCase
'$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");'
],
];
}