From 98590b0ccc91aa8994ba7110e97929104a28154a Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Thu, 23 Apr 2026 15:43:01 +0200 Subject: [PATCH 1/3] Improve inference for truethy comparisons --- src/Analyser/TypeSpecifier.php | 14 ++++++++++++++ .../nsrt/non-empty-string-str-containing-fns.php | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index 3e50294c94b..6441c094726 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -1548,6 +1548,20 @@ private function specifyTypesForConstantBinaryExpression( )->setRootExpr($rootExpr)); } + if ( + $context->true() + && $constantType->toBoolean()->isTrue()->yes() + && ($exprNode instanceof FuncCall || $exprNode instanceof Expr\MethodCall || $exprNode instanceof Expr\StaticCall) + ) { + $types = $this->create($exprNode, $constantType, $context, $scope)->setRootExpr($rootExpr); + + return $types->unionWith($this->specifyTypesInCondition( + $scope, + $exprNode, + TypeSpecifierContext::createTrue(), + )->setRootExpr($rootExpr)); + } + return null; } diff --git a/tests/PHPStan/Analyser/nsrt/non-empty-string-str-containing-fns.php b/tests/PHPStan/Analyser/nsrt/non-empty-string-str-containing-fns.php index 19482b7fd04..e7a771f241d 100644 --- a/tests/PHPStan/Analyser/nsrt/non-empty-string-str-containing-fns.php +++ b/tests/PHPStan/Analyser/nsrt/non-empty-string-str-containing-fns.php @@ -100,7 +100,7 @@ public function variants(string $s) { assertType('string', $s); if (strpos($s, ':') === 5) { - assertType('string', $s); // could be non-empty-string + assertType('non-empty-string', $s); } assertType('string', $s); if (strpos($s, ':') !== 5) { @@ -152,7 +152,7 @@ public function variants(string $s) { assertType('string', $s); if (mb_strpos($s, ':') === 5) { - assertType('string', $s); // could be non-empty-string + assertType('non-falsy-string', $s); } assertType('string', $s); if (mb_strpos($s, ':') !== 5) { From 647d6303f9def0a9522e7eaeca22bf3cc52cacb4 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Thu, 23 Apr 2026 15:43:44 +0200 Subject: [PATCH 2/3] Update non-empty-string-str-containing-fns.php --- .../Analyser/nsrt/non-empty-string-str-containing-fns.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Analyser/nsrt/non-empty-string-str-containing-fns.php b/tests/PHPStan/Analyser/nsrt/non-empty-string-str-containing-fns.php index e7a771f241d..94a4baa6d92 100644 --- a/tests/PHPStan/Analyser/nsrt/non-empty-string-str-containing-fns.php +++ b/tests/PHPStan/Analyser/nsrt/non-empty-string-str-containing-fns.php @@ -100,7 +100,7 @@ public function variants(string $s) { assertType('string', $s); if (strpos($s, ':') === 5) { - assertType('non-empty-string', $s); + assertType('non-falsy-string', $s); } assertType('string', $s); if (strpos($s, ':') !== 5) { From 4056899820b3220dfda714abb5ce37eaaa68c2e1 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Thu, 23 Apr 2026 16:08:53 +0200 Subject: [PATCH 3/3] Update non-empty-string-str-containing-fns.php --- .../non-empty-string-str-containing-fns.php | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/PHPStan/Analyser/nsrt/non-empty-string-str-containing-fns.php b/tests/PHPStan/Analyser/nsrt/non-empty-string-str-containing-fns.php index 94a4baa6d92..bc3887d19a7 100644 --- a/tests/PHPStan/Analyser/nsrt/non-empty-string-str-containing-fns.php +++ b/tests/PHPStan/Analyser/nsrt/non-empty-string-str-containing-fns.php @@ -92,19 +92,72 @@ public function variants(string $s) { if (strpos($s, ':') !== false) { assertType('non-falsy-string', $s); + } else { + assertType('string', $s); } assertType('string', $s); if (strpos($s, ':') === false) { assertType('string', $s); + } else { + assertType('non-falsy-string', $s); + } + assertType('string', $s); + + if (strpos($s, '0') == 0) { // 0|false + assertType('string', $s); + } else { + assertType('string', $s); + } + assertType('string', $s); + + $oneOrZero = rand(0, 1); + if (strpos($s, '0') == $oneOrZero) { // 0|1|false + assertType('string', $s); + } else { + assertType('string', $s); + } + assertType('string', $s); + + $oneOrZero = rand(0, 1); + if (strpos($s, '0') === $oneOrZero) { + assertType('string', $s); // could be non-empty-string + } else { + assertType('string', $s); + } + assertType('string', $s); + + if (strpos($s, '0') == 1) { + assertType('string', $s); // could be non-empty-string + } else { + assertType('string', $s); + } + assertType('string', $s); + + if (strpos($s, '0') === 0) { + assertType('string', $s); // could be non-empty-string + } else { + assertType('string', $s); + } + assertType('string', $s); + + if (strpos($s, '0') === 5) { + assertType('non-empty-string', $s); // could be non-falsy-string + } else { + assertType('string', $s); } assertType('string', $s); if (strpos($s, ':') === 5) { assertType('non-falsy-string', $s); + } else { + assertType('string', $s); } assertType('string', $s); + if (strpos($s, ':') !== 5) { assertType('string', $s); + } else { + assertType('non-falsy-string', $s); } assertType('string', $s);