Skip to content

Commit 04ea1d7

Browse files
committed
Fix false negative with checkExplicitMixed
1 parent b7bd0a9 commit 04ea1d7

File tree

8 files changed

+148
-22
lines changed

8 files changed

+148
-22
lines changed

src/Type/MixedType.php

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,13 @@
1818
use PHPStan\Type\Constant\ConstantBooleanType;
1919
use PHPStan\Type\Generic\TemplateMixedType;
2020
use PHPStan\Type\Generic\TemplateType;
21-
use PHPStan\Type\Traits\MaybeIterableTypeTrait;
22-
use PHPStan\Type\Traits\MaybeOffsetAccessibleTypeTrait;
2321
use PHPStan\Type\Traits\NonGenericTypeTrait;
2422
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
2523

2624
/** @api */
2725
class MixedType implements CompoundType, SubtractableType
2826
{
2927

30-
use MaybeIterableTypeTrait;
31-
use MaybeOffsetAccessibleTypeTrait;
3228
use NonGenericTypeTrait;
3329
use UndecidedComparisonCompoundTypeTrait;
3430

@@ -118,7 +114,7 @@ public function isSuperTypeOf(Type $type): TrinaryLogic
118114

119115
public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
120116
{
121-
return new MixedType();
117+
return new self($this->isExplicitMixed);
122118
}
123119

124120
public function isCallable(): TrinaryLogic
@@ -332,7 +328,44 @@ public function toString(): Type
332328

333329
public function toArray(): Type
334330
{
335-
return new ArrayType(new MixedType(), new MixedType());
331+
$mixed = new self($this->isExplicitMixed);
332+
333+
return new ArrayType($mixed, $mixed);
334+
}
335+
336+
public function isIterable(): TrinaryLogic
337+
{
338+
return TrinaryLogic::createMaybe();
339+
}
340+
341+
public function isIterableAtLeastOnce(): TrinaryLogic
342+
{
343+
return TrinaryLogic::createMaybe();
344+
}
345+
346+
public function getIterableKeyType(): Type
347+
{
348+
return new self($this->isExplicitMixed);
349+
}
350+
351+
public function getIterableValueType(): Type
352+
{
353+
return new self($this->isExplicitMixed);
354+
}
355+
356+
public function isOffsetAccessible(): TrinaryLogic
357+
{
358+
return TrinaryLogic::createMaybe();
359+
}
360+
361+
public function hasOffsetValueType(Type $offsetType): TrinaryLogic
362+
{
363+
return TrinaryLogic::createMaybe();
364+
}
365+
366+
public function getOffsetValueType(Type $offsetType): Type
367+
{
368+
return new self($this->isExplicitMixed);
336369
}
337370

338371
public function isExplicitMixed(): bool

src/Type/StrictMixedType.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,12 @@ public function hasOffsetValueType(Type $offsetType): TrinaryLogic
163163

164164
public function getOffsetValueType(Type $offsetType): Type
165165
{
166-
return new ErrorType();
166+
return $this;
167167
}
168168

169169
public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
170170
{
171-
return new ErrorType();
171+
return $this;
172172
}
173173

174174
public function isCallable(): TrinaryLogic
@@ -193,27 +193,27 @@ public function toBoolean(): BooleanType
193193

194194
public function toNumber(): Type
195195
{
196-
return new ErrorType();
196+
return $this;
197197
}
198198

199199
public function toInteger(): Type
200200
{
201-
return new ErrorType();
201+
return $this;
202202
}
203203

204204
public function toFloat(): Type
205205
{
206-
return new ErrorType();
206+
return $this;
207207
}
208208

209209
public function toString(): Type
210210
{
211-
return new ErrorType();
211+
return $this;
212212
}
213213

214214
public function toArray(): Type
215215
{
216-
return new ErrorType();
216+
return $this;
217217
}
218218

219219
public function inferTemplateTypes(Type $receivedType): TemplateTypeMap

tests/PHPStan/Levels/data/arrayDimFetches-9.json

Lines changed: 0 additions & 7 deletions
This file was deleted.

tests/PHPStan/Rules/Arrays/IterableInForeachRuleTest.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
class IterableInForeachRuleTest extends \PHPStan\Testing\RuleTestCase
1111
{
1212

13+
/** @var bool */
14+
private $checkExplicitMixed = false;
15+
1316
protected function getRule(): \PHPStan\Rules\Rule
1417
{
15-
return new IterableInForeachRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false));
18+
return new IterableInForeachRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed));
1619
}
1720

1821
public function testCheckWithMaybes(): void
@@ -34,4 +37,19 @@ public function testCheckWithMaybes(): void
3437
]);
3538
}
3639

40+
public function testBug5744(): void
41+
{
42+
$this->checkExplicitMixed = true;
43+
$this->analyse([__DIR__ . '/data/bug-5744.php'], [
44+
/*[
45+
'Argument of an invalid type mixed supplied for foreach, only iterables are supported.',
46+
15,
47+
],*/
48+
[
49+
'Argument of an invalid type mixed supplied for foreach, only iterables are supported.',
50+
28,
51+
],
52+
]);
53+
}
54+
3755
}

tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
class NonexistentOffsetInArrayDimFetchRuleTest extends \PHPStan\Testing\RuleTestCase
1111
{
1212

13+
/** @var bool */
14+
private $checkExplicitMixed = false;
15+
1316
protected function getRule(): \PHPStan\Rules\Rule
1417
{
15-
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false);
18+
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed);
1619

1720
return new NonexistentOffsetInArrayDimFetchRule(
1821
$ruleLevelHelper,
@@ -314,4 +317,19 @@ public function testBug5669(): void
314317
]);
315318
}
316319

320+
public function testBug5744(): void
321+
{
322+
$this->checkExplicitMixed = true;
323+
$this->analyse([__DIR__ . '/data/bug-5744.php'], [
324+
/*[
325+
'Cannot access offset \'permission\' on mixed.',
326+
16,
327+
],*/
328+
[
329+
'Cannot access offset \'permission\' on mixed.',
330+
29,
331+
],
332+
]);
333+
}
334+
317335
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Bug5744;
4+
5+
class HelloWorld
6+
{
7+
8+
/**
9+
* @phpstan-param mixed[] $plugin
10+
*/
11+
public function sayHello(array $plugin): void
12+
{
13+
if(isset($plugin["commands"]) and is_array($plugin["commands"])){
14+
$pluginCommands = $plugin["commands"];
15+
foreach($pluginCommands as $commandName => $commandData){
16+
var_dump($commandData["permission"]);
17+
}
18+
}
19+
}
20+
21+
/**
22+
* @phpstan-param mixed[] $plugin
23+
*/
24+
public function sayHello2(array $plugin): void
25+
{
26+
if(isset($plugin["commands"])){
27+
$pluginCommands = $plugin["commands"];
28+
foreach($pluginCommands as $commandName => $commandData){
29+
var_dump($commandData["permission"]);
30+
}
31+
}
32+
}
33+
34+
public function sayHello3(array $plugin): void
35+
{
36+
if(isset($plugin["commands"]) and is_array($plugin["commands"])){
37+
$pluginCommands = $plugin["commands"];
38+
foreach($pluginCommands as $commandName => $commandData){
39+
var_dump($commandData["permission"]);
40+
}
41+
}
42+
}
43+
}

tests/PHPStan/Type/MixedTypeTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ public function dataIsSuperTypeOf(): array
141141
new UnionType([new StringType(), new IntegerType()]),
142142
TrinaryLogic::createYes(),
143143
],
144+
26 => [
145+
new MixedType(),
146+
new StrictMixedType(),
147+
TrinaryLogic::createYes(),
148+
],
144149
];
145150
}
146151

tests/PHPStan/Type/TypeCombinatorTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1855,6 +1855,14 @@ public function dataUnion(): array
18551855
UnionType::class,
18561856
'array|(callable(): mixed)|(TCode of array<int, int>|int (class Foo, parameter))',
18571857
],
1858+
[
1859+
[
1860+
new MixedType(),
1861+
new StrictMixedType(),
1862+
],
1863+
MixedType::class,
1864+
'mixed=implicit',
1865+
],
18581866
];
18591867
}
18601868

@@ -3081,6 +3089,14 @@ public function dataIntersect(): array
30813089
UnionType::class,
30823090
'T of int|string (function my_array_keys(), parameter)',
30833091
],
3092+
[
3093+
[
3094+
new MixedType(),
3095+
new StrictMixedType(),
3096+
],
3097+
StrictMixedType::class,
3098+
'mixed',
3099+
],
30843100
];
30853101
}
30863102

0 commit comments

Comments
 (0)