Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
danog committed Feb 27, 2024
1 parent 563a453 commit 5597583
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -527,14 +527,7 @@ public static function handleSplice(
&& $replacement_arg_type
&& $replacement_arg_type->hasArray()
) {
/**
* @var TArray|TKeyedArray
*/
$replacement_array_type = $replacement_arg_type->getArray();

if (($replacement_array_type_generic = ArrayType::infer($replacement_array_type))
&& $replacement_array_type_generic->count === 0
&& $cover_whole_arr) {
if ($replacement_arg_type->areArraysAllEmpty() && $cover_whole_arr) {
$empty_array_type = Type::getEmptyArray();
AssignmentAnalyzer::assignByRefParam(
$statements_analyzer,
Expand Down Expand Up @@ -562,7 +555,7 @@ public static function handleSplice(
}
}

$by_ref_type = TypeCombiner::combine([$array_type, $replacement_array_type]);
$by_ref_type = TypeCombiner::combine([$array_type, ...$replacement_arg_type->getArrayValueTypes()]);

AssignmentAnalyzer::assignByRefParam(
$statements_analyzer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,21 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev
$statements_source = $event->getStatementsSource();
if (count($call_args) >= 2
&& ($array_arg_type = $statements_source->getNodeTypeProvider()->getType($call_args[0]->value))
&& $array_arg_type->isSingle()
&& $array_arg_type->hasArray()
&& ($array_type = ArrayType::infer($array_arg_type->getArray()))
&& $array_arg_type->isArray()
) {
$codebase = $statements_source->getCodebase();
$preserve_keys = isset($call_args[2])
&& ($preserve_keys_arg_type = $statements_source->getNodeTypeProvider()->getType($call_args[2]->value))
&& (string) $preserve_keys_arg_type !== 'false';

return Type::getList(
new Union([
$preserve_keys
? new TNonEmptyArray([$array_type->key, $array_type->value])
: Type::getNonEmptyListAtomic($array_type->value),
? new TNonEmptyArray([
Type::combineUnionTypeArray($array_arg_type->getArrayKeyTypes(), $codebase),
Type::combineUnionTypeArray($array_arg_type->getArrayValueTypes(), $codebase)
])
: Type::getNonEmptyListAtomic(Type::combineUnionTypeArray($array_arg_type->getArrayValueTypes(), $codebase)),
]),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,24 +139,19 @@ static function (array $sub) use ($null) {
}

$array_arg_atomic_type = null;
$array_arg_type = null;

if ($array_arg_union_type = $statements_source->node_data->getType($array_arg->value)) {
$arg_types = $array_arg_union_type->getAtomicTypes();

if (isset($arg_types['array'])) {
$array_arg_atomic_type = $arg_types['array'];

$array_arg_type = ArrayType::infer($array_arg_atomic_type);
}
}
$array_arg_type = ($array_arg_union_type = $statements_source->node_data->getType($array_arg->value))
&& $array_arg_union_type->hasArray();

$generic_key_type = null;
$mapping_return_type = null;

if ($function_call_arg && $function_call_type) {
$codebase = $statements_source->getCodebase();

if (count($call_args) === 2) {
$generic_key_type = $array_arg_type->key ?? Type::getArrayKey();
$generic_key_type = $array_arg_type
? Type::combineUnionTypeArray($array_arg_union_type->getArrayKeyTypes(), $codebase)
: Type::getArrayKey();
} else {
$generic_key_type = Type::getInt();
}
Expand Down Expand Up @@ -279,10 +274,10 @@ static function (array $sub) use ($null) {
]);
}

return count($call_args) === 2 && !($array_arg_type->is_list ?? false)
return count($call_args) === 2 && !$array_arg_union_type->hasList()
? new Union([
new TArray([
$array_arg_type->key ?? Type::getArrayKey(),
$generic_key_type ?? Type::getArrayKey(),
Type::getMixed(),
]),
])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,18 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev
&& ($array_arg_type = $type_provider->getType($call_args[0]->value))
&& ($size_arg_type = $type_provider->getType($call_args[1]->value))
&& ($value_arg_type = $type_provider->getType($call_args[2]->value))
&& $array_arg_type->isSingle()
&& $array_arg_type->hasArray()
&& ($array_type = ArrayType::infer($array_arg_type->getArray()))
&& $array_arg_type->isArray()
) {
$codebase = $statements_source->getCodebase();
$key_type = Type::combineUnionTypes($array_type->key, Type::getInt(), $codebase);
$value_type = Type::combineUnionTypes($array_type->value, $value_arg_type, $codebase);
$key_type = Type::combineUnionTypeArray([...$array_arg_type->getArrayKeyTypes(), Type::getInt()], $codebase);
$value_type = Type::combineUnionTypeArray([...$array_arg_type->getArrayValueTypes(), $value_arg_type], $codebase);
$can_return_empty = (
!$size_arg_type->isSingleIntLiteral()
|| $size_arg_type->getSingleIntLiteral()->value === 0
);

return new Union([
$array_type->is_list
$array_arg_type->isList()
? (
$can_return_empty
? Type::getListAtomic($value_type)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,61 +38,56 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev

$first_arg = $call_args[0]->value ?? null;

$first_arg_array = $first_arg
$first_arg_is_array = $first_arg
&& ($first_arg_type = $statements_source->node_data->getType($first_arg))
&& $first_arg_type->hasArray()
&& !$first_arg_type->hasMixed()
&& ($array_atomic_type = $first_arg_type->getArray())
&& ($array_atomic_type instanceof TArray
|| $array_atomic_type instanceof TKeyedArray)
? $array_atomic_type
: null;

if (!$first_arg_array) {
&& !$first_arg_type->hasMixed();

if (!$first_arg_is_array) {
return Type::getMixed();
}

$nullable = false;

if ($first_arg_array instanceof TArray) {
$value_type = $first_arg_array->type_params[1];

if ($first_arg_array->isEmptyArray()) {
return Type::getNull();
}
$value_types = [];
foreach ($first_arg_type->getArrays() as $first_arg_array) {
if ($first_arg_array instanceof TArray) {
$value_types []= $first_arg_array->type_params[1];

if (!$first_arg_array instanceof TNonEmptyArray) {
$nullable = true;
}
} else {
// special case where we know the type of the first element
if ($function_id === 'array_shift' && $first_arg_array->is_list && isset($first_arg_array->properties[0])) {
$value_type = $first_arg_array->properties[0];
if ($value_type->possibly_undefined) {
$value_type = $value_type->setPossiblyUndefined(false);
if (!$first_arg_array instanceof TNonEmptyArray) {
$nullable = true;
}
} else {
$value_type = $first_arg_array->getGenericValueType();

if (!$first_arg_array->isNonEmpty()) {
$nullable = true;
} elseif ($first_arg_array instanceof TKeyedArray) {
// special case where we know the type of the first element
if ($function_id === 'array_shift' && $first_arg_array->is_list && isset($first_arg_array->properties[0])) {
$value_type = $first_arg_array->properties[0];
if ($value_type->possibly_undefined) {
$value_type = $value_type->setPossiblyUndefined(false);
$nullable = true;
}
} else {
$value_type = $first_arg_array->getGenericValueType();

if (!$first_arg_array->isNonEmpty()) {
$nullable = true;
}
}
$value_types []= $value_type;
}
}

if ($nullable) {
$value_type = $value_type->getBuilder()->addType(new TNull);

$codebase = $statements_source->getCodebase();

if ($codebase->config->ignore_internal_nullable_issues) {
$value_type->ignore_nullable_issues = true;
}
$codebase = $statements_source->getCodebase();
$ignore_nullable_issues = false;

$value_type = $value_type->freeze();
if ($nullable) {
$value_types []= Type::getNull();
$ignore_nullable_issues = $codebase->config->ignore_internal_nullable_issues;
}

$value_type = Type::combineUnionTypeArray($value_types, $codebase);
$value_type->ignore_nullable_issues = $ignore_nullable_issues;

return $value_type;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,16 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev
$first_arg = $call_args[0]->value ?? null;
$second_arg = $call_args[1]->value ?? null;

$first_arg_array = $first_arg
$key_type = $first_arg
&& ($first_arg_type = $statements_source->node_data->getType($first_arg))
&& $first_arg_type->hasArray()
&& ($array_atomic_type = $first_arg_type->getArray())
&& ($array_atomic_type instanceof TArray
|| $array_atomic_type instanceof TKeyedArray)
? $array_atomic_type
? $first_arg_type->getArrayKeyTypes()
: null;

if (!$first_arg_array) {
if (!$key_type) {
return Type::getMixed();
}

if ($first_arg_array instanceof TArray) {
$key_type = $first_arg_array->type_params[0];
} else {
$key_type = $first_arg_array->getGenericKeyType();
}
$key_type = Type::combineUnionTypeArray($key_type, $statements_source->getCodebase());

if (!$second_arg) {
return $key_type;
Expand Down
10 changes: 9 additions & 1 deletion src/Psalm/Type/Atomic/TArray.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,15 @@ public function getMaxCount(): ?int
}
public function getCount(): ?int
{
return null;
return $this->isEmptyArray() ? 0 : null;
}
public function isEmpty(): bool
{
return $this->isEmptyArray();
}
public function isNonEmpty(): bool
{
return false;
}
public function getGenericKeyType(): Union
{
Expand Down
31 changes: 30 additions & 1 deletion src/Psalm/Type/Atomic/TClassStringMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*
* @psalm-immutable
*/
final class TClassStringMap extends Atomic
final class TClassStringMap extends Atomic implements ArrayInterface
{
use UnserializeMemoryUsageSuppressionTrait;
/**
Expand All @@ -35,6 +35,35 @@ public function __construct(
parent::__construct($from_docblock);
}

public function getCount(): ?int
{
return null;
}
public function getMaxCount(): ?int
{
return null;
}
public function getMinCount(): int
{
return 0;
}
public function isNonEmpty(): bool
{
return false;
}
public function isEmpty(): bool
{
return false;
}
public function getGenericValueType(): Union
{
return $this->as_type ? new Union([$this->as_type]) : Type::getObject();
}
public function getGenericKeyType(): Union
{
return Type::getClassString($this->as_type?->value ?? 'object');
}

public function getId(bool $exact = true, bool $nested = false): string
{
return 'class-string-map'
Expand Down
5 changes: 5 additions & 0 deletions src/Psalm/Type/Atomic/TKeyedArray.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ class TKeyedArray extends Atomic implements ArrayInterface
/** @var non-empty-lowercase-string */
protected const NAME_LIST = 'list';

public function isEmpty(): bool
{
return false;
}

/**
* Constructs a new instance of a generic type
*
Expand Down
8 changes: 8 additions & 0 deletions src/Psalm/Type/Atomic/TNonEmptyArray.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ public function getCount(): ?int
return $this->count;
}

public function isNonEmpty(): bool
{
return true;
}
public function isEmpty(): bool
{
return false;
}
/**
* @param array{Union, Union} $type_params
* @param positive-int|null $count
Expand Down

0 comments on commit 5597583

Please sign in to comment.