mirror of
https://github.com/danog/psalm.git
synced 2024-12-04 02:27:59 +01:00
Add more support for evaluating object-like type and fix empty check in foreach
This commit is contained in:
parent
93a5e6775d
commit
520ca5b559
@ -613,6 +613,10 @@ abstract class FunctionLikeChecker implements StatementsSource
|
|||||||
$type_match_found = true;
|
$type_match_found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($param_type_part->isArray() && $input_type_part->isObjectLike()) {
|
||||||
|
$type_match_found = true;
|
||||||
|
}
|
||||||
|
|
||||||
if ($param_type_part->isScalar() && $input_type_part->isScalarType()) {
|
if ($param_type_part->isScalar() && $input_type_part->isScalarType()) {
|
||||||
$type_match_found = true;
|
$type_match_found = true;
|
||||||
}
|
}
|
||||||
|
@ -1692,11 +1692,8 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var_dump($item_value_type . ' ' . ($item_value_type->isSingle() ? ' yes': 'no'));
|
|
||||||
|
|
||||||
// if this array looks like an object-like array, let's return that instead
|
// if this array looks like an object-like array, let's return that instead
|
||||||
if ($item_value_type && !$item_value_type->isSingle() && $item_key_type && $item_key_type->hasString() && !$item_key_type->hasInt()) {
|
if ($item_value_type && !$item_value_type->isSingle() && $item_key_type && $item_key_type->hasString() && !$item_key_type->hasInt()) {
|
||||||
var_dump('creating object-like array for ' . $item_value_type);
|
|
||||||
$stmt->inferredType = new Type\Union([new Type\Atomic('object-like')]);
|
$stmt->inferredType = new Type\Union([new Type\Atomic('object-like')]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1829,7 +1826,7 @@ class StatementsChecker
|
|||||||
if ($iterator_type) {
|
if ($iterator_type) {
|
||||||
foreach ($iterator_type->types as $return_type) {
|
foreach ($iterator_type->types as $return_type) {
|
||||||
// if it's an empty array, we cannot iterate over it
|
// if it's an empty array, we cannot iterate over it
|
||||||
if ((string) $return_type === 'array<empty, empty>') {
|
if ((string) $return_type === 'array<empty,empty>') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1945,6 +1942,11 @@ class StatementsChecker
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isset($foreach_context->vars_in_scope[$var])) {
|
||||||
|
unset($context->vars_in_scope[$var]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if ($foreach_context->vars_in_scope[$var]->isMixed()) {
|
if ($foreach_context->vars_in_scope[$var]->isMixed()) {
|
||||||
$context->vars_in_scope[$var] = $foreach_context->vars_in_scope[$var];
|
$context->vars_in_scope[$var] = $foreach_context->vars_in_scope[$var];
|
||||||
}
|
}
|
||||||
@ -2285,7 +2287,7 @@ class StatementsChecker
|
|||||||
if (isset($stmt->var->inferredType)) {
|
if (isset($stmt->var->inferredType)) {
|
||||||
$return_type = $stmt->var->inferredType;
|
$return_type = $stmt->var->inferredType;
|
||||||
|
|
||||||
if ($keyed_var_id) {
|
if ($keyed_array_var_id) {
|
||||||
// when we have a pattern like
|
// when we have a pattern like
|
||||||
// $a = [];
|
// $a = [];
|
||||||
// $a['b']['c']['d'] = 1;
|
// $a['b']['c']['d'] = 1;
|
||||||
@ -2309,10 +2311,9 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
$stmt->inferredType = $assignment_value_type;
|
$stmt->inferredType = $assignment_value_type;
|
||||||
|
|
||||||
var_dump('checkArrayAssignment: setting ' . $keyed_array_var_id . ' to ' . $context->vars_in_scope[$keyed_array_var_id]);
|
|
||||||
}
|
}
|
||||||
elseif (!$nesting) {
|
|
||||||
|
if (!$nesting) {
|
||||||
$assignment_type = new Type\Union([
|
$assignment_type = new Type\Union([
|
||||||
new Type\Generic(
|
new Type\Generic(
|
||||||
'array',
|
'array',
|
||||||
@ -2331,8 +2332,6 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
$context->vars_in_scope[$var_id] = $assignment_type;
|
$context->vars_in_scope[$var_id] = $assignment_type;
|
||||||
|
|
||||||
var_dump('checkArrayAssignment: setting ' . $var_id . ' to ' . $context->vars_in_scope[$var_id]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -2365,7 +2364,6 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($type->value === 'string' && $assignment_value_type->hasString() && !$assignment_key_type->hasString()) {
|
if ($type->value === 'string' && $assignment_value_type->hasString() && !$assignment_key_type->hasString()) {
|
||||||
var_dump((string)$assignment_key_type . $line_number);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3680,8 +3678,6 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($array_assignment) {
|
if ($array_assignment) {
|
||||||
var_dump('checkArrayAccess: in array assignment');
|
|
||||||
|
|
||||||
// if we're in an array assignment then we need to create some variables
|
// if we're in an array assignment then we need to create some variables
|
||||||
// e.g.
|
// e.g.
|
||||||
// $a = [];
|
// $a = [];
|
||||||
@ -3701,8 +3697,6 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
$stmt->inferredType = $keyed_assignment_type;
|
$stmt->inferredType = $keyed_assignment_type;
|
||||||
|
|
||||||
var_dump('checkArrayAccess: setting ' . $keyed_array_var_id . ' to ' . $context->vars_in_scope[$keyed_array_var_id]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($array_var_id === $var_id) {
|
if ($array_var_id === $var_id) {
|
||||||
@ -3725,8 +3719,6 @@ class StatementsChecker
|
|||||||
else {
|
else {
|
||||||
$context->vars_in_scope[$var_id] = $assignment_type;
|
$context->vars_in_scope[$var_id] = $assignment_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
var_dump('checkArrayAccess: setting ' . $var_id . ' to ' . $context->vars_in_scope[$var_id]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($type->type_params[$value_index]->isEmpty()) {
|
if ($type->type_params[$value_index]->isEmpty()) {
|
||||||
@ -3777,6 +3769,10 @@ class StatementsChecker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($keyed_array_var_id && isset($context->vars_in_scope[$keyed_array_var_id])) {
|
||||||
|
$stmt->inferredType = $context->vars_in_scope[$keyed_array_var_id];
|
||||||
|
}
|
||||||
|
|
||||||
if (!isset($stmt->inferredType)) {
|
if (!isset($stmt->inferredType)) {
|
||||||
$stmt->inferredType = Type::getMixed();
|
$stmt->inferredType = Type::getMixed();
|
||||||
}
|
}
|
||||||
|
@ -1049,10 +1049,11 @@ class TypeChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($simple_declared_types as $simple_declared_type) {
|
foreach ($simple_declared_types as $simple_declared_type) {
|
||||||
if ($simple_declared_type === 'mixed' ||
|
if ($simple_declared_type === 'mixed'
|
||||||
($simple_declared_type === 'object' && ClassLikeChecker::classOrInterfaceExists($differing_type)) ||
|
|| ($simple_declared_type === 'object' && ClassLikeChecker::classOrInterfaceExists($differing_type))
|
||||||
ClassChecker::classExtendsOrImplements($differing_type, $simple_declared_type) ||
|
|| ClassChecker::classExtendsOrImplements($differing_type, $simple_declared_type)
|
||||||
(in_array($differing_type, ['float', 'int']) && in_array($simple_declared_type, ['float', 'int']))
|
|| (in_array($differing_type, ['array', 'object-like']) && in_array($simple_declared_type, ['array', 'object-like']))
|
||||||
|
|| (in_array($differing_type, ['float', 'int']) && in_array($simple_declared_type, ['float', 'int']))
|
||||||
) {
|
) {
|
||||||
$is_match = true;
|
$is_match = true;
|
||||||
break;
|
break;
|
||||||
|
@ -39,6 +39,10 @@ class Atomic extends Type
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($parent->hasType('array') && $this->isObjectLike()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->value === 'false' && $parent->hasType('bool')) {
|
if ($this->value === 'false' && $parent->hasType('bool')) {
|
||||||
// this is fine
|
// this is fine
|
||||||
return true;
|
return true;
|
||||||
|
Loading…
Reference in New Issue
Block a user