[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
torshepherd wrote: @HighCommander4 is right here. I have no emotional dibs on completing this ๐ and it should be pretty easy to Claude up. But the current implementation had a major issue - conflicting edits from apply all would cause the document to get modified into a broken state And in attempting to fix that, I made it worse, where even nonconflicting edits now can incorrectly patch. How this should be resolved in the most rigorous way is still an open question: should we iteratively apply? What happens if two quick fixes are contradicting? What happens if one fix leads to more auto fixable things? Or should we just pick one of the fixes to apply in these cases? If you wanted to pick this up, the first step would be to flesh out how we want all of these edge cases to work and write tons of test cases. The implementation after that should be trivial for a coding agent for instance. But it comes down to reviewer burden ultimately https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
HighCommander4 wrote: > What's the current status of this PR or blockers? It looks like the PR needs both some additional work by the patch author (per [this comment](https://github.com/llvm/llvm-project/pull/79867#issuecomment-2896299157) and [this one](https://github.com/llvm/llvm-project/pull/79867#issuecomment-3468668464)), and some attention from reviewers. I'll let @torshepherd speak to his plans as patch author. If he doesn't plan to continue work on it, the PR may be available for another interested contributor to pick up. For the review part: clangd is currently experiencing a lack of review bandwidth, and is [looking for additional maintainers](https://discourse.llvm.org/t/help-needed-with-clangd-maintenance/89820). If, by chance, you have an employer who may have the resources and interest to invest in contributing to clangd development and maintenance, this would be a great time to do so. https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
ckandeler wrote: A really nice feature that I would love to see go in. But note that there are a number of test failures: ``` Failed Tests (4): Clangd Unit Tests :: ./ClangdTests/DiagnosticTest/ClangTidySelfContainedDiags Clangd Unit Tests :: ./ClangdTests/DiagnosticTest/ClangTidySelfContainedDiagsFormatting Clangd Unit Tests :: ./ClangdTests/IncludeFixerTest/Typo Clangd Unit Tests :: ./ClangdTests/IncludeFixerTest/UnresolvedSpecifierWithSemaCorrection ``` You need to adapt the expected fix suggestions accordingly. https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
torshepherd wrote: It's been a while since I looked at this, but from what I remember my attempt to fix the last part didn't quite work as expected. I can try to test it again, but I ended up just abandoning it at the time since I didn't really miss the functionality as much as I thought https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
HighCommander4 wrote: > @HighCommander4 I'm just wondering, have you had a chance to check out this > PR yet? If not, is there any chance you could find some time for it in the > coming weeks or months? My apologies, this has been languishing in my review queue and I haven't quite managed to get to it. Will do my best to make time for it in the coming weeks, but I've also added @llvm-beanz as another potential reviewer, in case he gets to it before I do :) https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
sr0lle wrote: @HighCommander4 I'm just wondering, have you had a chance to check out this PR yet? If not, is there any chance you could find some time for it in the coming weeks or months? https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
torshepherd wrote: @HighCommander4 I've pushed up the change that doesn't allow conflicting fixits. I'll now be smoke-testing this over the coming weeks while I work, but consider it ready for review again whenever you have a chance https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
https://github.com/torshepherd updated
https://github.com/llvm/llvm-project/pull/79867
>From 94dee94becb7d79b087e183754602e08a5c4669d Mon Sep 17 00:00:00 2001
From: Tor Shepherd
Date: Mon, 29 Jan 2024 11:44:25 -0500
Subject: [PATCH 1/2] [clangd] Add fix-all CodeActions
---
clang-tools-extra/clangd/Diagnostics.cpp | 72 ++-
clang-tools-extra/clangd/Protocol.h | 22 +-
.../clangd/unittests/DiagnosticsTests.cpp | 200 +-
3 files changed, 227 insertions(+), 67 deletions(-)
diff --git a/clang-tools-extra/clangd/Diagnostics.cpp
b/clang-tools-extra/clangd/Diagnostics.cpp
index d5eca083eb651..37ee5cd8fbc7a 100644
--- a/clang-tools-extra/clangd/Diagnostics.cpp
+++ b/clang-tools-extra/clangd/Diagnostics.cpp
@@ -339,6 +339,72 @@ std::string noteMessage(const Diag &Main, const DiagBase
&Note,
return capitalize(std::move(Result));
}
+std::optional
+generateApplyAllFromOption(const llvm::StringRef Name,
+ llvm::ArrayRef AllDiagnostics) {
+ Fix ApplyAll;
+ for (auto *const Diag : AllDiagnostics) {
+for (const auto &Fix : Diag->Fixes)
+ ApplyAll.Edits.insert(ApplyAll.Edits.end(), Fix.Edits.begin(),
+Fix.Edits.end());
+ }
+ llvm::sort(ApplyAll.Edits);
+ ApplyAll.Edits.erase(
+ std::unique(ApplyAll.Edits.begin(), ApplyAll.Edits.end()),
+ ApplyAll.Edits.end());
+ // Skip diagnostic categories that don't have multiple fixes to apply
+ if (ApplyAll.Edits.size() < 2U) {
+return std::nullopt;
+ }
+ ApplyAll.Message = llvm::formatv("apply all '{0}' fixes", Name);
+ return ApplyAll;
+}
+
+std::optional
+generateApplyAllFixesOption(llvm::ArrayRef AllDiagnostics) {
+ Fix ApplyAll;
+ for (auto const &Diag : AllDiagnostics) {
+for (const auto &Fix : Diag.Fixes)
+ ApplyAll.Edits.insert(ApplyAll.Edits.end(), Fix.Edits.begin(),
+Fix.Edits.end());
+ }
+ llvm::sort(ApplyAll.Edits);
+ ApplyAll.Edits.erase(
+ std::unique(ApplyAll.Edits.begin(), ApplyAll.Edits.end()),
+ ApplyAll.Edits.end());
+ if (ApplyAll.Edits.size() < 2U) {
+return std::nullopt;
+ }
+ ApplyAll.Message = "apply all clangd fixes";
+ return ApplyAll;
+}
+
+void appendApplyAlls(std::vector &AllDiagnostics) {
+ llvm::DenseMap> CategorizedFixes;
+
+ for (auto &Diag : AllDiagnostics) {
+// Keep track of fixable diagnostics for generating "apply all fixes"
+if (!Diag.Fixes.empty()) {
+ if (auto [It, DidEmplace] = CategorizedFixes.try_emplace(
+ Diag.Name, std::vector{&Diag});
+ !DidEmplace)
+It->second.emplace_back(&Diag);
+}
+ }
+
+ auto FixAllClangd = generateApplyAllFixesOption(AllDiagnostics);
+ for (const auto &[Name, DiagsForThisCategory] : CategorizedFixes) {
+auto FixAllForCategory =
+generateApplyAllFromOption(Name, DiagsForThisCategory);
+for (auto *Diag : DiagsForThisCategory) {
+ if (DiagsForThisCategory.size() >= 2U && FixAllForCategory.has_value())
+Diag->Fixes.emplace_back(*FixAllForCategory);
+ if (CategorizedFixes.size() >= 2U && FixAllClangd.has_value())
+Diag->Fixes.emplace_back(*FixAllClangd);
+}
+ }
+}
+
void setTags(clangd::Diag &D) {
static const auto *DeprecatedDiags = new llvm::DenseSet{
diag::warn_access_decl_deprecated,
@@ -575,7 +641,8 @@ std::vector StoreDiags::take(const
clang::tidy::ClangTidyContext *Tidy) {
// Do not forget to emit a pending diagnostic if there is one.
flushLastDiag();
- // Fill in name/source now that we have all the context needed to map them.
+ // Fill in name/source now that we have all the context needed to map
+ // them.
for (auto &Diag : Output) {
if (const char *ClangDiag = getDiagnosticCode(Diag.ID)) {
// Warnings controlled by -Wfoo are better recognized by that name.
@@ -619,6 +686,9 @@ std::vector StoreDiags::take(const
clang::tidy::ClangTidyContext *Tidy) {
llvm::erase_if(Output, [&](const Diag &D) {
return !SeenDiags.emplace(D.Range, D.Message).second;
});
+
+ appendApplyAlls(Output);
+
return std::move(Output);
}
diff --git a/clang-tools-extra/clangd/Protocol.h
b/clang-tools-extra/clangd/Protocol.h
index 358d4c6feaf87..113629f138e41 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -257,6 +257,10 @@ inline bool operator==(const TextEdit &L, const TextEdit
&R) {
return std::tie(L.newText, L.range, L.annotationId) ==
std::tie(R.newText, R.range, L.annotationId);
}
+inline bool operator<(const TextEdit &L, const TextEdit &R) {
+ return std::tie(L.newText, L.range, L.annotationId) <
+ std::tie(R.newText, R.range, L.annotationId);
+}
bool fromJSON(const llvm::json::Value &, TextEdit &, llvm::json::Path);
llvm::json::Value toJSON(const TextEdit &);
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TextEdit &);
@@ -281,7 +285,7 @@ struct TextDocumentEdit {
/// The text document to change.
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
torshepherd wrote: Ah, awesome. In that case note that I'll make the option not show up when changes would have conflicting overlap ranges https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
HighCommander4 wrote: > Bump @HighCommander4 - did you get a chance to review this? Hi @torshepherd! Sorry for not being more responsive on this. I haven't had as much time as I'd like to spend on clangd reviews recently, and certainly not enough to keep up with the volume of review requests that I get. That said, I haven't forgotten about this patch, and it's definitely on a shortlist that I'm planning to make time to look at over the next couple of weeks. https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
torshepherd wrote: Bump @HighCommander4 - did you get a chance to review this? I've been using this with great success in my local build for several months now, and the feature is _extremely_ handy. There is a slight bug that overlapping fixes are troublesome if you choose "clangd apply all", but it's minor. I can pretty easily push a fix to this, but I don't want to work more on this unless it has a chance of being reviewed/accepted https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
https://github.com/torshepherd updated
https://github.com/llvm/llvm-project/pull/79867
>From 94dee94becb7d79b087e183754602e08a5c4669d Mon Sep 17 00:00:00 2001
From: Tor Shepherd
Date: Mon, 29 Jan 2024 11:44:25 -0500
Subject: [PATCH] [clangd] Add fix-all CodeActions
---
clang-tools-extra/clangd/Diagnostics.cpp | 72 ++-
clang-tools-extra/clangd/Protocol.h | 22 +-
.../clangd/unittests/DiagnosticsTests.cpp | 200 +-
3 files changed, 227 insertions(+), 67 deletions(-)
diff --git a/clang-tools-extra/clangd/Diagnostics.cpp
b/clang-tools-extra/clangd/Diagnostics.cpp
index d5eca083eb6512..37ee5cd8fbc7ae 100644
--- a/clang-tools-extra/clangd/Diagnostics.cpp
+++ b/clang-tools-extra/clangd/Diagnostics.cpp
@@ -339,6 +339,72 @@ std::string noteMessage(const Diag &Main, const DiagBase
&Note,
return capitalize(std::move(Result));
}
+std::optional
+generateApplyAllFromOption(const llvm::StringRef Name,
+ llvm::ArrayRef AllDiagnostics) {
+ Fix ApplyAll;
+ for (auto *const Diag : AllDiagnostics) {
+for (const auto &Fix : Diag->Fixes)
+ ApplyAll.Edits.insert(ApplyAll.Edits.end(), Fix.Edits.begin(),
+Fix.Edits.end());
+ }
+ llvm::sort(ApplyAll.Edits);
+ ApplyAll.Edits.erase(
+ std::unique(ApplyAll.Edits.begin(), ApplyAll.Edits.end()),
+ ApplyAll.Edits.end());
+ // Skip diagnostic categories that don't have multiple fixes to apply
+ if (ApplyAll.Edits.size() < 2U) {
+return std::nullopt;
+ }
+ ApplyAll.Message = llvm::formatv("apply all '{0}' fixes", Name);
+ return ApplyAll;
+}
+
+std::optional
+generateApplyAllFixesOption(llvm::ArrayRef AllDiagnostics) {
+ Fix ApplyAll;
+ for (auto const &Diag : AllDiagnostics) {
+for (const auto &Fix : Diag.Fixes)
+ ApplyAll.Edits.insert(ApplyAll.Edits.end(), Fix.Edits.begin(),
+Fix.Edits.end());
+ }
+ llvm::sort(ApplyAll.Edits);
+ ApplyAll.Edits.erase(
+ std::unique(ApplyAll.Edits.begin(), ApplyAll.Edits.end()),
+ ApplyAll.Edits.end());
+ if (ApplyAll.Edits.size() < 2U) {
+return std::nullopt;
+ }
+ ApplyAll.Message = "apply all clangd fixes";
+ return ApplyAll;
+}
+
+void appendApplyAlls(std::vector &AllDiagnostics) {
+ llvm::DenseMap> CategorizedFixes;
+
+ for (auto &Diag : AllDiagnostics) {
+// Keep track of fixable diagnostics for generating "apply all fixes"
+if (!Diag.Fixes.empty()) {
+ if (auto [It, DidEmplace] = CategorizedFixes.try_emplace(
+ Diag.Name, std::vector{&Diag});
+ !DidEmplace)
+It->second.emplace_back(&Diag);
+}
+ }
+
+ auto FixAllClangd = generateApplyAllFixesOption(AllDiagnostics);
+ for (const auto &[Name, DiagsForThisCategory] : CategorizedFixes) {
+auto FixAllForCategory =
+generateApplyAllFromOption(Name, DiagsForThisCategory);
+for (auto *Diag : DiagsForThisCategory) {
+ if (DiagsForThisCategory.size() >= 2U && FixAllForCategory.has_value())
+Diag->Fixes.emplace_back(*FixAllForCategory);
+ if (CategorizedFixes.size() >= 2U && FixAllClangd.has_value())
+Diag->Fixes.emplace_back(*FixAllClangd);
+}
+ }
+}
+
void setTags(clangd::Diag &D) {
static const auto *DeprecatedDiags = new llvm::DenseSet{
diag::warn_access_decl_deprecated,
@@ -575,7 +641,8 @@ std::vector StoreDiags::take(const
clang::tidy::ClangTidyContext *Tidy) {
// Do not forget to emit a pending diagnostic if there is one.
flushLastDiag();
- // Fill in name/source now that we have all the context needed to map them.
+ // Fill in name/source now that we have all the context needed to map
+ // them.
for (auto &Diag : Output) {
if (const char *ClangDiag = getDiagnosticCode(Diag.ID)) {
// Warnings controlled by -Wfoo are better recognized by that name.
@@ -619,6 +686,9 @@ std::vector StoreDiags::take(const
clang::tidy::ClangTidyContext *Tidy) {
llvm::erase_if(Output, [&](const Diag &D) {
return !SeenDiags.emplace(D.Range, D.Message).second;
});
+
+ appendApplyAlls(Output);
+
return std::move(Output);
}
diff --git a/clang-tools-extra/clangd/Protocol.h
b/clang-tools-extra/clangd/Protocol.h
index 358d4c6feaf87d..113629f138e416 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -257,6 +257,10 @@ inline bool operator==(const TextEdit &L, const TextEdit
&R) {
return std::tie(L.newText, L.range, L.annotationId) ==
std::tie(R.newText, R.range, L.annotationId);
}
+inline bool operator<(const TextEdit &L, const TextEdit &R) {
+ return std::tie(L.newText, L.range, L.annotationId) <
+ std::tie(R.newText, R.range, L.annotationId);
+}
bool fromJSON(const llvm::json::Value &, TextEdit &, llvm::json::Path);
llvm::json::Value toJSON(const TextEdit &);
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TextEdit &);
@@ -281,7 +285,7 @@ struct TextDocumentEdit {
/// The text document to change.
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
torshepherd wrote: Ping https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
https://github.com/torshepherd updated
https://github.com/llvm/llvm-project/pull/79867
>From 636e8286b38839c9d90e9eb147ba59d588c3241c Mon Sep 17 00:00:00 2001
From: Tor Shepherd
Date: Mon, 29 Jan 2024 11:44:25 -0500
Subject: [PATCH] [clangd] Add fix-all CodeActions
---
clang-tools-extra/clangd/Diagnostics.cpp | 72 ++-
clang-tools-extra/clangd/Protocol.h | 22 +-
.../clangd/unittests/DiagnosticsTests.cpp | 200 +-
3 files changed, 227 insertions(+), 67 deletions(-)
diff --git a/clang-tools-extra/clangd/Diagnostics.cpp
b/clang-tools-extra/clangd/Diagnostics.cpp
index d5eca083eb6512..37ee5cd8fbc7ae 100644
--- a/clang-tools-extra/clangd/Diagnostics.cpp
+++ b/clang-tools-extra/clangd/Diagnostics.cpp
@@ -339,6 +339,72 @@ std::string noteMessage(const Diag &Main, const DiagBase
&Note,
return capitalize(std::move(Result));
}
+std::optional
+generateApplyAllFromOption(const llvm::StringRef Name,
+ llvm::ArrayRef AllDiagnostics) {
+ Fix ApplyAll;
+ for (auto *const Diag : AllDiagnostics) {
+for (const auto &Fix : Diag->Fixes)
+ ApplyAll.Edits.insert(ApplyAll.Edits.end(), Fix.Edits.begin(),
+Fix.Edits.end());
+ }
+ llvm::sort(ApplyAll.Edits);
+ ApplyAll.Edits.erase(
+ std::unique(ApplyAll.Edits.begin(), ApplyAll.Edits.end()),
+ ApplyAll.Edits.end());
+ // Skip diagnostic categories that don't have multiple fixes to apply
+ if (ApplyAll.Edits.size() < 2U) {
+return std::nullopt;
+ }
+ ApplyAll.Message = llvm::formatv("apply all '{0}' fixes", Name);
+ return ApplyAll;
+}
+
+std::optional
+generateApplyAllFixesOption(llvm::ArrayRef AllDiagnostics) {
+ Fix ApplyAll;
+ for (auto const &Diag : AllDiagnostics) {
+for (const auto &Fix : Diag.Fixes)
+ ApplyAll.Edits.insert(ApplyAll.Edits.end(), Fix.Edits.begin(),
+Fix.Edits.end());
+ }
+ llvm::sort(ApplyAll.Edits);
+ ApplyAll.Edits.erase(
+ std::unique(ApplyAll.Edits.begin(), ApplyAll.Edits.end()),
+ ApplyAll.Edits.end());
+ if (ApplyAll.Edits.size() < 2U) {
+return std::nullopt;
+ }
+ ApplyAll.Message = "apply all clangd fixes";
+ return ApplyAll;
+}
+
+void appendApplyAlls(std::vector &AllDiagnostics) {
+ llvm::DenseMap> CategorizedFixes;
+
+ for (auto &Diag : AllDiagnostics) {
+// Keep track of fixable diagnostics for generating "apply all fixes"
+if (!Diag.Fixes.empty()) {
+ if (auto [It, DidEmplace] = CategorizedFixes.try_emplace(
+ Diag.Name, std::vector{&Diag});
+ !DidEmplace)
+It->second.emplace_back(&Diag);
+}
+ }
+
+ auto FixAllClangd = generateApplyAllFixesOption(AllDiagnostics);
+ for (const auto &[Name, DiagsForThisCategory] : CategorizedFixes) {
+auto FixAllForCategory =
+generateApplyAllFromOption(Name, DiagsForThisCategory);
+for (auto *Diag : DiagsForThisCategory) {
+ if (DiagsForThisCategory.size() >= 2U && FixAllForCategory.has_value())
+Diag->Fixes.emplace_back(*FixAllForCategory);
+ if (CategorizedFixes.size() >= 2U && FixAllClangd.has_value())
+Diag->Fixes.emplace_back(*FixAllClangd);
+}
+ }
+}
+
void setTags(clangd::Diag &D) {
static const auto *DeprecatedDiags = new llvm::DenseSet{
diag::warn_access_decl_deprecated,
@@ -575,7 +641,8 @@ std::vector StoreDiags::take(const
clang::tidy::ClangTidyContext *Tidy) {
// Do not forget to emit a pending diagnostic if there is one.
flushLastDiag();
- // Fill in name/source now that we have all the context needed to map them.
+ // Fill in name/source now that we have all the context needed to map
+ // them.
for (auto &Diag : Output) {
if (const char *ClangDiag = getDiagnosticCode(Diag.ID)) {
// Warnings controlled by -Wfoo are better recognized by that name.
@@ -619,6 +686,9 @@ std::vector StoreDiags::take(const
clang::tidy::ClangTidyContext *Tidy) {
llvm::erase_if(Output, [&](const Diag &D) {
return !SeenDiags.emplace(D.Range, D.Message).second;
});
+
+ appendApplyAlls(Output);
+
return std::move(Output);
}
diff --git a/clang-tools-extra/clangd/Protocol.h
b/clang-tools-extra/clangd/Protocol.h
index e88c804692568f..d1e6cf35f9d9de 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -257,6 +257,10 @@ inline bool operator==(const TextEdit &L, const TextEdit
&R) {
return std::tie(L.newText, L.range, L.annotationId) ==
std::tie(R.newText, R.range, L.annotationId);
}
+inline bool operator<(const TextEdit &L, const TextEdit &R) {
+ return std::tie(L.newText, L.range, L.annotationId) <
+ std::tie(R.newText, R.range, L.annotationId);
+}
bool fromJSON(const llvm::json::Value &, TextEdit &, llvm::json::Path);
llvm::json::Value toJSON(const TextEdit &);
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TextEdit &);
@@ -281,7 +285,7 @@ struct TextDocumentEdit {
/// The text document to change.
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
torshepherd wrote: Also pinging @kadircet who recently worked on Include Cleaner's similar batch fixes https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
https://github.com/torshepherd updated
https://github.com/llvm/llvm-project/pull/79867
>From 7cb9e7b604a16414f31309b59747abbd7d3c0025 Mon Sep 17 00:00:00 2001
From: Tor Shepherd
Date: Mon, 29 Jan 2024 11:44:25 -0500
Subject: [PATCH] [clangd] Add fix-all CodeActions
---
clang-tools-extra/clangd/Diagnostics.cpp | 72 ++-
clang-tools-extra/clangd/Protocol.h | 22 +-
.../clangd/unittests/DiagnosticsTests.cpp | 200 +-
3 files changed, 227 insertions(+), 67 deletions(-)
diff --git a/clang-tools-extra/clangd/Diagnostics.cpp
b/clang-tools-extra/clangd/Diagnostics.cpp
index 704e61b1e4dd7..edef233be0290 100644
--- a/clang-tools-extra/clangd/Diagnostics.cpp
+++ b/clang-tools-extra/clangd/Diagnostics.cpp
@@ -339,6 +339,72 @@ std::string noteMessage(const Diag &Main, const DiagBase
&Note,
return capitalize(std::move(Result));
}
+std::optional
+generateApplyAllFromOption(const llvm::StringRef Name,
+ llvm::ArrayRef AllDiagnostics) {
+ Fix ApplyAll;
+ for (auto *const Diag : AllDiagnostics) {
+for (const auto &Fix : Diag->Fixes)
+ ApplyAll.Edits.insert(ApplyAll.Edits.end(), Fix.Edits.begin(),
+Fix.Edits.end());
+ }
+ llvm::sort(ApplyAll.Edits);
+ ApplyAll.Edits.erase(
+ std::unique(ApplyAll.Edits.begin(), ApplyAll.Edits.end()),
+ ApplyAll.Edits.end());
+ // Skip diagnostic categories that don't have multiple fixes to apply
+ if (ApplyAll.Edits.size() < 2U) {
+return std::nullopt;
+ }
+ ApplyAll.Message = llvm::formatv("apply all '{0}' fixes", Name);
+ return ApplyAll;
+}
+
+std::optional
+generateApplyAllFixesOption(llvm::ArrayRef AllDiagnostics) {
+ Fix ApplyAll;
+ for (auto const &Diag : AllDiagnostics) {
+for (const auto &Fix : Diag.Fixes)
+ ApplyAll.Edits.insert(ApplyAll.Edits.end(), Fix.Edits.begin(),
+Fix.Edits.end());
+ }
+ llvm::sort(ApplyAll.Edits);
+ ApplyAll.Edits.erase(
+ std::unique(ApplyAll.Edits.begin(), ApplyAll.Edits.end()),
+ ApplyAll.Edits.end());
+ if (ApplyAll.Edits.size() < 2U) {
+return std::nullopt;
+ }
+ ApplyAll.Message = "apply all clangd fixes";
+ return ApplyAll;
+}
+
+void appendApplyAlls(std::vector &AllDiagnostics) {
+ llvm::DenseMap> CategorizedFixes;
+
+ for (auto &Diag : AllDiagnostics) {
+// Keep track of fixable diagnostics for generating "apply all fixes"
+if (!Diag.Fixes.empty()) {
+ if (auto [It, DidEmplace] = CategorizedFixes.try_emplace(
+ Diag.Name, std::vector{&Diag});
+ !DidEmplace)
+It->second.emplace_back(&Diag);
+}
+ }
+
+ auto FixAllClangd = generateApplyAllFixesOption(AllDiagnostics);
+ for (const auto &[Name, DiagsForThisCategory] : CategorizedFixes) {
+auto FixAllForCategory =
+generateApplyAllFromOption(Name, DiagsForThisCategory);
+for (auto *Diag : DiagsForThisCategory) {
+ if (DiagsForThisCategory.size() >= 2U && FixAllForCategory.has_value())
+Diag->Fixes.emplace_back(*FixAllForCategory);
+ if (CategorizedFixes.size() >= 2U && FixAllClangd.has_value())
+Diag->Fixes.emplace_back(*FixAllClangd);
+}
+ }
+}
+
void setTags(clangd::Diag &D) {
static const auto *DeprecatedDiags = new llvm::DenseSet{
diag::warn_access_decl_deprecated,
@@ -575,7 +641,8 @@ std::vector StoreDiags::take(const
clang::tidy::ClangTidyContext *Tidy) {
// Do not forget to emit a pending diagnostic if there is one.
flushLastDiag();
- // Fill in name/source now that we have all the context needed to map them.
+ // Fill in name/source now that we have all the context needed to map
+ // them.
for (auto &Diag : Output) {
if (const char *ClangDiag = getDiagnosticCode(Diag.ID)) {
// Warnings controlled by -Wfoo are better recognized by that name.
@@ -619,6 +686,9 @@ std::vector StoreDiags::take(const
clang::tidy::ClangTidyContext *Tidy) {
llvm::erase_if(Output, [&](const Diag &D) {
return !SeenDiags.emplace(D.Range, D.Message).second;
});
+
+ appendApplyAlls(Output);
+
return std::move(Output);
}
diff --git a/clang-tools-extra/clangd/Protocol.h
b/clang-tools-extra/clangd/Protocol.h
index e88c804692568..d1e6cf35f9d9d 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -257,6 +257,10 @@ inline bool operator==(const TextEdit &L, const TextEdit
&R) {
return std::tie(L.newText, L.range, L.annotationId) ==
std::tie(R.newText, R.range, L.annotationId);
}
+inline bool operator<(const TextEdit &L, const TextEdit &R) {
+ return std::tie(L.newText, L.range, L.annotationId) <
+ std::tie(R.newText, R.range, L.annotationId);
+}
bool fromJSON(const llvm::json::Value &, TextEdit &, llvm::json::Path);
llvm::json::Value toJSON(const TextEdit &);
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TextEdit &);
@@ -281,7 +285,7 @@ struct TextDocumentEdit {
/// The text document to change.
Ver
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
https://github.com/torshepherd updated
https://github.com/llvm/llvm-project/pull/79867
>From 0ed7667a5b48064a492f7f125a4002912a35fec7 Mon Sep 17 00:00:00 2001
From: Tor Shepherd
Date: Mon, 29 Jan 2024 11:44:25 -0500
Subject: [PATCH 1/2] initial cut
---
clang-tools-extra/clangd/Diagnostics.cpp | 74 ++-
clang-tools-extra/clangd/Protocol.h | 22 +-
.../clangd/unittests/DiagnosticsTests.cpp | 200 +-
3 files changed, 229 insertions(+), 67 deletions(-)
diff --git a/clang-tools-extra/clangd/Diagnostics.cpp
b/clang-tools-extra/clangd/Diagnostics.cpp
index 704e61b1e4dd7..cf6d87cd45181 100644
--- a/clang-tools-extra/clangd/Diagnostics.cpp
+++ b/clang-tools-extra/clangd/Diagnostics.cpp
@@ -339,6 +339,74 @@ std::string noteMessage(const Diag &Main, const DiagBase
&Note,
return capitalize(std::move(Result));
}
+std::optional
+generateApplyAllFromOption(const llvm::StringRef Name,
+ llvm::ArrayRef AllDiagnostics) {
+ Fix ApplyAll;
+ for (auto *const Diag : AllDiagnostics) {
+for (const auto &Fix : Diag->Fixes)
+ ApplyAll.Edits.insert(ApplyAll.Edits.end(), Fix.Edits.begin(),
+Fix.Edits.end());
+ }
+ llvm::sort(ApplyAll.Edits);
+ ApplyAll.Edits.erase(
+ std::unique(ApplyAll.Edits.begin(), ApplyAll.Edits.end()),
+ ApplyAll.Edits.end());
+ // Skip diagnostic categories that don't have multiple fixes to apply
+ if (ApplyAll.Edits.size() < 2U) {
+return std::nullopt;
+ }
+ ApplyAll.Message = llvm::formatv("apply all '{0}' fixes", Name);
+ return ApplyAll;
+}
+
+std::optional
+generateApplyAllFixesOption(llvm::ArrayRef AllDiagnostics) {
+ Fix ApplyAll;
+ for (auto const &Diag : AllDiagnostics) {
+for (const auto &Fix : Diag.Fixes)
+ ApplyAll.Edits.insert(ApplyAll.Edits.end(), Fix.Edits.begin(),
+Fix.Edits.end());
+ }
+ llvm::sort(ApplyAll.Edits);
+ ApplyAll.Edits.erase(
+ std::unique(ApplyAll.Edits.begin(), ApplyAll.Edits.end()),
+ ApplyAll.Edits.end());
+ if (ApplyAll.Edits.size() < 2U) {
+return std::nullopt;
+ }
+ ApplyAll.Message = "apply all clangd fixes";
+ return ApplyAll;
+}
+
+void appendApplyAlls(std::vector &AllDiagnostics) {
+ llvm::DenseMap> CategorizedFixes;
+ size_t FixableOverall = 0U;
+
+ for (auto &Diag : AllDiagnostics) {
+// Keep track of fixable diagnostics for generating "apply all fixes"
+if (!Diag.Fixes.empty()) {
+ ++FixableOverall;
+ if (auto [It, DidEmplace] = CategorizedFixes.try_emplace(
+ Diag.Name, std::vector{&Diag});
+ !DidEmplace)
+It->second.emplace_back(&Diag);
+}
+ }
+
+ auto FixAllClangd = generateApplyAllFixesOption(AllDiagnostics);
+ for (const auto &[Name, DiagsForThisCategory] : CategorizedFixes) {
+auto FixAllForCategory =
+generateApplyAllFromOption(Name, DiagsForThisCategory);
+for (auto *Diag : DiagsForThisCategory) {
+ if (DiagsForThisCategory.size() >= 2U && FixAllForCategory.has_value())
+Diag->Fixes.emplace_back(*FixAllForCategory);
+ if (FixableOverall >= 2U && FixAllClangd.has_value())
+Diag->Fixes.emplace_back(*FixAllClangd);
+}
+ }
+}
+
void setTags(clangd::Diag &D) {
static const auto *DeprecatedDiags = new llvm::DenseSet{
diag::warn_access_decl_deprecated,
@@ -575,7 +643,8 @@ std::vector StoreDiags::take(const
clang::tidy::ClangTidyContext *Tidy) {
// Do not forget to emit a pending diagnostic if there is one.
flushLastDiag();
- // Fill in name/source now that we have all the context needed to map them.
+ // Fill in name/source now that we have all the context needed to map
+ // them.
for (auto &Diag : Output) {
if (const char *ClangDiag = getDiagnosticCode(Diag.ID)) {
// Warnings controlled by -Wfoo are better recognized by that name.
@@ -619,6 +688,9 @@ std::vector StoreDiags::take(const
clang::tidy::ClangTidyContext *Tidy) {
llvm::erase_if(Output, [&](const Diag &D) {
return !SeenDiags.emplace(D.Range, D.Message).second;
});
+
+ appendApplyAlls(Output);
+
return std::move(Output);
}
diff --git a/clang-tools-extra/clangd/Protocol.h
b/clang-tools-extra/clangd/Protocol.h
index e88c804692568..d1e6cf35f9d9d 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -257,6 +257,10 @@ inline bool operator==(const TextEdit &L, const TextEdit
&R) {
return std::tie(L.newText, L.range, L.annotationId) ==
std::tie(R.newText, R.range, L.annotationId);
}
+inline bool operator<(const TextEdit &L, const TextEdit &R) {
+ return std::tie(L.newText, L.range, L.annotationId) <
+ std::tie(R.newText, R.range, L.annotationId);
+}
bool fromJSON(const llvm::json::Value &, TextEdit &, llvm::json::Path);
llvm::json::Value toJSON(const TextEdit &);
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TextEdit &);
@@ -281,7 +285,7 @@ struct TextDocumentEdit {
/// The t
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
https://github.com/torshepherd edited https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
https://github.com/torshepherd edited https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
torshepherd wrote: Hmm I noticed another mini-issue. Ideally we shouldn't have a 'fix all clangd' quick fix if all of the issues come from the same source, only the 'fix all _' option https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
torshepherd wrote: Also, I noticed that the conversion function `toCodeAction(const Fix& F, ...)` specifically hardcodes all Fix objects to `QUICKFIX_KIND`. When we do https://github.com/clangd/clangd/issues/1446, it would be good to add a string for `SOURCE_FIX_ALL_KIND` and set these fix-alls to that `kind` https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
torshepherd wrote: Another thing that would be nice to solve in a follow-up (applies to unused includes as well btw) is to clean up the list of fixes when someone selects all and presses quick fix:  Not sure if this is possible server-side though https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
llvmbot wrote:
@llvm/pr-subscribers-clangd
@llvm/pr-subscribers-clang-tools-extra
Author: Tor Shepherd (torshepherd)
Changes
This PR adds the following features
1. If there are at least 2 distinct fixable diagnostics stemming from either
`clang` or `clang-tidy` overall and their edits are distinct as well, we add a
Fix to each one that applies all edits.
2. If there are at least 2 distinct fixable diagnostics stemming from the same
code (either clang error source or clang-tidy check name) and their edits are
distinct as well, we add a Fix to each one that applies all edits from that
category.
For example,

This addresses https://github.com/clangd/clangd/issues/830 (the server-side
approach). In a follow-up, I plan to address
https://github.com/clangd/clangd/issues/1446 and listen for client-side fix all
commands to do the same effect as 'apply all clangd fixes'. That only leaves
https://github.com/clangd/clangd/issues/1536 as the last open thread on
auto-fixes, and I think we can close that one as "won't do unless we get more
interest".
---
Patch is 23.48 KiB, truncated to 20.00 KiB below, full version:
https://github.com/llvm/llvm-project/pull/79867.diff
3 Files Affected:
- (modified) clang-tools-extra/clangd/Diagnostics.cpp (+73-1)
- (modified) clang-tools-extra/clangd/Protocol.h (+13-9)
- (modified) clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp (+143-57)
``diff
diff --git a/clang-tools-extra/clangd/Diagnostics.cpp
b/clang-tools-extra/clangd/Diagnostics.cpp
index 704e61b1e4dd792..cf6d87cd4518145 100644
--- a/clang-tools-extra/clangd/Diagnostics.cpp
+++ b/clang-tools-extra/clangd/Diagnostics.cpp
@@ -339,6 +339,74 @@ std::string noteMessage(const Diag &Main, const DiagBase
&Note,
return capitalize(std::move(Result));
}
+std::optional
+generateApplyAllFromOption(const llvm::StringRef Name,
+ llvm::ArrayRef AllDiagnostics) {
+ Fix ApplyAll;
+ for (auto *const Diag : AllDiagnostics) {
+for (const auto &Fix : Diag->Fixes)
+ ApplyAll.Edits.insert(ApplyAll.Edits.end(), Fix.Edits.begin(),
+Fix.Edits.end());
+ }
+ llvm::sort(ApplyAll.Edits);
+ ApplyAll.Edits.erase(
+ std::unique(ApplyAll.Edits.begin(), ApplyAll.Edits.end()),
+ ApplyAll.Edits.end());
+ // Skip diagnostic categories that don't have multiple fixes to apply
+ if (ApplyAll.Edits.size() < 2U) {
+return std::nullopt;
+ }
+ ApplyAll.Message = llvm::formatv("apply all '{0}' fixes", Name);
+ return ApplyAll;
+}
+
+std::optional
+generateApplyAllFixesOption(llvm::ArrayRef AllDiagnostics) {
+ Fix ApplyAll;
+ for (auto const &Diag : AllDiagnostics) {
+for (const auto &Fix : Diag.Fixes)
+ ApplyAll.Edits.insert(ApplyAll.Edits.end(), Fix.Edits.begin(),
+Fix.Edits.end());
+ }
+ llvm::sort(ApplyAll.Edits);
+ ApplyAll.Edits.erase(
+ std::unique(ApplyAll.Edits.begin(), ApplyAll.Edits.end()),
+ ApplyAll.Edits.end());
+ if (ApplyAll.Edits.size() < 2U) {
+return std::nullopt;
+ }
+ ApplyAll.Message = "apply all clangd fixes";
+ return ApplyAll;
+}
+
+void appendApplyAlls(std::vector &AllDiagnostics) {
+ llvm::DenseMap> CategorizedFixes;
+ size_t FixableOverall = 0U;
+
+ for (auto &Diag : AllDiagnostics) {
+// Keep track of fixable diagnostics for generating "apply all fixes"
+if (!Diag.Fixes.empty()) {
+ ++FixableOverall;
+ if (auto [It, DidEmplace] = CategorizedFixes.try_emplace(
+ Diag.Name, std::vector{&Diag});
+ !DidEmplace)
+It->second.emplace_back(&Diag);
+}
+ }
+
+ auto FixAllClangd = generateApplyAllFixesOption(AllDiagnostics);
+ for (const auto &[Name, DiagsForThisCategory] : CategorizedFixes) {
+auto FixAllForCategory =
+generateApplyAllFromOption(Name, DiagsForThisCategory);
+for (auto *Diag : DiagsForThisCategory) {
+ if (DiagsForThisCategory.size() >= 2U && FixAllForCategory.has_value())
+Diag->Fixes.emplace_back(*FixAllForCategory);
+ if (FixableOverall >= 2U && FixAllClangd.has_value())
+Diag->Fixes.emplace_back(*FixAllClangd);
+}
+ }
+}
+
void setTags(clangd::Diag &D) {
static const auto *DeprecatedDiags = new llvm::DenseSet{
diag::warn_access_decl_deprecated,
@@ -575,7 +643,8 @@ std::vector StoreDiags::take(const
clang::tidy::ClangTidyContext *Tidy) {
// Do not forget to emit a pending diagnostic if there is one.
flushLastDiag();
- // Fill in name/source now that we have all the context needed to map them.
+ // Fill in name/source now that we have all the context needed to map
+ // them.
for (auto &Diag : Output) {
if (const char *ClangDiag = getDiagnosticCode(Diag.ID)) {
// Warnings controlled by -Wfoo are better recognized by that name.
@@ -619,6 +688,9 @@ std::vector StoreDiags::take(const
clang::tidy::Clang
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
github-actions[bot] wrote: Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using `@` followed by their GitHub username. If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment โPingโ. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the [LLVM GitHub User Guide](https://llvm.org/docs/GitHub.html). You can also ask questions in a comment on this PR, on the [LLVM Discord](https://discord.com/invite/xS7Z362) or on the [forums](https://discourse.llvm.org/). https://github.com/llvm/llvm-project/pull/79867 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)
https://github.com/torshepherd created
https://github.com/llvm/llvm-project/pull/79867
This PR adds the following features
1. If there are at least 2 distinct fixable diagnostics stemming from either
`clang` or `clang-tidy` overall and their edits are distinct as well, we add a
Fix to each one that applies all edits.
2. If there are at least 2 distinct fixable diagnostics stemming from the same
code (either clang error source or clang-tidy check name) and their edits are
distinct as well, we add a Fix to each one that applies all edits from that
category.
For example,

This addresses https://github.com/clangd/clangd/issues/830 (the server-side
approach). In a follow-up, I plan to address
https://github.com/clangd/clangd/issues/1446 and listen for client-side fix all
commands to do the same effect as 'apply all clangd fixes'. That only leaves
https://github.com/clangd/clangd/issues/1536 as the last open thread on
auto-fixes, and I think we can close that one as "won't do unless we get more
interest".
>From 0ed7667a5b48064a492f7f125a4002912a35fec7 Mon Sep 17 00:00:00 2001
From: Tor Shepherd
Date: Mon, 29 Jan 2024 11:44:25 -0500
Subject: [PATCH] initial cut
---
clang-tools-extra/clangd/Diagnostics.cpp | 74 ++-
clang-tools-extra/clangd/Protocol.h | 22 +-
.../clangd/unittests/DiagnosticsTests.cpp | 200 +-
3 files changed, 229 insertions(+), 67 deletions(-)
diff --git a/clang-tools-extra/clangd/Diagnostics.cpp
b/clang-tools-extra/clangd/Diagnostics.cpp
index 704e61b1e4dd792..cf6d87cd4518145 100644
--- a/clang-tools-extra/clangd/Diagnostics.cpp
+++ b/clang-tools-extra/clangd/Diagnostics.cpp
@@ -339,6 +339,74 @@ std::string noteMessage(const Diag &Main, const DiagBase
&Note,
return capitalize(std::move(Result));
}
+std::optional
+generateApplyAllFromOption(const llvm::StringRef Name,
+ llvm::ArrayRef AllDiagnostics) {
+ Fix ApplyAll;
+ for (auto *const Diag : AllDiagnostics) {
+for (const auto &Fix : Diag->Fixes)
+ ApplyAll.Edits.insert(ApplyAll.Edits.end(), Fix.Edits.begin(),
+Fix.Edits.end());
+ }
+ llvm::sort(ApplyAll.Edits);
+ ApplyAll.Edits.erase(
+ std::unique(ApplyAll.Edits.begin(), ApplyAll.Edits.end()),
+ ApplyAll.Edits.end());
+ // Skip diagnostic categories that don't have multiple fixes to apply
+ if (ApplyAll.Edits.size() < 2U) {
+return std::nullopt;
+ }
+ ApplyAll.Message = llvm::formatv("apply all '{0}' fixes", Name);
+ return ApplyAll;
+}
+
+std::optional
+generateApplyAllFixesOption(llvm::ArrayRef AllDiagnostics) {
+ Fix ApplyAll;
+ for (auto const &Diag : AllDiagnostics) {
+for (const auto &Fix : Diag.Fixes)
+ ApplyAll.Edits.insert(ApplyAll.Edits.end(), Fix.Edits.begin(),
+Fix.Edits.end());
+ }
+ llvm::sort(ApplyAll.Edits);
+ ApplyAll.Edits.erase(
+ std::unique(ApplyAll.Edits.begin(), ApplyAll.Edits.end()),
+ ApplyAll.Edits.end());
+ if (ApplyAll.Edits.size() < 2U) {
+return std::nullopt;
+ }
+ ApplyAll.Message = "apply all clangd fixes";
+ return ApplyAll;
+}
+
+void appendApplyAlls(std::vector &AllDiagnostics) {
+ llvm::DenseMap> CategorizedFixes;
+ size_t FixableOverall = 0U;
+
+ for (auto &Diag : AllDiagnostics) {
+// Keep track of fixable diagnostics for generating "apply all fixes"
+if (!Diag.Fixes.empty()) {
+ ++FixableOverall;
+ if (auto [It, DidEmplace] = CategorizedFixes.try_emplace(
+ Diag.Name, std::vector{&Diag});
+ !DidEmplace)
+It->second.emplace_back(&Diag);
+}
+ }
+
+ auto FixAllClangd = generateApplyAllFixesOption(AllDiagnostics);
+ for (const auto &[Name, DiagsForThisCategory] : CategorizedFixes) {
+auto FixAllForCategory =
+generateApplyAllFromOption(Name, DiagsForThisCategory);
+for (auto *Diag : DiagsForThisCategory) {
+ if (DiagsForThisCategory.size() >= 2U && FixAllForCategory.has_value())
+Diag->Fixes.emplace_back(*FixAllForCategory);
+ if (FixableOverall >= 2U && FixAllClangd.has_value())
+Diag->Fixes.emplace_back(*FixAllClangd);
+}
+ }
+}
+
void setTags(clangd::Diag &D) {
static const auto *DeprecatedDiags = new llvm::DenseSet{
diag::warn_access_decl_deprecated,
@@ -575,7 +643,8 @@ std::vector StoreDiags::take(const
clang::tidy::ClangTidyContext *Tidy) {
// Do not forget to emit a pending diagnostic if there is one.
flushLastDiag();
- // Fill in name/source now that we have all the context needed to map them.
+ // Fill in name/source now that we have all the context needed to map
+ // them.
for (auto &Diag : Output) {
if (const char *ClangDiag = getDiagnosticCode(Diag.ID)) {
// Warnings controlled by -Wfoo are better recognized by that name.
@@ -619,6 +688,9 @@ std::vector StoreDiags::take(const
clang::tidy::Clan
