Skip to content

Commit 1196a5e

Browse files
phpstan-botclaude
andcommitted
Add rule tests for bug 14446 to verify no false positives
Add StrictComparisonOfDifferentTypesRule test with polluteScopeWithAlwaysIterableForeach: false to ensure no "Strict comparison using === between false and true" error. Add integration-style rule test in Bug14446Test that runs full analysis with OverwriteVariablesWithForeachRule registered, verifying no "Foreach overwrites $key" false positive. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5c825e2 commit 1196a5e

File tree

5 files changed

+129
-0
lines changed

5 files changed

+129
-0
lines changed

tests/PHPStan/Analyser/Bug14446Test.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Analyser;
44

5+
use PHPStan\File\FileHelper;
56
use PHPStan\Testing\TypeInferenceTestCase;
67
use PHPUnit\Framework\Attributes\DataProvider;
78

@@ -33,4 +34,19 @@ public static function getAdditionalConfigFiles(): array
3334
];
3435
}
3536

37+
public function testRule(): void
38+
{
39+
$file = self::getContainer()->getByType(FileHelper::class)->normalizePath(__DIR__ . '/data/bug-14446-rule.php');
40+
41+
$analyser = self::getContainer()->getByType(Analyser::class);
42+
$finalizer = self::getContainer()->getByType(AnalyserResultFinalizer::class);
43+
$errors = $finalizer->finalize(
44+
$analyser->analyse([$file], null, null, true),
45+
false,
46+
true,
47+
)->getErrors();
48+
49+
$this->assertNoErrors($errors);
50+
}
51+
3652
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
parameters:
22
polluteScopeWithAlwaysIterableForeach: false
3+
4+
services:
5+
-
6+
class: PHPStan\Rules\ForeachLoop\OverwriteVariablesWithForeachRule
7+
tags:
8+
- phpstan.rules.rule
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug14446Rule;
4+
5+
function test(bool $initial): void {
6+
$current = $initial;
7+
8+
while (true) {
9+
if (!$current) {
10+
break;
11+
}
12+
13+
$items = [1];
14+
foreach ($items as $item) {
15+
$current = false;
16+
}
17+
}
18+
19+
var_dump($initial === true);
20+
}
21+
22+
function testMaybeIterable(bool $initial): void {
23+
$current = $initial;
24+
25+
while (true) {
26+
if (!$current) {
27+
break;
28+
}
29+
30+
$items = rand() > 0 ? [1] : [];
31+
foreach ($items as $item) {
32+
$current = false;
33+
}
34+
}
35+
36+
var_dump($initial === true);
37+
}
38+
39+
/**
40+
* @param mixed $value
41+
*/
42+
function testForeachKeyOverwrite($value): void {
43+
if (is_array($value) && $value !== []) {
44+
$hasOnlyStringKey = true;
45+
foreach (array_keys($value) as $key) {
46+
if (is_int($key)) {
47+
$hasOnlyStringKey = false;
48+
break;
49+
}
50+
}
51+
52+
if ($hasOnlyStringKey) {
53+
foreach ($value as $key => $element) {
54+
}
55+
}
56+
}
57+
}

tests/PHPStan/Rules/Comparison/StrictComparisonOfDifferentTypesRuleTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ class StrictComparisonOfDifferentTypesRuleTest extends RuleTestCase
2020

2121
private bool $treatPhpDocTypesAsCertain = true;
2222

23+
private bool $polluteScopeWithAlwaysIterableForeach = true;
24+
2325
protected function getRule(): Rule
2426
{
2527
return new StrictComparisonOfDifferentTypesRule(
@@ -36,6 +38,11 @@ protected function shouldTreatPhpDocTypesAsCertain(): bool
3638
return $this->treatPhpDocTypesAsCertain;
3739
}
3840

41+
protected function shouldPolluteScopeWithAlwaysIterableForeach(): bool
42+
{
43+
return $this->polluteScopeWithAlwaysIterableForeach;
44+
}
45+
3946
public function testStrictComparison(): void
4047
{
4148
$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%</>.';
@@ -1184,4 +1191,10 @@ public function testBug13421(): void
11841191
$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-13421.php'], []);
11851192
}
11861193

1194+
public function testBug14446(): void
1195+
{
1196+
$this->polluteScopeWithAlwaysIterableForeach = false;
1197+
$this->analyse([__DIR__ . '/data/bug-14446.php'], []);
1198+
}
1199+
11871200
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug14446Rule;
4+
5+
function test(bool $initial): void {
6+
$current = $initial;
7+
8+
while (true) {
9+
if (!$current) {
10+
break;
11+
}
12+
13+
$items = [1];
14+
foreach ($items as $item) {
15+
$current = false;
16+
}
17+
}
18+
19+
var_dump($initial === true);
20+
}
21+
22+
function testMaybeIterable(bool $initial): void {
23+
$current = $initial;
24+
25+
while (true) {
26+
if (!$current) {
27+
break;
28+
}
29+
30+
$items = rand() > 0 ? [1] : [];
31+
foreach ($items as $item) {
32+
$current = false;
33+
}
34+
}
35+
36+
var_dump($initial === true);
37+
}

0 commit comments

Comments
 (0)