diff --git a/scripts/list-non-composer-based-rules.php b/scripts/list-non-composer-based-rules.php new file mode 100644 index 00000000000..337187818ac --- /dev/null +++ b/scripts/list-non-composer-based-rules.php @@ -0,0 +1,75 @@ +find([ + __DIR__ . '/../vendor/rector/rector-doctrine', + __DIR__ . '/../vendor/rector/rector-phpunit', + __DIR__ . '/../vendor/rector/rector-symfony', +]); + +$symfonyStyle = new SymfonyStyle(new ArrayInput([]), new ConsoleOutput()); +$symfonyStyle->writeln(sprintf('Found Rector %d rules', count($rectorClasses))); + +// 2. find "composer-based.php" sets, that bind rules to the installed package version +$rectorSetFilesFinder = new RectorSetFilesFinder(); + +$rectorSetFiles = $rectorSetFilesFinder->find([ + __DIR__ . '/../vendor/rector/rector-symfony/config/sets', + __DIR__ . '/../vendor/rector/rector-doctrine/config/sets', + __DIR__ . '/../vendor/rector/rector-phpunit/config/sets', +]); + +$composerBasedSetFiles = array_filter( + $rectorSetFiles, + static fn (string $rectorSetFile): bool => basename($rectorSetFile) === 'composer-based.php' +); + +$symfonyStyle->writeln(sprintf('Found %d composer-based sets', count($composerBasedSetFiles))); +$symfonyStyle->listing($composerBasedSetFiles); + +$usedRectorClassResolver = new UsedRectorClassResolver(); +$usedRectorRules = $usedRectorClassResolver->resolve($composerBasedSetFiles); + +$symfonyStyle->writeln( + sprintf('Found %d Rector rules used in composer-based sets', count($usedRectorRules)) +); + +// these rules are not bound to any package version, so they never belong to a composer-based set +$versionAgnosticSetFileNames = ['code-quality.php', 'typed-collections.php', 'typed-collections-docblocks.php']; + +$versionAgnosticSetFiles = array_filter($rectorSetFiles, static fn (string $rectorSetFile): bool => array_any($versionAgnosticSetFileNames, fn (string $versionAgnosticSetFileName): bool => str_ends_with(basename($rectorSetFile), $versionAgnosticSetFileName))); + +$versionAgnosticRectorRules = $usedRectorClassResolver->resolve($versionAgnosticSetFiles); + +$symfonyStyle->writeln( + sprintf( + 'Found %d Rector rules used in version-agnostic sets', + count($versionAgnosticRectorRules) + ) +); + +$nonComposerBasedRectorRules = array_diff($rectorClasses, $usedRectorRules, $versionAgnosticRectorRules); + +$symfonyStyle->newLine(); +$symfonyStyle->listing($nonComposerBasedRectorRules); + +$symfonyStyle->writeln( + sprintf( + 'Found %d Rector rules not in any composer-based set, likely dead', + count($nonComposerBasedRectorRules) + ) +); +$symfonyStyle->newLine(); diff --git a/scripts/list-unused-rules.php b/scripts/list-unused-rules.php index d8281ef9417..7d75890964c 100644 --- a/scripts/list-unused-rules.php +++ b/scripts/list-unused-rules.php @@ -2,10 +2,10 @@ declare(strict_types=1); -use Rector\Bridge\SetRectorsResolver; use Rector\Contract\Rector\ConfigurableRectorInterface; use Rector\Scripts\Finder\RectorClassFinder; use Rector\Scripts\Finder\RectorSetFilesFinder; +use Rector\Scripts\Resolver\UsedRectorClassResolver; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Style\SymfonyStyle; @@ -64,25 +64,3 @@ sprintf('Skipped %d configurable Rector rules', count($configurableRectorRules)) ); $symfonyStyle->newLine(); - -final class UsedRectorClassResolver -{ - /** - * @param string[] $rectorSetFiles - * @return string[] - */ - public function resolve(array $rectorSetFiles): array - { - $setRectorsResolver = new SetRectorsResolver(); - $rulesConfiguration = $setRectorsResolver->resolveFromFilePathsIncludingConfiguration($rectorSetFiles); - - $usedRectorRules = []; - foreach ($rulesConfiguration as $ruleConfiguration) { - $usedRectorRules[] = is_string($ruleConfiguration) ? $ruleConfiguration : array_keys($ruleConfiguration)[0]; - } - - sort($usedRectorRules); - - return array_unique($usedRectorRules); - } -} diff --git a/scripts/src/Finder/RectorClassFinder.php b/scripts/src/Finder/RectorClassFinder.php index dbc1b39c57a..3e5740cfe35 100644 --- a/scripts/src/Finder/RectorClassFinder.php +++ b/scripts/src/Finder/RectorClassFinder.php @@ -4,6 +4,7 @@ namespace Rector\Scripts\Finder; +use Deprecated; use Nette\Loaders\RobotLoader; use Rector\Configuration\Deprecation\Contract\DeprecatedInterface; use ReflectionClass; @@ -39,9 +40,30 @@ public function find(array $dirs): array continue; } + if ($this->isDeprecated($rectorClassReflection)) { + continue; + } + $usableRectorClasses[] = $rectorClass; } return $usableRectorClasses; } + + /** + * @param ReflectionClass $reflectionClass + */ + private function isDeprecated(ReflectionClass $reflectionClass): bool + { + if ($reflectionClass->getAttributes(Deprecated::class) !== []) { + return true; + } + + $docComment = $reflectionClass->getDocComment(); + if (! is_string($docComment)) { + return false; + } + + return str_contains($docComment, '@deprecated'); + } } diff --git a/scripts/src/Resolver/UsedRectorClassResolver.php b/scripts/src/Resolver/UsedRectorClassResolver.php new file mode 100644 index 00000000000..d28863f2504 --- /dev/null +++ b/scripts/src/Resolver/UsedRectorClassResolver.php @@ -0,0 +1,29 @@ +resolveFromFilePathsIncludingConfiguration($rectorSetFiles); + + $usedRectorRules = []; + foreach ($rulesConfiguration as $ruleConfiguration) { + $usedRectorRules[] = is_string($ruleConfiguration) ? $ruleConfiguration : array_keys($ruleConfiguration)[0]; + } + + sort($usedRectorRules); + + return array_unique($usedRectorRules); + } +} diff --git a/src/Configuration/RectorConfigBuilder.php b/src/Configuration/RectorConfigBuilder.php index 6619f46b1d0..dc47d11e396 100644 --- a/src/Configuration/RectorConfigBuilder.php +++ b/src/Configuration/RectorConfigBuilder.php @@ -754,6 +754,14 @@ public function withComposerBased( $this->sets[] = PHPUnitSetList::COMPOSER_BASED; } + if ($doctrine) { + // $this->sets[] = DoctrineSetList::COMPOSER_BASED; + } + + if ($twig) { + // $this->sets[] = TwigSetList::COMPOSER_BASED; + } + if ($symfony) { // single set, as every rule inside is bound to the installed Symfony package version on its own $this->sets[] = SymfonySetList::COMPOSER_BASED;