Skip to content

Commit

Permalink
[dx] warn about too high level configuratoin and suggets more efficie…
Browse files Browse the repository at this point in the history
…nt set
  • Loading branch information
TomasVotruba committed Feb 13, 2025
1 parent 449e5ff commit 1376702
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 13 deletions.
3 changes: 2 additions & 1 deletion rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
return RectorConfig::configure()
->withPreparedSets(
deadCode: true,
codeQuality: true,
// codeQuality: true,
codingStyle: true,
typeDeclarations: true,
privatization: true,
Expand All @@ -35,6 +35,7 @@
__DIR__ . '/build/build-preload.php',
])
->withRootFiles()
->withCodeQualityLevel(120)
->withImportNames(removeUnusedImports: true)
->withSkip([
StringClassNameToClassConstantRector::class,
Expand Down
1 change: 0 additions & 1 deletion src/Bridge/SetRectorsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions src/Config/RectorConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -441,4 +441,12 @@ public function getRectorClasses(): array
{
return $this->tags[RectorInterface::class] ?? [];
}

/**
* @param array<array{string, int, int}> $overflowLevels
*/
public function setOverflowLevels(array $overflowLevels): void
{
SimpleParameterProvider::addParameter(Option::OVERFLOW_LEVELS, $overflowLevels);
}
}
3 changes: 3 additions & 0 deletions src/Configuration/ConfigurationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -103,6 +105,7 @@ public function createFromInput(InputInterface $input): Configuration
$isReportingWithRealPath,
$onlyRule,
$onlySuffix,
$overflowLevels
);
}

Expand Down
5 changes: 5 additions & 0 deletions src/Configuration/Option.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
}
85 changes: 74 additions & 11 deletions src/Configuration/RectorConfigBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -41,6 +42,11 @@
*/
final class RectorConfigBuilder
{
/**
* @var int
*/
private const MAX_LEVEL_GAP = 10;

/**
* @var string[]
*/
Expand Down Expand Up @@ -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 !== []) {
Expand Down Expand Up @@ -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);
}
}

/**
Expand Down Expand Up @@ -931,41 +946,65 @@ 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);

return $this;
}

/**
* @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);

return $this;
}

/**
* @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
{
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand All @@ -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
Expand All @@ -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) {
Expand Down
17 changes: 17 additions & 0 deletions src/Console/Command/ProcessCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
11 changes: 11 additions & 0 deletions src/ValueObject/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
namespace Rector\ValueObject;

use Rector\ChangesReporting\Output\ConsoleOutputFormatter;
use Rector\ValueObject\Configuration\LevelOverflow;
use Webmozart\Assert\Assert;

final readonly class Configuration
{
/**
* @param string[] $fileExtensions
* @param string[] $paths
* @param LevelOverflow[] $levelOverflows
*/
public function __construct(
private bool $isDryRun = false,
Expand All @@ -29,6 +31,7 @@ public function __construct(
private bool $reportingWithRealPath = false,
private ?string $onlyRule = null,
private ?string $onlySuffix = null,
private array $levelOverflows = []
) {
}

Expand Down Expand Up @@ -113,4 +116,12 @@ public function getOnlySuffix(): ?string
{
return $this->onlySuffix;
}

/**
* @return LevelOverflow[]
*/
public function getLevelOverflows(): array
{
return $this->levelOverflows;
}
}
42 changes: 42 additions & 0 deletions src/ValueObject/Configuration/LevelOverflow.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Rector\ValueObject\Configuration;

final readonly class LevelOverflow
{
public function __construct(
private string $configurationName,
private int $level,
private int $ruleCount,
private string $suggestedRuleset,
private string $suggestedSetListConstant
) {
}

public function getConfigurationName(): string
{
return $this->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;
}
}

0 comments on commit 1376702

Please sign in to comment.