1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Improve return types of explode() with limit (#2286)

This commit is contained in:
Pavel Batečko 2019-11-02 10:10:38 +01:00 committed by Matthew Brown
parent a82e35a59f
commit b229ac875d
2 changed files with 96 additions and 12 deletions

View File

@ -249,20 +249,30 @@ class FunctionAnalyzer extends FunctionLikeAnalyzer
break;
case 'explode':
if (count($call_args) === 2) {
if (count($call_args) >= 2) {
$can_return_empty = isset($call_args[2])
&& (
!$call_args[2]->value instanceof PhpParser\Node\Scalar\LNumber
|| $call_args[2]->value->value < 0
);
if ($call_args[0]->value instanceof PhpParser\Node\Scalar\String_) {
if ($call_args[0]->value->value === '') {
return Type::getFalse();
}
return new Type\Union([
new Type\Atomic\TNonEmptyList(Type::getString())
$can_return_empty
? new Type\Atomic\TList(Type::getString())
: new Type\Atomic\TNonEmptyList(Type::getString())
]);
} elseif (isset($call_args[0]->value->inferredType)
&& $call_args[0]->value->inferredType->hasString()
) {
$falsable_array = new Type\Union([
new Type\Atomic\TNonEmptyList(Type::getString()),
$can_return_empty
? new Type\Atomic\TList(Type::getString())
: new Type\Atomic\TNonEmptyList(Type::getString()),
new Type\Atomic\TFalse
]);

View File

@ -945,19 +945,93 @@ class FunctionCallTest extends TestCase
'$c' => 'int',
],
],
'explode' => [
'<?php
/** @var string $string */
$elements = explode(" ", $string);',
'assertions' => [
'$elements' => 'non-empty-list<string>',
],
],
'explodeWithPositiveLimit' => [
'<?php
/** @var string $string */
$elements = explode(" ", $string, 5);',
'assertions' => [
'$elements' => 'non-empty-list<string>',
],
],
'explodeWithNegativeLimit' => [
'<?php
/** @var string $string */
$elements = explode(" ", $string, -5);',
'assertions' => [
'$elements' => 'list<string>',
],
],
'explodeWithDynamicLimit' => [
'<?php
/**
* @var string $string
* @var int $limit
*/
$elements = explode(" ", $string, $limit);',
'assertions' => [
'$elements' => 'list<string>',
],
],
'explodeWithDynamicDelimiter' => [
'<?php
/**
* @var string $delim
* @var string $string
*/
$elements = explode($delim, $string);',
'assertions' => [
'$elements' => 'false|non-empty-list<string>',
],
],
'explodeWithDynamicDelimiterAndPositiveLimit' => [
'<?php
/**
* @var string $delim
* @var string $string
*/
$elements = explode($delim, $string, 5);',
'assertions' => [
'$elements' => 'false|non-empty-list<string>',
],
],
'explodeWithDynamicDelimiterAndNegativeLimit' => [
'<?php
/**
* @var string $delim
* @var string $string
*/
$elements = explode($delim, $string, -5);',
'assertions' => [
'$elements' => 'false|list<string>',
],
],
'explodeWithDynamicDelimiterAndLimit' => [
'<?php
/**
* @var string $delim
* @var string $string
* @var int $limit
*/
$elements = explode($delim, $string, $limit);',
'assertions' => [
'$elements' => 'false|list<string>',
],
],
'explodeWithPossiblyFalse' => [
'<?php
/** @return array<int, string> */
function exploder(string $s) : array {
return explode(" ", $s);
/** @return non-empty-list<string> */
function exploder(string $d, string $s) : array {
return explode($d, $s);
}',
],
'explodeWithThirdArg' => [
'<?php
$elements = explode("_", "", -1);
$element = array_shift($elements);
assert(null !== $element);'
],
'allowPossiblyUndefinedClassInClassExists' => [
'<?php
if (class_exists(Foo::class)) {}',