mirror of
https://github.com/danog/psalm.git
synced 2024-12-03 10:07:52 +01:00
Progress
This commit is contained in:
parent
5b3358937f
commit
cc4461f756
@ -148,7 +148,12 @@ class ArrayAnalyzer
|
||||
if ($array_creation_info->can_be_empty) {
|
||||
$array_type = new TList($item_value_type ?? Type::getMixed());
|
||||
} else {
|
||||
$array_type = new TNonEmptyList($item_value_type ?? Type::getMixed());
|
||||
$array_type = new TKeyedArray(
|
||||
[$item_value_type ?? Type::getMixed()],
|
||||
null,
|
||||
[Type::getInt(), Type::getMixed()],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
$stmt_type = new Union([
|
||||
|
@ -345,7 +345,7 @@ class ArrayAssignmentAnalyzer
|
||||
&& $key_values[0] instanceof TLiteralInt
|
||||
) {
|
||||
$key_value = $key_values[0];
|
||||
$count = ($type->count ?? $type->min_count) ?? 1;
|
||||
$count = $type->getMinCount() ?? 1;
|
||||
if ($key_value->value < $count) {
|
||||
$has_matching_objectlike_property = true;
|
||||
|
||||
|
@ -179,15 +179,33 @@ class ArrayMergeReturnTypeProvider implements FunctionReturnTypeProviderInterfac
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($unpacked_type_part instanceof TMixed
|
||||
&& $unpacked_type_part->from_loop_isset
|
||||
) {
|
||||
$unpacked_type_part = new TArray([
|
||||
Type::getArrayKey(),
|
||||
Type::getMixed(true),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($unpacked_type_part instanceof TList) {
|
||||
$all_keyed_arrays = false;
|
||||
|
||||
if ($unpacked_type_part instanceof TNonEmptyList && !$unpacking_possibly_empty) {
|
||||
$any_nonempty = true;
|
||||
} else {
|
||||
$all_nonempty_lists = false;
|
||||
if ($is_replace) {
|
||||
foreach ($generic_properties as $key => $keyed_type) {
|
||||
if (is_string($key)) {
|
||||
continue;
|
||||
}
|
||||
$generic_properties[$key] = Type::combineUnionTypes(
|
||||
$keyed_type,
|
||||
$unpacked_type_part->type_param,
|
||||
$codebase
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$all_nonempty_lists = false;
|
||||
|
||||
} elseif ($unpacked_type_part instanceof TArray) {
|
||||
if ($unpacked_type_part->isEmptyArray()) {
|
||||
continue;
|
||||
@ -211,13 +229,6 @@ class ArrayMergeReturnTypeProvider implements FunctionReturnTypeProviderInterfac
|
||||
if ($unpacked_type_part instanceof TNonEmptyArray && !$unpacking_possibly_empty) {
|
||||
$any_nonempty = true;
|
||||
}
|
||||
} elseif ($unpacked_type_part instanceof TMixed
|
||||
&& $unpacked_type_part->from_loop_isset
|
||||
) {
|
||||
$unpacked_type_part = new TArray([
|
||||
Type::getArrayKey(),
|
||||
Type::getMixed(true),
|
||||
]);
|
||||
} else {
|
||||
return Type::getArray();
|
||||
}
|
||||
|
@ -722,15 +722,23 @@ class SimpleAssertionReconciler extends Reconciler
|
||||
$count
|
||||
);
|
||||
|
||||
$existing_var_type->removeType('array');
|
||||
$existing_var_type->addType(
|
||||
$non_empty_array
|
||||
);
|
||||
} elseif ($array_atomic_type instanceof TList) {
|
||||
$non_empty_list = new TNonEmptyList(
|
||||
$array_atomic_type->type_param,
|
||||
$count
|
||||
$properties = [];
|
||||
for ($x = 0; $x < $count; $x++) {
|
||||
$properties []= $array_atomic_type->type_param;
|
||||
}
|
||||
$non_empty_list = new TKeyedArray(
|
||||
$properties,
|
||||
null,
|
||||
null,
|
||||
true
|
||||
);
|
||||
|
||||
$existing_var_type->removeType('array');
|
||||
$existing_var_type->addType(
|
||||
$non_empty_list
|
||||
);
|
||||
|
@ -1520,13 +1520,22 @@ class TypeCombiner
|
||||
[Type::getInt(), $combination->array_type_params[1]],
|
||||
true
|
||||
);
|
||||
} else if ($combination->array_counts && count($combination->array_counts) === 1) {
|
||||
$cnt = array_keys($combination->array_counts)[0];
|
||||
$properties = [];
|
||||
for ($x = 0; $x < $cnt; $x++) {
|
||||
$properties []= $generic_type_params[1];
|
||||
}
|
||||
$array_type = new TKeyedArray(
|
||||
$properties,
|
||||
null,
|
||||
null,
|
||||
true
|
||||
);
|
||||
} else {
|
||||
/** @psalm-suppress ArgumentTypeCoercion */
|
||||
$array_type = new TNonEmptyList(
|
||||
$generic_type_params[1],
|
||||
$combination->array_counts && count($combination->array_counts) === 1
|
||||
? array_keys($combination->array_counts)[0]
|
||||
: null,
|
||||
$combination->array_min_counts
|
||||
? min(array_keys($combination->array_min_counts))
|
||||
: null
|
||||
@ -1536,9 +1545,6 @@ class TypeCombiner
|
||||
/** @psalm-suppress ArgumentTypeCoercion */
|
||||
$array_type = new TNonEmptyArray(
|
||||
$generic_type_params,
|
||||
$combination->array_counts && count($combination->array_counts) === 1
|
||||
? array_keys($combination->array_counts)[0]
|
||||
: null,
|
||||
$combination->array_min_counts
|
||||
? min(array_keys($combination->array_min_counts))
|
||||
: null
|
||||
|
@ -21,6 +21,7 @@ use Psalm\Type\Atomic\TFloat;
|
||||
use Psalm\Type\Atomic\TInt;
|
||||
use Psalm\Type\Atomic\TIntRange;
|
||||
use Psalm\Type\Atomic\TIterable;
|
||||
use Psalm\Type\Atomic\TKeyedArray;
|
||||
use Psalm\Type\Atomic\TList;
|
||||
use Psalm\Type\Atomic\TLiteralClassString;
|
||||
use Psalm\Type\Atomic\TLiteralFloat;
|
||||
@ -454,7 +455,12 @@ abstract class Type
|
||||
*/
|
||||
public static function getNonEmptyList(): Union
|
||||
{
|
||||
$type = new TNonEmptyList(new Union([new TMixed]));
|
||||
$type = new TKeyedArray(
|
||||
[self::getMixed()],
|
||||
null,
|
||||
[self::getInt(), self::getMixed()],
|
||||
true
|
||||
);
|
||||
|
||||
return new Union([$type]);
|
||||
}
|
||||
|
@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psalm\Type\Atomic;
|
||||
|
||||
use Psalm\Type\Union;
|
||||
|
||||
/**
|
||||
* Represents a non-empty list
|
||||
* @psalm-immutable
|
||||
*/
|
||||
class TNonEmptyList extends TList
|
||||
{
|
||||
/**
|
||||
* @var positive-int|null
|
||||
*/
|
||||
public $count;
|
||||
|
||||
/**
|
||||
* @var positive-int|null
|
||||
*/
|
||||
public $min_count;
|
||||
|
||||
/** @var non-empty-lowercase-string */
|
||||
public const KEY = 'non-empty-list';
|
||||
|
||||
/**
|
||||
* Constructs a new instance of a list
|
||||
*
|
||||
* @param positive-int|null $count
|
||||
* @param positive-int|null $min_count
|
||||
*/
|
||||
public function __construct(
|
||||
Union $type_param,
|
||||
?int $count = null,
|
||||
?int $min_count = null,
|
||||
bool $from_docblock = false
|
||||
) {
|
||||
$this->type_param = $type_param;
|
||||
$this->count = $count;
|
||||
$this->min_count = $min_count;
|
||||
$this->from_docblock = $from_docblock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param positive-int|null $count
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setCount(?int $count): self
|
||||
{
|
||||
if ($count === $this->count) {
|
||||
return $this;
|
||||
}
|
||||
$cloned = clone $this;
|
||||
$cloned->count = $count;
|
||||
return $cloned;
|
||||
}
|
||||
|
||||
public function getAssertionString(): string
|
||||
{
|
||||
return 'non-empty-list';
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user