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:
parent
9f246938ba
commit
b7002ceb9f
@ -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 {
|
||||
|
@ -251,6 +251,7 @@ class ArgumentsAnalyzer
|
||||
);
|
||||
}
|
||||
|
||||
if (!$param_storage->type_inferred) {
|
||||
if ($param_storage->type !== $param_storage->signature_type) {
|
||||
continue;
|
||||
}
|
||||
@ -265,8 +266,10 @@ class ArgumentsAnalyzer
|
||||
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)
|
||||
);
|
||||
|
@ -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
|
||||
|
@ -83,6 +83,11 @@ class FunctionLikeParameter
|
||||
*/
|
||||
public $assert_untainted = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $type_inferred = false;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param bool $by_ref
|
||||
|
@ -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]++;
|
||||
}
|
||||
}'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user