Skip to content

Commit 719330d

Browse files
github-actions[bot]phpstan-bot
authored andcommitted
Fix missing type narrowing after (string)$value comparison
- Added handling in TypeSpecifier::resolveNormalizedIdentical for Cast\String_ expressions - When (string)$x === '' or (string)$x !== '', propagate narrowing to inner expression $x - Types that produce '' when cast to string (null, false, '') are used as the narrowing type - New regression test in tests/PHPStan/Analyser/nsrt/bug-8231.php
1 parent c36922b commit 719330d

2 files changed

Lines changed: 63 additions & 0 deletions

File tree

src/Analyser/TypeSpecifier.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2934,6 +2934,27 @@ private function resolveNormalizedIdentical(Expr\BinaryOp\Identical $expr, Scope
29342934
}
29352935
}
29362936

2937+
// (string)$expr === '' - propagate narrowing to inner expression
2938+
if (
2939+
!$context->null()
2940+
&& $unwrappedLeftExpr instanceof Expr\Cast\String_
2941+
) {
2942+
$rightConstantStrings = $rightType->getConstantStrings();
2943+
if (count($rightConstantStrings) === 1 && $rightConstantStrings[0]->getValue() === '') {
2944+
// Types that produce '' when cast to string: null, false, ''
2945+
$castToEmptyStringType = TypeCombinator::union(
2946+
new NullType(),
2947+
new ConstantBooleanType(false),
2948+
new ConstantStringType(''),
2949+
);
2950+
$innerExpr = $unwrappedLeftExpr->expr;
2951+
$result = $this->create($leftExpr, $rightType, $context, $scope)->setRootExpr($expr);
2952+
return $result->unionWith(
2953+
$this->create($innerExpr, $castToEmptyStringType, $context, $scope)->setRootExpr($expr),
2954+
);
2955+
}
2956+
}
2957+
29372958
$expressions = $this->findTypeExpressionsFromBinaryOperation($scope, $expr);
29382959
if ($expressions !== null) {
29392960
$exprNode = $expressions[0];
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug8231;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
function foo(string $x): void {}
10+
11+
function test(string|null $x): void {
12+
if ((string)$x !== '') {
13+
assertType('non-empty-string', $x);
14+
foo($x);
15+
}
16+
}
17+
18+
function testIdentical(string|null $x): void {
19+
if ((string)$x === '') {
20+
assertType("''|null", $x);
21+
} else {
22+
assertType('non-empty-string', $x);
23+
}
24+
}
25+
26+
function testInt(int|null $x): void {
27+
if ((string)$x !== '') {
28+
assertType('int', $x);
29+
}
30+
}
31+
32+
function testIntString(int|string|null $x): void {
33+
if ((string)$x !== '') {
34+
assertType('int|non-empty-string', $x);
35+
}
36+
}
37+
38+
function testBool(bool|string|null $x): void {
39+
if ((string)$x !== '') {
40+
assertType('non-empty-string|true', $x);
41+
}
42+
}

0 commit comments

Comments
 (0)