mirror of
https://github.com/danog/psalm.git
synced 2024-12-02 09:37:59 +01:00
Fix #687 - interpret unpacked arguments in array_push and array_unshift
This commit is contained in:
parent
131cab2528
commit
0882b9c0f9
@ -285,6 +285,20 @@ class CallChecker
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 = Type::combineUnionTypes(
|
||||||
$by_ref_type,
|
$by_ref_type,
|
||||||
new Type\Union(
|
new Type\Union(
|
||||||
@ -292,15 +306,29 @@ class CallChecker
|
|||||||
new TArray(
|
new TArray(
|
||||||
[
|
[
|
||||||
Type::getInt(),
|
Type::getInt(),
|
||||||
isset($arg->value->inferredType)
|
clone $array_atomic_type->type_params[1]
|
||||||
? clone $arg->value->inferredType
|
|
||||||
: Type::getMixed(),
|
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$by_ref_type = Type::combineUnionTypes(
|
||||||
|
$by_ref_type,
|
||||||
|
new Type\Union(
|
||||||
|
[
|
||||||
|
new TArray(
|
||||||
|
[
|
||||||
|
Type::getInt(),
|
||||||
|
clone $arg->value->inferredType
|
||||||
|
]
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ExpressionChecker::assignByRefParam(
|
ExpressionChecker::assignByRefParam(
|
||||||
$statements_checker,
|
$statements_checker,
|
||||||
@ -750,8 +778,7 @@ class CallChecker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// for now stop when we encounter a packed argument
|
if (!$context->check_variables) {
|
||||||
if ($arg->unpack) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -762,7 +789,57 @@ class CallChecker
|
|||||||
$fq_class_name
|
$fq_class_name
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($context->check_variables) {
|
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(
|
if (self::checkFunctionArgumentType(
|
||||||
$statements_checker,
|
$statements_checker,
|
||||||
$arg->value->inferredType,
|
$arg->value->inferredType,
|
||||||
@ -777,7 +854,6 @@ class CallChecker
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} elseif ($function_param) {
|
} elseif ($function_param) {
|
||||||
$codebase->analyzer->incrementMixedCount($statements_checker->getCheckedFilePath());
|
$codebase->analyzer->incrementMixedCount($statements_checker->getCheckedFilePath());
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ namespace Psalm\Tests;
|
|||||||
class Php56Test extends TestCase
|
class Php56Test extends TestCase
|
||||||
{
|
{
|
||||||
use Traits\FileCheckerValidCodeParseTestTrait;
|
use Traits\FileCheckerValidCodeParseTestTrait;
|
||||||
|
use Traits\FileCheckerInvalidCodeParseTestTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array
|
* @return array
|
||||||
@ -84,6 +85,22 @@ class Php56Test extends TestCase
|
|||||||
$operators = [2, 3];
|
$operators = [2, 3];
|
||||||
echo add(1, ...$operators);',
|
echo add(1, ...$operators);',
|
||||||
],
|
],
|
||||||
|
'arrayPushArgumentUnpacking' => [
|
||||||
|
'<?php
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
function a(): array {
|
||||||
|
$a = [];
|
||||||
|
$b = ["foo", "bar"];
|
||||||
|
|
||||||
|
$a[] = "foo";
|
||||||
|
|
||||||
|
array_push($a, ...$b);
|
||||||
|
|
||||||
|
return $a;
|
||||||
|
}',
|
||||||
|
],
|
||||||
'arrayMergeArgumentUnpacking' => [
|
'arrayMergeArgumentUnpacking' => [
|
||||||
'<?php
|
'<?php
|
||||||
$a = [[1, 2]];
|
$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',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user