diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index ddb859558e9..a0a9d459d2c 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -34,6 +34,7 @@ jobs: - 'e2e/applied-rule-removed-node' - 'e2e/parallel with space' - 'e2e/removed-and-added-files-collector' + - 'e2e/multiple-class-psr4' name: End to end test - ${{ matrix.directory }} diff --git a/e2e/multiple-class-psr4/.gitignore b/e2e/multiple-class-psr4/.gitignore new file mode 100644 index 00000000000..61ead86667c --- /dev/null +++ b/e2e/multiple-class-psr4/.gitignore @@ -0,0 +1 @@ +/vendor diff --git a/e2e/multiple-class-psr4/composer.json b/e2e/multiple-class-psr4/composer.json new file mode 100644 index 00000000000..5468cd74606 --- /dev/null +++ b/e2e/multiple-class-psr4/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "php": "^8.1" + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/e2e/multiple-class-psr4/expected-output.diff b/e2e/multiple-class-psr4/expected-output.diff new file mode 100644 index 00000000000..99b4353cf10 --- /dev/null +++ b/e2e/multiple-class-psr4/expected-output.diff @@ -0,0 +1,43 @@ +! [NOTE] File + ! "./multiple-class-psr4/src/C + ! lassMatchFileName.php" will be added + + ! [NOTE] File + ! "./multiple-class-psr4/src/S + ! omeInterface.php" will be added + + ! [NOTE] File + ! "./multiple-class-psr4/src/S + ! omeClass.php" will be added + + ! [NOTE] File + ! "./multiple-class-psr4/src/S + ! omeOtherInterface.php" will be added + + [WARNING] File "src/ClassNoMatchFileName.php" will be removed + + +1 file with changes +=================== + +1) src/ClassMatchFileName.php:1 + + ---------- begin diff ---------- +@@ @@ + + namespace App; + +-class ClassMatchFileName implements SomeInterface +-{ +-} +- +-interface SomeInterface +-{ +-} + ----------- end diff ----------- + +Applied rules: + * MultipleClassFileToPsr4ClassesRector + + + [OK] 1 file would have changed (dry-run) by Rector diff --git a/e2e/multiple-class-psr4/rector.php b/e2e/multiple-class-psr4/rector.php new file mode 100644 index 00000000000..e57c88bb0ed --- /dev/null +++ b/e2e/multiple-class-psr4/rector.php @@ -0,0 +1,14 @@ +paths([ + __DIR__ . '/src', + ]); + + $rectorConfig->rule(MultipleClassFileToPsr4ClassesRector::class); +}; diff --git a/e2e/multiple-class-psr4/src/ClassMatchFileName.php b/e2e/multiple-class-psr4/src/ClassMatchFileName.php new file mode 100644 index 00000000000..650d519cc59 --- /dev/null +++ b/e2e/multiple-class-psr4/src/ClassMatchFileName.php @@ -0,0 +1,11 @@ +isAnonymous()) { + if ($this->classAnalyzer->isAnonymousClass($node)) { return null; } diff --git a/rules/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector.php b/rules/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector.php index ac5bfa3abac..4ab533f3977 100644 --- a/rules/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector.php +++ b/rules/PSR4/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector.php @@ -9,9 +9,11 @@ use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\Namespace_; use Rector\Core\Application\FileSystem\RemovedAndAddedFilesCollector; +use Rector\Core\NodeAnalyzer\ClassAnalyzer; use Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace; use Rector\Core\PhpParser\Printer\NeighbourClassLikePrinter; use Rector\Core\Rector\AbstractRector; +use Rector\FileSystemRector\ValueObject\AddedFileWithContent; use Rector\PSR4\FileInfoAnalyzer\FileInfoDeletionAnalyzer; use Rector\PSR4\NodeManipulator\NamespaceManipulator; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; @@ -27,6 +29,7 @@ public function __construct( private readonly FileInfoDeletionAnalyzer $fileInfoDeletionAnalyzer, private readonly NeighbourClassLikePrinter $neighbourClassLikePrinter, private readonly RemovedAndAddedFilesCollector $removedAndAddedFilesCollector, + private readonly ClassAnalyzer $classAnalyzer ) { } @@ -105,10 +108,17 @@ public function refactor(Node $node): ?Node return $nodeToReturn; } - // 2. nothing to return - remove the file - $this->removedAndAddedFilesCollector->removeFile($this->file->getFilePath()); + $isInaddedFiles = array_filter( + $this->removedAndAddedFilesCollector->getAddedFilesWithContent(), + fn (AddedFileWithContent $addedFileWithContent): bool => $addedFileWithContent->getFilePath() === $this->file->getFilePath() + ); + + if ($isInaddedFiles === []) { + // 2. nothing to return - remove the file + $this->removedAndAddedFilesCollector->removeFile($this->file->getFilePath()); + } - return null; + return $node; } private function hasAtLeastTwoClassLikes(Node $node): bool @@ -173,12 +183,12 @@ private function findNonAnonymousClassLikes(Node $node): array { $classLikes = $this->betterNodeFinder->findInstanceOf([$node], ClassLike::class); - return array_filter($classLikes, static function (ClassLike $classLike): bool { + return array_filter($classLikes, function (ClassLike $classLike): bool { if (! $classLike instanceof Class_) { return true; } - return ! $classLike->isAnonymous(); + return ! $this->classAnalyzer->isAnonymousClass($classLike); }); } }