Skip to content

Commit 00ec456

Browse files
committed
strtr returns non-empty-string
1 parent e3055ae commit 00ec456

File tree

4 files changed

+43
-4
lines changed

4 files changed

+43
-4
lines changed

src/Type/Php/ReplaceFunctionsDynamicReturnTypeExtension.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class ReplaceFunctionsDynamicReturnTypeExtension implements DynamicFunctionRetur
3030
'str_replace' => 2,
3131
'str_ireplace' => 2,
3232
'substr_replace' => 0,
33+
'strtr' => 0,
3334
];
3435

3536
/** @var array<string, int> */
@@ -38,6 +39,7 @@ class ReplaceFunctionsDynamicReturnTypeExtension implements DynamicFunctionRetur
3839
'str_replace' => 1,
3940
'str_ireplace' => 1,
4041
'substr_replace' => 1,
42+
'strtr' => 2,
4143
];
4244

4345
public function isFunctionSupported(FunctionReflection $functionReflection): bool
@@ -53,7 +55,11 @@ public function getTypeFromFunctionCall(
5355
{
5456
$type = $this->getPreliminarilyResolvedTypeFromFunctionCall($functionReflection, $functionCall, $scope);
5557

56-
$possibleTypes = ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType();
58+
$possibleTypes = ParametersAcceptorSelector::selectFromArgs(
59+
$scope,
60+
$functionCall->getArgs(),
61+
$functionReflection->getVariants(),
62+
)->getReturnType();
5763
// resolve conditional return types
5864
$possibleTypes = TypeUtils::resolveLateResolvableTypes($possibleTypes);
5965

@@ -71,12 +77,17 @@ private function getPreliminarilyResolvedTypeFromFunctionCall(
7177
): Type
7278
{
7379
$argumentPosition = $this->functionsSubjectPosition[$functionReflection->getName()];
80+
$defaultReturnType = ParametersAcceptorSelector::selectFromArgs(
81+
$scope,
82+
$functionCall->getArgs(),
83+
$functionReflection->getVariants(),
84+
)->getReturnType();
85+
7486
if (count($functionCall->getArgs()) <= $argumentPosition) {
75-
return ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType();
87+
return $defaultReturnType;
7688
}
7789

7890
$subjectArgumentType = $scope->getType($functionCall->getArgs()[$argumentPosition]->value);
79-
$defaultReturnType = ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType();
8091
if ($subjectArgumentType instanceof MixedType) {
8192
return TypeUtils::toBenevolentUnion($defaultReturnType);
8293
}

tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9060,7 +9060,7 @@ public function dataGeneralizeScope(): array
90609060
{
90619061
return [
90629062
[
9063-
'array<non-empty-array<int|string, array{saveCount: int<0, max>, removeCount: int<0, max>, loadCount: int<0, max>, hitCount: int<0, max>}>>',
9063+
'array<string, non-empty-array<int|string, array{saveCount: int<0, max>, removeCount: int<0, max>, loadCount: int<0, max>, hitCount: int<0, max>}>>',
90649064
'$statistics',
90659065
],
90669066
];

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,7 @@ public function dataFileAsserts(): iterable
11221122
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7913.php');
11231123
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Functions/data/bug-8280.php');
11241124
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8272.php');
1125+
yield from $this->gatherAssertTypes(__DIR__ . '/data/strtr.php');
11251126
}
11261127

11271128
/**
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace Strtr;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @param non-empty-string $nonEmptyString
9+
* @param non-falsy-string $nonFalseyString
10+
*/
11+
function doFoo(string $s, $nonEmptyString, $nonFalseyString) {
12+
assertType('string', strtr($s, 'f', 'b'));
13+
assertType('string', strtr($s, ['f' => 'b']));
14+
assertType('string', strtr($s, ['f' => 'b', 'o' => 'a']));
15+
16+
assertType('string', strtr($s, $s, $nonEmptyString));
17+
assertType('string', strtr($s, $nonEmptyString, $nonEmptyString));
18+
assertType('string', strtr($s, $nonFalseyString, $nonFalseyString));
19+
20+
assertType('non-empty-string', strtr($nonEmptyString, $s, $nonEmptyString));
21+
assertType('non-empty-string', strtr($nonEmptyString, $nonEmptyString, $nonEmptyString));
22+
assertType('non-empty-string', strtr($nonEmptyString, $nonFalseyString, $nonFalseyString));
23+
24+
assertType('non-empty-string', strtr($nonFalseyString, $s, $nonEmptyString));
25+
assertType('non-falsy-string', strtr($nonFalseyString, $nonEmptyString, $nonFalseyString));
26+
assertType('non-falsy-string', strtr($nonFalseyString, $nonFalseyString, $nonFalseyString));
27+
}

0 commit comments

Comments
 (0)