mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
#4997 added more precise type inference for count()
returning 0
or positive-int
on known arrays (#4999)
* #4997 added more precise stub for `count()` returning `0` or `positive-int` on known types * #4997 updated `count()` to support `\SimpleXmlElement` and `\ResourceBundle` counting, as well as handling hardcoded 2-element-arrays cases This patch: * adds support for `count(\SimpleXmlElement)` (https://www.php.net/manual/en/simplexmlelement.count.php) * adds support for `count(\ResourceBundle)` (https://www.php.net/manual/en/resourcebundle.count.php) * removes usage of global constants from stub (not supported - see https://www.php.net/manual/en/function.count.php) * adds support for identifying fixed-element-count arrays, for example `count(callable&array)`, which is always `2` * #4997 adapted `FunctionCallReturnTypeFetcher` to infer `TPositiveInt` for `count(TNonEmptyArray)` and `count(TNonEmptyList)` * The `FunctionCallReturnTypeFetcher` is responsible for defining the precise type of a `\count(T)` expression when given a `T`, so we baked the whole type resolution for `positive-int`, `0` and `positive-int|0` directly in there. While this complicates things, it is also true that it is not possible right now (for the stubs) to provide the level of detail around `count()` that is required by the type inference system for such a complex function with so many different semantics.
This commit is contained in:
parent
2daa27548e
commit
0b5a828f6f
@ -300,7 +300,7 @@ class FunctionCallReturnTypeFetcher
|
|||||||
return new Type\Union([
|
return new Type\Union([
|
||||||
$atomic_types['array']->count !== null
|
$atomic_types['array']->count !== null
|
||||||
? new Type\Atomic\TLiteralInt($atomic_types['array']->count)
|
? new Type\Atomic\TLiteralInt($atomic_types['array']->count)
|
||||||
: new Type\Atomic\TInt
|
: new Type\Atomic\TPositiveInt
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,7 +308,7 @@ class FunctionCallReturnTypeFetcher
|
|||||||
return new Type\Union([
|
return new Type\Union([
|
||||||
$atomic_types['array']->count !== null
|
$atomic_types['array']->count !== null
|
||||||
? new Type\Atomic\TLiteralInt($atomic_types['array']->count)
|
? new Type\Atomic\TLiteralInt($atomic_types['array']->count)
|
||||||
: new Type\Atomic\TInt
|
: new Type\Atomic\TPositiveInt
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,6 +319,11 @@ class FunctionCallReturnTypeFetcher
|
|||||||
new Type\Atomic\TLiteralInt(count($atomic_types['array']->properties))
|
new Type\Atomic\TLiteralInt(count($atomic_types['array']->properties))
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new Type\Union([
|
||||||
|
new Type\Atomic\TLiteralInt(0),
|
||||||
|
new Type\Atomic\TPositiveInt
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1054,6 +1054,83 @@ class FunctionCallTest extends TestCase
|
|||||||
}
|
}
|
||||||
}'
|
}'
|
||||||
],
|
],
|
||||||
|
'countNonEmptyArrayShouldBePositiveInt' => [
|
||||||
|
'<?php
|
||||||
|
/**
|
||||||
|
* @psalm-pure
|
||||||
|
* @param non-empty-list $x
|
||||||
|
* @return positive-int
|
||||||
|
*/
|
||||||
|
function example($x) : int {
|
||||||
|
return count($x);
|
||||||
|
}',
|
||||||
|
],
|
||||||
|
'countListShouldBeZeroOrPositive' => [
|
||||||
|
'<?php
|
||||||
|
/**
|
||||||
|
* @psalm-pure
|
||||||
|
* @param list $x
|
||||||
|
* @return positive-int|0
|
||||||
|
*/
|
||||||
|
function example($x) : int {
|
||||||
|
return count($x);
|
||||||
|
}',
|
||||||
|
],
|
||||||
|
'countArrayShouldBeZeroOrPositive' => [
|
||||||
|
'<?php
|
||||||
|
/**
|
||||||
|
* @psalm-pure
|
||||||
|
* @param array $x
|
||||||
|
* @return positive-int|0
|
||||||
|
*/
|
||||||
|
function example($x) : int {
|
||||||
|
return count($x);
|
||||||
|
}',
|
||||||
|
],
|
||||||
|
'countEmptyArrayShouldBeZero' => [
|
||||||
|
'<?php
|
||||||
|
/**
|
||||||
|
* @psalm-pure
|
||||||
|
* @param array<empty, empty> $x
|
||||||
|
* @return 0
|
||||||
|
*/
|
||||||
|
function example($x) : int {
|
||||||
|
return count($x);
|
||||||
|
}',
|
||||||
|
],
|
||||||
|
'countConstantSizeArrayShouldBeConstantInteger' => [
|
||||||
|
'<?php
|
||||||
|
/**
|
||||||
|
* @psalm-pure
|
||||||
|
* @param array{int, int, string} $x
|
||||||
|
* @return 3
|
||||||
|
*/
|
||||||
|
function example($x) : int {
|
||||||
|
return count($x);
|
||||||
|
}',
|
||||||
|
],
|
||||||
|
'countCallableArrayShouldBe2' => [
|
||||||
|
'<?php
|
||||||
|
/**
|
||||||
|
* @psalm-pure
|
||||||
|
* @return 2
|
||||||
|
*/
|
||||||
|
function example(callable $x) : int {
|
||||||
|
assert(is_array($x));
|
||||||
|
return count($x);
|
||||||
|
}',
|
||||||
|
],
|
||||||
|
'countOnPureObjectIsPure' => [
|
||||||
|
'<?php
|
||||||
|
class PureCountable implements \Countable {
|
||||||
|
/** @psalm-pure */
|
||||||
|
public function count(): int { return 1; }
|
||||||
|
}
|
||||||
|
/** @psalm-pure */
|
||||||
|
function example(PureCountable $x) : int {
|
||||||
|
return count($x);
|
||||||
|
}',
|
||||||
|
],
|
||||||
'refineWithTraitExists' => [
|
'refineWithTraitExists' => [
|
||||||
'<?php
|
'<?php
|
||||||
function foo(string $s) : void {
|
function foo(string $s) : void {
|
||||||
@ -1820,6 +1897,22 @@ class FunctionCallTest extends TestCase
|
|||||||
}',
|
}',
|
||||||
'error_message' => 'TypeDoesNotContainType',
|
'error_message' => 'TypeDoesNotContainType',
|
||||||
],
|
],
|
||||||
|
'countOnObjectCannotBePositive' => [
|
||||||
|
'<?php
|
||||||
|
/** @return positive-int|0 */
|
||||||
|
function example(\Countable $x) : int {
|
||||||
|
return count($x);
|
||||||
|
}',
|
||||||
|
'error_message' => 'LessSpecificReturnStatement',
|
||||||
|
],
|
||||||
|
'countOnUnknownObjectCannotBePure' => [
|
||||||
|
'<?php
|
||||||
|
/** @psalm-pure */
|
||||||
|
function example(\Countable $x) : int {
|
||||||
|
return count($x);
|
||||||
|
}',
|
||||||
|
'error_message' => 'ImpureFunctionCall',
|
||||||
|
],
|
||||||
'coerceCallMapArgsInStrictMode' => [
|
'coerceCallMapArgsInStrictMode' => [
|
||||||
'<?php
|
'<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
Loading…
Reference in New Issue
Block a user