@@ -337,6 +337,35 @@ std::string noteMessage(const Diag &Main, const DiagBase &Note,
337337 return capitalize (std::move (Result));
338338}
339339
340+ // Tests if any two `TextEdit`s in `Edits` conflict. Two `TextEdit`s
341+ // conflict if they have overlapping source ranges.
342+ // NOTE: This function is inspired by clang::internal::anyConflict
343+ bool anyConflict (const llvm::SmallVector<TextEdit, 1 > &Edits) {
344+ // A simple interval overlap detection algorithm. Sorts all ranges by their
345+ // begin location then finds the first overlap in one pass.
346+ llvm::SmallVector<const TextEdit *, 1 > All; // a copy of `Edits`
347+
348+ for (const TextEdit &E : Edits)
349+ All.push_back (&E);
350+ std::sort (All.begin (), All.end (), [](const TextEdit *H1, const TextEdit *H2) {
351+ return H1->range .start < H2->range .start ;
352+ });
353+
354+ const TextEdit *CurrHint = nullptr ;
355+
356+ for (const TextEdit *Hint : All) {
357+ if (!CurrHint || CurrHint->range .end < Hint->range .start ) {
358+ // Either to initialize `CurrHint` or `CurrHint` does not
359+ // overlap with `Hint`:
360+ CurrHint = Hint;
361+ } else
362+ // In case `Hint` overlaps the `CurrHint`, we found at least one
363+ // conflict:
364+ return true ;
365+ }
366+ return false ;
367+ }
368+
340369std::optional<Fix>
341370generateApplyAllFromOption (const llvm::StringRef Name,
342371 llvm::ArrayRef<Diag *> AllDiagnostics) {
@@ -350,8 +379,9 @@ generateApplyAllFromOption(const llvm::StringRef Name,
350379 ApplyAll.Edits .erase (
351380 std::unique (ApplyAll.Edits .begin (), ApplyAll.Edits .end ()),
352381 ApplyAll.Edits .end ());
353- // Skip diagnostic categories that don't have multiple fixes to apply
354- if (ApplyAll.Edits .size () < 2U ) {
382+ // Skip diagnostic categories that don't have multiple fixes to apply or that
383+ // have conflicting fixes to apply
384+ if (ApplyAll.Edits .size () < 2U || anyConflict (ApplyAll.Edits )) {
355385 return std::nullopt ;
356386 }
357387 ApplyAll.Message = llvm::formatv (" apply all '{0}' fixes" , Name);
@@ -370,7 +400,9 @@ generateApplyAllFixesOption(llvm::ArrayRef<Diag> AllDiagnostics) {
370400 ApplyAll.Edits .erase (
371401 std::unique (ApplyAll.Edits .begin (), ApplyAll.Edits .end ()),
372402 ApplyAll.Edits .end ());
373- if (ApplyAll.Edits .size () < 2U ) {
403+ // Skip diagnostics that don't have multiple fixes to apply or that have
404+ // conflicting fixes to apply
405+ if (ApplyAll.Edits .size () < 2U || anyConflict (ApplyAll.Edits )) {
374406 return std::nullopt ;
375407 }
376408 ApplyAll.Message = " apply all clangd fixes" ;
@@ -865,8 +897,7 @@ void StoreDiags::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
865897 }
866898 if (Message.empty ()) // either !SyntheticMessage, or we failed to make one.
867899 Info.FormatDiagnostic (Message);
868- LastDiag->Fixes .push_back (
869- Fix{std::string (Message), std::move (Edits), {}});
900+ LastDiag->Fixes .push_back (Fix{std::string (Message), std::move (Edits), {}});
870901 return true ;
871902 };
872903
0 commit comments