Index: clang-tools-extra/clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h =================================================================== --- clang-tools-extra/clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h +++ clang-tools-extra/clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h @@ -87,7 +87,8 @@ /// \li false If there were conflicts. bool mergeAndDeduplicate(const TUReplacements &TUs, const TUDiagnostics &TUDs, FileToChangesMap &FileChanges, - clang::SourceManager &SM); + clang::SourceManager &SM, + bool IgnoreInsertConflict = false); /// Apply \c AtomicChange on File and rewrite it. /// Index: clang-tools-extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp =================================================================== --- clang-tools-extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp +++ clang-tools-extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp @@ -202,7 +202,7 @@ bool mergeAndDeduplicate(const TUReplacements &TUs, const TUDiagnostics &TUDs, FileToChangesMap &FileChanges, - clang::SourceManager &SM) { + clang::SourceManager &SM, bool IgnoreInsertConflict) { auto GroupedReplacements = groupReplacements(TUs, TUDs, SM); bool ConflictDetected = false; @@ -231,7 +231,24 @@ // For now, printing directly the error reported by `AtomicChange` is // the easiest solution. errs() << llvm::toString(std::move(Err)) << "\n"; - ConflictDetected = true; + if (IgnoreInsertConflict) { + tooling::Replacements &Replacements = FileChange.getReplacements(); + unsigned NewOffset = + Replacements.getShiftedCodePosition(R.getOffset()); + unsigned NewLength = Replacements.getShiftedCodePosition( + R.getOffset() + R.getLength()) - + NewOffset; + if (NewLength == R.getLength()) { + tooling::Replacement RR = tooling::Replacement( + R.getFilePath(), NewOffset, NewLength, R.getReplacementText()); + Replacements = Replacements.merge(tooling::Replacements(RR)); + } else { + llvm::errs() + << "Can't resolve conflict, skipping the replacement.\n"; + ConflictDetected = true; + } + } else + ConflictDetected = true; } } FileChanges.try_emplace(Entry, Index: clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp =================================================================== --- clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp +++ clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp @@ -42,6 +42,11 @@ "merging/replacing."), cl::init(false), cl::cat(ReplacementCategory)); +static cl::opt IgnoreInsertConflict( + "ignore-insert-conflict", + cl::desc("Ignore insert conflict and keep running to fix."), + cl::init(false), cl::cat(ReplacementCategory)); + static cl::opt DoFormat( "format", cl::desc("Enable formatting of code changed by applying replacements.\n" @@ -131,7 +136,7 @@ SourceManager SM(Diagnostics, Files); FileToChangesMap Changes; - if (!mergeAndDeduplicate(TURs, TUDs, Changes, SM)) + if (!mergeAndDeduplicate(TURs, TUDs, Changes, SM, IgnoreInsertConflict)) return 1; tooling::ApplyChangesSpec Spec; Index: clang-tools-extra/clang-tidy/tool/run-clang-tidy.py =================================================================== --- clang-tools-extra/clang-tidy/tool/run-clang-tidy.py +++ clang-tools-extra/clang-tidy/tool/run-clang-tidy.py @@ -180,6 +180,7 @@ def apply_fixes(args, clang_apply_replacements_binary, tmpdir): """Calls clang-apply-fixes on a given directory.""" invocation = [clang_apply_replacements_binary] + invocation.append('-ignore-insert-conflict') if args.format: invocation.append('-format') if args.style: Index: clang-tools-extra/test/clang-apply-replacements/Inputs/ignore-conflict/file1.yaml =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-apply-replacements/Inputs/ignore-conflict/file1.yaml @@ -0,0 +1,24 @@ +--- +MainSourceFile: ignore-conflict.cpp +Diagnostics: + - DiagnosticName: test-ignore-conflict-insertion + DiagnosticMessage: + Message: Fix + FilePath: $(path)/ignore-conflict.cpp + FileOffset: 0 + Replacements: + - FilePath: $(path)/ignore-conflict.cpp + Offset: 0 + Length: 0 + ReplacementText: "#include \n" + - DiagnosticName: test-ignore-conflict-insertion + DiagnosticMessage: + Message: Fix + FilePath: $(path)/ignore-conflict.cpp + FileOffset: 0 + Replacements: + - FilePath: $(path)/ignore-conflict.cpp + Offset: 0 + Length: 0 + ReplacementText: "#include \n" +... Index: clang-tools-extra/test/clang-apply-replacements/Inputs/ignore-conflict/ignore-conflict.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-apply-replacements/Inputs/ignore-conflict/ignore-conflict.cpp @@ -0,0 +1,4 @@ +class MyType {}; +// CHECK: #include +// CHECK-NEXT: #include +// CEHCK-NEXT: class MyType {}; Index: clang-tools-extra/test/clang-apply-replacements/ignore-conflict.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-apply-replacements/ignore-conflict.cpp @@ -0,0 +1,5 @@ +// RUN: mkdir -p %T/Inputs/ignore-conflict +// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/ignore-conflict/ignore-conflict.cpp > %T/Inputs/ignore-conflict/ignore-conflict.cpp +// RUN: sed "s#\$(path)#%/T/Inputs/ignore-conflict#" %S/Inputs/ignore-conflict/file1.yaml > %T/Inputs/ignore-conflict/file1.yaml +// RUN: clang-apply-replacements --ignore-insert-conflict %T/Inputs/ignore-conflict +// RUN: FileCheck -input-file=%T/Inputs/ignore-conflict/ignore-conflict.cpp %S/Inputs/ignore-conflict/ignore-conflict.cpp Index: clang/include/clang/Tooling/Refactoring/AtomicChange.h =================================================================== --- clang/include/clang/Tooling/Refactoring/AtomicChange.h +++ clang/include/clang/Tooling/Refactoring/AtomicChange.h @@ -116,6 +116,8 @@ /// Returns a const reference to existing replacements. const Replacements &getReplacements() const { return Replaces; } + Replacements &getReplacements() { return Replaces; } + llvm::ArrayRef getInsertedHeaders() const { return InsertedHeaders; }