diff --git a/src/FileSystem/RectorFinder.php b/src/FileSystem/RectorFinder.php index 5ddf4552f..00f557510 100644 --- a/src/FileSystem/RectorFinder.php +++ b/src/FileSystem/RectorFinder.php @@ -140,8 +140,10 @@ private function findInDirectoriesAndCreateRuleMetadatas(array $directories, arr $ruleMetadatas = []; - foreach ($this->findRectorClasses($directories) as $rectorClass) { - $rectorReflectionClass = new ReflectionClass($rectorClass); + $findRectorClasses = $this->findRectorClasses($directories); + + foreach ($findRectorClasses as $findRectorClass) { + $rectorReflectionClass = new ReflectionClass($findRectorClass); if ($rectorReflectionClass->isAbstract()) { continue; } @@ -171,14 +173,14 @@ private function findInDirectoriesAndCreateRuleMetadatas(array $directories, arr throw new InvalidRuleDescriptionException( sprintf( 'Rule "%s" has invalid code samples:%s"%s"', - $rectorClass, + $findRectorClass, PHP_EOL . PHP_EOL, $throwable->getMessage() ) ); } - $ruleDefinition->setRuleClass($rectorClass); + $ruleDefinition->setRuleClass($findRectorClass); $currentRuleSets = $this->findRuleUsedSets($ruleDefinition, $rectorSets); @@ -187,10 +189,30 @@ private function findInDirectoriesAndCreateRuleMetadatas(array $directories, arr $ruleDefinition->getDescription(), $ruleDefinition->getCodeSamples(), $currentRuleSets, - (string) $rectorReflectionClass->getFileName() + (string) $rectorReflectionClass->getFileName(), + $this->isDuplicatedLastName($findRectorClasses, $rectorReflectionClass->getShortName()) ); } return $ruleMetadatas; } + + /** + * @param array> $findRectorClasses + */ + private function isDuplicatedLastName(array $findRectorClasses, string $lastName): bool + { + $count = 0; + foreach ($findRectorClasses as $findRectorClass) { + if (\str_ends_with($findRectorClass, '\\' . $lastName)) { + ++$count; + + if ($count === 2) { + return true; + } + } + } + + return false; + } } diff --git a/src/RuleFilter/ValueObject/RuleMetadata.php b/src/RuleFilter/ValueObject/RuleMetadata.php index c9cc7dfe8..edf3f5086 100644 --- a/src/RuleFilter/ValueObject/RuleMetadata.php +++ b/src/RuleFilter/ValueObject/RuleMetadata.php @@ -29,7 +29,8 @@ public function __construct( private readonly string $description, private array $codeSamples, private readonly array $sets, - private readonly string $rectorRuleFilePath + private readonly string $rectorRuleFilePath, + private readonly bool $sameNameInDifferentSet ) { Assert::isAOf($ruleClass, RectorInterface::class); Assert::allIsAOf($sets, RectorSet::class); @@ -43,9 +44,26 @@ public function getRuleShortClass(): string public function getSlug(): string { // turn "SomeRector" to "some-rector" - return str($this->getRuleShortClass()) + $lastSlug = str($this->getRuleShortClass()) ->snake('-') ->toString(); + + if ($this->sameNameInDifferentSet === false) { + return $lastSlug; + } + + $currentSet = current($this->sets); + + if ($currentSet === false) { + return $lastSlug; + } + + $slug = $currentSet->getSlug(); + if ($slug !== '') { + $slug .= '-'; + } + + return $slug . $lastSlug; } public function getDescription(): string diff --git a/tests/FileSystem/RectorFinderTest.php b/tests/FileSystem/RectorFinderTest.php new file mode 100644 index 000000000..57a2aa694 --- /dev/null +++ b/tests/FileSystem/RectorFinderTest.php @@ -0,0 +1,66 @@ +rectorFinder = new RectorFinder(new RectorSetsTreeProvider()); + } + + public function testFindDuplicated(): void + { + $foundRectors = $this->rectorFinder->find(); + + $shortNames = []; + $longNames = []; + foreach ($foundRectors as $ruleMetadata) { + $shortNames[] = $ruleMetadata->getRuleShortClass(); + $longNames[] = $ruleMetadata->getRectorClass(); + } + + $uniqueShortNames = array_unique($shortNames); + $uniqueLongNames = array_unique($longNames); + + $this->assertNotSame( + count($uniqueShortNames), + count($uniqueLongNames), + 'There are duplicated short class names.' + ); + + // get duplicated short names and report different slug + $duplicatedShortNames = array_diff_key($shortNames, $uniqueShortNames); + + $this->assertContains( + 'DeprecatedAnnotationToDeprecatedAttributeRector', + $duplicatedShortNames, + 'Expected DeprecatedAnnotationToDeprecatedAttributeRector to be one of the duplicated short names.' + ); + + foreach ($foundRectors as $foundRector) { + if ($foundRector->getRectorClass() === DeprecatedAnnotationToDeprecatedAttributeRector::class) { + $this->assertSame( + 'php-php-84-deprecated-annotation-to-deprecated-attribute-rector', + $foundRector->getSlug() + ); + } + + if ($foundRector->getRectorClass() === \Rector\Php85\Rector\Const_\DeprecatedAnnotationToDeprecatedAttributeRector::class) { + $this->assertSame( + 'php-php-85-deprecated-annotation-to-deprecated-attribute-rector', + $foundRector->getSlug() + ); + } + } + } +} diff --git a/tests/RuleFilter/RuleFilterTest.php b/tests/RuleFilter/RuleFilterTest.php index 330fb028b..f66201e14 100644 --- a/tests/RuleFilter/RuleFilterTest.php +++ b/tests/RuleFilter/RuleFilterTest.php @@ -20,14 +20,16 @@ public function testFilterBySetGroup(): void 'Some description', [], [], - 'some-rector.php' + 'some-rector.php', + false ); $ruleMetadataCommunity = new RuleMetadata( MigrateToSimplifiedAttributeRector::class, 'Some description', [], [], - 'some-rector.php' + 'some-rector.php', + false, ); $ruleFilter = $this->make(RuleFilter::class);