diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 437693c505a..21f2de6223e 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -35,6 +35,11 @@ jobs: - 'e2e/invalid-paths' - 'e2e/applied-polyfill-php80' - 'e2e/print-new-node' + - 'e2e/only-option' + - 'e2e/only-option-quote-double-equalnone' + - 'e2e/only-option-quote-single' + - 'e2e/only-option-quote-single-bsdouble' + - 'e2e/only-option-quote-single-equalnone' name: End to end test - ${{ matrix.directory }} diff --git a/e2e/e2eTestRunner.php b/e2e/e2eTestRunner.php index 066d67613c7..0f3a9b28289 100644 --- a/e2e/e2eTestRunner.php +++ b/e2e/e2eTestRunner.php @@ -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); diff --git a/e2e/only-option-quote-double-equalnone/cli-options.txt b/e2e/only-option-quote-double-equalnone/cli-options.txt new file mode 100644 index 00000000000..d01aa464c90 --- /dev/null +++ b/e2e/only-option-quote-double-equalnone/cli-options.txt @@ -0,0 +1 @@ +--only "Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector" diff --git a/e2e/only-option-quote-double-equalnone/composer.json b/e2e/only-option-quote-double-equalnone/composer.json new file mode 100644 index 00000000000..5468cd74606 --- /dev/null +++ b/e2e/only-option-quote-double-equalnone/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "php": "^8.1" + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/e2e/only-option-quote-double-equalnone/expected-output.diff b/e2e/only-option-quote-double-equalnone/expected-output.diff new file mode 100644 index 00000000000..1e79f39fc83 --- /dev/null +++ b/e2e/only-option-quote-double-equalnone/expected-output.diff @@ -0,0 +1,22 @@ +1 file with changes +=================== + +1) ../only-option/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 diff --git a/e2e/only-option-quote-double-equalnone/rector.php b/e2e/only-option-quote-double-equalnone/rector.php new file mode 100644 index 00000000000..ad19381b8b4 --- /dev/null +++ b/e2e/only-option-quote-double-equalnone/rector.php @@ -0,0 +1,18 @@ +paths([ + __DIR__ . '/../only-option/src', + ]); + + $rectorConfig->rules([ + RemoveAlwaysElseRector::class, + RemoveUnusedPrivateMethodRector::class, + ]); +}; diff --git a/e2e/only-option-quote-single-bsdouble/cli-options.txt b/e2e/only-option-quote-single-bsdouble/cli-options.txt new file mode 100644 index 00000000000..0a20188319d --- /dev/null +++ b/e2e/only-option-quote-single-bsdouble/cli-options.txt @@ -0,0 +1 @@ +--only='Rector\\DeadCode\\Rector\\ClassMethod\\RemoveUnusedPrivateMethodRector' diff --git a/e2e/only-option-quote-single-bsdouble/composer.json b/e2e/only-option-quote-single-bsdouble/composer.json new file mode 100644 index 00000000000..5468cd74606 --- /dev/null +++ b/e2e/only-option-quote-single-bsdouble/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "php": "^8.1" + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/e2e/only-option-quote-single-bsdouble/expected-output.diff b/e2e/only-option-quote-single-bsdouble/expected-output.diff new file mode 100644 index 00000000000..1e79f39fc83 --- /dev/null +++ b/e2e/only-option-quote-single-bsdouble/expected-output.diff @@ -0,0 +1,22 @@ +1 file with changes +=================== + +1) ../only-option/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 diff --git a/e2e/only-option-quote-single-bsdouble/rector.php b/e2e/only-option-quote-single-bsdouble/rector.php new file mode 100644 index 00000000000..ad19381b8b4 --- /dev/null +++ b/e2e/only-option-quote-single-bsdouble/rector.php @@ -0,0 +1,18 @@ +paths([ + __DIR__ . '/../only-option/src', + ]); + + $rectorConfig->rules([ + RemoveAlwaysElseRector::class, + RemoveUnusedPrivateMethodRector::class, + ]); +}; diff --git a/e2e/only-option-quote-single-equalnone/cli-options.txt b/e2e/only-option-quote-single-equalnone/cli-options.txt new file mode 100644 index 00000000000..9159b9e2101 --- /dev/null +++ b/e2e/only-option-quote-single-equalnone/cli-options.txt @@ -0,0 +1 @@ +--only 'Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector' diff --git a/e2e/only-option-quote-single-equalnone/composer.json b/e2e/only-option-quote-single-equalnone/composer.json new file mode 100644 index 00000000000..5468cd74606 --- /dev/null +++ b/e2e/only-option-quote-single-equalnone/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "php": "^8.1" + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/e2e/only-option-quote-single-equalnone/expected-output.diff b/e2e/only-option-quote-single-equalnone/expected-output.diff new file mode 100644 index 00000000000..1e79f39fc83 --- /dev/null +++ b/e2e/only-option-quote-single-equalnone/expected-output.diff @@ -0,0 +1,22 @@ +1 file with changes +=================== + +1) ../only-option/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 diff --git a/e2e/only-option-quote-single-equalnone/rector.php b/e2e/only-option-quote-single-equalnone/rector.php new file mode 100644 index 00000000000..ad19381b8b4 --- /dev/null +++ b/e2e/only-option-quote-single-equalnone/rector.php @@ -0,0 +1,18 @@ +paths([ + __DIR__ . '/../only-option/src', + ]); + + $rectorConfig->rules([ + RemoveAlwaysElseRector::class, + RemoveUnusedPrivateMethodRector::class, + ]); +}; diff --git a/e2e/only-option-quote-single/cli-options.txt b/e2e/only-option-quote-single/cli-options.txt new file mode 100644 index 00000000000..34057c5eb98 --- /dev/null +++ b/e2e/only-option-quote-single/cli-options.txt @@ -0,0 +1 @@ +--only='Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector' diff --git a/e2e/only-option-quote-single/composer.json b/e2e/only-option-quote-single/composer.json new file mode 100644 index 00000000000..5468cd74606 --- /dev/null +++ b/e2e/only-option-quote-single/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "php": "^8.1" + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/e2e/only-option-quote-single/expected-output.diff b/e2e/only-option-quote-single/expected-output.diff new file mode 100644 index 00000000000..1e79f39fc83 --- /dev/null +++ b/e2e/only-option-quote-single/expected-output.diff @@ -0,0 +1,22 @@ +1 file with changes +=================== + +1) ../only-option/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 diff --git a/e2e/only-option-quote-single/rector.php b/e2e/only-option-quote-single/rector.php new file mode 100644 index 00000000000..ad19381b8b4 --- /dev/null +++ b/e2e/only-option-quote-single/rector.php @@ -0,0 +1,18 @@ +paths([ + __DIR__ . '/../only-option/src', + ]); + + $rectorConfig->rules([ + RemoveAlwaysElseRector::class, + RemoveUnusedPrivateMethodRector::class, + ]); +}; diff --git a/e2e/only-option/cli-options.txt b/e2e/only-option/cli-options.txt new file mode 100644 index 00000000000..4109051ee1d --- /dev/null +++ b/e2e/only-option/cli-options.txt @@ -0,0 +1 @@ +--only="Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector" diff --git a/e2e/only-option/composer.json b/e2e/only-option/composer.json new file mode 100644 index 00000000000..5468cd74606 --- /dev/null +++ b/e2e/only-option/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "php": "^8.1" + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/e2e/only-option/expected-output.diff b/e2e/only-option/expected-output.diff new file mode 100644 index 00000000000..f72d80c3e56 --- /dev/null +++ b/e2e/only-option/expected-output.diff @@ -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 diff --git a/e2e/only-option/rector.php b/e2e/only-option/rector.php new file mode 100644 index 00000000000..407016c715e --- /dev/null +++ b/e2e/only-option/rector.php @@ -0,0 +1,19 @@ +paths([ + __DIR__ . '/src', + ]); + + $rectorConfig->rules([ + RemoveAlwaysElseRector::class, + RemoveUnusedPrivateMethodRector::class, + ]); +}; + diff --git a/e2e/only-option/src/MultiRules.php b/e2e/only-option/src/MultiRules.php new file mode 100644 index 00000000000..fc0db50a1fb --- /dev/null +++ b/e2e/only-option/src/MultiRules.php @@ -0,0 +1,17 @@ +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); @@ -90,6 +97,7 @@ public function createFromInput(InputInterface $input): Configuration $memoryLimit, $isDebug, $isReportingWithRealPath, + $onlyRule, ); } diff --git a/src/Configuration/ConfigurationRuleFilter.php b/src/Configuration/ConfigurationRuleFilter.php new file mode 100644 index 00000000000..266bab620f1 --- /dev/null +++ b/src/Configuration/ConfigurationRuleFilter.php @@ -0,0 +1,56 @@ +configuration = $configuration; + } + + /** + * @param array $rectors + * @return array + */ + 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 $rectors + * @return array + */ + public function filterOnlyRule(array $rectors, string $onlyRule): array + { + $activeRectors = []; + foreach ($rectors as $rector) { + if (is_a($rector, $onlyRule)) { + $activeRectors[] = $rector; + } + } + + return $activeRectors; + } +} diff --git a/src/Configuration/OnlyRuleResolver.php b/src/Configuration/OnlyRuleResolver.php new file mode 100644 index 00000000000..f9dcc6dac19 --- /dev/null +++ b/src/Configuration/OnlyRuleResolver.php @@ -0,0 +1,56 @@ +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, and add quotes around the rule name: --only="My\\Rector\\Rule"', + $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); + } +} diff --git a/src/Configuration/Option.php b/src/Configuration/Option.php index 44416da88d4..01d5c4c12b6 100644 --- a/src/Configuration/Option.php +++ b/src/Configuration/Option.php @@ -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 diff --git a/src/Console/Command/ListRulesCommand.php b/src/Console/Command/ListRulesCommand.php index 3863e250619..0ea37930a5e 100644 --- a/src/Console/Command/ListRulesCommand.php +++ b/src/Console/Command/ListRulesCommand.php @@ -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; @@ -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(); @@ -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(); @@ -79,13 +89,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** * @return array> */ - 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); diff --git a/src/Console/Command/ProcessCommand.php b/src/Console/Command/ProcessCommand.php index 7857010fa0e..9cfdd09a711 100644 --- a/src/Console/Command/ProcessCommand.php +++ b/src/Console/Command/ProcessCommand.php @@ -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; @@ -42,6 +43,7 @@ public function __construct( private readonly ConfigurationFactory $configurationFactory, private readonly DeprecatedRulesReporter $deprecatedRulesReporter, private readonly MissConfigurationReporter $missConfigurationReporter, + private ConfigurationRuleFilter $configurationRuleFilter, ) { parent::__construct(); } @@ -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) { diff --git a/src/Console/Command/WorkerCommand.php b/src/Console/Command/WorkerCommand.php index 36ea55e0393..aa7d0df4bb0 100644 --- a/src/Console/Command/WorkerCommand.php +++ b/src/Console/Command/WorkerCommand.php @@ -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; @@ -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(); } @@ -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(); diff --git a/src/Console/ProcessConfigureDecorator.php b/src/Console/ProcessConfigureDecorator.php index a45f9f6e876..541cfc48960 100644 --- a/src/Console/ProcessConfigureDecorator.php +++ b/src/Console/ProcessConfigureDecorator.php @@ -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'); diff --git a/src/DependencyInjection/LazyContainerFactory.php b/src/DependencyInjection/LazyContainerFactory.php index 2e1e20b24ec..3f7231cb054 100644 --- a/src/DependencyInjection/LazyContainerFactory.php +++ b/src/DependencyInjection/LazyContainerFactory.php @@ -47,6 +47,8 @@ use Rector\CodingStyle\Contract\ClassNameImport\ClassNameImportSkipVoterInterface; use Rector\Config\RectorConfig; use Rector\Configuration\ConfigInitializer; +use Rector\Configuration\ConfigurationRuleFilter; +use Rector\Configuration\OnlyRuleResolver; use Rector\Configuration\RenamedClassesDataCollector; use Rector\Console\Command\CustomRuleCommand; use Rector\Console\Command\ListRulesCommand; @@ -399,6 +401,8 @@ public function create(): RectorConfig return $inflectorFactory->build(); }); + $rectorConfig->singleton(ConfigurationRuleFilter::class); + $rectorConfig->singleton(ProcessCommand::class); $rectorConfig->singleton(WorkerCommand::class); $rectorConfig->singleton(SetupCICommand::class); @@ -409,6 +413,10 @@ public function create(): RectorConfig ->needs('$rectors') ->giveTagged(RectorInterface::class); + $rectorConfig->when(OnlyRuleResolver::class) + ->needs('$rectors') + ->giveTagged(RectorInterface::class); + $rectorConfig->singleton(FileProcessor::class); $rectorConfig->singleton(PostFileProcessor::class); diff --git a/src/Exception/Configuration/RectorRuleNotFoundException.php b/src/Exception/Configuration/RectorRuleNotFoundException.php new file mode 100644 index 00000000000..0f2e17919e1 --- /dev/null +++ b/src/Exception/Configuration/RectorRuleNotFoundException.php @@ -0,0 +1,11 @@ +filePathHelper->relativePath($config)); } + if ($input->getOption(Option::ONLY) !== null) { + $workerCommandArray[] = self::OPTION_DASHES . Option::ONLY; + $workerCommandArray[] = escapeshellarg($input->getOption(Option::ONLY)); + } + return implode(' ', $workerCommandArray); } diff --git a/src/PhpParser/NodeTraverser/RectorNodeTraverser.php b/src/PhpParser/NodeTraverser/RectorNodeTraverser.php index e5fb6d1b25d..c5e58c7b7bf 100644 --- a/src/PhpParser/NodeTraverser/RectorNodeTraverser.php +++ b/src/PhpParser/NodeTraverser/RectorNodeTraverser.php @@ -8,6 +8,7 @@ use PhpParser\Node\Stmt; use PhpParser\NodeTraverser; use PhpParser\NodeVisitor; +use Rector\Configuration\ConfigurationRuleFilter; use Rector\Contract\Rector\RectorInterface; use Rector\VersionBonding\PhpVersionedFilter; @@ -25,7 +26,8 @@ final class RectorNodeTraverser extends NodeTraverser */ public function __construct( private array $rectors, - private readonly PhpVersionedFilter $phpVersionedFilter + private readonly PhpVersionedFilter $phpVersionedFilter, + private readonly ConfigurationRuleFilter $configurationRuleFilter, ) { parent::__construct(); } @@ -93,6 +95,9 @@ private function prepareNodeVisitors(): void // filer out by version $this->visitors = $this->phpVersionedFilter->filter($this->rectors); + // filter by configuration + $this->visitors = $this->configurationRuleFilter->filter($this->visitors); + $this->areNodeVisitorsPrepared = true; } } diff --git a/src/ValueObject/Configuration.php b/src/ValueObject/Configuration.php index b99186a5894..f34315fea7d 100644 --- a/src/ValueObject/Configuration.php +++ b/src/ValueObject/Configuration.php @@ -26,7 +26,8 @@ public function __construct( private bool $isParallel = false, private string|null $memoryLimit = null, private bool $isDebug = false, - private bool $reportingWithRealPath = false + private bool $reportingWithRealPath = false, + private ?string $onlyRule = null ) { } @@ -54,6 +55,11 @@ public function getFileExtensions(): array return $this->fileExtensions; } + public function getOnlyRule(): ?string + { + return $this->onlyRule; + } + /** * @return string[] */ diff --git a/tests/Configuration/OnlyRuleResolverTest.php b/tests/Configuration/OnlyRuleResolverTest.php new file mode 100644 index 00000000000..64cd89d0b1b --- /dev/null +++ b/tests/Configuration/OnlyRuleResolverTest.php @@ -0,0 +1,79 @@ +bootFromConfigFiles([__DIR__ . '/config/only_rule_resolver_config.php']); + $rectorConfig = self::getContainer(); + + $this->resolver = new OnlyRuleResolver(iterator_to_array($rectorConfig->tagged(RectorInterface::class))); + } + + public function testResolveOk(): void + { + $this->assertEquals( + \Rector\DeadCode\Rector\Assign\RemoveDoubleAssignRector::class, + $this->resolver->resolve('Rector\\DeadCode\\Rector\\Assign\\RemoveDoubleAssignRector') + ); + } + + public function testResolveOkLeadingBackslash(): void + { + $this->assertEquals( + \Rector\DeadCode\Rector\Assign\RemoveDoubleAssignRector::class, + $this->resolver->resolve('\\Rector\\DeadCode\\Rector\\Assign\\RemoveDoubleAssignRector') + ); + } + + public function testResolveOkDoubleBackslashes(): void + { + $this->assertEquals( + \Rector\DeadCode\Rector\Assign\RemoveDoubleAssignRector::class, + $this->resolver->resolve('\\\\Rector\\\\DeadCode\\\\Rector\\\\Assign\\\\RemoveDoubleAssignRector'), + 'We want to fix wrongly double-quoted backslashes automatically' + ); + } + + public function testResolveOkSingleQuotes(): void + { + $this->assertEquals( + \Rector\DeadCode\Rector\Assign\RemoveDoubleAssignRector::class, + $this->resolver->resolve("'Rector\\DeadCode\\Rector\\Assign\\RemoveDoubleAssignRector'"), + 'Remove stray single quotes on Windows systems' + ); + } + + public function testResolveMissingBackslash(): void + { + $this->expectExceptionMessage( + 'Rule "RectorDeadCodeRectorAssignRemoveDoubleAssignRector" was not found.' . PHP_EOL + . 'The rule has no namespace. Make sure to escape the backslashes, and add quotes around the rule name: --only="My\\Rector\\Rule"' + ); + $this->expectException(RectorRuleNotFoundException::class); + + $this->resolver->resolve('RectorDeadCodeRectorAssignRemoveDoubleAssignRector'); + } + + public function testResolveNotFound(): void + { + $this->expectExceptionMessage( + 'Rule "This\Rule\Does\Not\Exist" was not found.' . PHP_EOL + . 'Make sure it is registered in your config or in one of the sets' + ); + $this->expectException(RectorRuleNotFoundException::class); + + $this->resolver->resolve('This\\Rule\\Does\\Not\\Exist'); + } +} diff --git a/tests/Configuration/config/only_rule_resolver_config.php b/tests/Configuration/config/only_rule_resolver_config.php new file mode 100644 index 00000000000..88890f0dbd4 --- /dev/null +++ b/tests/Configuration/config/only_rule_resolver_config.php @@ -0,0 +1,10 @@ +withRules([RemoveDoubleAssignRector::class, RemoveUnusedPrivateMethodRector::class]);