mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 05:41:20 +01:00
Fix #702 - prevent bad array to iterable cast
This commit is contained in:
parent
acacde173e
commit
6e67030925
@ -490,28 +490,69 @@ class TypeChecker
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($container_type_part instanceof TNamedObject &&
|
||||
strtolower($container_type_part->value) === 'iterable' &&
|
||||
(
|
||||
$input_type_part instanceof TArray ||
|
||||
$input_type_part instanceof ObjectLike ||
|
||||
(
|
||||
$input_type_part instanceof TNamedObject &&
|
||||
(
|
||||
strtolower($input_type_part->value) === 'traversable'
|
||||
|| $codebase->classExtendsOrImplements(
|
||||
$input_type_part->value,
|
||||
'Traversable'
|
||||
)
|
||||
|| $codebase->interfaceExtends(
|
||||
$input_type_part->value,
|
||||
'Traversable'
|
||||
if ($container_type_part instanceof TNamedObject
|
||||
&& strtolower($container_type_part->value) === 'iterable'
|
||||
) {
|
||||
if ($input_type_part instanceof TArray || $input_type_part instanceof ObjectLike) {
|
||||
if (!$container_type_part instanceof TGenericObject) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($input_type_part instanceof ObjectLike) {
|
||||
$input_type_part = $input_type_part->getGenericArrayType();
|
||||
}
|
||||
|
||||
$all_types_contain = true;
|
||||
|
||||
foreach ($input_type_part->type_params as $i => $input_param) {
|
||||
$container_param = $container_type_part->type_params[$i];
|
||||
|
||||
if ($i === 0
|
||||
&& $input_param->isMixed()
|
||||
&& $container_param->hasString()
|
||||
&& $container_param->hasInt()
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$input_param->isEmpty() &&
|
||||
!self::isContainedBy(
|
||||
$codebase,
|
||||
$input_param,
|
||||
$container_param,
|
||||
$input_param->ignore_nullable_issues,
|
||||
$input_param->ignore_falsable_issues,
|
||||
$has_scalar_match,
|
||||
$type_coerced,
|
||||
$type_coerced_from_mixed
|
||||
)
|
||||
) {
|
||||
$all_types_contain = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($all_types_contain) {
|
||||
$to_string_cast = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($input_type_part instanceof TNamedObject
|
||||
&& (strtolower($input_type_part->value) === 'traversable'
|
||||
|| $codebase->classExtendsOrImplements(
|
||||
$input_type_part->value,
|
||||
'Traversable'
|
||||
) || $codebase->interfaceExtends(
|
||||
$input_type_part->value,
|
||||
'Traversable'
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($container_type_part instanceof TScalar && $input_type_part instanceof Scalar) {
|
||||
|
@ -858,6 +858,18 @@ class FunctionCallTest extends TestCase
|
||||
}',
|
||||
'error_message' => 'FalsableReturnStatement',
|
||||
],
|
||||
'complainAboutArrayToIterable' => [
|
||||
'<?php
|
||||
class A {}
|
||||
class B {}
|
||||
/**
|
||||
* @param iterable<mixed,A> $p
|
||||
*/
|
||||
function takesIterableOfA(iterable $p): void {}
|
||||
|
||||
takesIterableOfA([new B]); // should complain',
|
||||
'error_message' => 'InvalidArgument',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user