From ba1ece5605ece6cf8134733b77c49b2ca45cde76 Mon Sep 17 00:00:00 2001
From: sayuprc <bepdielibe+github@gmail.com>
Date: Wed, 19 Mar 2025 21:50:30 +0900
Subject: [PATCH 1/3] 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 48eea97d6f..ed3961f2fd 100644
--- a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php
+++ b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php
@@ -147,6 +147,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 021f8cd455..2cc9411d5f 100644
--- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php
+++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php
@@ -971,4 +971,10 @@ public function testAlwaysTruePregMatch(): void
 		$this->analyse([__DIR__ . '/data/always-true-preg-match.php'], []);
 	}
 
+	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 @@
+<?php declare(strict_types = 1);
+
+namespace Bug12755;
+
+class HelloWorld
+{
+	/**
+	 * @param array{
+	 *     key1: ?int,
+	 *     key2: ?string,
+	 * } $myArray
+	 */
+	public function testOther(array $myArray): ?\stdClass
+	{
+	    if (\in_array(null, $myArray, true)) {
+	        return null;
+	    }
+
+		return (object) $myArray;
+	}
+		
+	/**
+	 * @param array{
+	 *     key1: ?bool,
+	 * } $myArray
+	 */
+	public function testBool(array $myArray): ?\stdClass
+	{
+	    if (\in_array(null, $myArray, true)) {
+	        return null;
+	    }
+
+		return (object) $myArray;
+	}
+}

From b51489350a6407cddaa4b6517eea09607a152011 Mon Sep 17 00:00:00 2001
From: sayuprc <bepdielibe+github@gmail.com>
Date: Thu, 20 Mar 2025 23:10:46 +0900
Subject: [PATCH 2/3] 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 ed3961f2fd..8195c0d815 100644
--- a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php
+++ b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php
@@ -147,11 +147,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 c4ad5bd03ee218997b15e6f57f7392147bf5712a Mon Sep 17 00:00:00 2001
From: sayuprc <bepdielibe+github@gmail.com>
Date: Fri, 21 Mar 2025 14:47:14 +0900
Subject: [PATCH 3/3] 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 2cc9411d5f..999889f963 100644
--- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php
+++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php
@@ -973,8 +973,16 @@ public function testAlwaysTruePregMatch(): void
 
 	public function testBug12755(): void
 	{
+		$tipText = 'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%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<MyEnum::*> $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)) {
+		}
+	}
 }