mirror of
https://github.com/danog/psalm.git
synced 2024-12-11 16:59:45 +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) {
|
if ($array_creation_info->can_be_empty) {
|
||||||
$array_type = new TList($item_value_type ?? Type::getMixed());
|
$array_type = new TList($item_value_type ?? Type::getMixed());
|
||||||
} else {
|
} 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([
|
$stmt_type = new Union([
|
||||||
|
@ -345,7 +345,7 @@ class ArrayAssignmentAnalyzer
|
|||||||
&& $key_values[0] instanceof TLiteralInt
|
&& $key_values[0] instanceof TLiteralInt
|
||||||
) {
|
) {
|
||||||
$key_value = $key_values[0];
|
$key_value = $key_values[0];
|
||||||
$count = ($type->count ?? $type->min_count) ?? 1;
|
$count = $type->getMinCount() ?? 1;
|
||||||
if ($key_value->value < $count) {
|
if ($key_value->value < $count) {
|
||||||
$has_matching_objectlike_property = true;
|
$has_matching_objectlike_property = true;
|
||||||
|
|
||||||
|
@ -179,15 +179,33 @@ class ArrayMergeReturnTypeProvider implements FunctionReturnTypeProviderInterfac
|
|||||||
continue;
|
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) {
|
if ($unpacked_type_part instanceof TList) {
|
||||||
$all_keyed_arrays = false;
|
$all_keyed_arrays = false;
|
||||||
|
|
||||||
if ($unpacked_type_part instanceof TNonEmptyList && !$unpacking_possibly_empty) {
|
if ($is_replace) {
|
||||||
$any_nonempty = true;
|
foreach ($generic_properties as $key => $keyed_type) {
|
||||||
} else {
|
if (is_string($key)) {
|
||||||
$all_nonempty_lists = false;
|
continue;
|
||||||
|
}
|
||||||
|
$generic_properties[$key] = Type::combineUnionTypes(
|
||||||
|
$keyed_type,
|
||||||
|
$unpacked_type_part->type_param,
|
||||||
|
$codebase
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$all_nonempty_lists = false;
|
||||||
|
|
||||||
} elseif ($unpacked_type_part instanceof TArray) {
|
} elseif ($unpacked_type_part instanceof TArray) {
|
||||||
if ($unpacked_type_part->isEmptyArray()) {
|
if ($unpacked_type_part->isEmptyArray()) {
|
||||||
continue;
|
continue;
|
||||||
@ -211,13 +229,6 @@ class ArrayMergeReturnTypeProvider implements FunctionReturnTypeProviderInterfac
|
|||||||
if ($unpacked_type_part instanceof TNonEmptyArray && !$unpacking_possibly_empty) {
|
if ($unpacked_type_part instanceof TNonEmptyArray && !$unpacking_possibly_empty) {
|
||||||
$any_nonempty = true;
|
$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 {
|
} else {
|
||||||
return Type::getArray();
|
return Type::getArray();
|
||||||
}
|
}
|
||||||
|
@ -722,15 +722,23 @@ class SimpleAssertionReconciler extends Reconciler
|
|||||||
$count
|
$count
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$existing_var_type->removeType('array');
|
||||||
$existing_var_type->addType(
|
$existing_var_type->addType(
|
||||||
$non_empty_array
|
$non_empty_array
|
||||||
);
|
);
|
||||||
} elseif ($array_atomic_type instanceof TList) {
|
} elseif ($array_atomic_type instanceof TList) {
|
||||||
$non_empty_list = new TNonEmptyList(
|
$properties = [];
|
||||||
$array_atomic_type->type_param,
|
for ($x = 0; $x < $count; $x++) {
|
||||||
$count
|
$properties []= $array_atomic_type->type_param;
|
||||||
|
}
|
||||||
|
$non_empty_list = new TKeyedArray(
|
||||||
|
$properties,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$existing_var_type->removeType('array');
|
||||||
$existing_var_type->addType(
|
$existing_var_type->addType(
|
||||||
$non_empty_list
|
$non_empty_list
|
||||||
);
|
);
|
||||||
|
@ -1520,13 +1520,22 @@ class TypeCombiner
|
|||||||
[Type::getInt(), $combination->array_type_params[1]],
|
[Type::getInt(), $combination->array_type_params[1]],
|
||||||
true
|
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 {
|
} else {
|
||||||
/** @psalm-suppress ArgumentTypeCoercion */
|
/** @psalm-suppress ArgumentTypeCoercion */
|
||||||
$array_type = new TNonEmptyList(
|
$array_type = new TNonEmptyList(
|
||||||
$generic_type_params[1],
|
$generic_type_params[1],
|
||||||
$combination->array_counts && count($combination->array_counts) === 1
|
|
||||||
? array_keys($combination->array_counts)[0]
|
|
||||||
: null,
|
|
||||||
$combination->array_min_counts
|
$combination->array_min_counts
|
||||||
? min(array_keys($combination->array_min_counts))
|
? min(array_keys($combination->array_min_counts))
|
||||||
: null
|
: null
|
||||||
@ -1536,9 +1545,6 @@ class TypeCombiner
|
|||||||
/** @psalm-suppress ArgumentTypeCoercion */
|
/** @psalm-suppress ArgumentTypeCoercion */
|
||||||
$array_type = new TNonEmptyArray(
|
$array_type = new TNonEmptyArray(
|
||||||
$generic_type_params,
|
$generic_type_params,
|
||||||
$combination->array_counts && count($combination->array_counts) === 1
|
|
||||||
? array_keys($combination->array_counts)[0]
|
|
||||||
: null,
|
|
||||||
$combination->array_min_counts
|
$combination->array_min_counts
|
||||||
? min(array_keys($combination->array_min_counts))
|
? min(array_keys($combination->array_min_counts))
|
||||||
: null
|
: null
|
||||||
|
@ -21,6 +21,7 @@ use Psalm\Type\Atomic\TFloat;
|
|||||||
use Psalm\Type\Atomic\TInt;
|
use Psalm\Type\Atomic\TInt;
|
||||||
use Psalm\Type\Atomic\TIntRange;
|
use Psalm\Type\Atomic\TIntRange;
|
||||||
use Psalm\Type\Atomic\TIterable;
|
use Psalm\Type\Atomic\TIterable;
|
||||||
|
use Psalm\Type\Atomic\TKeyedArray;
|
||||||
use Psalm\Type\Atomic\TList;
|
use Psalm\Type\Atomic\TList;
|
||||||
use Psalm\Type\Atomic\TLiteralClassString;
|
use Psalm\Type\Atomic\TLiteralClassString;
|
||||||
use Psalm\Type\Atomic\TLiteralFloat;
|
use Psalm\Type\Atomic\TLiteralFloat;
|
||||||
@ -454,7 +455,12 @@ abstract class Type
|
|||||||
*/
|
*/
|
||||||
public static function getNonEmptyList(): Union
|
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]);
|
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