Skip to content

Commit 7f295eb

Browse files
Improve TrimDynamicReturnType
1 parent f001793 commit 7f295eb

File tree

4 files changed

+92
-12
lines changed

4 files changed

+92
-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: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
use PHPStan\Type\IntersectionType;
1515
use PHPStan\Type\StringType;
1616
use PHPStan\Type\Type;
17+
use PHPStan\Type\TypeCombinator;
18+
1719
use function count;
1820
use function ltrim;
1921

@@ -53,12 +55,27 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
5355

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

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

61-
return new ClassStringType();
78+
return TypeCombinator::union(...$result);
6279
}
6380

6481
return $defaultType;

src/Type/Php/TrimFunctionDynamicReturnTypeExtension.php

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
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;
1719

@@ -37,6 +39,7 @@ public function getTypeFromFunctionCall(
3739

3840
$stringType = $scope->getType($args[0]->value);
3941
$accessory = [];
42+
$defaultType = new StringType();
4043
if ($stringType->isLowercaseString()->yes()) {
4144
$accessory[] = new AccessoryLowercaseStringType();
4245
}
@@ -45,10 +48,40 @@ public function getTypeFromFunctionCall(
4548
}
4649
if (count($accessory) > 0) {
4750
$accessory[] = new StringType();
48-
return new IntersectionType($accessory);
51+
$defaultType = new IntersectionType($accessory);
4952
}
5053

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

5487
}

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('\'\'|\'bar\'', trim($foo, $fooOrBar));
25+
assertType('\'\'|\'bar\'', 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)