Skip to content

Commit cca1d2b

Browse files
Improve TrimDynamicReturnType
1 parent f001793 commit cca1d2b

File tree

4 files changed

+93
-12
lines changed

4 files changed

+93
-12
lines changed

phpstan-baseline.neon

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,12 +1560,6 @@ parameters:
15601560
count: 1
15611561
path: src/Type/Php/JsonThrowOnErrorDynamicReturnTypeExtension.php
15621562

1563-
-
1564-
message: '#^Doing instanceof PHPStan\\Type\\Constant\\ConstantStringType is error\-prone and deprecated\. Use Type\:\:getConstantStrings\(\) instead\.$#'
1565-
identifier: phpstanApi.instanceofType
1566-
count: 2
1567-
path: src/Type/Php/LtrimFunctionReturnTypeExtension.php
1568-
15691563
-
15701564
message: '#^Doing instanceof PHPStan\\Type\\Constant\\ConstantStringType is error\-prone and deprecated\. Use Type\:\:getConstantStrings\(\) instead\.$#'
15711565
identifier: phpstanApi.instanceofType

src/Type/Php/LtrimFunctionReturnTypeExtension.php

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPStan\Type\IntersectionType;
1515
use PHPStan\Type\StringType;
1616
use PHPStan\Type\Type;
17+
use PHPStan\Type\TypeCombinator;
1718
use function count;
1819
use function ltrim;
1920

@@ -53,12 +54,27 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
5354

5455
$trimChars = $scope->getType($functionCall->getArgs()[1]->value);
5556

56-
if ($trimChars instanceof ConstantStringType && $trimChars->getValue() === '\\' && $string->isClassString()->yes()) {
57-
if ($string instanceof ConstantStringType) {
58-
return new ConstantStringType(ltrim($string->getValue(), $trimChars->getValue()), true);
57+
$trimConstantStrings = $trimChars->getConstantStrings();
58+
if (count($trimConstantStrings) > 0) {
59+
$result = [];
60+
$stringConstantStrings = $string->getConstantStrings();
61+
62+
foreach ($trimConstantStrings as $trimConstantString) {
63+
if (count($stringConstantStrings) > 0) {
64+
foreach ($stringConstantStrings as $stringConstantString) {
65+
$result[] = new ConstantStringType(
66+
ltrim($stringConstantString->getValue(), $trimConstantString->getValue()),
67+
true,
68+
);
69+
}
70+
} elseif ($trimConstantString->getValue() === '\\' && $string->isClassString()->yes()) {
71+
$result[] = new ClassStringType();
72+
} else {
73+
return $defaultType;
74+
}
5975
}
6076

61-
return new ClassStringType();
77+
return TypeCombinator::union(...$result);
6278
}
6379

6480
return $defaultType;

src/Type/Php/TrimFunctionDynamicReturnTypeExtension.php

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88
use PHPStan\Reflection\FunctionReflection;
99
use PHPStan\Type\Accessory\AccessoryLowercaseStringType;
1010
use PHPStan\Type\Accessory\AccessoryUppercaseStringType;
11+
use PHPStan\Type\Constant\ConstantStringType;
1112
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
1213
use PHPStan\Type\IntersectionType;
1314
use PHPStan\Type\StringType;
1415
use PHPStan\Type\Type;
16+
use PHPStan\Type\TypeCombinator;
1517
use function count;
1618
use function in_array;
19+
use function rtrim;
20+
use function trim;
1721

1822
#[AutowiredService]
1923
final class TrimFunctionDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
@@ -37,6 +41,7 @@ public function getTypeFromFunctionCall(
3741

3842
$stringType = $scope->getType($args[0]->value);
3943
$accessory = [];
44+
$defaultType = new StringType();
4045
if ($stringType->isLowercaseString()->yes()) {
4146
$accessory[] = new AccessoryLowercaseStringType();
4247
}
@@ -45,10 +50,40 @@ public function getTypeFromFunctionCall(
4550
}
4651
if (count($accessory) > 0) {
4752
$accessory[] = new StringType();
48-
return new IntersectionType($accessory);
53+
$defaultType = new IntersectionType($accessory);
4954
}
5055

51-
return new StringType();
56+
if (count($functionCall->getArgs()) !== 2) {
57+
return $defaultType;
58+
}
59+
60+
$trimChars = $scope->getType($functionCall->getArgs()[1]->value);
61+
62+
$trimConstantStrings = $trimChars->getConstantStrings();
63+
if (count($trimConstantStrings) > 0) {
64+
$result = [];
65+
$stringConstantStrings = $stringType->getConstantStrings();
66+
$functionName = $functionReflection->getName();
67+
68+
foreach ($trimConstantStrings as $trimConstantString) {
69+
if (count($stringConstantStrings) === 0) {
70+
return $defaultType;
71+
}
72+
73+
foreach ($stringConstantStrings as $stringConstantString) {
74+
$result[] = new ConstantStringType(
75+
$functionName === 'rtrim'
76+
? rtrim($stringConstantString->getValue(), $trimConstantString->getValue())
77+
: trim($stringConstantString->getValue(), $trimConstantString->getValue()),
78+
true,
79+
);
80+
}
81+
}
82+
83+
return TypeCombinator::union(...$result);
84+
}
85+
86+
return $defaultType;
5287
}
5388

5489
}

tests/PHPStan/Analyser/nsrt/trim.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace Trim;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Foo
8+
{
9+
10+
/**
11+
* @param 'foo' $foo
12+
* @param 'foo'|'bar' $fooOrBar
13+
* @param string $string
14+
*/
15+
public function doTrim($foo, $fooOrBar, $string): void
16+
{
17+
assertType('string', trim($string, $foo));
18+
assertType('string', ltrim($string, $foo));
19+
assertType('string', rtrim($string, $foo));
20+
21+
assertType('lowercase-string', trim($foo, $string));
22+
assertType('lowercase-string', ltrim($foo, $string));
23+
assertType('lowercase-string', rtrim($foo, $string));
24+
assertType('\'\'|\'foo\'', trim($foo, $fooOrBar));
25+
assertType('\'\'|\'foo\'', ltrim($foo, $fooOrBar));
26+
assertType('\'\'|\'foo\'', rtrim($foo, $fooOrBar));
27+
28+
assertType('lowercase-string', trim($fooOrBar, $string));
29+
assertType('lowercase-string', ltrim($fooOrBar, $string));
30+
assertType('lowercase-string', rtrim($fooOrBar, $string));
31+
assertType('\'\'|\'bar\'', trim($fooOrBar, $foo));
32+
assertType('\'\'|\'bar\'', ltrim($fooOrBar, $foo));
33+
assertType('\'\'|\'bar\'', rtrim($fooOrBar, $foo));
34+
}
35+
36+
}

0 commit comments

Comments
 (0)