Skip to content

Commit

Permalink
strtr returns non-empty-string
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm authored and ondrejmirtes committed Nov 4, 2022
1 parent 2ddf1e5 commit d91411b
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 4 deletions.
17 changes: 14 additions & 3 deletions src/Type/Php/ReplaceFunctionsDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class ReplaceFunctionsDynamicReturnTypeExtension implements DynamicFunctionRetur
'str_replace' => 2,
'str_ireplace' => 2,
'substr_replace' => 0,
'strtr' => 0,
];

/** @var array<string, int> */
Expand All @@ -38,6 +39,7 @@ class ReplaceFunctionsDynamicReturnTypeExtension implements DynamicFunctionRetur
'str_replace' => 1,
'str_ireplace' => 1,
'substr_replace' => 1,
'strtr' => 2,
];

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

$possibleTypes = ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType();
$possibleTypes = ParametersAcceptorSelector::selectFromArgs(
$scope,
$functionCall->getArgs(),
$functionReflection->getVariants(),
)->getReturnType();
// resolve conditional return types
$possibleTypes = TypeUtils::resolveLateResolvableTypes($possibleTypes);

Expand All @@ -71,12 +77,17 @@ private function getPreliminarilyResolvedTypeFromFunctionCall(
): Type
{
$argumentPosition = $this->functionsSubjectPosition[$functionReflection->getName()];
$defaultReturnType = ParametersAcceptorSelector::selectFromArgs(
$scope,
$functionCall->getArgs(),
$functionReflection->getVariants(),
)->getReturnType();

if (count($functionCall->getArgs()) <= $argumentPosition) {
return ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType();
return $defaultReturnType;
}

$subjectArgumentType = $scope->getType($functionCall->getArgs()[$argumentPosition]->value);
$defaultReturnType = ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType();
if ($subjectArgumentType instanceof MixedType) {
return TypeUtils::toBenevolentUnion($defaultReturnType);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9060,7 +9060,7 @@ public function dataGeneralizeScope(): array
{
return [
[
'array<non-empty-array<int|string, array{saveCount: int<0, max>, removeCount: int<0, max>, loadCount: int<0, max>, hitCount: int<0, max>}>>',
'array<string, non-empty-array<int|string, array{saveCount: int<0, max>, removeCount: int<0, max>, loadCount: int<0, max>, hitCount: int<0, max>}>>',
'$statistics',
],
];
Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7913.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Functions/data/bug-8280.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8272.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/strtr.php');
}

/**
Expand Down
27 changes: 27 additions & 0 deletions tests/PHPStan/Analyser/data/strtr.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Strtr;

use function PHPStan\Testing\assertType;

/**
* @param non-empty-string $nonEmptyString
* @param non-falsy-string $nonFalseyString
*/
function doFoo(string $s, $nonEmptyString, $nonFalseyString) {
assertType('string', strtr($s, 'f', 'b'));
assertType('string', strtr($s, ['f' => 'b']));
assertType('string', strtr($s, ['f' => 'b', 'o' => 'a']));

assertType('string', strtr($s, $s, $nonEmptyString));
assertType('string', strtr($s, $nonEmptyString, $nonEmptyString));
assertType('string', strtr($s, $nonFalseyString, $nonFalseyString));

assertType('non-empty-string', strtr($nonEmptyString, $s, $nonEmptyString));
assertType('non-empty-string', strtr($nonEmptyString, $nonEmptyString, $nonEmptyString));
assertType('non-empty-string', strtr($nonEmptyString, $nonFalseyString, $nonFalseyString));

assertType('non-empty-string', strtr($nonFalseyString, $s, $nonEmptyString));
assertType('non-falsy-string', strtr($nonFalseyString, $nonEmptyString, $nonFalseyString));
assertType('non-falsy-string', strtr($nonFalseyString, $nonFalseyString, $nonFalseyString));
}

0 comments on commit d91411b

Please sign in to comment.