Skip to content

Commit

Permalink
StrlenFunctionReturnTypeExtension: cover more scalar types
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm committed Aug 20, 2021
1 parent d5b2c88 commit 2f991d1
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 3 deletions.
38 changes: 37 additions & 1 deletion src/Type/Php/StrlenFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Type\BooleanType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
use PHPStan\Type\IntegerRangeType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\TypeUtils;

class StrlenFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
{
Expand All @@ -30,8 +33,41 @@ public function getTypeFromFunctionCall(
}

$argType = $scope->getType($args[0]->value);

$constantStrings = TypeUtils::getConstantStrings($argType);
$min = null;
$max = null;
foreach ($constantStrings as $constantString) {
$len = strlen($constantString->getValue());

if ($min === null) {
$min = $len;
$max = $len;
}

if ($len < $min) {
$min = $len;
}
if ($len <= $max) {
continue;
}

$max = $len;
}

// $max is always != null, when $min is != null
if ($min !== null) {
return IntegerRangeType::fromInterval($min, $max);
}

$bool = new BooleanType();
if ($bool->isSuperTypeOf($argType)->yes()) {
return IntegerRangeType::fromInterval(0, 1);
}

$isNonEmpty = $argType->isNonEmptyString();
if ($isNonEmpty->yes()) {
$integer = new IntegerType();
if ($isNonEmpty->yes() || $integer->isSuperTypeOf($argType)->yes()) {
return IntegerRangeType::fromInterval(1, null);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Analyser/data/bug-5129.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function sayHello(string $s): void
assertType('0', strlen($this->foo));

$this->foo = 'x';
assertType('int<1, max>', strlen($this->foo));
assertType('1', strlen($this->foo));
if (strlen($this->foo) > 0) {
return;
}
Expand Down
7 changes: 6 additions & 1 deletion tests/PHPStan/Analyser/data/non-empty-string.php
Original file line number Diff line number Diff line change
Expand Up @@ -302,8 +302,9 @@ class MoreNonEmptyStringFunctions

/**
* @param non-empty-string $nonEmpty
* @param '1'|'2'|'5'|'10' $constUnion
*/
public function doFoo(string $s, string $nonEmpty, int $i)
public function doFoo(string $s, string $nonEmpty, int $i, bool $bool, $constUnion)
{
assertType('string', addslashes($s));
assertType('non-empty-string', addslashes($nonEmpty));
Expand Down Expand Up @@ -349,8 +350,12 @@ public function doFoo(string $s, string $nonEmpty, int $i)
assertType('non-empty-string', vsprintf($nonEmpty, []));

assertType('0', strlen(''));
assertType('5', strlen('hallo'));
assertType('int<0, 1>', strlen($bool));
assertType('int<1, max>', strlen($i));
assertType('int<0, max>', strlen($s));
assertType('int<1, max>', strlen($nonEmpty));
assertType('int<1, 2>', strlen($constUnion));

assertType('non-empty-string', str_pad($nonEmpty, 0));
assertType('non-empty-string', str_pad($nonEmpty, 1));
Expand Down

0 comments on commit 2f991d1

Please sign in to comment.