1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00

Return a non-empty-(lowercase-)string from mb_strtolower

This commit is contained in:
RobChett 2023-04-19 19:26:20 +01:00
parent 958eb5ae07
commit 74c1576b9c
3 changed files with 65 additions and 9 deletions

View File

@ -482,15 +482,25 @@ class FunctionCallReturnTypeFetcher
return $call_map_return_type;
case 'mb_strtolower':
$string_arg_type = $statements_analyzer->node_data->getType($call_args[0]->value);
if ($string_arg_type !== null && $string_arg_type->isNonEmptyString()) {
$returnType = Type::getNonEmptyLowercaseString();
} else {
$returnType = Type::getLowercaseString();
}
if (count($call_args) < 2) {
return Type::getLowercaseString();
return $returnType;
} else {
$second_arg_type = $statements_analyzer->node_data->getType($call_args[1]->value);
if ($second_arg_type && $second_arg_type->isNull()) {
return Type::getLowercaseString();
return $returnType;
}
}
return Type::getString();
if ($string_arg_type !== null && $string_arg_type->isNonEmptyString()) {
return Type::getNonEmptyString();
} else {
return Type::getString();
}
}
}

View File

@ -34,6 +34,7 @@ use Psalm\Type\Atomic\TMixed;
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TNever;
use Psalm\Type\Atomic\TNonEmptyLowercaseString;
use Psalm\Type\Atomic\TNonEmptyString;
use Psalm\Type\Atomic\TNonspecificLiteralInt;
use Psalm\Type\Atomic\TNonspecificLiteralString;
use Psalm\Type\Atomic\TString;
@ -1016,6 +1017,25 @@ trait UnionTrait
) === count($this->types);
}
/**
* @psalm-mutation-free
* @return bool true if this is a string
*/
public function isNonEmptyString(bool $check_templates = false): bool
{
return count(
array_filter(
$this->types,
static fn($type): bool => $type instanceof TNonEmptyString
|| ($type instanceof TLiteralString && $type->value !== '')
|| ($check_templates
&& $type instanceof TTemplateParam
&& $type->as->isNonEmptyString()
)
),
) === count($this->types);
}
/**
* @psalm-mutation-free
* @return bool true if this is a boolean

View File

@ -2026,20 +2026,46 @@ class FunctionCallTest extends TestCase
],
'mb_strtolowerProducesStringWithSecondArgument' => [
'code' => '<?php
$r = mb_strtolower("École", "BASE64");
/** @var non-empty-string $a */
$a = "École";
$r = mb_strtolower($a, "BASE64");
/** @var string $b */
$b = "";
$s = mb_strtolower($b, "BASE64");
$t = mb_strtolower("ABC", "BASE64");
$u = mb_strtolower("", "BASE64");
',
'assertions' => [
'$r===' => 'string',
'$r===' => 'non-empty-string',
'$s===' => 'string',
'$t===' => 'non-empty-string',
'$u===' => 'string',
],
],
'mb_strtolowerProducesLowercaseStringWithNullOrAbsentEncoding' => [
'code' => '<?php
$a = mb_strtolower("AAA");
$b = mb_strtolower("AAA", null);
/** @var non-empty-string $i */
$i = "École";
/** @var string $j */
$j = "";
$a = mb_strtolower($i);
$b = mb_strtolower($i, null);
$c = mb_strtolower($j);
$d = mb_strtolower($j, null);
$e = mb_strtolower("AAA");
$f = mb_strtolower("AAA", null);
$g = mb_strtolower("");
$h = mb_strtolower("", null);
',
'assertions' => [
'$a===' => 'lowercase-string',
'$b===' => 'lowercase-string',
'$a===' => 'non-empty-lowercase-string',
'$b===' => 'non-empty-lowercase-string',
'$c===' => 'lowercase-string',
'$d===' => 'lowercase-string',
'$e===' => 'non-empty-lowercase-string',
'$f===' => 'non-empty-lowercase-string',
'$g===' => 'lowercase-string',
'$h===' => 'lowercase-string',
],
'ignored_issues' => [],
'php_version' => '8.1',