diff --git a/clang-tools-extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp b/clang-tools-extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp --- a/clang-tools-extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp +++ b/clang-tools-extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp @@ -143,18 +143,26 @@ llvm::DenseMap> GroupedReplacements; - // Deduplicate identical replacements in diagnostics. + // Deduplicate identical replacements in diagnostics unless they are from the + // same TU. // FIXME: Find an efficient way to deduplicate on diagnostics level. - llvm::DenseMap> + llvm::DenseMap> DiagReplacements; - auto AddToGroup = [&](const tooling::Replacement &R, bool FromDiag) { + auto AddToGroup = [&](const tooling::Replacement &R, + const tooling::TranslationUnitDiagnostics *SourceTU) { // Use the file manager to deduplicate paths. FileEntries are // automatically canonicalized. if (auto Entry = SM.getFileManager().getFile(R.getFilePath())) { - if (FromDiag) { + if (SourceTU) { auto &Replaces = DiagReplacements[*Entry]; - if (!Replaces.insert(R).second) + auto It = Replaces.find(R); + if (It == Replaces.end()) + Replaces.emplace(R, SourceTU); + else if (It->second != SourceTU) + // This replacement is a duplicate of one suggested by another TU. return; } GroupedReplacements[*Entry].push_back(R); @@ -166,14 +174,14 @@ for (const auto &TU : TUs) for (const tooling::Replacement &R : TU.Replacements) - AddToGroup(R, false); + AddToGroup(R, nullptr); for (const auto &TU : TUDs) for (const auto &D : TU.Diagnostics) if (const auto *ChoosenFix = tooling::selectFirstFix(D)) { for (const auto &Fix : *ChoosenFix) for (const tooling::Replacement &R : Fix.second) - AddToGroup(R, true); + AddToGroup(R, &TU); } // Sort replacements per file to keep consistent behavior when diff --git a/clang-tools-extra/test/clang-apply-replacements/Inputs/identical-in-TU/file1.yaml b/clang-tools-extra/test/clang-apply-replacements/Inputs/identical-in-TU/file1.yaml new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-apply-replacements/Inputs/identical-in-TU/file1.yaml @@ -0,0 +1,19 @@ +--- +MainSourceFile: identical_in_TU.cpp +Diagnostics: + - DiagnosticName: test-identical-insertion + DiagnosticMessage: + Message: Fix + FilePath: $(path)/identical_in_TU.cpp + FileOffset: 12 + Replacements: + - FilePath: $(path)/identical_in_TU.cpp + Offset: 12 + Length: 0 + ReplacementText: '0' + - FilePath: $(path)/identical_in_TU.cpp + Offset: 12 + Length: 0 + ReplacementText: '0' +... + diff --git a/clang-tools-extra/test/clang-apply-replacements/Inputs/identical-in-TU/file2.yaml b/clang-tools-extra/test/clang-apply-replacements/Inputs/identical-in-TU/file2.yaml new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-apply-replacements/Inputs/identical-in-TU/file2.yaml @@ -0,0 +1,19 @@ +--- +MainSourceFile: identical-in-TU.cpp +Diagnostics: + - DiagnosticName: test-identical-insertion + DiagnosticMessage: + Message: Fix + FilePath: $(path)/identical-in-TU.cpp + FileOffset: 12 + Replacements: + - FilePath: $(path)/identical-in-TU.cpp + Offset: 12 + Length: 0 + ReplacementText: '0' + - FilePath: $(path)/identical-in-TU.cpp + Offset: 12 + Length: 0 + ReplacementText: '0' +... + diff --git a/clang-tools-extra/test/clang-apply-replacements/Inputs/identical-in-TU/identical-in-TU.cpp b/clang-tools-extra/test/clang-apply-replacements/Inputs/identical-in-TU/identical-in-TU.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-apply-replacements/Inputs/identical-in-TU/identical-in-TU.cpp @@ -0,0 +1,2 @@ +class MyType {}; +// CHECK: class MyType00 {}; diff --git a/clang-tools-extra/test/clang-apply-replacements/identical-in-TU.cpp b/clang-tools-extra/test/clang-apply-replacements/identical-in-TU.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-apply-replacements/identical-in-TU.cpp @@ -0,0 +1,11 @@ +// RUN: mkdir -p %T/Inputs/identical-in-TU + +// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/identical-in-TU/identical-in-TU.cpp > %T/Inputs/identical-in-TU/identical-in-TU.cpp +// RUN: sed "s#\$(path)#%/T/Inputs/identical-in-TU#" %S/Inputs/identical-in-TU/file1.yaml > %T/Inputs/identical-in-TU/file1.yaml +// RUN: sed "s#\$(path)#%/T/Inputs/identical-in-TU#" %S/Inputs/identical-in-TU/file2.yaml > %T/Inputs/identical-in-TU/file2.yaml +// RUN: clang-apply-replacements %T/Inputs/identical-in-TU +// RUN: FileCheck -input-file=%T/Inputs/identical-in-TU/identical-in-TU.cpp %S/Inputs/identical-in-TU/identical-in-TU.cpp + +// Similar to identical test but each yaml file contains the same fix twice. +// This check ensures that only the duplicated replacements in a single yaml +// file are applied twice. Addresses PR45150.