1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00

Fix issue with byref template params leaking

This commit is contained in:
Matthew Brown 2017-11-20 00:12:17 -05:00
parent ccc0583bf8
commit 957600623c
3 changed files with 81 additions and 28 deletions

View File

@ -1768,25 +1768,13 @@ class CallChecker
) === false) {
return false;
}
continue;
}
ExpressionChecker::assignByRefParam(
$statements_checker,
$arg->value,
$by_ref_type,
$context,
$method_id && (strpos($method_id, '::') !== false || !FunctionChecker::inCallMap($method_id))
);
} else {
if ($arg->value instanceof PhpParser\Node\Expr\Variable) {
if (ExpressionChecker::analyzeVariable(
$statements_checker,
$arg->value,
$context,
$by_ref,
$by_ref_type
$context
) === false) {
return false;
}
@ -1922,24 +1910,66 @@ class CallChecker
if ($function_param
&& $function_param->by_ref
&& ($arg->value instanceof PhpParser\Node\Scalar
) {
if ($arg->value instanceof PhpParser\Node\Scalar
|| $arg->value instanceof PhpParser\Node\Expr\ClassConstFetch
|| $arg->value instanceof PhpParser\Node\Expr\ConstFetch
|| $arg->value instanceof PhpParser\Node\Expr\FuncCall
|| $arg->value instanceof PhpParser\Node\Expr\MethodCall
)
) {
if (IssueBuffer::accepts(
new InvalidPassByReference(
'Parameter ' . ($argument_offset + 1) . ' of ' . $method_id . ' expects a variable',
$code_location
),
$statements_checker->getSuppressedIssues()
)) {
return false;
) {
if (IssueBuffer::accepts(
new InvalidPassByReference(
'Parameter ' . ($argument_offset + 1) . ' of ' . $method_id . ' expects a variable',
$code_location
),
$statements_checker->getSuppressedIssues()
)) {
return false;
}
continue;
}
break;
if (!in_array(
$method_id,
[
'shuffle', 'sort', 'rsort', 'usort', 'ksort', 'asort',
'krsort', 'arsort', 'natcasesort', 'natsort', 'reset',
'end', 'next', 'prev', 'array_pop', 'array_shift',
'array_push', 'array_unshift',
],
true
)) {
$by_ref_type = null;
if ($last_param) {
if ($argument_offset < count($function_params)) {
$by_ref_type = $function_params[$argument_offset]->type;
} else {
$by_ref_type = $last_param->type;
}
if ($template_types && $by_ref_type) {
if ($generic_params === null) {
$generic_params = [];
}
$by_ref_type = clone $by_ref_type;
$by_ref_type->replaceTemplateTypes($template_types, $generic_params);
}
}
$by_ref_type = $by_ref_type ?: Type::getMixed();
ExpressionChecker::assignByRefParam(
$statements_checker,
$arg->value,
$by_ref_type,
$context,
$method_id && (strpos($method_id, '::') !== false || !FunctionChecker::inCallMap($method_id))
);
}
}
if (isset($arg->value->inferredType)) {
@ -1948,7 +1978,7 @@ class CallChecker
if ($function_param->is_variadic) {
if (!$param_type->hasArray() || !$param_type->types['array'] instanceof TArray) {
break;
continue;
}
$param_type = clone $param_type->types['array']->type_params[1];

View File

@ -84,17 +84,19 @@ function array_diff(array $arr, array $arr2, array $arr3 = null, array $arr4 = n
function array_diff_key(array $arr, array $arr2, array $arr3 = null, array $arr4 = null) {}
/**
* @template TKey
* @template TValue
*
* @param array<mixed, TValue> $arr
* @param array<TKey, TValue> $arr
* @return TValue
*/
function array_shift(array &$arr) {}
/**
* @template TKey
* @template TValue
*
* @param array<mixed, TValue> $arr
* @param array<TKey, TValue> $arr
* @return TValue
*/
function array_pop(array &$arr) {}

View File

@ -362,6 +362,27 @@ class TemplateTest extends TestCase
'$b' => 'array<int, string>',
],
],
'genericArrayPop' => [
'<?php
/**
* @template TValue
*
* @param array<mixed, TValue> $arr
* @return TValue
*/
function my_array_pop(array &$arr) {
return array_pop($arr);
}
/** @var mixed */
$b = ["a" => 5, "c" => 6];
$a = my_array_pop($b);',
'assertions' => [
'$a' => 'mixed',
'$b' => 'array<mixed, mixed>',
],
'error_levels' => ['MixedAssignment', 'MixedArgument'],
],
];
}