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

Fix #687 - interpret unpacked arguments in array_push and array_unshift

This commit is contained in:
Matt Brown 2018-04-20 17:14:38 -04:00
parent 131cab2528
commit 0882b9c0f9
2 changed files with 140 additions and 28 deletions

View File

@ -285,21 +285,49 @@ class CallChecker
return false;
}
$by_ref_type = Type::combineUnionTypes(
$by_ref_type,
new Type\Union(
[
new TArray(
if (!isset($arg->value->inferredType) || $arg->value->inferredType->isMixed()) {
$by_ref_type = Type::combineUnionTypes(
$by_ref_type,
new Type\Union([new TArray([Type::getInt(), Type::getMixed()])])
);
} elseif ($arg->unpack) {
if ($arg->value->inferredType->hasArray()) {
/** @var Type\Atomic\TArray|Type\Atomic\ObjectLike */
$array_atomic_type = $arg->value->inferredType->getTypes()['array'];
if ($array_atomic_type instanceof Type\Atomic\ObjectLike) {
$array_atomic_type = $array_atomic_type->getGenericArrayType();
}
$by_ref_type = Type::combineUnionTypes(
$by_ref_type,
new Type\Union(
[
Type::getInt(),
isset($arg->value->inferredType)
? clone $arg->value->inferredType
: Type::getMixed(),
new TArray(
[
Type::getInt(),
clone $array_atomic_type->type_params[1]
]
),
]
)
);
}
} else {
$by_ref_type = Type::combineUnionTypes(
$by_ref_type,
new Type\Union(
[
new TArray(
[
Type::getInt(),
clone $arg->value->inferredType
]
),
]
)
);
),
]
)
);
}
}
ExpressionChecker::assignByRefParam(
@ -750,8 +778,7 @@ class CallChecker
}
}
// for now stop when we encounter a packed argument
if ($arg->unpack) {
if (!$context->check_variables) {
break;
}
@ -762,20 +789,69 @@ class CallChecker
$fq_class_name
);
if ($context->check_variables) {
if (self::checkFunctionArgumentType(
$statements_checker,
$arg->value->inferredType,
$fleshed_out_type,
$cased_method_id,
$argument_offset,
new CodeLocation($statements_checker->getSource(), $arg->value),
$arg->value,
$context,
$function_param->by_ref
) === false) {
return false;
if ($arg->unpack) {
if ($arg->value->inferredType->hasArray()) {
/** @var Type\Atomic\TArray|Type\Atomic\ObjectLike */
$array_atomic_type = $arg->value->inferredType->getTypes()['array'];
if ($array_atomic_type instanceof Type\Atomic\ObjectLike) {
$array_atomic_type = $array_atomic_type->getGenericArrayType();
}
if (self::checkFunctionArgumentType(
$statements_checker,
$array_atomic_type->type_params[1],
$fleshed_out_type,
$cased_method_id,
$argument_offset,
new CodeLocation($statements_checker->getSource(), $arg->value),
$arg->value,
$context,
$function_param->by_ref
) === false) {
return false;
}
} elseif ($arg->value->inferredType->isMixed()) {
$codebase->analyzer->incrementMixedCount($statements_checker->getCheckedFilePath());
if (IssueBuffer::accepts(
new MixedArgument(
'Argument ' . ($argument_offset + 1) . $cased_method_id
. ' cannot be mixed, expecting array',
$code_location
),
$statements_checker->getSuppressedIssues()
)) {
return false;
}
} else {
if (IssueBuffer::accepts(
new InvalidArgument(
'Argument ' . ($argument_offset + 1) . $cased_method_id
. ' expects array, ' . $arg->value->inferredType . ' provided',
$code_location
),
$statements_checker->getSuppressedIssues()
)) {
return false;
}
}
break;
}
if (self::checkFunctionArgumentType(
$statements_checker,
$arg->value->inferredType,
$fleshed_out_type,
$cased_method_id,
$argument_offset,
new CodeLocation($statements_checker->getSource(), $arg->value),
$arg->value,
$context,
$function_param->by_ref
) === false) {
return false;
}
}
} elseif ($function_param) {

View File

@ -4,6 +4,7 @@ namespace Psalm\Tests;
class Php56Test extends TestCase
{
use Traits\FileCheckerValidCodeParseTestTrait;
use Traits\FileCheckerInvalidCodeParseTestTrait;
/**
* @return array
@ -84,6 +85,22 @@ class Php56Test extends TestCase
$operators = [2, 3];
echo add(1, ...$operators);',
],
'arrayPushArgumentUnpacking' => [
'<?php
/**
* @return string[]
*/
function a(): array {
$a = [];
$b = ["foo", "bar"];
$a[] = "foo";
array_push($a, ...$b);
return $a;
}',
],
'arrayMergeArgumentUnpacking' => [
'<?php
$a = [[1, 2]];
@ -207,4 +224,23 @@ class Php56Test extends TestCase
],
];
}
/**
* @return array
*/
public function providerFileCheckerInvalidCodeParse()
{
return [
'arrayPushArgumentUnpackingWithBadArg' => [
'<?php
$a = [];
$b = "hello";
$a[] = "foo";
array_push($a, ...$b);',
'error_message' => 'InvalidArgument',
],
];
}
}