diff --git a/src/Application/ApplicationFileProcessor.php b/src/Application/ApplicationFileProcessor.php index b7fff79015e..8c58b6e3129 100644 --- a/src/Application/ApplicationFileProcessor.php +++ b/src/Application/ApplicationFileProcessor.php @@ -192,7 +192,9 @@ private function processFile(File $file, Configuration $configuration): FileProc if ($fileProcessResult->getSystemErrors() !== []) { $this->changedFilesDetector->invalidateFile($file->getFilePath()); - } elseif (! $configuration->isDryRun() || ! $fileProcessResult->getFileDiff() instanceof FileDiff) { + } elseif (! $configuration->isDryRun() || ! $fileProcessResult->hasChanged()) { + // gate on the actual content change, not on FileDiff: a FileDiff also carries reported line changes + // that print identically, and such files would otherwise be re-processed on every dry run // selective runs are safe to cache now — the key is scoped to the rule selection $this->changedFilesDetector->cacheFile($file->getFilePath()); } diff --git a/tests/Application/ApplicationFileProcessor/ApplicationFileProcessorTest.php b/tests/Application/ApplicationFileProcessor/ApplicationFileProcessorTest.php index 9d90a5fdd28..f6e49f890de 100644 --- a/tests/Application/ApplicationFileProcessor/ApplicationFileProcessorTest.php +++ b/tests/Application/ApplicationFileProcessor/ApplicationFileProcessorTest.php @@ -43,6 +43,42 @@ public function testCleanFileIsCachedAsUnchanged(): void $this->assertFalse($this->changedFilesDetector->hasFileChanged($filePath)); } + public function testDryRunCachesFileWhoseReportedChangesLeaveContentUnchanged(): void + { + self::$rectorConfig = null; + $this->bootFromConfigFiles([__DIR__ . '/config-import-names.php']); + $applicationFileProcessor = $this->make(ApplicationFileProcessor::class); + $changedFilesDetector = $this->make(ChangedFilesDetector::class); + + $filePath = __DIR__ . '/Source/WithAliasedImportOfSameShortName.php'; + + // name importing reports line changes here, but the printed file is identical, so the diff is empty + $processResult = $applicationFileProcessor->processFiles([$filePath], new Configuration(isDryRun: true)); + + $fileDiffs = $processResult->getFileDiffs(onlyWithChanges: false); + $this->assertCount(1, $fileDiffs); + $this->assertSame('', $fileDiffs[0]->getDiff()); + $this->assertFalse($changedFilesDetector->hasFileChanged($filePath)); + + $changedFilesDetector->clear(); + } + + public function testDryRunDoesNotCacheFileWithPendingChanges(): void + { + self::$rectorConfig = null; + $this->bootFromConfigFiles([__DIR__ . '/config-max-changes.php']); + $applicationFileProcessor = $this->make(ApplicationFileProcessor::class); + $changedFilesDetector = $this->make(ChangedFilesDetector::class); + + $filePath = __DIR__ . '/Source/WithTwoClosuresFirst.php'; + + $applicationFileProcessor->processFiles([$filePath], new Configuration(isDryRun: true)); + + $this->assertTrue($changedFilesDetector->hasFileChanged($filePath)); + + $changedFilesDetector->clear(); + } + public function testOnlyRuleRunCachesUnderOwnScopeWithoutPoisoningFullRun(): void { $filePath = __DIR__ . '/Source/CleanFile.php'; diff --git a/tests/Application/ApplicationFileProcessor/Source/Rules/Money.php b/tests/Application/ApplicationFileProcessor/Source/Rules/Money.php new file mode 100644 index 00000000000..606c7f13632 --- /dev/null +++ b/tests/Application/ApplicationFileProcessor/Source/Rules/Money.php @@ -0,0 +1,9 @@ +cacheDirectory(sys_get_temp_dir() . '/_rector_import_names_test'); + $rectorConfig->importNames(); + $rectorConfig->rule(StringableForToStringRector::class); +};