diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index fd243efb16..f02e5f51d2 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -197,15 +197,31 @@ public function specifyTypesInCondition( $context, $rootExpr, ); - } elseif ( - $expr instanceof Expr\Cast\String_ - || $expr instanceof Expr\Cast\Double - || $expr instanceof Expr\Cast\Int_ - || $expr instanceof Expr\Cast\Bool_ - ) { + } elseif ($expr instanceof Expr\Cast\Bool_) { + return $this->specifyTypesInCondition( + $scope, + new Node\Expr\BinaryOp\Equal($expr->expr, new ConstFetch(new Name\FullyQualified('true'))), + $context, + $rootExpr, + ); + } elseif ($expr instanceof Expr\Cast\String_) { + return $this->specifyTypesInCondition( + $scope, + new Node\Expr\BinaryOp\NotEqual($expr->expr, new Node\Scalar\String_('')), + $context, + $rootExpr, + ); + } elseif ($expr instanceof Expr\Cast\Int_) { + return $this->specifyTypesInCondition( + $scope, + new Node\Expr\BinaryOp\NotEqual($expr->expr, new Node\Scalar\LNumber(0)), + $context, + $rootExpr, + ); + } elseif ($expr instanceof Expr\Cast\Double) { return $this->specifyTypesInCondition( $scope, - new Node\Expr\BinaryOp\NotEqual($expr->expr, new ConstFetch(new Name\FullyQualified('false'))), + new Node\Expr\BinaryOp\NotEqual($expr->expr, new Node\Scalar\DNumber(0.0)), $context, $rootExpr, ); diff --git a/tests/PHPStan/Analyser/nsrt/narrow-cast.php b/tests/PHPStan/Analyser/nsrt/narrow-cast.php index afd5224ae0..82e09e0bd3 100644 --- a/tests/PHPStan/Analyser/nsrt/narrow-cast.php +++ b/tests/PHPStan/Analyser/nsrt/narrow-cast.php @@ -31,9 +31,9 @@ function doFoo(string $x, array $arr): void { /** @param int<-5, 5> $x */ function castString($x, string $s, bool $b) { if ((string) $x) { - assertType('int<-5, -1>|int<1, 5>', $x); + assertType('int<-5, 5>', $x); } else { - assertType('0', $x); + assertType('int<-5, 5>', $x); } if ((string) $b) { @@ -63,8 +63,14 @@ function castInt($x, string $s, bool $b) { assertType('false', $b); } + if ((int) $s) { + assertType('string', $s); + } else { + assertType('string', $s); + } + if ((int) strpos($s, 'xy')) { - assertType('non-falsy-string', $s); + assertType('string', $s); } else { assertType('string', $s); } @@ -73,9 +79,9 @@ function castInt($x, string $s, bool $b) { /** @param int<-5, 5> $x */ function castFloat($x, string $s, bool $b) { if ((float) $x) { - assertType('int<-5, -1>|int<1, 5>', $x); + assertType('int<-5, 5>', $x); } else { - assertType('0', $x); + assertType('int<-5, 5>', $x); } if ((float) $b) { @@ -85,8 +91,8 @@ function castFloat($x, string $s, bool $b) { } if ((float) $s) { - assertType('non-falsy-string', $s); + assertType('string', $s); } else { - assertType("''|'0'", $s); + assertType("string", $s); } } diff --git a/tests/PHPStan/Rules/Comparison/ElseIfConstantConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/ElseIfConstantConditionRuleTest.php index 7d3b008d90..0643363ef1 100644 --- a/tests/PHPStan/Rules/Comparison/ElseIfConstantConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ElseIfConstantConditionRuleTest.php @@ -4,6 +4,7 @@ use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; +use const PHP_VERSION_ID; /** * @extends RuleTestCase @@ -121,4 +122,19 @@ public function testReportPhpDoc(): void ]); } + public function testBug11674(): void + { + if (PHP_VERSION_ID < 80000) { + $this->markTestSkipped('Test requires PHP 8.0'); + } + + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-11674.php'], [ + [ + 'Elseif condition is always false.', + 28, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Comparison/data/bug-11674.php b/tests/PHPStan/Rules/Comparison/data/bug-11674.php new file mode 100644 index 0000000000..7af6660da4 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-11674.php @@ -0,0 +1,40 @@ += 8.0 + +namespace Bug11674; + +class Test { + + private ?string $param; + + function show() : void { + if ((int) $this->param) { + echo 1; + } elseif ($this->param) { + echo 2; + } + } + + function show2() : void { + if ((float) $this->param) { + echo 1; + } elseif ($this->param) { + echo 2; + } + } + + function show3() : void { + if ((bool) $this->param) { + echo 1; + } elseif ($this->param) { + echo 2; + } + } + + function show4() : void { + if ((string) $this->param) { + echo 1; + } elseif ($this->param) { + echo 2; + } + } +}