Skip to content

Commit dd417b0

Browse files
zonuexeclaude
andcommitted
Cover the falsey-but-not-false context and stabilize the CI message
Add array_all()/array_any() `=== false` test cases whose else branch is the truthy result of the call. This reaches the falsey-but-not-false type-specifier context that a plain `if (array_all(...))` never hits, so the `$context->false()` discriminator is now covered by mutation testing. Also make the array_all() impossible-check reproducer use a literal-false predicate: `is_string($key)` on a list key is inferred as `false` on current PHP but as `bool` on the downgraded 7.4/8.0 builds, which made the reported closure type - and thus the expected error message - unstable across the CI matrix. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 16b1ac6 commit dd417b0

4 files changed

Lines changed: 42 additions & 7 deletions

File tree

‎tests/PHPStan/Analyser/nsrt/array-all.php‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,23 @@ public function testByRefParameter($array) {
307307
}
308308
}
309309

310+
/**
311+
* Strict `=== false` comparison. The else branch is the truthy result of
312+
* array_all(), so elements are narrowed there; it exercises the falsey-but-
313+
* not-false type-specifier context, which a plain `if (array_all(...))` does
314+
* not reach.
315+
*
316+
* @param array<mixed> $array
317+
*/
318+
public function testStrictFalseComparison($array) {
319+
if (array_all($array, fn ($value) => is_int($value)) === false) {
320+
assertType("non-empty-array<mixed>", $array);
321+
} else {
322+
assertType("array<int>", $array);
323+
}
324+
assertType("array<mixed>", $array);
325+
}
326+
310327
}
311328

312329
/**

‎tests/PHPStan/Analyser/nsrt/array-any.php‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,22 @@ public function testShapeRequiredImpossible($array) {
140140
}
141141
}
142142

143+
/**
144+
* Strict `=== false` comparison. The else branch is the truthy result of
145+
* array_any() (at least one match, so the array is non-empty); it exercises
146+
* the falsey-but-not-false type-specifier context.
147+
*
148+
* @param array<mixed> $array
149+
*/
150+
public function testStrictFalseComparison($array) {
151+
if (array_any($array, fn ($value) => $value === null) === false) {
152+
assertType("array<mixed~null>", $array);
153+
} else {
154+
assertType("non-empty-array<mixed>", $array);
155+
}
156+
assertType("array<mixed>", $array);
157+
}
158+
143159
}
144160

145161
/**

‎tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -287,12 +287,7 @@ public function testArrayAllNonEmptyList(): void
287287
$this->analyse([__DIR__ . '/data/array-all-non-empty-list.php'], [
288288
[
289289
'Call to function array_all() with non-empty-list<mixed> and Closure(mixed, mixed): false will always evaluate to false.',
290-
13,
291-
'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%</>.',
292-
],
293-
[
294-
'Call to function is_string() with int<0, max> will always evaluate to false.',
295-
13,
290+
20,
296291
'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%</>.',
297292
],
298293
]);

‎tests/PHPStan/Rules/Comparison/data/array-all-non-empty-list.php‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,18 @@ class Foo
66
{
77

88
/**
9+
* A predicate that can never hold makes array_all() on a non-empty array
10+
* impossible: every element would have to satisfy it, but the array is
11+
* guaranteed to have at least one element. A literal-false predicate keeps
12+
* the reported closure type stable across PHP versions (a predicate like
13+
* is_string($key) is inferred as false on newer PHP but bool on the
14+
* downgraded builds).
15+
*
916
* @param non-empty-list<mixed> $array
1017
*/
1118
public function doFoo(array $array): void
1219
{
13-
if (array_all($array, fn ($value, $key) => is_string($key))) {
20+
if (array_all($array, fn ($value, $key) => false)) {
1421
echo 'never';
1522
}
1623
}

0 commit comments

Comments
 (0)