diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index bcb9dafae0..159a523599 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -1303,6 +1303,28 @@ private function specifyTypesForConstantStringBinaryExpression( )->setRootExpr($rootExpr); } + if ( + $context->false() + && $exprNode instanceof FuncCall + && $exprNode->name instanceof Name + && in_array(strtolower((string) $exprNode->name), [ + 'trim', 'ltrim', 'rtrim', + 'mb_trim', 'mb_ltrim', 'mb_rtrim', + ], true) + && isset($exprNode->getArgs()[0]) + && $constantStringValue === '' + ) { + return $this->create( + $exprNode->getArgs()[0]->value, + TypeCombinator::intersect( + new StringType(), + new AccessoryNonEmptyStringType(), + ), + $context->negate(), + $scope, + )->setRootExpr($rootExpr); + } + return null; } diff --git a/tests/PHPStan/Analyser/nsrt/bug-12973-php84.php b/tests/PHPStan/Analyser/nsrt/bug-12973-php84.php new file mode 100644 index 0000000000..cfbe20ce83 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-12973-php84.php @@ -0,0 +1,29 @@ += 8.4 + +namespace Bug12973Php84; + +use function PHPStan\Testing\assertType; + +function mbtrim($value): void +{ + if (mb_trim($value) === '') { + assertType('mixed', $value); + } else { + assertType('non-empty-string', $value); + } + assertType('mixed', $value); + + if (mb_ltrim($value) === '') { + assertType('mixed', $value); + } else { + assertType('non-empty-string', $value); + } + assertType('mixed', $value); + + if (mb_rtrim($value) === '') { + assertType('mixed', $value); + } else { + assertType('non-empty-string', $value); + } + assertType('mixed', $value); +} diff --git a/tests/PHPStan/Analyser/nsrt/bug-12973.php b/tests/PHPStan/Analyser/nsrt/bug-12973.php new file mode 100644 index 0000000000..14116eef93 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-12973.php @@ -0,0 +1,79 @@ +checkNullables = true; + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-12973.php'], []); + } + }