Skip to content

Commit e7780fd

Browse files
committed
Fix conflicts
1 parent 5594a67 commit e7780fd

File tree

1 file changed

+36
-5
lines changed

1 file changed

+36
-5
lines changed

clang-tools-extra/clangd/Diagnostics.cpp

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
340369
std::optional<Fix>
341370
generateApplyAllFromOption(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

Comments
 (0)