Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Disjoint keyed array unions #9778

Draft
wants to merge 19 commits into
base: master
Choose a base branch
from
Draft
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ public static function checkIteratorType(
}

// if it's an empty array, we cannot iterate over it
if ($iterator_atomic_type instanceof TArray && $iterator_atomic_type->isEmptyArray()) {
if ($iterator_atomic_type instanceof TArray && $iterator_atomic_type->isEmpty()) {
$always_non_empty_array = false;
$has_valid_iterator = true;
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -512,18 +512,18 @@ private static function updateArrayAssignmentChildType(
&& $key_type
&& $key_type->isTemplatedClassString()
) {
/**
* @var TClassStringMap
*/
$class_string_map = $parent_type->getArray();
foreach ($parent_type->getArrays() as $class_string_map) {
if (!$class_string_map instanceof TClassStringMap) {
continue;
}
/**
* @var TTemplateParamClass
*/
$offset_type_part = $key_type->getSingleAtomic();
$offset_type_part = $key_type->getSingleAtomic();

$template_result = new TemplateResult(
[],
[
$template_result = new TemplateResult(
[],
[
$offset_type_part->param_name => [
$offset_type_part->defining_class => new Union([
new TTemplateParam(
Expand All @@ -535,20 +535,21 @@ private static function updateArrayAssignmentChildType(
),
]),
],
],
);
],
);

$value_type = TemplateInferredTypeReplacer::replace(
$value_type,
$template_result,
$codebase,
);
$value_type = TemplateInferredTypeReplacer::replace(
$value_type,
$template_result,
$codebase,
);

$array_atomic_type_class_string = new TClassStringMap(
$class_string_map->param_name,
$class_string_map->as_type,
$value_type,
);
$array_atomic_type_class_string = new TClassStringMap(
$class_string_map->param_name,
$class_string_map->as_type,
$value_type,
);
}
} else {
$array_atomic_type_array = [
$array_atomic_key_type,
Expand All @@ -573,9 +574,12 @@ private static function updateArrayAssignmentChildType(
if (!$current_dim && !$context->inside_loop) {
$atomic_root_types = $root_type->getAtomicTypes();

if (isset($atomic_root_types['array'])) {
$atomic_root_type_array = $atomic_root_types['array'];

foreach ($atomic_root_types as $atomic_root_type_array) {
if (!$atomic_root_type_array instanceof TKeyedArray
&& !$atomic_root_type_array instanceof TArray
) {
continue;
}

if ($array_atomic_type_class_string) {
$array_atomic_type = new TNonEmptyArray([
Expand Down Expand Up @@ -695,16 +699,14 @@ private static function updateArrayAssignmentChildType(
if ($from_countable_object_like) {
$atomic_root_types = $new_child_type->getAtomicTypes();

if (isset($atomic_root_types['array'])) {
$atomic_root_type_array = $atomic_root_types['array'];


$changed = false;
foreach ($atomic_root_types as $k => $atomic_root_type_array) {
if ($atomic_root_type_array instanceof TNonEmptyArray
&& $atomic_root_type_array->count !== null
) {
$atomic_root_types['array'] =
$atomic_root_types[$k] =
$atomic_root_type_array->setCount($atomic_root_type_array->count+1);
$new_child_type = new Union($atomic_root_types);
$changed = true;
} elseif ($atomic_root_type_array instanceof TKeyedArray
&& $atomic_root_type_array->is_list) {
$properties = $atomic_root_type_array->properties;
Expand All @@ -721,12 +723,14 @@ private static function updateArrayAssignmentChildType(
$properties []= $atomic_root_type_array->fallback_params[1];
}

$atomic_root_types['array'] =
$atomic_root_types[$k] =
$atomic_root_type_array->setProperties($properties);

$new_child_type = new Union($atomic_root_types);
$changed = true;
}
}
if ($changed) {
$new_child_type = new Union($atomic_root_types);
}
}

return $new_child_type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Union;

use function assert;
use function count;
use function explode;
use function implode;
Expand Down Expand Up @@ -121,30 +122,29 @@ public static function checkArgumentMatches(
}

$param_type = $function_param->type;


$param_types = [];
if ($function_param->is_variadic
&& $param_type
&& $param_type->hasArray()
) {
$array_type = $param_type->getArray();

if ($array_type instanceof TKeyedArray && $array_type->is_list) {
$param_type = $array_type->getGenericValueType();
} elseif ($array_type instanceof TArray) {
$param_type = $array_type->type_params[1];
}
$param_types = $param_type->getArrayValueTypes();
} elseif ($param_type) {
$param_types = [$param_type];
}

if ($param_type && !$param_type->hasMixed()) {
IssueBuffer::maybeAdd(
new MixedArgument(
'Argument ' . ($argument_offset + 1) . ' of ' . $cased_method_id
. ' cannot be mixed, expecting ' . $param_type,
new CodeLocation($statements_analyzer->getSource(), $arg->value),
$cased_method_id,
),
$statements_analyzer->getSuppressedIssues(),
);
foreach ($param_types as $param_type) {
if ($param_type->hasMixed()) {
IssueBuffer::maybeAdd(
new MixedArgument(
'Argument ' . ($argument_offset + 1) . ' of ' . $cased_method_id
. ' cannot be mixed, expecting ' . $param_type,
new CodeLocation($statements_analyzer->getSource(), $arg->value),
$cased_method_id,
),
$statements_analyzer->getSuppressedIssues(),
);
}
}
}

Expand Down Expand Up @@ -471,62 +471,63 @@ private static function checkFunctionLikeTypeMatches(
}

if ($arg_value_type->hasArray()) {
$unpacked_atomic_array = $arg_value_type->getArray();
$arg_key_allowed = true;
foreach ($arg_value_type->getArrays() as $unpacked_atomic_array) {
if ($unpacked_atomic_array instanceof TKeyedArray) {
if (!$allow_named_args && !$unpacked_atomic_array->getGenericKeyType()->isInt()) {
$arg_key_allowed = false;
}

if ($unpacked_atomic_array instanceof TKeyedArray) {
if (!$allow_named_args && !$unpacked_atomic_array->getGenericKeyType()->isInt()) {
$arg_key_allowed = false;
}

if ($function_param->is_variadic) {
$arg_value_type = $unpacked_atomic_array->getGenericValueType();
} elseif ($codebase->analysis_php_version_id >= 8_00_00
if ($function_param->is_variadic) {
$arg_value_type = $unpacked_atomic_array->getGenericValueType();
} elseif ($codebase->analysis_php_version_id >= 8_00_00
&& $allow_named_args
&& isset($unpacked_atomic_array->properties[$function_param->name])
) {
$arg_value_type = $unpacked_atomic_array->properties[$function_param->name];
} elseif ($unpacked_atomic_array->is_list
) {
$arg_value_type = $unpacked_atomic_array->properties[$function_param->name];
} elseif ($unpacked_atomic_array->is_list
&& isset($unpacked_atomic_array->properties[$unpacked_argument_offset])
) {
$arg_value_type = $unpacked_atomic_array->properties[$unpacked_argument_offset];
} elseif ($unpacked_atomic_array->fallback_params) {
$arg_value_type = $unpacked_atomic_array->fallback_params[1];
} elseif ($function_param->is_optional && $function_param->default_type) {
if ($function_param->default_type instanceof Union) {
$arg_value_type = $function_param->default_type;
} else {
$arg_value_type_atomic = ConstantTypeResolver::resolve(
$codebase->classlikes,
$function_param->default_type,
$statements_analyzer,
);
) {
$arg_value_type = $unpacked_atomic_array->properties[$unpacked_argument_offset];
} elseif ($unpacked_atomic_array->fallback_params) {
$arg_value_type = $unpacked_atomic_array->fallback_params[1];
} elseif ($function_param->is_optional && $function_param->default_type) {
if ($function_param->default_type instanceof Union) {
$arg_value_type = $function_param->default_type;
} else {
$arg_value_type_atomic = ConstantTypeResolver::resolve(
$codebase->classlikes,
$function_param->default_type,
$statements_analyzer,
);

$arg_value_type = new Union([$arg_value_type_atomic]);
$arg_value_type = new Union([$arg_value_type_atomic]);
}
} else {
$arg_value_type = Type::getMixed();
}
} else {
} elseif ($unpacked_atomic_array instanceof TClassStringMap) {
$arg_value_type = Type::getMixed();
} else {
assert($unpacked_atomic_array instanceof TArray);
if (!$allow_named_args && !$unpacked_atomic_array->type_params[0]->isInt()) {
$arg_key_allowed = false;
}
$arg_value_type = $unpacked_atomic_array->type_params[1];
}
} elseif ($unpacked_atomic_array instanceof TClassStringMap) {
$arg_value_type = Type::getMixed();
} else {
if (!$allow_named_args && !$unpacked_atomic_array->type_params[0]->isInt()) {
$arg_key_allowed = false;
}
$arg_value_type = $unpacked_atomic_array->type_params[1];
}

if (!$arg_key_allowed) {
IssueBuffer::maybeAdd(
new NamedArgumentNotAllowed(
'Method ' . $cased_method_id
if (!$arg_key_allowed) {
IssueBuffer::maybeAdd(
new NamedArgumentNotAllowed(
'Method ' . $cased_method_id
. ' called with named unpacked array ' . $unpacked_atomic_array->getId()
. ' (array with string keys)',
new CodeLocation($statements_analyzer->getSource(), $arg->value),
$cased_method_id,
),
$statements_analyzer->getSuppressedIssues(),
);
new CodeLocation($statements_analyzer->getSource(), $arg->value),
$cased_method_id,
),
$statements_analyzer->getSuppressedIssues(),
);
}
}
} else {
$non_iterable = false;
Expand Down Expand Up @@ -1245,16 +1246,8 @@ private static function verifyExplicitParam(
}
} elseif ($param_type_part instanceof TCallable) {
$can_be_callable_like_array = false;
if ($param_type->hasArray()) {
$param_array_type = $param_type->getArray();

$row_type = null;
if ($param_array_type instanceof TArray) {
$row_type = $param_array_type->type_params[1];
} elseif ($param_array_type instanceof TKeyedArray) {
$row_type = $param_array_type->getGenericValueType();
}


foreach ($param_type->getArrayValueTypes() as $row_type) {
if ($row_type &&
($row_type->hasMixed() || $row_type->hasString())
) {
Expand Down
Loading
Loading