diff --git a/rector.php b/rector.php index 997d7c85b14..b4c30dac811 100644 --- a/rector.php +++ b/rector.php @@ -10,7 +10,7 @@ return RectorConfig::configure() ->withPreparedSets( deadCode: true, - codeQuality: true, + // codeQuality: true, codingStyle: true, typeDeclarations: true, privatization: true, @@ -35,6 +35,7 @@ __DIR__ . '/build/build-preload.php', ]) ->withRootFiles() + ->withCodeQualityLevel(120) ->withImportNames(removeUnusedImports: true) ->withSkip([ StringClassNameToClassConstantRector::class, diff --git a/src/Bridge/SetRectorsResolver.php b/src/Bridge/SetRectorsResolver.php index eaa3a1a264c..f50457965bc 100644 --- a/src/Bridge/SetRectorsResolver.php +++ b/src/Bridge/SetRectorsResolver.php @@ -10,7 +10,6 @@ /** * @api - * @experimental since 1.1.2 * Utils class to ease building bridges by 3rd-party tools * * @see \Rector\Tests\Bridge\SetRectorsResolverTest diff --git a/src/Config/RectorConfig.php b/src/Config/RectorConfig.php index d7bc29326b4..bc1c9eaf05f 100644 --- a/src/Config/RectorConfig.php +++ b/src/Config/RectorConfig.php @@ -441,4 +441,12 @@ public function getRectorClasses(): array { return $this->tags[RectorInterface::class] ?? []; } + + /** + * @param array $overflowLevels + */ + public function setOverflowLevels(array $overflowLevels): void + { + SimpleParameterProvider::addParameter(Option::OVERFLOW_LEVELS, $overflowLevels); + } } diff --git a/src/Configuration/ConfigurationFactory.php b/src/Configuration/ConfigurationFactory.php index 8bbb5737505..9c019a0a06a 100644 --- a/src/Configuration/ConfigurationFactory.php +++ b/src/Configuration/ConfigurationFactory.php @@ -87,6 +87,8 @@ public function createFromInput(InputInterface $input): Configuration $isReportingWithRealPath = SimpleParameterProvider::provideBoolParameter(Option::ABSOLUTE_FILE_PATH); + $overflowLevels = SimpleParameterProvider::provideArrayParameter(Option::OVERFLOW_LEVELS); + return new Configuration( $isDryRun, $showProgressBar, @@ -103,6 +105,7 @@ public function createFromInput(InputInterface $input): Configuration $isReportingWithRealPath, $onlyRule, $onlySuffix, + $overflowLevels ); } diff --git a/src/Configuration/Option.php b/src/Configuration/Option.php index 50d34a0bcf9..ca6293af602 100644 --- a/src/Configuration/Option.php +++ b/src/Configuration/Option.php @@ -285,4 +285,9 @@ final class Option * @internal To filter files by specific suffix */ public const ONLY_SUFFIX = 'only-suffix'; + + /** + * @internal To report overflow levels in ->with*Level() methods + */ + public const OVERFLOW_LEVELS = 'overflow_levels'; } diff --git a/src/Configuration/RectorConfigBuilder.php b/src/Configuration/RectorConfigBuilder.php index fa777e37091..eb402c080dd 100644 --- a/src/Configuration/RectorConfigBuilder.php +++ b/src/Configuration/RectorConfigBuilder.php @@ -32,6 +32,7 @@ use Rector\Symfony\Set\JMSSetList; use Rector\Symfony\Set\SensiolabsSetList; use Rector\Symfony\Set\SymfonySetList; +use Rector\ValueObject\Configuration\LevelOverflow; use Rector\ValueObject\PhpVersion; use Symfony\Component\Finder\Finder; use Webmozart\Assert\Assert; @@ -41,6 +42,11 @@ */ final class RectorConfigBuilder { + /** + * @var int + */ + private const MAX_LEVEL_GAP = 10; + /** * @var string[] */ @@ -170,6 +176,11 @@ final class RectorConfigBuilder */ private array $setProviders = []; + /** + * @var LevelOverflow[] + */ + private array $levelOverflows = []; + public function __invoke(RectorConfig $rectorConfig): void { if ($this->setGroups !== [] || $this->setProviders !== []) { @@ -350,6 +361,10 @@ public function __invoke(RectorConfig $rectorConfig): void if ($this->editorUrl !== null) { $rectorConfig->editorUrl($this->editorUrl); } + + if ($this->levelOverflows !== []) { + $rectorConfig->setOverflowLevels($this->levelOverflows); + } } /** @@ -931,16 +946,28 @@ public function withSymfonyContainerPhp(string $symfonyContainerPhpFile): self } /** - * @experimental since 0.19.7 Raise your dead-code coverage from the safest rules + * Raise your type coverage from the safest type rules * to more affecting ones, one level at a time */ - public function withDeadCodeLevel(int $level): self + public function withTypeCoverageLevel(int $level): self { Assert::natural($level); - $this->isDeadCodeLevelUsed = true; + $this->isTypeCoverageLevelUsed = true; - $levelRules = LevelRulesResolver::resolve($level, DeadCodeLevel::RULES, __METHOD__); + $levelRules = LevelRulesResolver::resolve($level, TypeDeclarationLevel::RULES, __METHOD__); + + // too high + $levelRulesCount = count($levelRules); + if ($levelRulesCount + self::MAX_LEVEL_GAP < $level) { + $this->levelOverflows[] = new LevelOverflow( + 'withTypeCoverageLevel', + $level, + $levelRulesCount, + 'typeCoverage', + 'TYPE_DECLARATION' + ); + } $this->rules = array_merge($this->rules, $levelRules); @@ -948,16 +975,28 @@ public function withDeadCodeLevel(int $level): self } /** - * @experimental since 0.19.7 Raise your type coverage from the safest type rules + * Raise your dead-code coverage from the safest rules * to more affecting ones, one level at a time */ - public function withTypeCoverageLevel(int $level): self + public function withDeadCodeLevel(int $level): self { Assert::natural($level); - $this->isTypeCoverageLevelUsed = true; + $this->isDeadCodeLevelUsed = true; - $levelRules = LevelRulesResolver::resolve($level, TypeDeclarationLevel::RULES, __METHOD__); + $levelRules = LevelRulesResolver::resolve($level, DeadCodeLevel::RULES, __METHOD__); + + // too high + $levelRulesCount = count($levelRules); + if ($levelRulesCount + self::MAX_LEVEL_GAP < $level) { + $this->levelOverflows[] = new LevelOverflow( + 'withDeadCodeLevel', + $level, + $levelRulesCount, + 'deadCode', + 'DEAD_CODE' + ); + } $this->rules = array_merge($this->rules, $levelRules); @@ -965,7 +1004,7 @@ public function withTypeCoverageLevel(int $level): self } /** - * @experimental Since 1.2.5 Raise your PHP level from, one level at a time + * Raise your PHP level from, one level at a time */ public function withPhpLevel(int $level): self { @@ -1000,7 +1039,7 @@ public function withPhpLevel(int $level): self } /** - * @experimental Raise your code quality from the safest rules + * Raise your code quality from the safest rules * to more affecting ones, one level at a time */ public function withCodeQualityLevel(int $level): self @@ -1011,6 +1050,18 @@ public function withCodeQualityLevel(int $level): self $levelRules = LevelRulesResolver::resolve($level, CodeQualityLevel::RULES, __METHOD__); + // too high + $levelRulesCount = count($levelRules); + if ($levelRulesCount + self::MAX_LEVEL_GAP < $level) { + $this->levelOverflows[] = new LevelOverflow( + 'withCodeQualityLevel', + $level, + $levelRulesCount, + 'codeQuality', + 'CODE_QUALITY' + ); + } + $this->rules = array_merge($this->rules, $levelRules); foreach (CodeQualityLevel::RULES_WITH_CONFIGURATION as $rectorClass => $configuration) { @@ -1021,7 +1072,7 @@ public function withCodeQualityLevel(int $level): self } /** - * @experimental Raise your coding style from the safest rules + * Raise your coding style from the safest rules * to more affecting ones, one level at a time */ public function withCodingStyleLevel(int $level): self @@ -1032,6 +1083,18 @@ public function withCodingStyleLevel(int $level): self $levelRules = LevelRulesResolver::resolve($level, CodingStyleLevel::RULES, __METHOD__); + // too high + $levelRulesCount = count($levelRules); + if ($levelRulesCount + self::MAX_LEVEL_GAP < $level) { + $this->levelOverflows[] = new LevelOverflow( + 'withCodingStyleLevel', + $level, + $levelRulesCount, + 'codingStyle', + 'CODING_STYLE' + ); + } + $this->rules = array_merge($this->rules, $levelRules); foreach (CodingStyleLevel::RULES_WITH_CONFIGURATION as $rectorClass => $configuration) { diff --git a/src/Console/Command/ProcessCommand.php b/src/Console/Command/ProcessCommand.php index 92ae7beece8..d32f302c36f 100644 --- a/src/Console/Command/ProcessCommand.php +++ b/src/Console/Command/ProcessCommand.php @@ -99,6 +99,23 @@ protected function execute(InputInterface $input, OutputInterface $output): int $paths = $configuration->getPaths(); + // 0. warn about too high levels + foreach ($configuration->getLevelOverflows() as $levelOverflow) { + $suggestedSetMethod = PHP_VERSION_ID >= 80000 ? sprintf( + '->withPreparedSets(%s: true)', + $levelOverflow->getSuggestedRuleset() + ) : sprintf('->withSets(SetList::%s)', $levelOverflow->getSuggestedSetListConstant()); + + $this->symfonyStyle->warning(sprintf( + 'The "->%s()" level contains only %d rules, but you set level to %d.%sYou are using the full set now! Time to switch to more efficient "%s".', + $levelOverflow->getConfigurationName(), + $levelOverflow->getRuleCount(), + $levelOverflow->getLevel(), + PHP_EOL, + $suggestedSetMethod, + )); + } + // 1. add files and directories to static locator $this->dynamicSourceLocatorDecorator->addPaths($paths); if ($this->dynamicSourceLocatorDecorator->isPathsEmpty()) { diff --git a/src/ValueObject/Configuration.php b/src/ValueObject/Configuration.php index d1188e661db..82f5f72cf01 100644 --- a/src/ValueObject/Configuration.php +++ b/src/ValueObject/Configuration.php @@ -5,6 +5,7 @@ namespace Rector\ValueObject; use Rector\ChangesReporting\Output\ConsoleOutputFormatter; +use Rector\ValueObject\Configuration\LevelOverflow; use Webmozart\Assert\Assert; final readonly class Configuration @@ -12,6 +13,7 @@ /** * @param string[] $fileExtensions * @param string[] $paths + * @param LevelOverflow[] $levelOverflows */ public function __construct( private bool $isDryRun = false, @@ -29,6 +31,7 @@ public function __construct( private bool $reportingWithRealPath = false, private ?string $onlyRule = null, private ?string $onlySuffix = null, + private array $levelOverflows = [] ) { } @@ -113,4 +116,12 @@ public function getOnlySuffix(): ?string { return $this->onlySuffix; } + + /** + * @return LevelOverflow[] + */ + public function getLevelOverflows(): array + { + return $this->levelOverflows; + } } diff --git a/src/ValueObject/Configuration/LevelOverflow.php b/src/ValueObject/Configuration/LevelOverflow.php new file mode 100644 index 00000000000..525793eab8a --- /dev/null +++ b/src/ValueObject/Configuration/LevelOverflow.php @@ -0,0 +1,42 @@ +configurationName; + } + + public function getLevel(): int + { + return $this->level; + } + + public function getRuleCount(): int + { + return $this->ruleCount; + } + + public function getSuggestedRuleset(): string + { + return $this->suggestedRuleset; + } + + public function getSuggestedSetListConstant(): string + { + return $this->suggestedSetListConstant; + } +}