[clang-tools-extra] [clangd] Add 'apply all clangd fixes' and 'apply all '_' fixes' QuickFixes (PR #79867)

2026-02-26 Thread Tor Shepherd via cfe-commits

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)

2026-02-25 Thread Nathan Ridge via cfe-commits

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)

2025-10-30 Thread Christian Kandeler via cfe-commits

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)

2025-05-20 Thread Tor Shepherd via cfe-commits

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)

2025-05-20 Thread Nathan Ridge via cfe-commits

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)

2025-05-20 Thread via cfe-commits

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)

2024-06-14 Thread Tor Shepherd via cfe-commits

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)

2024-06-14 Thread Tor Shepherd via cfe-commits

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)

2024-05-04 Thread Tor Shepherd via cfe-commits

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)

2024-05-04 Thread Nathan Ridge via cfe-commits

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)

2024-05-04 Thread Tor Shepherd via cfe-commits

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)

2024-02-27 Thread Tor Shepherd via cfe-commits

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)

2024-02-13 Thread Tor Shepherd via cfe-commits

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)

2024-02-11 Thread Tor Shepherd via cfe-commits

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)

2024-02-04 Thread Tor Shepherd via cfe-commits

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)

2024-02-01 Thread Tor Shepherd via cfe-commits

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)

2024-01-30 Thread Tor Shepherd via cfe-commits

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)

2024-01-30 Thread Tor Shepherd via cfe-commits

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)

2024-01-30 Thread Tor Shepherd via cfe-commits

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)

2024-01-30 Thread Tor Shepherd via cfe-commits

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)

2024-01-29 Thread Tor Shepherd via cfe-commits

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)

2024-01-29 Thread Tor Shepherd via cfe-commits

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:

![image](https://github.com/llvm/llvm-project/assets/49597791/974d1426-0b8d-41f7-8532-0a5b454e3ac9)

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)

2024-01-29 Thread via cfe-commits

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,

![image](https://github.com/llvm/llvm-project/assets/49597791/c419cf60-b689-4e87-8453-fecafba2d81b)

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)

2024-01-29 Thread via cfe-commits

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)

2024-01-29 Thread Tor Shepherd via cfe-commits

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,

![image](https://github.com/llvm/llvm-project/assets/49597791/c419cf60-b689-4e87-8453-fecafba2d81b)

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