mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 05:41:20 +01:00
Fix treatment of closure params in array_map
This commit is contained in:
parent
df0d426f61
commit
3ca4a576e7
@ -593,7 +593,9 @@ class NonDivArithmeticOpAnalyzer
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Mod) {
|
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) {
|
} elseif (!$result_type) {
|
||||||
$result_type = $always_positive ? Type::getPositiveInt(true) : Type::getInt(true);
|
$result_type = $always_positive ? Type::getPositiveInt(true) : Type::getInt(true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -251,22 +251,25 @@ class ArgumentsAnalyzer
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($param_storage->type !== $param_storage->signature_type) {
|
if (!$param_storage->type_inferred) {
|
||||||
continue;
|
if ($param_storage->type !== $param_storage->signature_type) {
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$type_match_found = UnionTypeComparator::isContainedBy(
|
$type_match_found = UnionTypeComparator::isContainedBy(
|
||||||
$codebase,
|
$codebase,
|
||||||
$replaced_type_part->params[$closure_param_offset]->type,
|
$replaced_type_part->params[$closure_param_offset]->type,
|
||||||
$param_storage->type
|
$param_storage->type
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!$type_match_found) {
|
if (!$type_match_found) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$param_storage->type = $replaced_type_part->params[$closure_param_offset]->type;
|
$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') {
|
if ($method_id === 'array_map' || $method_id === 'array_filter') {
|
||||||
ArrayFetchAnalyzer::taintArrayFetch(
|
ArrayFetchAnalyzer::taintArrayFetch(
|
||||||
@ -318,12 +321,14 @@ class ArgumentsAnalyzer
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$existing_type = $statements_analyzer->node_data->getType($arg->value);
|
||||||
|
|
||||||
\Psalm\Internal\Type\UnionTemplateHandler::replaceTemplateTypesWithStandins(
|
\Psalm\Internal\Type\UnionTemplateHandler::replaceTemplateTypesWithStandins(
|
||||||
$generic_param_type,
|
$generic_param_type,
|
||||||
$replace_template_result,
|
$replace_template_result,
|
||||||
$codebase,
|
$codebase,
|
||||||
$statements_analyzer,
|
$statements_analyzer,
|
||||||
$statements_analyzer->node_data->getType($arg->value),
|
$existing_type,
|
||||||
$argument_offset,
|
$argument_offset,
|
||||||
'fn-' . ($context->calling_method_id ?: $context->calling_function_id)
|
'fn-' . ($context->calling_method_id ?: $context->calling_function_id)
|
||||||
);
|
);
|
||||||
|
@ -888,7 +888,7 @@ class ArrayFunctionArgumentsAnalyzer
|
|||||||
if ($union_comparison_results->type_coerced_from_mixed) {
|
if ($union_comparison_results->type_coerced_from_mixed) {
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
new MixedArgumentTypeCoercion(
|
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',
|
$closure_param_type->getId() . ', parent type ' . $input_type->getId() . ' provided',
|
||||||
new CodeLocation($statements_analyzer->getSource(), $closure_arg),
|
new CodeLocation($statements_analyzer->getSource(), $closure_arg),
|
||||||
$method_id
|
$method_id
|
||||||
@ -900,7 +900,7 @@ class ArrayFunctionArgumentsAnalyzer
|
|||||||
} else {
|
} else {
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
new ArgumentTypeCoercion(
|
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',
|
$closure_param_type->getId() . ', parent type ' . $input_type->getId() . ' provided',
|
||||||
new CodeLocation($statements_analyzer->getSource(), $closure_arg),
|
new CodeLocation($statements_analyzer->getSource(), $closure_arg),
|
||||||
$method_id
|
$method_id
|
||||||
@ -922,7 +922,7 @@ class ArrayFunctionArgumentsAnalyzer
|
|||||||
if ($union_comparison_results->scalar_type_match_found) {
|
if ($union_comparison_results->scalar_type_match_found) {
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
new InvalidScalarArgument(
|
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',
|
$closure_param_type->getId() . ', ' . $input_type->getId() . ' provided',
|
||||||
new CodeLocation($statements_analyzer->getSource(), $closure_arg),
|
new CodeLocation($statements_analyzer->getSource(), $closure_arg),
|
||||||
$method_id
|
$method_id
|
||||||
@ -934,7 +934,7 @@ class ArrayFunctionArgumentsAnalyzer
|
|||||||
} elseif ($types_can_be_identical) {
|
} elseif ($types_can_be_identical) {
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
new PossiblyInvalidArgument(
|
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 '
|
. $closure_param_type->getId() . ', possibly different type '
|
||||||
. $input_type->getId() . ' provided',
|
. $input_type->getId() . ' provided',
|
||||||
new CodeLocation($statements_analyzer->getSource(), $closure_arg),
|
new CodeLocation($statements_analyzer->getSource(), $closure_arg),
|
||||||
@ -946,7 +946,7 @@ class ArrayFunctionArgumentsAnalyzer
|
|||||||
}
|
}
|
||||||
} elseif (IssueBuffer::accepts(
|
} elseif (IssueBuffer::accepts(
|
||||||
new InvalidArgument(
|
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',
|
$closure_param_type->getId() . ', ' . $input_type->getId() . ' provided',
|
||||||
new CodeLocation($statements_analyzer->getSource(), $closure_arg),
|
new CodeLocation($statements_analyzer->getSource(), $closure_arg),
|
||||||
$method_id
|
$method_id
|
||||||
|
@ -83,6 +83,11 @@ class FunctionLikeParameter
|
|||||||
*/
|
*/
|
||||||
public $assert_untainted = false;
|
public $assert_untainted = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $type_inferred = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param bool $by_ref
|
* @param bool $by_ref
|
||||||
|
@ -1041,6 +1041,22 @@ class ForeachTest extends \Psalm\Tests\TestCase
|
|||||||
echo $key . " " . $value;
|
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…
x
Reference in New Issue
Block a user