From 63cc1b734dc42cb66890530d7c300ae553d7e3af Mon Sep 17 00:00:00 2001 From: sayuprc Date: Wed, 19 Mar 2025 21:50:30 +0900 Subject: [PATCH 1/4] Fix always-true detection in in_array with union type haystack --- .../Comparison/ImpossibleCheckTypeHelper.php | 4 +++ ...mpossibleCheckTypeFunctionCallRuleTest.php | 6 ++++ .../Rules/Comparison/data/bug-12755.php | 35 +++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 tests/PHPStan/Rules/Comparison/data/bug-12755.php diff --git a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php index 5cba66c18e..690546d1fc 100644 --- a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php +++ b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php @@ -148,6 +148,10 @@ public function findSpecifiedType( foreach ($haystackArrayTypes as $haystackArrayType) { if ($haystackArrayType instanceof ConstantArrayType) { foreach ($haystackArrayType->getValueTypes() as $i => $haystackArrayValueType) { + if ($haystackArrayValueType instanceof UnionType) { + continue; + } + if ($haystackArrayType->isOptionalKey($i)) { continue; } diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php index 219e0450c1..8224289ce7 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php @@ -1004,4 +1004,10 @@ public function testBugPR3404(): void ]); } + public function testBug12755(): void + { + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-12755.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Comparison/data/bug-12755.php b/tests/PHPStan/Rules/Comparison/data/bug-12755.php new file mode 100644 index 0000000000..c273792e2f --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-12755.php @@ -0,0 +1,35 @@ + Date: Thu, 20 Mar 2025 23:10:46 +0900 Subject: [PATCH 2/4] Simplify --- src/Rules/Comparison/ImpossibleCheckTypeHelper.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php index 690546d1fc..c9c628d460 100644 --- a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php +++ b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php @@ -148,11 +148,7 @@ public function findSpecifiedType( foreach ($haystackArrayTypes as $haystackArrayType) { if ($haystackArrayType instanceof ConstantArrayType) { foreach ($haystackArrayType->getValueTypes() as $i => $haystackArrayValueType) { - if ($haystackArrayValueType instanceof UnionType) { - continue; - } - - if ($haystackArrayType->isOptionalKey($i)) { + if ($haystackArrayValueType instanceof UnionType || $haystackArrayType->isOptionalKey($i)) { continue; } From 1ee3ce0db11bc4963f346eb043d07abbafd7610d Mon Sep 17 00:00:00 2001 From: sayuprc Date: Fri, 21 Mar 2025 14:47:14 +0900 Subject: [PATCH 3/4] Add test cases --- ...mpossibleCheckTypeFunctionCallRuleTest.php | 10 +++- .../Rules/Comparison/data/bug-12755.php | 55 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php index 8224289ce7..aec75a890a 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php @@ -1006,8 +1006,16 @@ public function testBugPR3404(): void public function testBug12755(): 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-12755.php'], []); + $this->analyse([__DIR__ . '/data/bug-12755.php'], [ + [ + 'Call to function in_array() with arguments null, array{key1: bool|null, key2: null} and true will always evaluate to true.', + 51, + $tipText, + ], + ]); } } diff --git a/tests/PHPStan/Rules/Comparison/data/bug-12755.php b/tests/PHPStan/Rules/Comparison/data/bug-12755.php index c273792e2f..7b65f96d6f 100644 --- a/tests/PHPStan/Rules/Comparison/data/bug-12755.php +++ b/tests/PHPStan/Rules/Comparison/data/bug-12755.php @@ -2,6 +2,13 @@ namespace Bug12755; +class MyEnum +{ + public const ONE = 'one'; + public const TWO = 'two'; + public const THREE = 'three'; +} + class HelloWorld { /** @@ -32,4 +39,52 @@ public function testBool(array $myArray): ?\stdClass return (object) $myArray; } + + /** + * @param array{ + * key1: ?bool, + * key2: null, + * } $myArray + */ + public function testNull(array $myArray): ?\stdClass + { + if (\in_array(null, $myArray, true)) { + return null; + } + + return (object) $myArray; + } + + /** + * @param list $stack + */ + public function testEnum(array $stack): bool + { + return count($stack) === 1 && in_array(MyEnum::ONE, $stack, true); + } + + /** + * @param array{1|2|3} $stack + * @param array{1|2|3, 1|2|3} $stack2 + * @param array{1|2|3, 2|3} $stack3 + * @param array{a?: 1, b: 2|3} $stack4 + * @param array{a?: 1} $stack5 + */ + public function sayHello(array $stack, array $stack2, array $stack3, array $stack4, array $stack5): void + { + if (in_array(1, $stack, true)) { + } + + if (in_array(1, $stack2, true)) { + } + + if (in_array(1, $stack3, true)) { + } + + if (in_array(1, $stack4, true)) { + } + + if (in_array(1, $stack5, true)) { + } + } } From d73a322fc490f27308d8a5edd2fc3e1d6e5ef2d7 Mon Sep 17 00:00:00 2001 From: sayuprc Date: Tue, 6 May 2025 22:53:35 +0900 Subject: [PATCH 4/4] Improvement to a better way --- 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 c9c628d460..6eadedd10c 100644 --- a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php +++ b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php @@ -148,7 +148,7 @@ public function findSpecifiedType( foreach ($haystackArrayTypes as $haystackArrayType) { if ($haystackArrayType instanceof ConstantArrayType) { foreach ($haystackArrayType->getValueTypes() as $i => $haystackArrayValueType) { - if ($haystackArrayValueType instanceof UnionType || $haystackArrayType->isOptionalKey($i)) { + if (count($haystackArrayValueType->getFiniteTypes()) > 1 || $haystackArrayType->isOptionalKey($i)) { continue; }