From dbf3eada8f8a5f9fc3ce75c9409d183596e51c73 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 11 Aug 2025 11:07:20 +0200 Subject: [PATCH 01/13] Fix false positive of function.alreadyNarrowedType (function call variable assignment) --- .../Comparison/ImpossibleCheckTypeHelper.php | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php index 0551a2b2d1..54570de4ae 100644 --- a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php +++ b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php @@ -68,11 +68,19 @@ public function findSpecifiedType( if ($node->isFirstClassCallable()) { return null; } - $argsCount = count($node->getArgs()); + $args = $node->getArgs(); + $argsCount = count($args); + + foreach ($args as $arg) { + if ($arg->value instanceof Expr\Assign) { + return null; + } + } + if ($node->name instanceof Node\Name) { $functionName = strtolower((string) $node->name); if ($functionName === 'assert' && $argsCount >= 1) { - $arg = $node->getArgs()[0]->value; + $arg = $args[0]->value; $assertValue = ($this->treatPhpDocTypesAsCertain ? $scope->getType($arg) : $scope->getNativeType($arg))->toBoolean(); $assertValueIsTrue = $assertValue->isTrue()->yes(); if (! $assertValueIsTrue && ! $assertValue->isFalse()->yes()) { @@ -96,7 +104,7 @@ public function findSpecifiedType( } elseif ($functionName === 'array_search') { return null; } elseif ($functionName === 'in_array' && $argsCount >= 2) { - $haystackArg = $node->getArgs()[1]->value; + $haystackArg = $args[1]->value; $haystackType = ($this->treatPhpDocTypesAsCertain ? $scope->getType($haystackArg) : $scope->getNativeType($haystackArg)); if ($haystackType instanceof MixedType) { return null; @@ -106,12 +114,12 @@ public function findSpecifiedType( return null; } - $needleArg = $node->getArgs()[0]->value; + $needleArg = $args[0]->value; $needleType = ($this->treatPhpDocTypesAsCertain ? $scope->getType($needleArg) : $scope->getNativeType($needleArg)); $isStrictComparison = false; if ($argsCount >= 3) { - $strictNodeType = $scope->getType($node->getArgs()[2]->value); + $strictNodeType = $scope->getType($args[2]->value); $isStrictComparison = $strictNodeType->isTrue()->yes(); } @@ -192,7 +200,7 @@ public function findSpecifiedType( } } } elseif ($functionName === 'method_exists' && $argsCount >= 2) { - $objectArg = $node->getArgs()[0]->value; + $objectArg = $args[0]->value; $objectType = ($this->treatPhpDocTypesAsCertain ? $scope->getType($objectArg) : $scope->getNativeType($objectArg)); if ($objectType instanceof ConstantStringType @@ -201,7 +209,7 @@ public function findSpecifiedType( return false; } - $methodArg = $node->getArgs()[1]->value; + $methodArg = $args[1]->value; $methodType = ($this->treatPhpDocTypesAsCertain ? $scope->getType($methodArg) : $scope->getNativeType($methodArg)); if ($methodType instanceof ConstantStringType) { From d93419255dddb4d0ed9e8e94db959901e774b329 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 11 Aug 2025 11:08:06 +0200 Subject: [PATCH 02/13] Added regression test --- ...mpossibleCheckTypeFunctionCallRuleTest.php | 6 ++++ .../Rules/Comparison/data/bug-13268.php | 28 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 tests/PHPStan/Rules/Comparison/data/bug-13268.php diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php index 5c311602a0..d5eba13a0a 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php @@ -1049,4 +1049,10 @@ public function testBug6788(): void $this->analyse([__DIR__ . '/data/bug-6788.php'], []); } + public function testBug13268(): void + { + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-13268.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Comparison/data/bug-13268.php b/tests/PHPStan/Rules/Comparison/data/bug-13268.php new file mode 100644 index 0000000000..0bdbd130eb --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-13268.php @@ -0,0 +1,28 @@ + Date: Tue, 12 Aug 2025 10:32:04 +0200 Subject: [PATCH 03/13] improve fix --- .../Comparison/ImpossibleCheckTypeHelper.php | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php index 54570de4ae..ab412e21ec 100644 --- a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php +++ b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php @@ -70,13 +70,6 @@ public function findSpecifiedType( } $args = $node->getArgs(); $argsCount = count($args); - - foreach ($args as $arg) { - if ($arg->value instanceof Expr\Assign) { - return null; - } - } - if ($node->name instanceof Node\Name) { $functionName = strtolower((string) $node->name); if ($functionName === 'assert' && $argsCount >= 1) { @@ -286,6 +279,16 @@ public function findSpecifiedType( $results = []; + $assignedInCallVars = []; + if ($node instanceof Expr\CallLike) { + foreach ($node->getArgs() as $arg) { + if (!$arg->value instanceof Expr\Assign) { + continue; + } + + $assignedInCallVars[] = $arg->value; + } + } foreach ($sureTypes as $sureType) { if (self::isSpecified($typeSpecifierScope, $node, $sureType[0])) { $results[] = TrinaryLogic::createMaybe(); @@ -301,6 +304,14 @@ public function findSpecifiedType( /** @var Type $resultType */ $resultType = $sureType[1]; + foreach ($assignedInCallVars as $assignedInCallVar) { + if ($sureType[0] !== $assignedInCallVar->var) { + continue; + } + + $argumentType = $scope->getType($assignedInCallVar->expr); + } + $results[] = $resultType->isSuperTypeOf($argumentType)->result; } From 4c347f0ea3d679ee118feda8290ef76b76d4d731 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 12 Aug 2025 10:40:57 +0200 Subject: [PATCH 04/13] Added regression test --- .../ImpossibleCheckTypeFunctionCallRuleTest.php | 16 ++++++++++++++++ .../PHPStan/Rules/Comparison/data/bug-12087.php | 15 +++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 tests/PHPStan/Rules/Comparison/data/bug-12087.php diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php index d5eba13a0a..9250b96e84 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php @@ -1049,10 +1049,26 @@ public function testBug6788(): void $this->analyse([__DIR__ . '/data/bug-6788.php'], []); } + #[RequiresPhp('>= 8.0')] public function testBug13268(): void { $this->treatPhpDocTypesAsCertain = true; $this->analyse([__DIR__ . '/data/bug-13268.php'], []); } + #[RequiresPhp('>= 8.0')] + public function testBug12087(): void + { + $tipText = 'Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.'; + + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-12087.php'], [ + [ + 'Call to function is_null() with null will always evaluate to true.', + 14, + $tipText, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Comparison/data/bug-12087.php b/tests/PHPStan/Rules/Comparison/data/bug-12087.php new file mode 100644 index 0000000000..1ecb72ac6f --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-12087.php @@ -0,0 +1,15 @@ + Date: Tue, 12 Aug 2025 10:46:06 +0200 Subject: [PATCH 05/13] Added regression test --- ...mpossibleCheckTypeFunctionCallRuleTest.php | 14 +++++++++++ .../Rules/Comparison/data/bug-9666.php | 24 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 tests/PHPStan/Rules/Comparison/data/bug-9666.php diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php index 9250b96e84..f87ee109d3 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php @@ -1071,4 +1071,18 @@ public function testBug12087(): void ]); } + public function testBug9666(): void + { + $tipText = 'Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.'; + + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-9666.php'], [ + [ + 'Call to function is_bool() with bool will always evaluate to true.', + 20, + $tipText, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Comparison/data/bug-9666.php b/tests/PHPStan/Rules/Comparison/data/bug-9666.php new file mode 100644 index 0000000000..028601f5c2 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-9666.php @@ -0,0 +1,24 @@ + + */ + function b() + { + return []; + } +} + +function doFoo() { + $a = new A(); + $b = $a->b(); + $c = null; + if ($b && is_bool($c = reset($b))) { + // + } +} + From 3d65f2926c60d84064d55641c09b8f426c16967e Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 12 Aug 2025 10:49:18 +0200 Subject: [PATCH 06/13] Added regression test --- ...mpossibleCheckTypeFunctionCallRuleTest.php | 12 ++++++ .../Rules/Comparison/data/bug-7773.php | 40 +++++++++++++++++++ .../Rules/Comparison/data/bug-9445.php | 20 ++++++++++ 3 files changed, 72 insertions(+) create mode 100644 tests/PHPStan/Rules/Comparison/data/bug-7773.php create mode 100644 tests/PHPStan/Rules/Comparison/data/bug-9445.php diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php index f87ee109d3..d5f76ed938 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php @@ -1085,4 +1085,16 @@ public function testBug9666(): void ]); } + public function testBug9445(): void + { + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-9445.php'], []); + } + + public function testBug7773(): void + { + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-7773.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Comparison/data/bug-7773.php b/tests/PHPStan/Rules/Comparison/data/bug-7773.php new file mode 100644 index 0000000000..f7619b92a1 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-7773.php @@ -0,0 +1,40 @@ + $data json array + * @return string json string + * @throws JSONEncodingException + */ + public static function JSONEncode(array $data): string + { + if (!is_string($data = json_encode($data))) + throw new JSONEncodingException(); + return $data; + } + + /** + * Decodes the JSON data as an array + * @param string $data json string + * @return array json array + * @throws JSONDecodingException + */ + public static function JSONDecode(string $data): array + { + if (!is_array($data = json_decode($data, true))) + throw new JSONDecodingException(); + return $data; + } +} diff --git a/tests/PHPStan/Rules/Comparison/data/bug-9445.php b/tests/PHPStan/Rules/Comparison/data/bug-9445.php new file mode 100644 index 0000000000..b8693f6f8a --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-9445.php @@ -0,0 +1,20 @@ +id === $foo->id) { + return true; + } + } while (!is_null($foo = $foo->parent)); + + return false; + } +} From 0c0e77a14de4bb53ef54774c1bf3ca655c4e5d65 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 12 Aug 2025 10:53:08 +0200 Subject: [PATCH 07/13] fix php 8.x build --- .../Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php | 2 +- tests/PHPStan/Rules/Comparison/data/bug-12087.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php index d5f76ed938..1b5cc046ca 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php @@ -1056,7 +1056,7 @@ public function testBug13268(): void $this->analyse([__DIR__ . '/data/bug-13268.php'], []); } - #[RequiresPhp('>= 8.0')] + #[RequiresPhp('>= 8.1')] public function testBug12087(): void { $tipText = 'Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.'; diff --git a/tests/PHPStan/Rules/Comparison/data/bug-12087.php b/tests/PHPStan/Rules/Comparison/data/bug-12087.php index 1ecb72ac6f..31b18128b1 100644 --- a/tests/PHPStan/Rules/Comparison/data/bug-12087.php +++ b/tests/PHPStan/Rules/Comparison/data/bug-12087.php @@ -1,4 +1,4 @@ -= 8.1 namespace Bug12087; From f48222246b26e31df2492792def490cec3181976 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 12 Aug 2025 11:02:19 +0200 Subject: [PATCH 08/13] Test MethodCall, StaticMethodCall --- .../ImpossibleCheckTypeMethodCallRuleTest.php | 15 ++++++++ ...sibleCheckTypeStaticMethodCallRuleTest.php | 16 ++++++++ .../Rules/Comparison/data/bug-12087b.php | 38 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 tests/PHPStan/Rules/Comparison/data/bug-12087b.php diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeMethodCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeMethodCallRuleTest.php index 3fd625a4b1..fb64511aff 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeMethodCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeMethodCallRuleTest.php @@ -275,6 +275,21 @@ public function testBug12473(): void ]); } + #[RequiresPhp('>= 8.1')] + public function testBug12087b(): void + { + $tipText = 'Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.'; + + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-12087b.php'], [ + [ + 'Call to method Bug12087b\MyAssert::is_null() with null will always evaluate to true.', + 37, + $tipText, + ], + ]); + } + public static function getAdditionalConfigFiles(): array { return [ diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeStaticMethodCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeStaticMethodCallRuleTest.php index 1b43cdea9d..8a68085379 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeStaticMethodCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeStaticMethodCallRuleTest.php @@ -5,6 +5,7 @@ use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\RequiresPhp; /** * @extends RuleTestCase @@ -145,6 +146,21 @@ public function testReportAlwaysTrueInLastCondition(bool $reportAlwaysTrueInLast $this->analyse([__DIR__ . '/data/impossible-static-method-report-always-true-last-condition.php'], $expectedErrors); } + #[RequiresPhp('>= 8.1')] + public function testBug12087b(): void + { + $tipText = 'Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.'; + + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-12087b.php'], [ + [ + 'Call to static method Bug12087b\MyAssert::static_is_null() with null will always evaluate to true.', + 31, + $tipText, + ], + ]); + } + public static function getAdditionalConfigFiles(): array { return [ diff --git a/tests/PHPStan/Rules/Comparison/data/bug-12087b.php b/tests/PHPStan/Rules/Comparison/data/bug-12087b.php new file mode 100644 index 0000000000..3c26c567e9 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-12087b.php @@ -0,0 +1,38 @@ += 8.1 + +namespace Bug12087b; + +enum Button: int +{ + case On = 1; + + case Off = 0; +} + +class MyAssert { + /** + * @return ($value is null ? true : false) + */ + static public function static_is_null(mixed $value): bool { + return $value === null; + } + + /** + * @return ($value is null ? true : false) + */ + public function is_null(mixed $value): bool { + return $value === null; + } +} + +function doFoo(): void { + $value = 10; + + MyAssert::static_is_null($value = Button::tryFrom($value)); +} + +function doBar(MyAssert $assert): void { + $value = 10; + + $assert->is_null($value = Button::tryFrom($value)); +} From b6fcb8cd7b8b9872fdf797da63928ec987ac3963 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 12 Aug 2025 11:12:06 +0200 Subject: [PATCH 09/13] fix php 7.x build --- .../Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php | 1 + tests/PHPStan/Rules/Comparison/data/bug-9445.php | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php index 1b5cc046ca..d55fb7e310 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php @@ -1085,6 +1085,7 @@ public function testBug9666(): void ]); } + #[RequiresPhp('>= 8.0')] public function testBug9445(): void { $this->treatPhpDocTypesAsCertain = true; diff --git a/tests/PHPStan/Rules/Comparison/data/bug-9445.php b/tests/PHPStan/Rules/Comparison/data/bug-9445.php index b8693f6f8a..8fd828d0cc 100644 --- a/tests/PHPStan/Rules/Comparison/data/bug-9445.php +++ b/tests/PHPStan/Rules/Comparison/data/bug-9445.php @@ -1,4 +1,6 @@ -= 8.0 + +declare(strict_types=1); namespace Bug9445; From 092d7b6049bb7b9ee3139b84f6b33b9ad463409e Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 2 Sep 2025 10:09:43 +0200 Subject: [PATCH 10/13] Create bug-12087c.php --- .../Rules/Comparison/data/bug-12087c.php | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/PHPStan/Rules/Comparison/data/bug-12087c.php diff --git a/tests/PHPStan/Rules/Comparison/data/bug-12087c.php b/tests/PHPStan/Rules/Comparison/data/bug-12087c.php new file mode 100644 index 0000000000..a86cf4f7f0 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-12087c.php @@ -0,0 +1,22 @@ += 8.1 + +namespace Bug12087c; + +enum Button: int +{ + case On = 1; + + case Off = 0; +} + +function doFoo() { + $value = 10; + + is_null($foo = $value = Button::tryFrom($value)); +} + +function doFoo2() { + $value = 10; + + is_null($foo ??= Button::tryFrom($value)); +} From f21b1d37aeafce02a9b73ef41805b6e02762d926 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 3 Sep 2025 08:15:15 +0200 Subject: [PATCH 11/13] Support nested assigns --- .../Comparison/ImpossibleCheckTypeHelper.php | 17 ++++++++++++++--- .../ImpossibleCheckTypeFunctionCallRuleTest.php | 15 +++++++++++++++ .../Rules/Comparison/data/bug-12087c.php | 12 ++++-------- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php index ab412e21ec..30fae967a4 100644 --- a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php +++ b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php @@ -282,11 +282,22 @@ public function findSpecifiedType( $assignedInCallVars = []; if ($node instanceof Expr\CallLike) { foreach ($node->getArgs() as $arg) { - if (!$arg->value instanceof Expr\Assign) { - continue; + $expr = $arg->value; + while ($expr instanceof Expr\Assign) { + $expr = $expr->expr; } + $assignedExpr = $expr; + + $expr = $arg->value; + while ($expr instanceof Expr\Assign) { + $assignedInCallVars[] = new Expr\Assign( + $expr->var, + $assignedExpr, + $expr->getAttributes() + ); - $assignedInCallVars[] = $arg->value; + $expr = $expr->expr; + } } } foreach ($sureTypes as $sureType) { diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php index d55fb7e310..011381af0f 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php @@ -1071,6 +1071,21 @@ public function testBug12087(): void ]); } + #[RequiresPhp('>= 8.1')] + public function testBug12087c(): void + { + $tipText = 'Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.'; + + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-12087c.php'], [ + [ + 'Call to function is_null() with null will always evaluate to true.', + 17, + $tipText, + ], + ]); + } + public function testBug9666(): void { $tipText = 'Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.'; diff --git a/tests/PHPStan/Rules/Comparison/data/bug-12087c.php b/tests/PHPStan/Rules/Comparison/data/bug-12087c.php index a86cf4f7f0..a979a80f32 100644 --- a/tests/PHPStan/Rules/Comparison/data/bug-12087c.php +++ b/tests/PHPStan/Rules/Comparison/data/bug-12087c.php @@ -9,14 +9,10 @@ enum Button: int case Off = 0; } -function doFoo() { - $value = 10; - - is_null($foo = $value = Button::tryFrom($value)); -} - -function doFoo2() { +function doFoo() +{ + $foo = 'abc'; $value = 10; - is_null($foo ??= Button::tryFrom($value)); + is_null($value = $foo = Button::tryFrom($value)); } From 644083a8b3466088355171e8ac7a12755b4cf19f Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 3 Sep 2025 08:27:29 +0200 Subject: [PATCH 12/13] cs --- src/Rules/Comparison/ImpossibleCheckTypeHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php index 30fae967a4..7d6585ebaf 100644 --- a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php +++ b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php @@ -293,7 +293,7 @@ public function findSpecifiedType( $assignedInCallVars[] = new Expr\Assign( $expr->var, $assignedExpr, - $expr->getAttributes() + $expr->getAttributes(), ); $expr = $expr->expr; From d657baff9abdbaa6707a903afafdfb6abba5fd93 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 3 Sep 2025 08:38:19 +0200 Subject: [PATCH 13/13] add more tests --- .../ImpossibleCheckTypeFunctionCallRuleTest.php | 8 ++++++++ tests/PHPStan/Rules/Comparison/data/bug-12087c.php | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php index 011381af0f..087098ec01 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php @@ -1083,6 +1083,14 @@ public function testBug12087c(): void 17, $tipText, ], + [ + 'Call to function is_null() with 10 will always evaluate to false.', + 23, + ], + [ + 'Call to function is_null() with null will always evaluate to true.', + 29, + ], ]); } diff --git a/tests/PHPStan/Rules/Comparison/data/bug-12087c.php b/tests/PHPStan/Rules/Comparison/data/bug-12087c.php index a979a80f32..b19be4b824 100644 --- a/tests/PHPStan/Rules/Comparison/data/bug-12087c.php +++ b/tests/PHPStan/Rules/Comparison/data/bug-12087c.php @@ -16,3 +16,16 @@ function doFoo() is_null($value = $foo = Button::tryFrom($value)); } + +function doFoo2() { + $value = 10; + + is_null($value ??= Button::tryFrom($value)); +} + +function doFoo3() { + $value = null; + + is_null($value ??= Button::tryFrom($value)); +} +