Skip to content

Commit

Permalink
New RuleLevelHelper::accepts() behaviour only for bleedingEdge
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ondrejmirtes committed Jan 28, 2023
1 parent c0ff269 commit 941fc81
Show file tree
Hide file tree
Showing 59 changed files with 202 additions and 57 deletions.
1 change: 1 addition & 0 deletions conf/bleedingEdge.neon
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ parameters:
disableUnreachableBranchesRules: true
varTagType: true
closureDefaultParameterTypeRule: true
newRuleLevelHelper: true
3 changes: 3 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ parameters:
disableUnreachableBranchesRules: false
varTagType: false
closureDefaultParameterTypeRule: false
newRuleLevelHelper: false
fileExtensions:
- php
checkAdvancedIsset: false
Expand Down Expand Up @@ -284,6 +285,7 @@ parametersSchema:
disableUnreachableBranchesRules: bool()
varTagType: bool()
closureDefaultParameterTypeRule: bool()
newRuleLevelHelper: bool()
])
fileExtensions: listOf(string())
checkAdvancedIsset: bool()
Expand Down Expand Up @@ -1104,6 +1106,7 @@ services:
checkUnionTypes: %checkUnionTypes%
checkExplicitMixed: %checkExplicitMixed%
checkImplicitMixed: %checkImplicitMixed%
newRuleLevelHelper: %featureToggles.newRuleLevelHelper%
checkBenevolentUnionTypes: %checkBenevolentUnionTypes%

-
Expand Down
144 changes: 142 additions & 2 deletions src/Rules/RuleLevelHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\UnionType;
use PHPStan\Type\VerbosityLevel;
use function array_merge;
use function count;
use function sprintf;
use function strpos;
Expand All @@ -33,6 +34,7 @@ public function __construct(
private bool $checkUnionTypes,
private bool $checkExplicitMixed,
private bool $checkImplicitMixed,
private bool $newRuleLevelHelper,
private bool $checkBenevolentUnionTypes,
)
{
Expand Down Expand Up @@ -105,10 +107,148 @@ private function transformAcceptedType(Type $acceptingType, Type $acceptedType):

public function acceptsWithReason(Type $acceptingType, Type $acceptedType, bool $strictTypes): RuleLevelHelperAcceptsResult
{
[$acceptedType, $checkForUnion] = $this->transformAcceptedType($acceptingType, $acceptedType);
$acceptingType = $this->transformCommonType($acceptingType);
if ($this->newRuleLevelHelper) {
[$acceptedType, $checkForUnion] = $this->transformAcceptedType($acceptingType, $acceptedType);
$acceptingType = $this->transformCommonType($acceptingType);

$accepts = $acceptingType->acceptsWithReason($acceptedType, $strictTypes);

return new RuleLevelHelperAcceptsResult(
$checkForUnion ? $accepts->yes() : !$accepts->no(),
$accepts->reasons,
);
}

$checkForUnion = $this->checkUnionTypes;

if ($this->checkBenevolentUnionTypes) {
$traverse = static function (Type $type, callable $traverse) use (&$checkForUnion): Type {
if ($type instanceof BenevolentUnionType) {
$checkForUnion = true;
return new UnionType($type->getTypes());
}

return $traverse($type);
};

$acceptedType = TypeTraverser::map($acceptedType, $traverse);
}

if (
$this->checkExplicitMixed
) {
$traverse = static function (Type $type, callable $traverse): Type {
if ($type instanceof TemplateMixedType) {
return $type->toStrictMixedType();
}
if (
$type instanceof MixedType
&& $type->isExplicitMixed()
) {
return new StrictMixedType();
}

return $traverse($type);
};
$acceptingType = TypeTraverser::map($acceptingType, $traverse);
$acceptedType = TypeTraverser::map($acceptedType, $traverse);
}

if (
$this->checkImplicitMixed
) {
$traverse = static function (Type $type, callable $traverse): Type {
if ($type instanceof TemplateMixedType) {
return $type->toStrictMixedType();
}
if (
$type instanceof MixedType
&& !$type->isExplicitMixed()
) {
return new StrictMixedType();
}

return $traverse($type);
};
$acceptingType = TypeTraverser::map($acceptingType, $traverse);
$acceptedType = TypeTraverser::map($acceptedType, $traverse);
}

if (
!$this->checkNullables
&& !$acceptingType instanceof NullType
&& !$acceptedType instanceof NullType
&& !$acceptedType instanceof BenevolentUnionType
) {
$acceptedType = TypeCombinator::removeNull($acceptedType);
}

$accepts = $acceptingType->acceptsWithReason($acceptedType, $strictTypes);
if ($accepts->yes()) {
return new RuleLevelHelperAcceptsResult(true, $accepts->reasons);
}
if ($acceptingType instanceof UnionType) {
$reasons = [];
foreach ($acceptingType->getTypes() as $innerType) {
$accepts = self::acceptsWithReason($innerType, $acceptedType, $strictTypes);
if ($accepts->result) {
return $accepts;
}

$reasons = array_merge($reasons, $accepts->reasons);
}

return new RuleLevelHelperAcceptsResult(false, $reasons);
}

if (
$acceptedType->isArray()->yes()
&& $acceptingType->isArray()->yes()
&& (
$acceptedType->isConstantArray()->no()
|| !$acceptedType->isIterableAtLeastOnce()->no()
)
&& $acceptingType->isConstantArray()->no()
) {
if ($acceptingType->isIterableAtLeastOnce()->yes() && !$acceptedType->isIterableAtLeastOnce()->yes()) {
$verbosity = VerbosityLevel::getRecommendedLevelByType($acceptingType, $acceptedType);
return new RuleLevelHelperAcceptsResult(false, [
sprintf(
'%s %s empty.',
$acceptedType->describe($verbosity),
$acceptedType->isIterableAtLeastOnce()->no() ? 'is' : 'might be',
),
]);
}

if (
$acceptingType->isList()->yes()
&& !$acceptedType->isList()->yes()
) {
$report = $checkForUnion || $acceptedType->isList()->no();

if ($report) {
$verbosity = VerbosityLevel::getRecommendedLevelByType($acceptingType, $acceptedType);
return new RuleLevelHelperAcceptsResult(false, [
sprintf(
'%s %s a list.',
$acceptedType->describe($verbosity),
$acceptedType->isList()->no() ? 'is not' : 'might not be',
),
]);
}
}

return self::acceptsWithReason(
$acceptingType->getIterableKeyType(),
$acceptedType->getIterableKeyType(),
$strictTypes,
)->and(self::acceptsWithReason(
$acceptingType->getIterableValueType(),
$acceptedType->getIterableValueType(),
$strictTypes,
));
}

return new RuleLevelHelperAcceptsResult(
$checkForUnion ? $accepts->yes() : !$accepts->no(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protected function getRule(): Rule
{
return new AppendedArrayItemTypeRule(
new PropertyReflectionFinder(),
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, false),
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, true, false),
);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Arrays/ArrayDestructuringRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class ArrayDestructuringRuleTest extends RuleTestCase

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

return new ArrayDestructuringRule(
$ruleLevelHelper,
Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Arrays/ArrayUnpackingRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ protected function getRule(): Rule
{
return new ArrayUnpackingRule(
self::getContainer()->getByType(PhpVersion::class),
new RuleLevelHelper($this->createReflectionProvider(), true, false, $this->checkUnions, false, false, $this->checkBenevolentUnions),
new RuleLevelHelper($this->createReflectionProvider(), true, false, $this->checkUnions, false, false, true, $this->checkBenevolentUnions),
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class InvalidKeyInArrayDimFetchRuleTest extends RuleTestCase

protected function getRule(): Rule
{
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, false);
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, true, false);
return new InvalidKeyInArrayDimFetchRule($ruleLevelHelper, true);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Arrays/IterableInForeachRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class IterableInForeachRuleTest extends RuleTestCase

protected function getRule(): Rule
{
return new IterableInForeachRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, false, false));
return new IterableInForeachRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, false, true, false));
}

public function testCheckWithMaybes(): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class NonexistentOffsetInArrayDimFetchRuleTest extends RuleTestCase

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

return new NonexistentOffsetInArrayDimFetchRule(
$ruleLevelHelper,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class OffsetAccessAssignOpRuleTest extends RuleTestCase

protected function getRule(): Rule
{
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, $this->checkUnions, false, false, false);
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, $this->checkUnions, false, false, true, false);
return new OffsetAccessAssignOpRule($ruleLevelHelper);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class OffsetAccessAssignmentRuleTest extends RuleTestCase

protected function getRule(): Rule
{
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, $this->checkUnionTypes, false, false, false);
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, $this->checkUnionTypes, false, false, true, false);
return new OffsetAccessAssignmentRule($ruleLevelHelper);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class OffsetAccessValueAssignmentRuleTest extends RuleTestCase

protected function getRule(): Rule
{
return new OffsetAccessValueAssignmentRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, false));
return new OffsetAccessValueAssignmentRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, true, false));
}

public function testRule(): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class UnpackIterableInArrayRuleTest extends RuleTestCase

protected function getRule(): Rule
{
return new UnpackIterableInArrayRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, false));
return new UnpackIterableInArrayRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, true, false));
}

public function testRule(): void
Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Cast/EchoRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class EchoRuleTest extends RuleTestCase
protected function getRule(): Rule
{
return new EchoRule(
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, false),
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, true, false),
);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Cast/InvalidCastRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class InvalidCastRuleTest extends RuleTestCase
protected function getRule(): Rule
{
$broker = $this->createReflectionProvider();
return new InvalidCastRule($broker, new RuleLevelHelper($broker, true, false, true, false, false, false));
return new InvalidCastRule($broker, new RuleLevelHelper($broker, true, false, true, false, false, true, false));
}

public function testRule(): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ protected function getRule(): Rule
{
return new InvalidPartOfEncapsedStringRule(
new ExprPrinter(new Printer()),
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, false),
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, true, false),
);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Cast/PrintRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class PrintRuleTest extends RuleTestCase
protected function getRule(): Rule
{
return new PrintRule(
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, false),
new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, true, false),
);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Classes/ClassAttributesRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ protected function getRule(): Rule
new AttributesCheck(
$reflectionProvider,
new FunctionCallParametersCheck(
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, false),
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, true, false),
new NullsafeCheck(),
new PhpVersion(80000),
new UnresolvableTypeHelper(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ protected function getRule(): Rule
new AttributesCheck(
$reflectionProvider,
new FunctionCallParametersCheck(
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, false),
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, true, false),
new NullsafeCheck(),
new PhpVersion(80000),
new UnresolvableTypeHelper(),
Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Classes/ClassConstantRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class ClassConstantRuleTest extends RuleTestCase
protected function getRule(): Rule
{
$broker = $this->createReflectionProvider();
return new ClassConstantRule($broker, new RuleLevelHelper($broker, true, false, true, false, false, false), new ClassCaseSensitivityCheck($broker, true), new PhpVersion($this->phpVersion));
return new ClassConstantRule($broker, new RuleLevelHelper($broker, true, false, true, false, false, true, false), new ClassCaseSensitivityCheck($broker, true), new PhpVersion($this->phpVersion));
}

public function testClassConstant(): void
Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Classes/InstantiationRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ protected function getRule(): Rule
$broker = $this->createReflectionProvider();
return new InstantiationRule(
$broker,
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),
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),
new ClassCaseSensitivityCheck($broker, true),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ protected function getRule(): Rule
new AttributesCheck(
$reflectionProvider,
new FunctionCallParametersCheck(
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, false),
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, true, false),
new NullsafeCheck(),
new PhpVersion(80100),
new UnresolvableTypeHelper(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ protected function getRule(): Rule
new AttributesCheck(
$reflectionProvider,
new FunctionCallParametersCheck(
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, false),
new RuleLevelHelper($reflectionProvider, true, false, true, false, false, true, false),
new NullsafeCheck(),
new PhpVersion(80000),
new UnresolvableTypeHelper(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ protected function getRule(): Rule
true,
false,
false,
true,
false,
)));
}
Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class CallCallablesRuleTest extends RuleTestCase

protected function getRule(): Rule
{
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, false, false);
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, false, true, false);
return new CallCallablesRule(
new FunctionCallParametersCheck(
$ruleLevelHelper,
Expand Down
Loading

0 comments on commit 941fc81

Please sign in to comment.