Skip to content

Commit 941fc81

Browse files
committed
New RuleLevelHelper::accepts() behaviour only for bleedingEdge
Because the new behaviour behaves slightly differently - for example, arrays with nullable values might be reported sooner than level 8 if it's a constant array where one of the value types is null.
1 parent c0ff269 commit 941fc81

File tree

59 files changed

+202
-57
lines changed

Some content is hidden

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

59 files changed

+202
-57
lines changed

conf/bleedingEdge.neon

+1
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ parameters:
2929
disableUnreachableBranchesRules: true
3030
varTagType: true
3131
closureDefaultParameterTypeRule: true
32+
newRuleLevelHelper: true

conf/config.neon

+3
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ parameters:
5959
disableUnreachableBranchesRules: false
6060
varTagType: false
6161
closureDefaultParameterTypeRule: false
62+
newRuleLevelHelper: false
6263
fileExtensions:
6364
- php
6465
checkAdvancedIsset: false
@@ -284,6 +285,7 @@ parametersSchema:
284285
disableUnreachableBranchesRules: bool()
285286
varTagType: bool()
286287
closureDefaultParameterTypeRule: bool()
288+
newRuleLevelHelper: bool()
287289
])
288290
fileExtensions: listOf(string())
289291
checkAdvancedIsset: bool()
@@ -1104,6 +1106,7 @@ services:
11041106
checkUnionTypes: %checkUnionTypes%
11051107
checkExplicitMixed: %checkExplicitMixed%
11061108
checkImplicitMixed: %checkImplicitMixed%
1109+
newRuleLevelHelper: %featureToggles.newRuleLevelHelper%
11071110
checkBenevolentUnionTypes: %checkBenevolentUnionTypes%
11081111

11091112
-

src/Rules/RuleLevelHelper.php

+142-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use PHPStan\Type\TypeTraverser;
2020
use PHPStan\Type\UnionType;
2121
use PHPStan\Type\VerbosityLevel;
22+
use function array_merge;
2223
use function count;
2324
use function sprintf;
2425
use function strpos;
@@ -33,6 +34,7 @@ public function __construct(
3334
private bool $checkUnionTypes,
3435
private bool $checkExplicitMixed,
3536
private bool $checkImplicitMixed,
37+
private bool $newRuleLevelHelper,
3638
private bool $checkBenevolentUnionTypes,
3739
)
3840
{
@@ -105,10 +107,148 @@ private function transformAcceptedType(Type $acceptingType, Type $acceptedType):
105107

106108
public function acceptsWithReason(Type $acceptingType, Type $acceptedType, bool $strictTypes): RuleLevelHelperAcceptsResult
107109
{
108-
[$acceptedType, $checkForUnion] = $this->transformAcceptedType($acceptingType, $acceptedType);
109-
$acceptingType = $this->transformCommonType($acceptingType);
110+
if ($this->newRuleLevelHelper) {
111+
[$acceptedType, $checkForUnion] = $this->transformAcceptedType($acceptingType, $acceptedType);
112+
$acceptingType = $this->transformCommonType($acceptingType);
113+
114+
$accepts = $acceptingType->acceptsWithReason($acceptedType, $strictTypes);
115+
116+
return new RuleLevelHelperAcceptsResult(
117+
$checkForUnion ? $accepts->yes() : !$accepts->no(),
118+
$accepts->reasons,
119+
);
120+
}
121+
122+
$checkForUnion = $this->checkUnionTypes;
123+
124+
if ($this->checkBenevolentUnionTypes) {
125+
$traverse = static function (Type $type, callable $traverse) use (&$checkForUnion): Type {
126+
if ($type instanceof BenevolentUnionType) {
127+
$checkForUnion = true;
128+
return new UnionType($type->getTypes());
129+
}
130+
131+
return $traverse($type);
132+
};
133+
134+
$acceptedType = TypeTraverser::map($acceptedType, $traverse);
135+
}
136+
137+
if (
138+
$this->checkExplicitMixed
139+
) {
140+
$traverse = static function (Type $type, callable $traverse): Type {
141+
if ($type instanceof TemplateMixedType) {
142+
return $type->toStrictMixedType();
143+
}
144+
if (
145+
$type instanceof MixedType
146+
&& $type->isExplicitMixed()
147+
) {
148+
return new StrictMixedType();
149+
}
150+
151+
return $traverse($type);
152+
};
153+
$acceptingType = TypeTraverser::map($acceptingType, $traverse);
154+
$acceptedType = TypeTraverser::map($acceptedType, $traverse);
155+
}
156+
157+
if (
158+
$this->checkImplicitMixed
159+
) {
160+
$traverse = static function (Type $type, callable $traverse): Type {
161+
if ($type instanceof TemplateMixedType) {
162+
return $type->toStrictMixedType();
163+
}
164+
if (
165+
$type instanceof MixedType
166+
&& !$type->isExplicitMixed()
167+
) {
168+
return new StrictMixedType();
169+
}
170+
171+
return $traverse($type);
172+
};
173+
$acceptingType = TypeTraverser::map($acceptingType, $traverse);
174+
$acceptedType = TypeTraverser::map($acceptedType, $traverse);
175+
}
176+
177+
if (
178+
!$this->checkNullables
179+
&& !$acceptingType instanceof NullType
180+
&& !$acceptedType instanceof NullType
181+
&& !$acceptedType instanceof BenevolentUnionType
182+
) {
183+
$acceptedType = TypeCombinator::removeNull($acceptedType);
184+
}
110185

111186
$accepts = $acceptingType->acceptsWithReason($acceptedType, $strictTypes);
187+
if ($accepts->yes()) {
188+
return new RuleLevelHelperAcceptsResult(true, $accepts->reasons);
189+
}
190+
if ($acceptingType instanceof UnionType) {
191+
$reasons = [];
192+
foreach ($acceptingType->getTypes() as $innerType) {
193+
$accepts = self::acceptsWithReason($innerType, $acceptedType, $strictTypes);
194+
if ($accepts->result) {
195+
return $accepts;
196+
}
197+
198+
$reasons = array_merge($reasons, $accepts->reasons);
199+
}
200+
201+
return new RuleLevelHelperAcceptsResult(false, $reasons);
202+
}
203+
204+
if (
205+
$acceptedType->isArray()->yes()
206+
&& $acceptingType->isArray()->yes()
207+
&& (
208+
$acceptedType->isConstantArray()->no()
209+
|| !$acceptedType->isIterableAtLeastOnce()->no()
210+
)
211+
&& $acceptingType->isConstantArray()->no()
212+
) {
213+
if ($acceptingType->isIterableAtLeastOnce()->yes() && !$acceptedType->isIterableAtLeastOnce()->yes()) {
214+
$verbosity = VerbosityLevel::getRecommendedLevelByType($acceptingType, $acceptedType);
215+
return new RuleLevelHelperAcceptsResult(false, [
216+
sprintf(
217+
'%s %s empty.',
218+
$acceptedType->describe($verbosity),
219+
$acceptedType->isIterableAtLeastOnce()->no() ? 'is' : 'might be',
220+
),
221+
]);
222+
}
223+
224+
if (
225+
$acceptingType->isList()->yes()
226+
&& !$acceptedType->isList()->yes()
227+
) {
228+
$report = $checkForUnion || $acceptedType->isList()->no();
229+
230+
if ($report) {
231+
$verbosity = VerbosityLevel::getRecommendedLevelByType($acceptingType, $acceptedType);
232+
return new RuleLevelHelperAcceptsResult(false, [
233+
sprintf(
234+
'%s %s a list.',
235+
$acceptedType->describe($verbosity),
236+
$acceptedType->isList()->no() ? 'is not' : 'might not be',
237+
),
238+
]);
239+
}
240+
}
241+
242+
return self::acceptsWithReason(
243+
$acceptingType->getIterableKeyType(),
244+
$acceptedType->getIterableKeyType(),
245+
$strictTypes,
246+
)->and(self::acceptsWithReason(
247+
$acceptingType->getIterableValueType(),
248+
$acceptedType->getIterableValueType(),
249+
$strictTypes,
250+
));
251+
}
112252

113253
return new RuleLevelHelperAcceptsResult(
114254
$checkForUnion ? $accepts->yes() : !$accepts->no(),

tests/PHPStan/Rules/Arrays/AppendedArrayItemTypeRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ protected function getRule(): Rule
1717
{
1818
return new AppendedArrayItemTypeRule(
1919
new PropertyReflectionFinder(),
20-
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, false),
20+
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, true, false),
2121
);
2222
}
2323

tests/PHPStan/Rules/Arrays/ArrayDestructuringRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class ArrayDestructuringRuleTest extends RuleTestCase
1717

1818
protected function getRule(): Rule
1919
{
20-
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, false);
20+
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, true, false);
2121

2222
return new ArrayDestructuringRule(
2323
$ruleLevelHelper,

tests/PHPStan/Rules/Arrays/ArrayUnpackingRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ protected function getRule(): Rule
2222
{
2323
return new ArrayUnpackingRule(
2424
self::getContainer()->getByType(PhpVersion::class),
25-
new RuleLevelHelper($this->createReflectionProvider(), true, false, $this->checkUnions, false, false, $this->checkBenevolentUnions),
25+
new RuleLevelHelper($this->createReflectionProvider(), true, false, $this->checkUnions, false, false, true, $this->checkBenevolentUnions),
2626
);
2727
}
2828

tests/PHPStan/Rules/Arrays/InvalidKeyInArrayDimFetchRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class InvalidKeyInArrayDimFetchRuleTest extends RuleTestCase
1414

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

tests/PHPStan/Rules/Arrays/IterableInForeachRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class IterableInForeachRuleTest extends RuleTestCase
1717

1818
protected function getRule(): Rule
1919
{
20-
return new IterableInForeachRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, false, false));
20+
return new IterableInForeachRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, false, true, false));
2121
}
2222

2323
public function testCheckWithMaybes(): void

tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class NonexistentOffsetInArrayDimFetchRuleTest extends RuleTestCase
1919

2020
protected function getRule(): Rule
2121
{
22-
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, false, false);
22+
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, false, true, false);
2323

2424
return new NonexistentOffsetInArrayDimFetchRule(
2525
$ruleLevelHelper,

tests/PHPStan/Rules/Arrays/OffsetAccessAssignOpRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class OffsetAccessAssignOpRuleTest extends RuleTestCase
1717

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

tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class OffsetAccessAssignmentRuleTest extends RuleTestCase
1717

1818
protected function getRule(): Rule
1919
{
20-
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, $this->checkUnionTypes, false, false, false);
20+
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, $this->checkUnionTypes, false, false, true, false);
2121
return new OffsetAccessAssignmentRule($ruleLevelHelper);
2222
}
2323

tests/PHPStan/Rules/Arrays/OffsetAccessValueAssignmentRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class OffsetAccessValueAssignmentRuleTest extends RuleTestCase
1515

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

2121
public function testRule(): void

tests/PHPStan/Rules/Arrays/UnpackIterableInArrayRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class UnpackIterableInArrayRuleTest extends RuleTestCase
1515

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

2121
public function testRule(): void

tests/PHPStan/Rules/Cast/EchoRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class EchoRuleTest extends RuleTestCase
1616
protected function getRule(): Rule
1717
{
1818
return new EchoRule(
19-
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, false),
19+
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, true, false),
2020
);
2121
}
2222

tests/PHPStan/Rules/Cast/InvalidCastRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class InvalidCastRuleTest extends RuleTestCase
1616
protected function getRule(): Rule
1717
{
1818
$broker = $this->createReflectionProvider();
19-
return new InvalidCastRule($broker, new RuleLevelHelper($broker, true, false, true, false, false, false));
19+
return new InvalidCastRule($broker, new RuleLevelHelper($broker, true, false, true, false, false, true, false));
2020
}
2121

2222
public function testRule(): void

tests/PHPStan/Rules/Cast/InvalidPartOfEncapsedStringRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ protected function getRule(): Rule
1919
{
2020
return new InvalidPartOfEncapsedStringRule(
2121
new ExprPrinter(new Printer()),
22-
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, false),
22+
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, true, false),
2323
);
2424
}
2525

tests/PHPStan/Rules/Cast/PrintRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class PrintRuleTest extends RuleTestCase
1616
protected function getRule(): Rule
1717
{
1818
return new PrintRule(
19-
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, false),
19+
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, true, false),
2020
);
2121
}
2222

tests/PHPStan/Rules/Classes/ClassAttributesRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ protected function getRule(): Rule
2727
new AttributesCheck(
2828
$reflectionProvider,
2929
new FunctionCallParametersCheck(
30-
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, false),
30+
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, true, false),
3131
new NullsafeCheck(),
3232
new PhpVersion(80000),
3333
new UnresolvableTypeHelper(),

tests/PHPStan/Rules/Classes/ClassConstantAttributesRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ protected function getRule(): Rule
2626
new AttributesCheck(
2727
$reflectionProvider,
2828
new FunctionCallParametersCheck(
29-
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, false),
29+
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, true, false),
3030
new NullsafeCheck(),
3131
new PhpVersion(80000),
3232
new UnresolvableTypeHelper(),

tests/PHPStan/Rules/Classes/ClassConstantRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class ClassConstantRuleTest extends RuleTestCase
2020
protected function getRule(): Rule
2121
{
2222
$broker = $this->createReflectionProvider();
23-
return new ClassConstantRule($broker, new RuleLevelHelper($broker, true, false, true, false, false, false), new ClassCaseSensitivityCheck($broker, true), new PhpVersion($this->phpVersion));
23+
return new ClassConstantRule($broker, new RuleLevelHelper($broker, true, false, true, false, false, true, false), new ClassCaseSensitivityCheck($broker, true), new PhpVersion($this->phpVersion));
2424
}
2525

2626
public function testClassConstant(): void

tests/PHPStan/Rules/Classes/InstantiationRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ protected function getRule(): Rule
2424
$broker = $this->createReflectionProvider();
2525
return new InstantiationRule(
2626
$broker,
27-
new FunctionCallParametersCheck(new RuleLevelHelper($broker, true, false, true, false, false, false), new NullsafeCheck(), new PhpVersion(80000), new UnresolvableTypeHelper(), new PropertyReflectionFinder(), true, true, true, true, true),
27+
new FunctionCallParametersCheck(new RuleLevelHelper($broker, true, false, true, false, false, true, false), new NullsafeCheck(), new PhpVersion(80000), new UnresolvableTypeHelper(), new PropertyReflectionFinder(), true, true, true, true, true),
2828
new ClassCaseSensitivityCheck($broker, true),
2929
);
3030
}

tests/PHPStan/Rules/EnumCases/EnumCaseAttributesRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ protected function getRule(): Rule
2626
new AttributesCheck(
2727
$reflectionProvider,
2828
new FunctionCallParametersCheck(
29-
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, false),
29+
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, true, false),
3030
new NullsafeCheck(),
3131
new PhpVersion(80100),
3232
new UnresolvableTypeHelper(),

tests/PHPStan/Rules/Functions/ArrowFunctionAttributesRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ protected function getRule(): Rule
2626
new AttributesCheck(
2727
$reflectionProvider,
2828
new FunctionCallParametersCheck(
29-
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, false),
29+
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, true, false),
3030
new NullsafeCheck(),
3131
new PhpVersion(80000),
3232
new UnresolvableTypeHelper(),

tests/PHPStan/Rules/Functions/ArrowFunctionReturnTypeRuleTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ protected function getRule(): Rule
2323
true,
2424
false,
2525
false,
26+
true,
2627
false,
2728
)));
2829
}

tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class CallCallablesRuleTest extends RuleTestCase
2222

2323
protected function getRule(): Rule
2424
{
25-
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, false, false);
25+
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, false, true, false);
2626
return new CallCallablesRule(
2727
new FunctionCallParametersCheck(
2828
$ruleLevelHelper,

0 commit comments

Comments
 (0)