Skip to content

Commit

Permalink
[experimental] Add withTypeCoverageLevel() method to streamline Recto…
Browse files Browse the repository at this point in the history
…r integration to new projects (#5553)

* [experimental] Add withTypeCoverageLevel() method to streamline Rector integration to new projects

* add withDeadCodeLevel() method
  • Loading branch information
TomasVotruba authored Feb 5, 2024
1 parent 70f10cb commit 487f162
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 0 deletions.
2 changes: 2 additions & 0 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
earlyReturn: true,
naming: true
)
// @experimental since 0.19.7 for more smooth Rector integration to new project
->withTypeCoverageLevel(10)
->withPhpSets()
->withRules([DeclareStrictTypesRector::class])
->withPaths([
Expand Down
123 changes: 123 additions & 0 deletions src/Configuration/Levels/DeadCodeLevel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

declare(strict_types=1);

namespace Rector\Configuration\Levels;

use Rector\CodeQuality\Rector\FunctionLike\SimplifyUselessVariableRector;
use Rector\Contract\Rector\RectorInterface;
use Rector\DeadCode\Rector\Array_\RemoveDuplicatedArrayKeyRector;
use Rector\DeadCode\Rector\Assign\RemoveDoubleAssignRector;
use Rector\DeadCode\Rector\Assign\RemoveUnusedVariableAssignRector;
use Rector\DeadCode\Rector\BooleanAnd\RemoveAndTrueRector;
use Rector\DeadCode\Rector\Cast\RecastingRemovalRector;
use Rector\DeadCode\Rector\ClassConst\RemoveUnusedPrivateClassConstantRector;
use Rector\DeadCode\Rector\ClassMethod\RemoveEmptyClassMethodRector;
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedConstructorParamRector;
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodParameterRector;
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector;
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPromotedPropertyRector;
use Rector\DeadCode\Rector\ClassMethod\RemoveUselessParamTagRector;
use Rector\DeadCode\Rector\ClassMethod\RemoveUselessReturnExprInConstructRector;
use Rector\DeadCode\Rector\ClassMethod\RemoveUselessReturnTagRector;
use Rector\DeadCode\Rector\Concat\RemoveConcatAutocastRector;
use Rector\DeadCode\Rector\ConstFetch\RemovePhpVersionIdCheckRector;
use Rector\DeadCode\Rector\Expression\RemoveDeadStmtRector;
use Rector\DeadCode\Rector\Expression\SimplifyMirrorAssignRector;
use Rector\DeadCode\Rector\For_\RemoveDeadContinueRector;
use Rector\DeadCode\Rector\For_\RemoveDeadIfForeachForRector;
use Rector\DeadCode\Rector\For_\RemoveDeadLoopRector;
use Rector\DeadCode\Rector\Foreach_\RemoveUnusedForeachKeyRector;
use Rector\DeadCode\Rector\FunctionLike\RemoveDeadReturnRector;
use Rector\DeadCode\Rector\If_\RemoveAlwaysTrueIfConditionRector;
use Rector\DeadCode\Rector\If_\RemoveDeadInstanceOfRector;
use Rector\DeadCode\Rector\If_\RemoveTypedPropertyDeadInstanceOfRector;
use Rector\DeadCode\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector;
use Rector\DeadCode\Rector\If_\SimplifyIfElseWithSameContentRector;
use Rector\DeadCode\Rector\If_\UnwrapFutureCompatibleIfPhpVersionRector;
use Rector\DeadCode\Rector\Node\RemoveNonExistingVarAnnotationRector;
use Rector\DeadCode\Rector\Plus\RemoveDeadZeroAndOneOperationRector;
use Rector\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRector;
use Rector\DeadCode\Rector\Property\RemoveUselessVarTagRector;
use Rector\DeadCode\Rector\PropertyProperty\RemoveNullPropertyInitializationRector;
use Rector\DeadCode\Rector\Return_\RemoveDeadConditionAboveReturnRector;
use Rector\DeadCode\Rector\StaticCall\RemoveParentCallWithoutParentRector;
use Rector\DeadCode\Rector\Stmt\RemoveUnreachableStatementRector;
use Rector\DeadCode\Rector\Switch_\RemoveDuplicatedCaseInSwitchRector;
use Rector\DeadCode\Rector\Ternary\TernaryToBooleanOrFalseToBooleanAndRector;
use Rector\DeadCode\Rector\TryCatch\RemoveDeadTryCatchRector;

/**
* Key 0 = level 0
* Key 50 = level 50
*
* Start at 0, go slowly higher, one level per PR, and improve your rule coverage
*
* From the safest rules to more changing ones.
*
* @experimental Since 0.19.7 This list can change in time, based on community feedback,
* what rules are safer than other. The safest rules will be always in the top.
*/
final class DeadCodeLevel
{
/**
* Mind that return type declarations are the safest to add,
* followed by property, then params
*
* @var array<class-string<RectorInterface>>
*/
public const RULE_LIST = [
// easy picks
RemoveUnusedForeachKeyRector::class,
RemoveDuplicatedArrayKeyRector::class,
RecastingRemovalRector::class,
RemoveAndTrueRector::class,
SimplifyMirrorAssignRector::class,
RemoveDeadContinueRector::class,
RemoveUnusedNonEmptyArrayBeforeForeachRector::class,
RemoveNullPropertyInitializationRector::class,
RemoveUselessReturnExprInConstructRector::class,

RemoveTypedPropertyDeadInstanceOfRector::class,
TernaryToBooleanOrFalseToBooleanAndRector::class,
RemoveDoubleAssignRector::class,
RemoveConcatAutocastRector::class,
SimplifyIfElseWithSameContentRector::class,
SimplifyUselessVariableRector::class,
RemoveDeadZeroAndOneOperationRector::class,

// docblock
RemoveUselessParamTagRector::class,
RemoveUselessReturnTagRector::class,
RemoveNonExistingVarAnnotationRector::class,
RemoveUselessVarTagRector::class,
RemovePhpVersionIdCheckRector::class,

RemoveAlwaysTrueIfConditionRector::class,
RemoveUnusedPrivateClassConstantRector::class,
RemoveUnusedPrivatePropertyRector::class,

RemoveDuplicatedCaseInSwitchRector::class,
RemoveDeadInstanceOfRector::class,

RemoveDeadTryCatchRector::class,
RemoveDeadIfForeachForRector::class,
RemoveDeadStmtRector::class,
UnwrapFutureCompatibleIfPhpVersionRector::class,
RemoveParentCallWithoutParentRector::class,
RemoveDeadConditionAboveReturnRector::class,
RemoveDeadLoopRector::class,

// removing methods could be risky if there is some magic loading them
RemoveUnusedPromotedPropertyRector::class,
RemoveUnusedPrivateMethodParameterRector::class,
RemoveUnusedPrivateMethodRector::class,
RemoveUnreachableStatementRector::class,
RemoveUnusedVariableAssignRector::class,

// this could break framework magic autowiring in some cases
RemoveUnusedConstructorParamRector::class,
RemoveEmptyClassMethodRector::class,
RemoveDeadReturnRector::class,
];
}
35 changes: 35 additions & 0 deletions src/Configuration/Levels/LevelRulesResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Rector\Configuration\Levels;

use Rector\Contract\Rector\RectorInterface;
use Webmozart\Assert\Assert;

final class LevelRulesResolver
{
/**
* @param array<class-string<RectorInterface>> $availableRules
* @return array<class-string<RectorInterface>>
*/
public static function resolve(int $level, array $availableRules, string $methodName): array
{
$rulesCount = count($availableRules);

Assert::range(
$level,
0,
$rulesCount - 1,
'Level %s is not available "' . $methodName . '" method. Pick one between %2$s (lowest) and %3$s (highest).'
);

$levelRules = [];

for ($i = 0; $i <= $level; ++$i) {
$levelRules[] = $availableRules[$i];
}

return $levelRules;
}
}
94 changes: 94 additions & 0 deletions src/Configuration/Levels/TypeCoverageLevel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

declare(strict_types=1);

namespace Rector\Configuration\Levels;

use Rector\TypeDeclaration\Rector\ArrowFunction\AddArrowFunctionReturnTypeRector;
use Rector\TypeDeclaration\Rector\Class_\MergeDateTimePropertyTypeDeclarationRector;
use Rector\TypeDeclaration\Rector\Class_\PropertyTypeFromStrictSetterGetterRector;
use Rector\TypeDeclaration\Rector\Class_\ReturnTypeFromStrictTernaryRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeBasedOnPHPUnitDataProviderRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeFromPropertyTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector;
use Rector\TypeDeclaration\Rector\ClassMethod\BoolReturnTypeFromStrictScalarReturnsRector;
use Rector\TypeDeclaration\Rector\ClassMethod\NumericReturnTypeFromStrictScalarReturnsRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByParentCallTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromReturnDirectArrayRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromReturnNewRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictBoolReturnExprRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictConstantReturnRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictNativeCallRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictNewArrayRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictParamRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictScalarReturnExprRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictTypedCallRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnUnionTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\StrictArrayParamDimFetchRector;
use Rector\TypeDeclaration\Rector\ClassMethod\StrictStringParamConcatRector;
use Rector\TypeDeclaration\Rector\Empty_\EmptyOnNullableObjectToInstanceOfRector;
use Rector\TypeDeclaration\Rector\FunctionLike\AddParamTypeSplFixedArrayRector;
use Rector\TypeDeclaration\Rector\FunctionLike\AddReturnTypeDeclarationFromYieldsRector;
use Rector\TypeDeclaration\Rector\Property\TypedPropertyFromStrictConstructorRector;
use Rector\TypeDeclaration\Rector\Property\TypedPropertyFromStrictSetUpRector;

/**
* Key 0 = level 0
* Key 50 = level 50
*
* Start at 0, go slowly higher, one level per PR, and improve your type coverage
*
* From the safest rules to more changing ones.
* @experimental Since 0.19.7 This list can change in time, based on community feedback,
* what rules are safer than other. The safest rules will be always in the top.
*/
final class TypeCoverageLevel
{
/**
* Mind that return type declarations are the safest to add,
* followed by property, then params
*
* @var array<class-string>
*/
public const RULE_LIST = [
// php 7.0
AddVoidReturnTypeWhereNoReturnRector::class,
// php 7.4
AddArrowFunctionReturnTypeRector::class,
ReturnTypeFromStrictNewArrayRector::class,
ReturnTypeFromStrictConstantReturnRector::class,
NumericReturnTypeFromStrictScalarReturnsRector::class,
ReturnTypeFromStrictScalarReturnExprRector::class,
ReturnTypeFromStrictBoolReturnExprRector::class,
ReturnTypeFromStrictTernaryRector::class,
EmptyOnNullableObjectToInstanceOfRector::class,

// php 7.4
TypedPropertyFromStrictConstructorRector::class,
ReturnTypeFromReturnDirectArrayRector::class,
AddParamTypeSplFixedArrayRector::class,
AddReturnTypeDeclarationFromYieldsRector::class,
AddParamTypeBasedOnPHPUnitDataProviderRector::class,

// php 7.4
TypedPropertyFromStrictSetUpRector::class,
ReturnTypeFromReturnNewRector::class,
BoolReturnTypeFromStrictScalarReturnsRector::class,
ReturnTypeFromStrictNativeCallRector::class,
ReturnTypeFromStrictTypedCallRector::class,

// param
AddMethodCallBasedStrictParamTypeRector::class,
ParamTypeByParentCallTypeRector::class,
ReturnUnionTypeRector::class,

// more risky rules
ReturnTypeFromStrictParamRector::class,
AddParamTypeFromPropertyTypeRector::class,
MergeDateTimePropertyTypeDeclarationRector::class,
PropertyTypeFromStrictSetterGetterRector::class,
StrictArrayParamDimFetchRector::class,
StrictStringParamConcatRector::class,
];
}
37 changes: 37 additions & 0 deletions src/Configuration/RectorConfigBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

use Rector\Caching\Contract\ValueObject\Storage\CacheStorageInterface;
use Rector\Config\RectorConfig;
use Rector\Configuration\Levels\DeadCodeLevel;
use Rector\Configuration\Levels\LevelRulesResolver;
use Rector\Configuration\Levels\TypeCoverageLevel;
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\Contract\Rector\RectorInterface;
use Rector\Doctrine\Set\DoctrineSetList;
Expand Down Expand Up @@ -582,4 +585,38 @@ public function withSymfonyContainerPhp(string $symfonyContainerPhpFile): self
$this->symfonyContainerPhpFile = $symfonyContainerPhpFile;
return $this;
}

/**
* @experimental since 0.19.7 Raise your dead-code coverage from the safest rules
* to more affecting ones, one level at a time
*/
public function withDeadCodeLevel(int $level): self
{
$levelRules = LevelRulesResolver::resolve(
$level,
DeadCodeLevel::RULE_LIST,
'RectorConfig::withDeadCodeLevel()'
);

$this->rules = array_merge($this->rules, $levelRules);

return $this;
}

/**
* @experimental since 0.19.7 Raise your type coverage from the safest type rules
* to more affecting ones, one level at a time
*/
public function withTypeCoverageLevel(int $level): self
{
$levelRules = LevelRulesResolver::resolve(
$level,
TypeCoverageLevel::RULE_LIST,
'RectorConfig::withTypeCoverageLevel()'
);

$this->rules = array_merge($this->rules, $levelRules);

return $this;
}
}
2 changes: 2 additions & 0 deletions src/Console/Command/ListRulesCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ protected function configure(): void
$this->setName('list-rules');
$this->setDescription('Show loaded Rectors');

$this->setAliases(['show-rules']);

$this->addOption(
Option::OUTPUT_FORMAT,
null,
Expand Down

0 comments on commit 487f162

Please sign in to comment.