Skip to content

Commit da0b2eb

Browse files
committed
An option for checking explicit mixed
Eventually this will become level 9
1 parent 6a38951 commit da0b2eb

41 files changed

Lines changed: 372 additions & 38 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

conf/config.neon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ parameters:
1616
checkAlwaysTrueInstanceof: false
1717
checkAlwaysTrueStrictComparison: false
1818
checkClassCaseSensitivity: false
19+
checkExplicitMixed: false
1920
checkFunctionArgumentTypes: false
2021
checkFunctionNameCase: false
2122
checkGenericClassInNonGenericObjectType: false
@@ -145,6 +146,7 @@ parametersSchema:
145146
checkAlwaysTrueInstanceof: bool()
146147
checkAlwaysTrueStrictComparison: bool()
147148
checkClassCaseSensitivity: bool()
149+
checkExplicitMixed: bool()
148150
checkFunctionArgumentTypes: bool()
149151
checkFunctionNameCase: bool()
150152
checkGenericClassInNonGenericObjectType: bool()
@@ -596,6 +598,7 @@ services:
596598
checkNullables: %checkNullables%
597599
checkThisOnly: %checkThisOnly%
598600
checkUnionTypes: %checkUnionTypes%
601+
checkExplicitMixed: %checkExplicitMixed%
599602

600603
-
601604
class: PHPStan\Rules\UnusedFunctionParametersCheck

src/Rules/RuleLevelHelper.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
use PHPStan\Type\BenevolentUnionType;
99
use PHPStan\Type\CompoundType;
1010
use PHPStan\Type\ErrorType;
11+
use PHPStan\Type\Generic\TemplateMixedType;
1112
use PHPStan\Type\MixedType;
1213
use PHPStan\Type\NeverType;
1314
use PHPStan\Type\NullType;
1415
use PHPStan\Type\ObjectWithoutClassType;
1516
use PHPStan\Type\StaticType;
17+
use PHPStan\Type\StrictMixedType;
1618
use PHPStan\Type\Type;
1719
use PHPStan\Type\TypeCombinator;
1820
use PHPStan\Type\TypeUtils;
@@ -29,17 +31,21 @@ class RuleLevelHelper
2931

3032
private bool $checkUnionTypes;
3133

34+
private bool $checkExplicitMixed;
35+
3236
public function __construct(
3337
ReflectionProvider $reflectionProvider,
3438
bool $checkNullables,
3539
bool $checkThisOnly,
36-
bool $checkUnionTypes
40+
bool $checkUnionTypes,
41+
bool $checkExplicitMixed
3742
)
3843
{
3944
$this->reflectionProvider = $reflectionProvider;
4045
$this->checkNullables = $checkNullables;
4146
$this->checkThisOnly = $checkThisOnly;
4247
$this->checkUnionTypes = $checkUnionTypes;
48+
$this->checkExplicitMixed = $checkExplicitMixed;
4349
}
4450

4551
public function isThis(Expr $expression): bool
@@ -49,6 +55,14 @@ public function isThis(Expr $expression): bool
4955

5056
public function accepts(Type $acceptingType, Type $acceptedType, bool $strictTypes): bool
5157
{
58+
if (
59+
$this->checkExplicitMixed
60+
&& $acceptedType instanceof MixedType
61+
&& $acceptedType->isExplicitMixed()
62+
) {
63+
$acceptedType = new StrictMixedType();
64+
}
65+
5266
if (
5367
!$this->checkNullables
5468
&& !$acceptingType instanceof NullType
@@ -111,6 +125,15 @@ public function findTypeToCheck(
111125
if (!$this->checkNullables && !$type instanceof NullType) {
112126
$type = \PHPStan\Type\TypeCombinator::removeNull($type);
113127
}
128+
if (
129+
$this->checkExplicitMixed
130+
&& $type instanceof MixedType
131+
&& !$type instanceof TemplateMixedType
132+
&& $type->isExplicitMixed()
133+
) {
134+
return new FoundTypeResult(new StrictMixedType(), [], []);
135+
}
136+
114137
if ($type instanceof MixedType || $type instanceof NeverType) {
115138
return new FoundTypeResult(new ErrorType(), [], []);
116139
}

src/Type/StrictMixedType.php

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type;
4+
5+
use PHPStan\Reflection\ClassMemberAccessAnswerer;
6+
use PHPStan\Reflection\ConstantReflection;
7+
use PHPStan\Reflection\MethodReflection;
8+
use PHPStan\Reflection\PropertyReflection;
9+
use PHPStan\TrinaryLogic;
10+
use PHPStan\Type\Generic\TemplateMixedType;
11+
use PHPStan\Type\Generic\TemplateTypeMap;
12+
use PHPStan\Type\Generic\TemplateTypeVariance;
13+
14+
class StrictMixedType implements CompoundType
15+
{
16+
17+
public function getReferencedClasses(): array
18+
{
19+
return [];
20+
}
21+
22+
public function accepts(Type $type, bool $strictTypes): TrinaryLogic
23+
{
24+
return TrinaryLogic::createNo();
25+
}
26+
27+
public function isAcceptedBy(Type $acceptingType, bool $strictTypes): TrinaryLogic
28+
{
29+
return TrinaryLogic::createFromBoolean(
30+
$acceptingType instanceof MixedType && !$acceptingType instanceof TemplateMixedType
31+
);
32+
}
33+
34+
public function isSuperTypeOf(Type $type): TrinaryLogic
35+
{
36+
return TrinaryLogic::createFromBoolean($type instanceof self);
37+
}
38+
39+
public function isSubTypeOf(Type $otherType): TrinaryLogic
40+
{
41+
return TrinaryLogic::createFromBoolean($otherType instanceof self);
42+
}
43+
44+
public function equals(Type $type): bool
45+
{
46+
return $type instanceof self;
47+
}
48+
49+
public function describe(VerbosityLevel $level): string
50+
{
51+
return 'mixed';
52+
}
53+
54+
public function canAccessProperties(): TrinaryLogic
55+
{
56+
return TrinaryLogic::createNo();
57+
}
58+
59+
public function hasProperty(string $propertyName): TrinaryLogic
60+
{
61+
return TrinaryLogic::createNo();
62+
}
63+
64+
public function getProperty(string $propertyName, ClassMemberAccessAnswerer $scope): PropertyReflection
65+
{
66+
throw new \PHPStan\ShouldNotHappenException();
67+
}
68+
69+
public function canCallMethods(): TrinaryLogic
70+
{
71+
return TrinaryLogic::createNo();
72+
}
73+
74+
public function hasMethod(string $methodName): TrinaryLogic
75+
{
76+
return TrinaryLogic::createNo();
77+
}
78+
79+
public function getMethod(string $methodName, ClassMemberAccessAnswerer $scope): MethodReflection
80+
{
81+
throw new \PHPStan\ShouldNotHappenException();
82+
}
83+
84+
public function canAccessConstants(): TrinaryLogic
85+
{
86+
return TrinaryLogic::createNo();
87+
}
88+
89+
public function hasConstant(string $constantName): TrinaryLogic
90+
{
91+
return TrinaryLogic::createNo();
92+
}
93+
94+
public function getConstant(string $constantName): ConstantReflection
95+
{
96+
throw new \PHPStan\ShouldNotHappenException();
97+
}
98+
99+
public function isIterable(): TrinaryLogic
100+
{
101+
return TrinaryLogic::createNo();
102+
}
103+
104+
public function isIterableAtLeastOnce(): TrinaryLogic
105+
{
106+
return TrinaryLogic::createNo();
107+
}
108+
109+
public function getIterableKeyType(): Type
110+
{
111+
return $this;
112+
}
113+
114+
public function getIterableValueType(): Type
115+
{
116+
return $this;
117+
}
118+
119+
public function isArray(): TrinaryLogic
120+
{
121+
return TrinaryLogic::createNo();
122+
}
123+
124+
public function isOffsetAccessible(): TrinaryLogic
125+
{
126+
return TrinaryLogic::createNo();
127+
}
128+
129+
public function hasOffsetValueType(Type $offsetType): TrinaryLogic
130+
{
131+
return TrinaryLogic::createNo();
132+
}
133+
134+
public function getOffsetValueType(Type $offsetType): Type
135+
{
136+
return new ErrorType();
137+
}
138+
139+
public function setOffsetValueType(?Type $offsetType, Type $valueType): Type
140+
{
141+
return new ErrorType();
142+
}
143+
144+
public function isCallable(): TrinaryLogic
145+
{
146+
return TrinaryLogic::createNo();
147+
}
148+
149+
public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array
150+
{
151+
return [];
152+
}
153+
154+
public function isCloneable(): TrinaryLogic
155+
{
156+
return TrinaryLogic::createNo();
157+
}
158+
159+
public function toBoolean(): BooleanType
160+
{
161+
return new BooleanType();
162+
}
163+
164+
public function toNumber(): Type
165+
{
166+
return new ErrorType();
167+
}
168+
169+
public function toInteger(): Type
170+
{
171+
return new ErrorType();
172+
}
173+
174+
public function toFloat(): Type
175+
{
176+
return new ErrorType();
177+
}
178+
179+
public function toString(): Type
180+
{
181+
return new ErrorType();
182+
}
183+
184+
public function toArray(): Type
185+
{
186+
return new ErrorType();
187+
}
188+
189+
public function inferTemplateTypes(Type $receivedType): TemplateTypeMap
190+
{
191+
return TemplateTypeMap::createEmpty();
192+
}
193+
194+
public function getReferencedTemplateTypes(TemplateTypeVariance $positionVariance): array
195+
{
196+
return [];
197+
}
198+
199+
public function traverse(callable $cb): Type
200+
{
201+
return $this;
202+
}
203+
204+
/**
205+
* @param mixed[] $properties
206+
* @return Type
207+
*/
208+
public static function __set_state(array $properties): Type
209+
{
210+
return new self();
211+
}
212+
213+
}

tests/PHPStan/Rules/Arrays/AppendedArrayItemTypeRuleTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ protected function getRule(): \PHPStan\Rules\Rule
1515
{
1616
return new AppendedArrayItemTypeRule(
1717
new PropertyReflectionFinder(),
18-
new RuleLevelHelper($this->createReflectionProvider(), true, false, true)
18+
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false)
1919
);
2020
}
2121

tests/PHPStan/Rules/Arrays/IterableInForeachRuleTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class IterableInForeachRuleTest extends \PHPStan\Testing\RuleTestCase
1212

1313
protected function getRule(): \PHPStan\Rules\Rule
1414
{
15-
return new IterableInForeachRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true));
15+
return new IterableInForeachRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false));
1616
}
1717

1818
public function testCheckWithMaybes(): void

tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class NonexistentOffsetInArrayDimFetchRuleTest extends \PHPStan\Testing\RuleTest
1313
protected function getRule(): \PHPStan\Rules\Rule
1414
{
1515
return new NonexistentOffsetInArrayDimFetchRule(
16-
new RuleLevelHelper($this->createReflectionProvider(), true, false, true),
16+
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false),
1717
true
1818
);
1919
}

tests/PHPStan/Rules/Arrays/OffsetAccessAssignOpRuleTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class OffsetAccessAssignOpRuleTest extends \PHPStan\Testing\RuleTestCase
1616

1717
protected function getRule(): Rule
1818
{
19-
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, $this->checkUnions);
19+
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, $this->checkUnions, false);
2020
return new OffsetAccessAssignOpRule($ruleLevelHelper);
2121
}
2222

tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class OffsetAccessAssignmentRuleTest extends \PHPStan\Testing\RuleTestCase
1515

1616
protected function getRule(): \PHPStan\Rules\Rule
1717
{
18-
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, $this->checkUnionTypes);
18+
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, $this->checkUnionTypes, false);
1919
return new OffsetAccessAssignmentRule($ruleLevelHelper);
2020
}
2121

tests/PHPStan/Rules/Arrays/OffsetAccessValueAssignmentRuleTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class OffsetAccessValueAssignmentRuleTest extends RuleTestCase
1414

1515
protected function getRule(): Rule
1616
{
17-
return new OffsetAccessValueAssignmentRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true));
17+
return new OffsetAccessValueAssignmentRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false));
1818
}
1919

2020
public function testRule(): void

tests/PHPStan/Rules/Arrays/UnpackIterableInArrayRuleTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class UnpackIterableInArrayRuleTest extends RuleTestCase
1414

1515
protected function getRule(): Rule
1616
{
17-
return new UnpackIterableInArrayRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true));
17+
return new UnpackIterableInArrayRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false));
1818
}
1919

2020
public function testRule(): void

0 commit comments

Comments
 (0)