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

Fix treatment of closure params in array_map

This commit is contained in:
Brown 2020-08-29 11:10:09 -04:00 committed by Daniil Gentili
parent 9f246938ba
commit b7002ceb9f
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
5 changed files with 45 additions and 17 deletions

View File

@ -593,7 +593,9 @@ class NonDivArithmeticOpAnalyzer
}
if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Mod) {
$result_type = $always_positive ? Type::getPositiveInt() : Type::getInt();
$result_type = $always_positive
? new Type\Union([new Type\Atomic\TPositiveInt(), new TLiteralInt(0)])
: Type::getInt();
} elseif (!$result_type) {
$result_type = $always_positive ? Type::getPositiveInt(true) : Type::getInt(true);
} else {

View File

@ -251,22 +251,25 @@ class ArgumentsAnalyzer
);
}
if ($param_storage->type !== $param_storage->signature_type) {
continue;
}
if (!$param_storage->type_inferred) {
if ($param_storage->type !== $param_storage->signature_type) {
continue;
}
$type_match_found = UnionTypeComparator::isContainedBy(
$codebase,
$replaced_type_part->params[$closure_param_offset]->type,
$param_storage->type
);
$type_match_found = UnionTypeComparator::isContainedBy(
$codebase,
$replaced_type_part->params[$closure_param_offset]->type,
$param_storage->type
);
if (!$type_match_found) {
continue;
if (!$type_match_found) {
continue;
}
}
}
$param_storage->type = $replaced_type_part->params[$closure_param_offset]->type;
$param_storage->type_inferred = true;
if ($method_id === 'array_map' || $method_id === 'array_filter') {
ArrayFetchAnalyzer::taintArrayFetch(
@ -318,12 +321,14 @@ class ArgumentsAnalyzer
[]
);
$existing_type = $statements_analyzer->node_data->getType($arg->value);
\Psalm\Internal\Type\UnionTemplateHandler::replaceTemplateTypesWithStandins(
$generic_param_type,
$replace_template_result,
$codebase,
$statements_analyzer,
$statements_analyzer->node_data->getType($arg->value),
$existing_type,
$argument_offset,
'fn-' . ($context->calling_method_id ?: $context->calling_function_id)
);

View File

@ -888,7 +888,7 @@ class ArrayFunctionArgumentsAnalyzer
if ($union_comparison_results->type_coerced_from_mixed) {
if (IssueBuffer::accepts(
new MixedArgumentTypeCoercion(
'First parameter of closure passed to function ' . $method_id . ' expects ' .
'Parameter ' . ($i + 1) . ' of closure passed to function ' . $method_id . ' expects ' .
$closure_param_type->getId() . ', parent type ' . $input_type->getId() . ' provided',
new CodeLocation($statements_analyzer->getSource(), $closure_arg),
$method_id
@ -900,7 +900,7 @@ class ArrayFunctionArgumentsAnalyzer
} else {
if (IssueBuffer::accepts(
new ArgumentTypeCoercion(
'First parameter of closure passed to function ' . $method_id . ' expects ' .
'Parameter ' . ($i + 1) . ' of closure passed to function ' . $method_id . ' expects ' .
$closure_param_type->getId() . ', parent type ' . $input_type->getId() . ' provided',
new CodeLocation($statements_analyzer->getSource(), $closure_arg),
$method_id
@ -922,7 +922,7 @@ class ArrayFunctionArgumentsAnalyzer
if ($union_comparison_results->scalar_type_match_found) {
if (IssueBuffer::accepts(
new InvalidScalarArgument(
'First parameter of closure passed to function ' . $method_id . ' expects ' .
'Parameter ' . ($i + 1) . ' of closure passed to function ' . $method_id . ' expects ' .
$closure_param_type->getId() . ', ' . $input_type->getId() . ' provided',
new CodeLocation($statements_analyzer->getSource(), $closure_arg),
$method_id
@ -934,7 +934,7 @@ class ArrayFunctionArgumentsAnalyzer
} elseif ($types_can_be_identical) {
if (IssueBuffer::accepts(
new PossiblyInvalidArgument(
'First parameter of closure passed to function ' . $method_id . ' expects '
'Parameter ' . ($i + 1) . ' of closure passed to function ' . $method_id . ' expects '
. $closure_param_type->getId() . ', possibly different type '
. $input_type->getId() . ' provided',
new CodeLocation($statements_analyzer->getSource(), $closure_arg),
@ -946,7 +946,7 @@ class ArrayFunctionArgumentsAnalyzer
}
} elseif (IssueBuffer::accepts(
new InvalidArgument(
'First parameter of closure passed to function ' . $method_id . ' expects ' .
'Parameter ' . ($i + 1) . ' of closure passed to function ' . $method_id . ' expects ' .
$closure_param_type->getId() . ', ' . $input_type->getId() . ' provided',
new CodeLocation($statements_analyzer->getSource(), $closure_arg),
$method_id

View File

@ -83,6 +83,11 @@ class FunctionLikeParameter
*/
public $assert_untainted = false;
/**
* @var bool
*/
public $type_inferred = false;
/**
* @param string $name
* @param bool $by_ref

View File

@ -1041,6 +1041,22 @@ class ForeachTest extends \Psalm\Tests\TestCase
echo $key . " " . $value;
}'
],
'loopClosure' => [
'<?php
/**
* @param list<0> $currentIndexes
*/
function cartesianProduct(array $currentIndexes): void {
while (rand(0, 1)) {
array_map(
function ($index) { echo $index; },
$currentIndexes
);
$currentIndexes[0]++;
}
}'
],
];
}