Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "--only" option to process only a single rule #6441

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
- 'e2e/invalid-paths'
- 'e2e/applied-polyfill-php80'
- 'e2e/print-new-node'
- 'e2e/only-option'

name: End to end test - ${{ matrix.directory }}

Expand Down
6 changes: 6 additions & 0 deletions e2e/e2eTestRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@
$e2eCommand .= ' -a ' . $argv[2];
}

$cliOptions = 'cli-options.txt';
if (file_exists($cliOptions)) {
$e2eCommand .= ' ' . trim(file_get_contents($cliOptions));
}


exec($e2eCommand, $output, $exitCode);
$output = trim(implode("\n", $output));
$output = str_replace(__DIR__, '.', $output);
Expand Down
1 change: 1 addition & 0 deletions e2e/only-option/cli-options.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--only="Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add single quotes and double slashes mix?
That's what caused troubles in past.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that should be equal https://3v4l.org/u19fl, but indeed needs verification as cli value maybe transform different

7 changes: 7 additions & 0 deletions e2e/only-option/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"require": {
"php": "^8.1"
},
"minimum-stability": "dev",
"prefer-stable": true
}
22 changes: 22 additions & 0 deletions e2e/only-option/expected-output.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
1 file with changes
===================

1) src/MultiRules.php:9

---------- begin diff ----------
@@ @@
echo 'a statement';
}
}
-
- private function notUsed()
- {
- }
}
----------- end diff -----------

Applied rules:
* RemoveUnusedPrivateMethodRector


[OK] 1 file would have been changed (dry-run) by Rector
19 changes: 19 additions & 0 deletions e2e/only-option/rector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector;
use Rector\EarlyReturn\Rector\If_\RemoveAlwaysElseRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/src',
]);

$rectorConfig->rules([
RemoveAlwaysElseRector::class,
RemoveUnusedPrivateMethodRector::class,
]);
};

17 changes: 17 additions & 0 deletions e2e/only-option/src/MultiRules.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

final class MultiRules
{
public function doSomething()
{
if (true === false) {
return -1;
} else {
echo 'a statement';
}
}

private function notUsed()
{
}
}
13 changes: 13 additions & 0 deletions e2e/only-option/src/RemoveAlwaysElse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

class RemoveAlwaysElse
{
public function run($value)
{
if ($value) {
throw new \InvalidStateException;
} else {
return 10;
}
}
}
12 changes: 10 additions & 2 deletions src/Configuration/ConfigurationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
final readonly class ConfigurationFactory
{
public function __construct(
private SymfonyStyle $symfonyStyle
private SymfonyStyle $symfonyStyle,
private readonly OnlyRuleResolver $onlyRuleResolver,
) {
}

Expand All @@ -41,7 +42,8 @@ public function createForTests(array $paths): Configuration
false,
null,
false,
false
false,
null
);
}

Expand All @@ -62,6 +64,11 @@ public function createFromInput(InputInterface $input): Configuration

$fileExtensions = SimpleParameterProvider::provideArrayParameter(Option::FILE_EXTENSIONS);

$onlyRule = $input->getOption(Option::ONLY);
if ($onlyRule !== null) {
$onlyRule = $this->onlyRuleResolver->resolve($onlyRule);
}

$isParallel = SimpleParameterProvider::provideBoolParameter(Option::PARALLEL);
$parallelPort = (string) $input->getOption(Option::PARALLEL_PORT);
$parallelIdentifier = (string) $input->getOption(Option::PARALLEL_IDENTIFIER);
Expand Down Expand Up @@ -90,6 +97,7 @@ public function createFromInput(InputInterface $input): Configuration
$memoryLimit,
$isDebug,
$isReportingWithRealPath,
$onlyRule,
);
}

Expand Down
56 changes: 56 additions & 0 deletions src/Configuration/ConfigurationRuleFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace Rector\Configuration;

use Rector\Contract\Rector\RectorInterface;
use Rector\ValueObject\Configuration;

/**
* Modify available rector rules based on the configuration options
*/
final class ConfigurationRuleFilter
{
private ?Configuration $configuration = null;

public function setConfiguration(Configuration $configuration): void
{
$this->configuration = $configuration;
}

/**
* @param array<RectorInterface> $rectors
* @return array<RectorInterface>
*/
public function filter(array $rectors): array
{
if ($this->configuration === null) {
return $rectors;
}

$onlyRule = $this->configuration->getOnlyRule();
if ($onlyRule !== null) {
$rectors = $this->filterOnlyRule($rectors, $onlyRule);
return $rectors;
}

return $rectors;
}

/**
* @param array<RectorInterface> $rectors
* @return array<RectorInterface>
*/
public function filterOnlyRule(array $rectors, string $onlyRule): array
{
$activeRectors = [];
foreach ($rectors as $rector) {
if (is_a($rector, $onlyRule)) {
$activeRectors[] = $rector;
}
}

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

declare(strict_types=1);

namespace Rector\Configuration;

use Rector\Contract\Rector\RectorInterface;
use Rector\Exception\Configuration\RectorRuleNotFoundException;

/**
* @see \Rector\Tests\Configuration\OnlyRuleResolverTest
*/
final class OnlyRuleResolver
{
/**
* @param RectorInterface[] $rectors
*/
public function __construct(
private readonly array $rectors
) {
}

public function resolve(string $rule): string
{
$rule = ltrim($rule, '\\');

foreach ($this->rectors as $rector) {
if ($rector::class === $rule) {
return $rule;
}
}

if (strpos($rule, '\\') === false) {
$message = sprintf(
'Rule "%s" was not found.%sThe rule has no namespace - make sure to escape the backslashes correctly.',
$rule,
PHP_EOL
);
} else {
$message = sprintf(
'Rule "%s" was not found.%sMake sure it is registered in your config or in one of the sets',
$rule,
PHP_EOL
);
}
throw new RectorRuleNotFoundException($message);
}
}
5 changes: 5 additions & 0 deletions src/Configuration/Option.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ final class Option
*/
public const CLEAR_CACHE = 'clear-cache';

/**
* @var string
*/
public const ONLY = 'only';

/**
* @internal Use @see \Rector\Config\RectorConfig::parallel() instead
* @var string
Expand Down
18 changes: 16 additions & 2 deletions src/Console/Command/ListRulesCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use Nette\Utils\Json;
use Rector\ChangesReporting\Output\ConsoleOutputFormatter;
use Rector\Configuration\ConfigurationRuleFilter;
use Rector\Configuration\OnlyRuleResolver;
use Rector\Configuration\Option;
use Rector\Contract\Rector\RectorInterface;
use Rector\PostRector\Contract\Rector\PostRectorInterface;
Expand All @@ -24,6 +26,8 @@ final class ListRulesCommand extends Command
public function __construct(
private readonly SymfonyStyle $symfonyStyle,
private readonly SkippedClassResolver $skippedClassResolver,
private readonly OnlyRuleResolver $onlyRuleResolver,
private readonly ConfigurationRuleFilter $configurationRuleFilter,
private readonly array $rectors
) {
parent::__construct();
Expand All @@ -43,11 +47,17 @@ protected function configure(): void
'Select output format',
ConsoleOutputFormatter::NAME
);

$this->addOption(Option::ONLY, null, InputOption::VALUE_REQUIRED, 'Fully qualified rule class name');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$rectorClasses = $this->resolveRectorClasses();
$onlyRule = $input->getOption(Option::ONLY);
if ($onlyRule !== null) {
$onlyRule = $this->onlyRuleResolver->resolve($onlyRule);
}
$rectorClasses = $this->resolveRectorClasses($onlyRule);

$skippedClasses = $this->getSkippedCheckers();

Expand Down Expand Up @@ -79,13 +89,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int
/**
* @return array<class-string<RectorInterface>>
*/
private function resolveRectorClasses(): array
private function resolveRectorClasses(?string $onlyRule): array
{
$customRectors = array_filter(
$this->rectors,
static fn (RectorInterface $rector): bool => ! $rector instanceof PostRectorInterface
);

if ($onlyRule !== null) {
$customRectors = $this->configurationRuleFilter->filterOnlyRule($customRectors, $onlyRule);
}

$rectorClasses = array_map(static fn (RectorInterface $rector): string => $rector::class, $customRectors);
sort($rectorClasses);

Expand Down
3 changes: 3 additions & 0 deletions src/Console/Command/ProcessCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Rector\ChangesReporting\Output\JsonOutputFormatter;
use Rector\Configuration\ConfigInitializer;
use Rector\Configuration\ConfigurationFactory;
use Rector\Configuration\ConfigurationRuleFilter;
use Rector\Configuration\Option;
use Rector\Configuration\Parameter\SimpleParameterProvider;
use Rector\Console\ExitCode;
Expand Down Expand Up @@ -42,6 +43,7 @@ public function __construct(
private readonly ConfigurationFactory $configurationFactory,
private readonly DeprecatedRulesReporter $deprecatedRulesReporter,
private readonly MissConfigurationReporter $missConfigurationReporter,
private ConfigurationRuleFilter $configurationRuleFilter,
) {
parent::__construct();
}
Expand Down Expand Up @@ -85,6 +87,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$configuration = $this->configurationFactory->createFromInput($input);
$this->memoryLimiter->adjust($configuration);
$this->configurationRuleFilter->setConfiguration($configuration);

// disable console output in case of json output formatter
if ($configuration->getOutputFormat() === JsonOutputFormatter::NAME) {
Expand Down
5 changes: 4 additions & 1 deletion src/Console/Command/WorkerCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use React\Socket\TcpConnector;
use Rector\Application\ApplicationFileProcessor;
use Rector\Configuration\ConfigurationFactory;
use Rector\Configuration\ConfigurationRuleFilter;
use Rector\Console\ProcessConfigureDecorator;
use Rector\Parallel\ValueObject\Bridge;
use Rector\StaticReflection\DynamicSourceLocatorDecorator;
Expand Down Expand Up @@ -44,7 +45,8 @@ public function __construct(
private readonly DynamicSourceLocatorDecorator $dynamicSourceLocatorDecorator,
private readonly ApplicationFileProcessor $applicationFileProcessor,
private readonly MemoryLimiter $memoryLimiter,
private readonly ConfigurationFactory $configurationFactory
private readonly ConfigurationFactory $configurationFactory,
private readonly ConfigurationRuleFilter $configurationRuleFilter,
) {
parent::__construct();
}
Expand All @@ -63,6 +65,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
{
$configuration = $this->configurationFactory->createFromInput($input);
$this->memoryLimiter->adjust($configuration);
$this->configurationRuleFilter->setConfiguration($configuration);

$streamSelectLoop = new StreamSelectLoop();
$parallelIdentifier = $configuration->getParallelIdentifier();
Expand Down
2 changes: 2 additions & 0 deletions src/Console/ProcessConfigureDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ public static function decorate(Command $command): void
ConsoleOutputFormatter::NAME
);

$command->addOption(Option::ONLY, null, InputOption::VALUE_REQUIRED, 'Fully qualified rule class name');

$command->addOption(Option::DEBUG, null, InputOption::VALUE_NONE, 'Display debug output.');
$command->addOption(Option::MEMORY_LIMIT, null, InputOption::VALUE_REQUIRED, 'Memory limit for process');
$command->addOption(Option::CLEAR_CACHE, null, InputOption::VALUE_NONE, 'Clear unchanged files cache');
Expand Down
Loading