1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-02 17:52:45 +01:00
This commit is contained in:
Daniil Gentili 2022-11-26 17:06:12 +01:00
parent 546f2b325b
commit 51e6b1155d
15 changed files with 55 additions and 164 deletions

View File

@ -386,8 +386,6 @@ class ForeachAnalyzer
/**
* @param PhpParser\Node\Stmt\Foreach_|PhpParser\Node\Expr\YieldFrom $stmt
*
* @psalm-suppress ComplexMethod
*
* @return false|null
*/
public static function checkIteratorType(

View File

@ -597,21 +597,30 @@ class ArrayAssignmentAnalyzer
&& $atomic_root_types['array']->isNonEmpty()
)
) {
$prop_count = null;
if ($atomic_root_types['array'] instanceof TNonEmptyArray) {
$prop_count = $atomic_root_types['array']->count;
} else {
$min_count = $atomic_root_types['array']->getMinCount();
if ($min_count === $atomic_root_types['array']->getMaxCount()) {
$prop_count = $min_count;
}
}
if ($array_atomic_type_array) {
$array_atomic_type = new TNonEmptyArray(
$array_atomic_type_array,
$atomic_root_types['array']->count
$prop_count
);
} else {
} else if ($prop_count !== null) {
$array_atomic_type = new TKeyedArray(
array_fill(
0,
$atomic_root_types['array']->count,
$prop_count,
$array_atomic_type_list
),
null,
[
new Union([new TIntRange($atomic_root_types['array']->count, null)]),
new Union([new TIntRange($prop_count, null)]),
$array_atomic_type_list
],
true
@ -623,7 +632,7 @@ class ArrayAssignmentAnalyzer
if ($array_atomic_type_array) {
$array_atomic_type = new TNonEmptyArray(
$array_atomic_type_array,
$atomic_root_types['array']->count
count($atomic_root_types['array']->properties)
);
} elseif ($atomic_root_types['array']->is_list) {
$array_atomic_type = $atomic_root_types['array'];
@ -634,12 +643,12 @@ class ArrayAssignmentAnalyzer
$array_atomic_type = new TKeyedArray(
array_fill(
0,
$atomic_root_types['array']->count,
count($atomic_root_types['array']->properties),
$array_atomic_type_list
),
null,
[
new Union([new TIntRange($atomic_root_types['array']->count, null)]),
new Union([new TIntRange(count($atomic_root_types['array']->properties), null)]),
$array_atomic_type_list
],
true

View File

@ -127,8 +127,8 @@ class ArgumentAnalyzer
$array_type = $param_type->getAtomicTypes()['array'];
if ($array_type instanceof TKeyedArray && $array_type->is_list) {
$param_type = $array_type->type_param;
} else {
$param_type = $array_type->getGenericValueType();
} elseif ($array_type instanceof TArray) {
$param_type = $array_type->type_params[1];
}
}

View File

@ -225,7 +225,7 @@ class ArrayMapReturnTypeProvider implements FunctionReturnTypeProviderInterface
if (($array_arg_atomic_type instanceof TKeyedArray && $array_arg_atomic_type->is_list)
|| count($call_args) !== 2
) {
if ($array_arg_atomic_type->isNonEmpty()) {
if ($array_arg_atomic_type instanceof TKeyedArray && $array_arg_atomic_type->isNonEmpty()) {
return Type::getNonEmptyList(
$mapping_return_type
);

View File

@ -217,10 +217,6 @@ class ArrayMergeReturnTypeProvider implements FunctionReturnTypeProviderInterfac
$inner_value_type = null;
if ($inner_key_types) {
/**
* Truthy&array-shape-list doesn't reconcile correctly, will be fixed for 5.x by #8050.
* @psalm-suppress InvalidScalarArgument
*/
$inner_key_type = TypeCombiner::combine($inner_key_types, $codebase, true);
}

View File

@ -53,7 +53,7 @@ class ArrayReverseReturnTypeProvider implements FunctionReturnTypeProviderInterf
return $first_arg_type;
}
if ($first_arg_array instanceof TKeyedArray && $first_arg_array->is_list) {
if ($first_arg_array->is_list) {
$second_arg = $call_args[1]->value ?? null;
if (!$second_arg
@ -64,7 +64,7 @@ class ArrayReverseReturnTypeProvider implements FunctionReturnTypeProviderInterf
return $first_arg_type;
}
return $first_arg_array->setProperties(array_reverse($first_arg_array->properties));
return new Union([$first_arg_array->setProperties(array_reverse($first_arg_array->properties))]);
}
return new Union([$first_arg_array->getGenericArrayType()]);

View File

@ -33,7 +33,7 @@ class ArraySpliceReturnTypeProvider implements FunctionReturnTypeProviderInterfa
$first_arg = $call_args[0]->value ?? null;
$first_arg_array = $first_arg
$array_type = $first_arg
&& ($first_arg_type = $statements_source->node_data->getType($first_arg))
&& $first_arg_type->hasType('array')
&& ($array_atomic_type = $first_arg_type->getAtomicTypes()['array'])
@ -42,18 +42,12 @@ class ArraySpliceReturnTypeProvider implements FunctionReturnTypeProviderInterfa
? $array_atomic_type
: null;
if (!$first_arg_array) {
if (!$array_type) {
return Type::getArray();
}
if ($first_arg_array instanceof TKeyedArray) {
$first_arg_array = $first_arg_array->getGenericArrayType();
}
if ($first_arg_array instanceof TArray) {
$array_type = new TArray($first_arg_array->type_params);
} else {
$array_type = new TArray([Type::getInt(), $first_arg_array->type_param]);
if ($array_type instanceof TKeyedArray) {
$array_type = $array_type->getGenericArrayType();
}
if (!$array_type->type_params[0]->hasString()) {

View File

@ -55,24 +55,6 @@ class ArrayUniqueReturnTypeProvider implements FunctionReturnTypeProviderInterfa
return new Union([$first_arg_array]);
}
if ($first_arg_array instanceof TKeyedArray && $first_arg_array->is_list) {
if (!$first_arg_array->properties[0]->possibly_undefined) {
return new Union([
new TNonEmptyArray([
Type::getInt(),
$first_arg_array->type_param
])
]);
}
return new Union([
new TArray([
Type::getInt(),
$first_arg_array->type_param
])
]);
}
return new Union([$first_arg_array->getGenericArrayType()]);
}
}

View File

@ -3,22 +3,16 @@
namespace Psalm\Internal\Type\Comparator;
use Psalm\Codebase;
use Psalm\Type;
use Psalm\Type\Atomic;
use Psalm\Type\Atomic\TArray;
use Psalm\Type\Atomic\TClassStringMap;
use Psalm\Type\Atomic\TKeyedArray;
use Psalm\Type\Atomic\TList;
use Psalm\Type\Atomic\TLiteralInt;
use Psalm\Type\Atomic\TLiteralString;
use Psalm\Type\Atomic\TNever;
use Psalm\Type\Atomic\TNonEmptyArray;
use Psalm\Type\Atomic\TNonEmptyList;
use Psalm\Type\Union;
use function array_map;
use function range;
/**
* @internal
*/
@ -128,40 +122,7 @@ class ArrayTypeComparator
);
}
if ($input_type_part instanceof TList
&& $container_type_part instanceof TList
) {
if (!UnionTypeComparator::isContainedBy(
$codebase,
$input_type_part->type_param,
$container_type_part->type_param,
$input_type_part->type_param->ignore_nullable_issues,
$input_type_part->type_param->ignore_falsable_issues,
$atomic_comparison_result,
$allow_interface_equality
)) {
return false;
}
return $input_type_part instanceof TNonEmptyList
|| !$container_type_part instanceof TNonEmptyList;
}
if ($container_type_part instanceof TKeyedArray) {
if ($container_type_part->is_list) {
$container_type_part = $container_type_part->isNonEmpty()
? Type::getNonEmptyListAtomic($container_type_part->getGenericValueType())
: Type::getListAtomic($container_type_part->getGenericValueType());
return self::isContainedBy(
$codebase,
$input_type_part,
$container_type_part,
$allow_interface_equality,
$atomic_comparison_result
);
}
$container_type_part = $container_type_part->getGenericArrayType();
}
@ -183,37 +144,6 @@ class ArrayTypeComparator
]);
}
if ($container_type_part instanceof TList) {
$all_types_contain = false;
if ($atomic_comparison_result) {
$atomic_comparison_result->type_coerced = true;
}
$container_type_part = new TArray([Type::getInt(), $container_type_part->type_param]);
}
if ($input_type_part instanceof TList) {
if ($input_type_part instanceof TNonEmptyList) {
// if the array has a known size < 10, make sure the array keys are literal ints
if ($input_type_part->count !== null && $input_type_part->count < 10) {
$literal_ints = array_map(
static fn($i): TLiteralInt => new TLiteralInt($i),
range(0, $input_type_part->count - 1)
);
$input_type_part = new TNonEmptyArray([
new Union($literal_ints),
$input_type_part->type_param
]);
} else {
$input_type_part = new TNonEmptyArray([Type::getInt(), $input_type_part->type_param]);
}
} else {
$input_type_part = new TArray([Type::getInt(), $input_type_part->type_param]);
}
}
foreach ($input_type_part->type_params as $i => $input_param) {
if ($i > 1) {
break;

View File

@ -20,13 +20,11 @@ use Psalm\Type\Atomic\TGenericObject;
use Psalm\Type\Atomic\TIterable;
use Psalm\Type\Atomic\TKeyOf;
use Psalm\Type\Atomic\TKeyedArray;
use Psalm\Type\Atomic\TList;
use Psalm\Type\Atomic\TLiteralString;
use Psalm\Type\Atomic\TMixed;
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TNever;
use Psalm\Type\Atomic\TNonEmptyArray;
use Psalm\Type\Atomic\TNonEmptyList;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\Atomic\TObjectWithProperties;
@ -740,18 +738,6 @@ class AtomicTypeComparator
Atomic $type2_part,
bool $allow_interface_equality = true
): bool {
if ((get_class($type1_part) === TList::class
&& $type2_part instanceof TNonEmptyList)
|| (get_class($type2_part) === TList::class
&& $type1_part instanceof TNonEmptyList)
) {
return UnionTypeComparator::canExpressionTypesBeIdentical(
$codebase,
$type1_part->type_param,
$type2_part->type_param
);
}
if ((get_class($type1_part) === TArray::class
&& $type2_part instanceof TNonEmptyArray)
|| (get_class($type2_part) === TArray::class

View File

@ -46,7 +46,6 @@ 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\TLiteralInt;
use Psalm\Type\Atomic\TLiteralString;
use Psalm\Type\Atomic\TLowercaseString;
@ -359,7 +358,7 @@ class SimpleAssertionReconciler extends Reconciler
if ($assertion_type instanceof TKeyedArray
&& $assertion_type->is_list
&& $assertion_type->type_param->isMixed()
&& $assertion_type->getGenericValueType()->isMixed()
) {
return self::reconcileList(
$assertion,
@ -690,6 +689,9 @@ class SimpleAssertionReconciler extends Reconciler
return $existing_var_type->freeze();
}
/**
* @param array<string> $suppressed_issues
*/
private static function reconcileExactlyCountable(
Union $existing_var_type,
HasExactCount $assertion,
@ -747,12 +749,10 @@ class SimpleAssertionReconciler extends Reconciler
$array_atomic_type->properties
)
));
} elseif ($existing_var_type->is_list) {
} elseif ($array_atomic_type->is_list) {
$properties = $array_atomic_type->properties;
for ($x = $prop_min_count; $x < $assertion->count; $x++) {
$properties[$x] = isset($properties[$x])
? $properties[$x]->setPossiblyUndefined(false)
: $array_atomic_type->fallback_params[1];
$properties[$x] = $properties[$x]->setPossiblyUndefined(false);
}
$array_atomic_type = new TKeyedArray(
$properties,

View File

@ -59,13 +59,10 @@ use Psalm\Type\Reconciler;
use Psalm\Type\Union;
use function assert;
use function count;
use function get_class;
use function max;
use function strpos;
use const INF;
/**
* @internal
*/

View File

@ -1488,12 +1488,21 @@ class TypeCombiner
true
);
} else {
/** @psalm-suppress ArgumentTypeCoercion */
$array_type = Type::getNonEmptyListAtomic(
$generic_type_params[1],
$combination->array_min_counts
? min(array_keys($combination->array_min_counts))
: null
$cnt = $combination->array_min_counts
? min(array_keys($combination->array_min_counts))
: 0;
$properties = [];
for ($x = 0; $x < $cnt; $x++) {
$properties []= $generic_type_params[1];
}
if (!$properties) {
$properties []= $generic_type_params[1]->setPossiblyUndefined(true);
}
$array_type = new TKeyedArray(
$properties,
null,
[new Union([new TIntRange($cnt, null)]), $generic_type_params[1]],
true
);
}
} else {

View File

@ -464,7 +464,8 @@ abstract class Type
[$of->setPossiblyUndefined(true)],
null,
[self::getInt(), $of],
true
true,
$from_docblock
);
}
@ -477,26 +478,11 @@ abstract class Type
[$of->setPossiblyUndefined(false)],
null,
[self::getInt(), $of],
true
true,
$from_docblock
);
}
/**
* @psalm-pure
*/
public static function getCallableListAtomic(Union $of, bool $from_docblock = false): Atomic
{
// The following code will be uncommented in Psalm 5.1
//$of = $of->setPossiblyUndefined(false);
//return new TCallableKeyedArray(
// [$of, $of],
// null,
// [self::getInt(), $of],
// true
//);
return new TCallableList($of, null, null, $from_docblock);
}
/**
* @psalm-pure
*/

View File

@ -370,6 +370,9 @@ class TKeyedArray extends Atomic
return false;
}
/**
* @return int<0, max>
*/
public function getMinCount(): int
{
if ($this->is_list) {
@ -380,7 +383,7 @@ class TKeyedArray extends Atomic
}
}
$prop_min_count = 0;
foreach ($this->properties as $k => $property) {
foreach ($this->properties as $property) {
if (!$property->possibly_undefined) {
$prop_min_count++;
}
@ -390,6 +393,7 @@ class TKeyedArray extends Atomic
/**
* Returns null if there is no upper limit.
* @return int<1, max>|null
*/
public function getMaxCount(): ?int
{