Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit a3d0910

Browse files
authored
Fix checking generic mixed type based on config
1 parent 8f82b79 commit a3d0910

12 files changed

+633
-25
lines changed

‎src/Rules/RuleLevelHelper.php‎

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,13 @@ private function transformCommonType(Type $type): Type
6363

6464
return TypeTraverser::map($type, function (Type $type, callable $traverse) {
6565
if ($type instanceof TemplateMixedType) {
66-
return $type->toStrictMixedType();
66+
if (!$this->newRuleLevelHelper) {
67+
return $type->toStrictMixedType();
68+
}
69+
70+
if ($this->checkExplicitMixed) {
71+
return $type->toStrictMixedType();
72+
}
6773
}
6874
if (
6975
$type instanceof MixedType
@@ -301,22 +307,39 @@ public function findTypeToCheck(
301307
$type = TypeCombinator::removeNull($type);
302308
}
303309

304-
if (
305-
$this->checkExplicitMixed
306-
&& $type instanceof MixedType
307-
&& !$type instanceof TemplateMixedType
308-
&& $type->isExplicitMixed()
309-
) {
310-
return new FoundTypeResult(new StrictMixedType(), [], [], null);
311-
}
310+
if ($this->newRuleLevelHelper) {
311+
if (
312+
($this->checkExplicitMixed || $this->checkImplicitMixed)
313+
&& $type instanceof MixedType
314+
&& ($type->isExplicitMixed() ? $this->checkExplicitMixed : $this->checkImplicitMixed)
315+
) {
316+
return new FoundTypeResult(
317+
$type instanceof TemplateMixedType
318+
? $type->toStrictMixedType()
319+
: new StrictMixedType(),
320+
[],
321+
[],
322+
null,
323+
);
324+
}
325+
} else {
326+
if (
327+
$this->checkExplicitMixed
328+
&& $type instanceof MixedType
329+
&& !$type instanceof TemplateMixedType
330+
&& $type->isExplicitMixed()
331+
) {
332+
return new FoundTypeResult(new StrictMixedType(), [], [], null);
333+
}
312334

313-
if (
314-
$this->checkImplicitMixed
315-
&& $type instanceof MixedType
316-
&& !$type instanceof TemplateMixedType
317-
&& !$type->isExplicitMixed()
318-
) {
319-
return new FoundTypeResult(new StrictMixedType(), [], [], null);
335+
if (
336+
$this->checkImplicitMixed
337+
&& $type instanceof MixedType
338+
&& !$type instanceof TemplateMixedType
339+
&& !$type->isExplicitMixed()
340+
) {
341+
return new FoundTypeResult(new StrictMixedType(), [], [], null);
342+
}
320343
}
321344

322345
if ($type instanceof MixedType || $type instanceof NeverType) {

‎tests/PHPStan/Rules/Arrays/IterableInForeachRuleTest.php‎

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use PHPStan\Rules\Rule;
66
use PHPStan\Rules\RuleLevelHelper;
77
use PHPStan\Testing\RuleTestCase;
8+
use function array_merge;
9+
use function usort;
810
use const PHP_VERSION_ID;
911

1012
/**
@@ -15,9 +17,11 @@ class IterableInForeachRuleTest extends RuleTestCase
1517

1618
private bool $checkExplicitMixed = false;
1719

20+
private bool $checkImplicitMixed = false;
21+
1822
protected function getRule(): Rule
1923
{
20-
return new IterableInForeachRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, false, true, false));
24+
return new IterableInForeachRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, $this->checkImplicitMixed, true, false));
2125
}
2226

2327
public function testCheckWithMaybes(): void
@@ -80,4 +84,60 @@ public function testBug4335(): void
8084
$this->analyse([__DIR__ . '/data/bug-4335.php'], []);
8185
}
8286

87+
public function dataMixed(): array
88+
{
89+
$explicitOnlyErrors = [
90+
[
91+
'Argument of an invalid type T of mixed supplied for foreach, only iterables are supported.',
92+
11,
93+
],
94+
[
95+
'Argument of an invalid type mixed supplied for foreach, only iterables are supported.',
96+
14,
97+
],
98+
];
99+
$implicitOnlyErrors = [
100+
[
101+
'Argument of an invalid type mixed supplied for foreach, only iterables are supported.',
102+
17,
103+
],
104+
];
105+
$combinedErrors = array_merge($explicitOnlyErrors, $implicitOnlyErrors);
106+
usort($combinedErrors, static fn (array $a, array $b): int => $a[1] <=> $b[1]);
107+
108+
return [
109+
[
110+
true,
111+
false,
112+
$explicitOnlyErrors,
113+
],
114+
[
115+
false,
116+
true,
117+
$implicitOnlyErrors,
118+
],
119+
[
120+
true,
121+
true,
122+
$combinedErrors,
123+
],
124+
[
125+
false,
126+
false,
127+
[],
128+
],
129+
];
130+
}
131+
132+
/**
133+
* @dataProvider dataMixed
134+
* @param list<array{0: string, 1: int, 2?: string}> $errors
135+
*/
136+
public function testMixed(bool $checkExplicitMixed, bool $checkImplicitMixed, array $errors): void
137+
{
138+
$this->checkExplicitMixed = $checkExplicitMixed;
139+
$this->checkImplicitMixed = $checkImplicitMixed;
140+
$this->analyse([__DIR__ . '/data/foreach-mixed.php'], $errors);
141+
}
142+
83143
}

‎tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php‎

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ class NonexistentOffsetInArrayDimFetchRuleTest extends RuleTestCase
1515

1616
private bool $checkExplicitMixed = false;
1717

18+
private bool $checkImplicitMixed = false;
19+
1820
private bool $bleedingEdge = false;
1921

2022
protected function getRule(): Rule
2123
{
22-
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, false, true, false);
24+
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, $this->checkImplicitMixed, true, false);
2325

2426
return new NonexistentOffsetInArrayDimFetchRule(
2527
$ruleLevelHelper,
@@ -747,4 +749,24 @@ public function testBug8166(): void
747749
]);
748750
}
749751

752+
public function testMixed(): void
753+
{
754+
$this->checkExplicitMixed = true;
755+
$this->checkImplicitMixed = true;
756+
$this->analyse([__DIR__ . '/data/offset-access-mixed.php'], [
757+
[
758+
'Cannot access offset 5 on T of mixed.',
759+
11,
760+
],
761+
[
762+
'Cannot access offset 5 on mixed.',
763+
16,
764+
],
765+
[
766+
'Cannot access offset 5 on mixed.',
767+
21,
768+
],
769+
]);
770+
}
771+
750772
}

‎tests/PHPStan/Rules/Arrays/UnpackIterableInArrayRuleTest.php‎

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use PHPStan\Rules\Rule;
66
use PHPStan\Rules\RuleLevelHelper;
77
use PHPStan\Testing\RuleTestCase;
8+
use function array_merge;
9+
use function usort;
810
use const PHP_VERSION_ID;
911

1012
/**
@@ -13,9 +15,13 @@
1315
class UnpackIterableInArrayRuleTest extends RuleTestCase
1416
{
1517

18+
private bool $checkExplicitMixed = false;
19+
20+
private bool $checkImplicitMixed = false;
21+
1622
protected function getRule(): Rule
1723
{
18-
return new UnpackIterableInArrayRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, true, false));
24+
return new UnpackIterableInArrayRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, $this->checkImplicitMixed, true, false));
1925
}
2026

2127
public function testRule(): void
@@ -50,4 +56,60 @@ public function testRuleWithNullsafeVariant(): void
5056
]);
5157
}
5258

59+
public function dataMixed(): array
60+
{
61+
$explicitOnlyErrors = [
62+
[
63+
'Only iterables can be unpacked, T of mixed given.',
64+
11,
65+
],
66+
[
67+
'Only iterables can be unpacked, mixed given.',
68+
12,
69+
],
70+
];
71+
$implicitOnlyErrors = [
72+
[
73+
'Only iterables can be unpacked, mixed given.',
74+
13,
75+
],
76+
];
77+
$combinedErrors = array_merge($explicitOnlyErrors, $implicitOnlyErrors);
78+
usort($combinedErrors, static fn (array $a, array $b): int => $a[1] <=> $b[1]);
79+
80+
return [
81+
[
82+
true,
83+
false,
84+
$explicitOnlyErrors,
85+
],
86+
[
87+
false,
88+
true,
89+
$implicitOnlyErrors,
90+
],
91+
[
92+
true,
93+
true,
94+
$combinedErrors,
95+
],
96+
[
97+
false,
98+
false,
99+
[],
100+
],
101+
];
102+
}
103+
104+
/**
105+
* @dataProvider dataMixed
106+
* @param list<array{0: string, 1: int, 2?: string}> $errors
107+
*/
108+
public function testMixed(bool $checkExplicitMixed, bool $checkImplicitMixed, array $errors): void
109+
{
110+
$this->checkExplicitMixed = $checkExplicitMixed;
111+
$this->checkImplicitMixed = $checkImplicitMixed;
112+
$this->analyse([__DIR__ . '/data/unpack-mixed.php'], $errors);
113+
}
114+
53115
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php declare(strict_types=1); // lint >= 8.0
2+
3+
namespace ForeachMixed;
4+
5+
/**
6+
* @template T
7+
* @param T $t
8+
*/
9+
function foo(mixed $t, mixed $explicit, $implicit): void
10+
{
11+
foreach ($t as $v) {
12+
}
13+
14+
foreach ($explicit as $v) {
15+
}
16+
17+
foreach ($implicit as $v) {
18+
}
19+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php declare(strict_types=1); // lint >= 8.0
2+
3+
namespace OffsetAccessMixed;
4+
5+
/**
6+
* @template T
7+
* @param T $a
8+
*/
9+
function foo(mixed $a): void
10+
{
11+
var_dump($a[5]);
12+
}
13+
14+
function foo2(mixed $a): void
15+
{
16+
var_dump($a[5]);
17+
}
18+
19+
function foo3($a): void
20+
{
21+
var_dump($a[5]);
22+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php declare(strict_types=1); // lint >= 8.0
2+
3+
namespace UnpackMixed;
4+
5+
/**
6+
* @template T
7+
* @param T $t
8+
*/
9+
function foo(mixed $t, mixed $explicit, $implicit): void
10+
{
11+
var_dump([...$t]);
12+
var_dump([...$explicit]);
13+
var_dump([...$implicit]);
14+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /