[clang-tools-extra] [clangd] Add inlay hints for default function arguments and implicit lambda captures (PR #95712)

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

https://github.com/torshepherd updated 
https://github.com/llvm/llvm-project/pull/95712

>From 07e5dcc71dc20c9d7482fc076cbeeb5e97a797d7 Mon Sep 17 00:00:00 2001
From: Tor Shepherd 
Date: Wed, 19 Jun 2024 09:55:34 -0400
Subject: [PATCH 1/2] after comments

---
 clang-tools-extra/clangd/Config.h |  2 +
 clang-tools-extra/clangd/ConfigCompile.cpp| 11 ++-
 clang-tools-extra/clangd/ConfigFragment.h |  5 ++
 clang-tools-extra/clangd/ConfigYAML.cpp   |  9 +-
 clang-tools-extra/clangd/InlayHints.cpp   | 87 ++-
 clang-tools-extra/clangd/Protocol.cpp |  6 ++
 clang-tools-extra/clangd/Protocol.h   | 15 
 .../clangd/unittests/InlayHintTests.cpp   | 57 
 8 files changed, 187 insertions(+), 5 deletions(-)

diff --git a/clang-tools-extra/clangd/Config.h 
b/clang-tools-extra/clangd/Config.h
index 41143b9ebc8d2..5100214244454 100644
--- a/clang-tools-extra/clangd/Config.h
+++ b/clang-tools-extra/clangd/Config.h
@@ -148,6 +148,8 @@ struct Config {
 bool DeducedTypes = true;
 bool Designators = true;
 bool BlockEnd = false;
+bool LambdaCaptures = false;
+bool DefaultArguments = false;
 // Limit the length of type names in inlay hints. (0 means no limit)
 uint32_t TypeNameLimit = 32;
   } InlayHints;
diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp 
b/clang-tools-extra/clangd/ConfigCompile.cpp
index f32f674443ffe..89486ce9fa034 100644
--- a/clang-tools-extra/clangd/ConfigCompile.cpp
+++ b/clang-tools-extra/clangd/ConfigCompile.cpp
@@ -43,7 +43,6 @@
 #include "llvm/Support/Regex.h"
 #include "llvm/Support/SMLoc.h"
 #include "llvm/Support/SourceMgr.h"
-#include 
 #include 
 #include 
 #include 
@@ -654,6 +653,16 @@ struct FragmentCompiler {
   Out.Apply.push_back([Value(**F.BlockEnd)](const Params &, Config ) {
 C.InlayHints.BlockEnd = Value;
   });
+if (F.LambdaCaptures)
+  Out.Apply.push_back(
+  [Value(**F.LambdaCaptures)](const Params &, Config ) {
+C.InlayHints.LambdaCaptures = Value;
+  });
+if (F.DefaultArguments)
+  Out.Apply.push_back(
+  [Value(**F.DefaultArguments)](const Params &, Config ) {
+C.InlayHints.DefaultArguments = Value;
+  });
 if (F.TypeNameLimit)
   Out.Apply.push_back(
   [Value(**F.TypeNameLimit)](const Params &, Config ) {
diff --git a/clang-tools-extra/clangd/ConfigFragment.h 
b/clang-tools-extra/clangd/ConfigFragment.h
index f3e51a9b6dbc4..ad30b43f34874 100644
--- a/clang-tools-extra/clangd/ConfigFragment.h
+++ b/clang-tools-extra/clangd/ConfigFragment.h
@@ -331,6 +331,11 @@ struct Fragment {
 std::optional> Designators;
 /// Show defined symbol names at the end of a definition block.
 std::optional> BlockEnd;
+/// Show names of captured variables by default capture groups in lambdas.
+std::optional> LambdaCaptures;
+/// Show parameter names and default values of default arguments after all
+/// of the explicit arguments.
+std::optional> DefaultArguments;
 /// Limit the length of type name hints. (0 means no limit)
 std::optional> TypeNameLimit;
   };
diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp 
b/clang-tools-extra/clangd/ConfigYAML.cpp
index 3e9b6a07d3b32..58e5f8bead101 100644
--- a/clang-tools-extra/clangd/ConfigYAML.cpp
+++ b/clang-tools-extra/clangd/ConfigYAML.cpp
@@ -14,7 +14,6 @@
 #include "llvm/Support/YAMLParser.h"
 #include 
 #include 
-#include 
 
 namespace clang {
 namespace clangd {
@@ -264,6 +263,14 @@ class Parser {
   if (auto Value = boolValue(N, "BlockEnd"))
 F.BlockEnd = *Value;
 });
+Dict.handle("LambdaCaptures", [&](Node ) {
+  if (auto Value = boolValue(N, "LambdaCaptures"))
+F.LambdaCaptures = *Value;
+});
+Dict.handle("DefaultArguments", [&](Node ) {
+  if (auto Value = boolValue(N, "DefaultArguments"))
+F.DefaultArguments = *Value;
+});
 Dict.handle("TypeNameLimit", [&](Node ) {
   if (auto Value = uint32Value(N, "TypeNameLimit"))
 F.TypeNameLimit = *Value;
diff --git a/clang-tools-extra/clangd/InlayHints.cpp 
b/clang-tools-extra/clangd/InlayHints.cpp
index cd4f1931b3ce1..2631c17409eab 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -11,27 +11,37 @@
 #include "Config.h"
 #include "HeuristicResolver.h"
 #include "ParsedAST.h"
+#include "Protocol.h"
 #include "SourceCode.h"
 #include "clang/AST/ASTDiagnostic.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/LambdaCapture.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/Builtins.h"
+#include "clang/Basic/Lambda.h"
 #include "clang/Basic/OperatorKinds.h"
+#include 

[clang-tools-extra] [clangd] Add inlay hints for default function arguments and implicit lambda captures (PR #95712)

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

torshepherd wrote:

Ok I took a stab at doing lambda captures as hover instead.

- Targeting the exact '=' or '&' is nontrivial since the capture list isn't in 
the AST, the lambda is just `LambdaExpr` with children for the parameters and 
the CompoundStmt body.
- `auto MyLambda = [...` already produces a nice function-like hover over the 
variable. I tested adding a "Captures:" bullet-list, it works well enough but 
isn't very noticeable.
- We could take the above HoverInfo over the variable containing the lambda and 
apply it to hovering the lambda itself as well?


It strikes me, if we could either add HoverInfo or styling to the variables 
within the body of the lambda themselves, that would be the most readable 
representation probably.

For instance:

- Can we add an inlay hint like `[&] ` before actual variables in the body of 
the lambda?
- Can we apply a semantic token to style lambda captures differently? Maybe as 
`property` since they are similar conceptually to struct members?
- Can we use the `mutable`-ness of the lambda to color tokens as const if they 
are captured by copy and the lambda is not mutable?

Overall I think capture hints should be removed from this PR so we can get 
default args in in the meantime.

https://github.com/llvm/llvm-project/pull/95712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Add inlay hints for default function arguments and implicit lambda captures (PR #95712)

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

https://github.com/torshepherd updated 
https://github.com/llvm/llvm-project/pull/95712

>From 3830a49faf5e20781f864d459f5478221740ac3d Mon Sep 17 00:00:00 2001
From: Tor Shepherd 
Date: Wed, 19 Jun 2024 09:55:34 -0400
Subject: [PATCH 1/2] after comments

---
 clang-tools-extra/clangd/Config.h |  2 +
 clang-tools-extra/clangd/ConfigCompile.cpp| 11 ++-
 clang-tools-extra/clangd/ConfigFragment.h |  5 ++
 clang-tools-extra/clangd/ConfigYAML.cpp   |  9 +-
 clang-tools-extra/clangd/InlayHints.cpp   | 87 ++-
 clang-tools-extra/clangd/Protocol.cpp |  6 ++
 clang-tools-extra/clangd/Protocol.h   | 15 
 .../clangd/unittests/InlayHintTests.cpp   | 57 
 8 files changed, 187 insertions(+), 5 deletions(-)

diff --git a/clang-tools-extra/clangd/Config.h 
b/clang-tools-extra/clangd/Config.h
index 41143b9ebc8d2..5100214244454 100644
--- a/clang-tools-extra/clangd/Config.h
+++ b/clang-tools-extra/clangd/Config.h
@@ -148,6 +148,8 @@ struct Config {
 bool DeducedTypes = true;
 bool Designators = true;
 bool BlockEnd = false;
+bool LambdaCaptures = false;
+bool DefaultArguments = false;
 // Limit the length of type names in inlay hints. (0 means no limit)
 uint32_t TypeNameLimit = 32;
   } InlayHints;
diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp 
b/clang-tools-extra/clangd/ConfigCompile.cpp
index f32f674443ffe..89486ce9fa034 100644
--- a/clang-tools-extra/clangd/ConfigCompile.cpp
+++ b/clang-tools-extra/clangd/ConfigCompile.cpp
@@ -43,7 +43,6 @@
 #include "llvm/Support/Regex.h"
 #include "llvm/Support/SMLoc.h"
 #include "llvm/Support/SourceMgr.h"
-#include 
 #include 
 #include 
 #include 
@@ -654,6 +653,16 @@ struct FragmentCompiler {
   Out.Apply.push_back([Value(**F.BlockEnd)](const Params &, Config ) {
 C.InlayHints.BlockEnd = Value;
   });
+if (F.LambdaCaptures)
+  Out.Apply.push_back(
+  [Value(**F.LambdaCaptures)](const Params &, Config ) {
+C.InlayHints.LambdaCaptures = Value;
+  });
+if (F.DefaultArguments)
+  Out.Apply.push_back(
+  [Value(**F.DefaultArguments)](const Params &, Config ) {
+C.InlayHints.DefaultArguments = Value;
+  });
 if (F.TypeNameLimit)
   Out.Apply.push_back(
   [Value(**F.TypeNameLimit)](const Params &, Config ) {
diff --git a/clang-tools-extra/clangd/ConfigFragment.h 
b/clang-tools-extra/clangd/ConfigFragment.h
index f3e51a9b6dbc4..ad30b43f34874 100644
--- a/clang-tools-extra/clangd/ConfigFragment.h
+++ b/clang-tools-extra/clangd/ConfigFragment.h
@@ -331,6 +331,11 @@ struct Fragment {
 std::optional> Designators;
 /// Show defined symbol names at the end of a definition block.
 std::optional> BlockEnd;
+/// Show names of captured variables by default capture groups in lambdas.
+std::optional> LambdaCaptures;
+/// Show parameter names and default values of default arguments after all
+/// of the explicit arguments.
+std::optional> DefaultArguments;
 /// Limit the length of type name hints. (0 means no limit)
 std::optional> TypeNameLimit;
   };
diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp 
b/clang-tools-extra/clangd/ConfigYAML.cpp
index 3e9b6a07d3b32..58e5f8bead101 100644
--- a/clang-tools-extra/clangd/ConfigYAML.cpp
+++ b/clang-tools-extra/clangd/ConfigYAML.cpp
@@ -14,7 +14,6 @@
 #include "llvm/Support/YAMLParser.h"
 #include 
 #include 
-#include 
 
 namespace clang {
 namespace clangd {
@@ -264,6 +263,14 @@ class Parser {
   if (auto Value = boolValue(N, "BlockEnd"))
 F.BlockEnd = *Value;
 });
+Dict.handle("LambdaCaptures", [&](Node ) {
+  if (auto Value = boolValue(N, "LambdaCaptures"))
+F.LambdaCaptures = *Value;
+});
+Dict.handle("DefaultArguments", [&](Node ) {
+  if (auto Value = boolValue(N, "DefaultArguments"))
+F.DefaultArguments = *Value;
+});
 Dict.handle("TypeNameLimit", [&](Node ) {
   if (auto Value = uint32Value(N, "TypeNameLimit"))
 F.TypeNameLimit = *Value;
diff --git a/clang-tools-extra/clangd/InlayHints.cpp 
b/clang-tools-extra/clangd/InlayHints.cpp
index cd4f1931b3ce1..2631c17409eab 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -11,27 +11,37 @@
 #include "Config.h"
 #include "HeuristicResolver.h"
 #include "ParsedAST.h"
+#include "Protocol.h"
 #include "SourceCode.h"
 #include "clang/AST/ASTDiagnostic.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/LambdaCapture.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/Builtins.h"
+#include "clang/Basic/Lambda.h"
 #include "clang/Basic/OperatorKinds.h"
+#include 

[clang-tools-extra] [clangd] Add inlay hints for default function arguments and implicit lambda captures (PR #95712)

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

https://github.com/torshepherd updated 
https://github.com/llvm/llvm-project/pull/95712

>From 3830a49faf5e20781f864d459f5478221740ac3d Mon Sep 17 00:00:00 2001
From: Tor Shepherd 
Date: Wed, 19 Jun 2024 09:55:34 -0400
Subject: [PATCH 1/2] after comments

---
 clang-tools-extra/clangd/Config.h |  2 +
 clang-tools-extra/clangd/ConfigCompile.cpp| 11 ++-
 clang-tools-extra/clangd/ConfigFragment.h |  5 ++
 clang-tools-extra/clangd/ConfigYAML.cpp   |  9 +-
 clang-tools-extra/clangd/InlayHints.cpp   | 87 ++-
 clang-tools-extra/clangd/Protocol.cpp |  6 ++
 clang-tools-extra/clangd/Protocol.h   | 15 
 .../clangd/unittests/InlayHintTests.cpp   | 57 
 8 files changed, 187 insertions(+), 5 deletions(-)

diff --git a/clang-tools-extra/clangd/Config.h 
b/clang-tools-extra/clangd/Config.h
index 41143b9ebc8d2..5100214244454 100644
--- a/clang-tools-extra/clangd/Config.h
+++ b/clang-tools-extra/clangd/Config.h
@@ -148,6 +148,8 @@ struct Config {
 bool DeducedTypes = true;
 bool Designators = true;
 bool BlockEnd = false;
+bool LambdaCaptures = false;
+bool DefaultArguments = false;
 // Limit the length of type names in inlay hints. (0 means no limit)
 uint32_t TypeNameLimit = 32;
   } InlayHints;
diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp 
b/clang-tools-extra/clangd/ConfigCompile.cpp
index f32f674443ffe..89486ce9fa034 100644
--- a/clang-tools-extra/clangd/ConfigCompile.cpp
+++ b/clang-tools-extra/clangd/ConfigCompile.cpp
@@ -43,7 +43,6 @@
 #include "llvm/Support/Regex.h"
 #include "llvm/Support/SMLoc.h"
 #include "llvm/Support/SourceMgr.h"
-#include 
 #include 
 #include 
 #include 
@@ -654,6 +653,16 @@ struct FragmentCompiler {
   Out.Apply.push_back([Value(**F.BlockEnd)](const Params &, Config ) {
 C.InlayHints.BlockEnd = Value;
   });
+if (F.LambdaCaptures)
+  Out.Apply.push_back(
+  [Value(**F.LambdaCaptures)](const Params &, Config ) {
+C.InlayHints.LambdaCaptures = Value;
+  });
+if (F.DefaultArguments)
+  Out.Apply.push_back(
+  [Value(**F.DefaultArguments)](const Params &, Config ) {
+C.InlayHints.DefaultArguments = Value;
+  });
 if (F.TypeNameLimit)
   Out.Apply.push_back(
   [Value(**F.TypeNameLimit)](const Params &, Config ) {
diff --git a/clang-tools-extra/clangd/ConfigFragment.h 
b/clang-tools-extra/clangd/ConfigFragment.h
index f3e51a9b6dbc4..ad30b43f34874 100644
--- a/clang-tools-extra/clangd/ConfigFragment.h
+++ b/clang-tools-extra/clangd/ConfigFragment.h
@@ -331,6 +331,11 @@ struct Fragment {
 std::optional> Designators;
 /// Show defined symbol names at the end of a definition block.
 std::optional> BlockEnd;
+/// Show names of captured variables by default capture groups in lambdas.
+std::optional> LambdaCaptures;
+/// Show parameter names and default values of default arguments after all
+/// of the explicit arguments.
+std::optional> DefaultArguments;
 /// Limit the length of type name hints. (0 means no limit)
 std::optional> TypeNameLimit;
   };
diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp 
b/clang-tools-extra/clangd/ConfigYAML.cpp
index 3e9b6a07d3b32..58e5f8bead101 100644
--- a/clang-tools-extra/clangd/ConfigYAML.cpp
+++ b/clang-tools-extra/clangd/ConfigYAML.cpp
@@ -14,7 +14,6 @@
 #include "llvm/Support/YAMLParser.h"
 #include 
 #include 
-#include 
 
 namespace clang {
 namespace clangd {
@@ -264,6 +263,14 @@ class Parser {
   if (auto Value = boolValue(N, "BlockEnd"))
 F.BlockEnd = *Value;
 });
+Dict.handle("LambdaCaptures", [&](Node ) {
+  if (auto Value = boolValue(N, "LambdaCaptures"))
+F.LambdaCaptures = *Value;
+});
+Dict.handle("DefaultArguments", [&](Node ) {
+  if (auto Value = boolValue(N, "DefaultArguments"))
+F.DefaultArguments = *Value;
+});
 Dict.handle("TypeNameLimit", [&](Node ) {
   if (auto Value = uint32Value(N, "TypeNameLimit"))
 F.TypeNameLimit = *Value;
diff --git a/clang-tools-extra/clangd/InlayHints.cpp 
b/clang-tools-extra/clangd/InlayHints.cpp
index cd4f1931b3ce1..2631c17409eab 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -11,27 +11,37 @@
 #include "Config.h"
 #include "HeuristicResolver.h"
 #include "ParsedAST.h"
+#include "Protocol.h"
 #include "SourceCode.h"
 #include "clang/AST/ASTDiagnostic.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/LambdaCapture.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/Builtins.h"
+#include "clang/Basic/Lambda.h"
 #include "clang/Basic/OperatorKinds.h"
+#include 

[clang-tools-extra] [clangd] Add inlay hints for default function arguments and implicit lambda captures (PR #95712)

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


@@ -372,6 +382,34 @@ maybeDropCxxExplicitObjectParameters(ArrayRef Params) {
   return Params;
 }
 
+llvm::StringRef getLambdaCaptureName(const LambdaCapture ) {
+  if (Capture.capturesVariable())
+return Capture.getCapturedVar()->getName();
+  if (Capture.capturesThis())
+return llvm::StringRef{"this"};
+  return llvm::StringRef{"unknown"};
+}
+
+template 
+std::string joinAndTruncate(R &, size_t MaxLength,
+P &) {
+  std::string Out;
+  bool IsFirst = true;
+  for (auto & : Range) {
+if (!IsFirst)
+  Out.append(", ");
+else
+  IsFirst = false;

torshepherd wrote:

Changed this to use raw_string_ostream, lmk how it looks

https://github.com/llvm/llvm-project/pull/95712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Add inlay hints for default function arguments and implicit lambda captures (PR #95712)

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


@@ -372,6 +382,34 @@ maybeDropCxxExplicitObjectParameters(ArrayRef Params) {
   return Params;
 }
 
+llvm::StringRef getLambdaCaptureName(const LambdaCapture ) {
+  if (Capture.capturesVariable())
+return Capture.getCapturedVar()->getName();
+  if (Capture.capturesThis())
+return llvm::StringRef{"this"};
+  return llvm::StringRef{"unknown"};

torshepherd wrote:

Changed this to a switch case

https://github.com/llvm/llvm-project/pull/95712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Add inlay hints for default function arguments and implicit lambda captures (PR #95712)

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


@@ -15,9 +15,12 @@
 #include "support/Context.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include 
 #include 
+#include 

torshepherd wrote:

Yep, move

https://github.com/llvm/llvm-project/pull/95712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Add inlay hints for default function arguments and implicit lambda captures (PR #95712)

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

https://github.com/torshepherd updated 
https://github.com/llvm/llvm-project/pull/95712

>From 3830a49faf5e20781f864d459f5478221740ac3d Mon Sep 17 00:00:00 2001
From: Tor Shepherd 
Date: Wed, 19 Jun 2024 09:55:34 -0400
Subject: [PATCH 1/2] after comments

---
 clang-tools-extra/clangd/Config.h |  2 +
 clang-tools-extra/clangd/ConfigCompile.cpp| 11 ++-
 clang-tools-extra/clangd/ConfigFragment.h |  5 ++
 clang-tools-extra/clangd/ConfigYAML.cpp   |  9 +-
 clang-tools-extra/clangd/InlayHints.cpp   | 87 ++-
 clang-tools-extra/clangd/Protocol.cpp |  6 ++
 clang-tools-extra/clangd/Protocol.h   | 15 
 .../clangd/unittests/InlayHintTests.cpp   | 57 
 8 files changed, 187 insertions(+), 5 deletions(-)

diff --git a/clang-tools-extra/clangd/Config.h 
b/clang-tools-extra/clangd/Config.h
index 41143b9ebc8d2..5100214244454 100644
--- a/clang-tools-extra/clangd/Config.h
+++ b/clang-tools-extra/clangd/Config.h
@@ -148,6 +148,8 @@ struct Config {
 bool DeducedTypes = true;
 bool Designators = true;
 bool BlockEnd = false;
+bool LambdaCaptures = false;
+bool DefaultArguments = false;
 // Limit the length of type names in inlay hints. (0 means no limit)
 uint32_t TypeNameLimit = 32;
   } InlayHints;
diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp 
b/clang-tools-extra/clangd/ConfigCompile.cpp
index f32f674443ffe..89486ce9fa034 100644
--- a/clang-tools-extra/clangd/ConfigCompile.cpp
+++ b/clang-tools-extra/clangd/ConfigCompile.cpp
@@ -43,7 +43,6 @@
 #include "llvm/Support/Regex.h"
 #include "llvm/Support/SMLoc.h"
 #include "llvm/Support/SourceMgr.h"
-#include 
 #include 
 #include 
 #include 
@@ -654,6 +653,16 @@ struct FragmentCompiler {
   Out.Apply.push_back([Value(**F.BlockEnd)](const Params &, Config ) {
 C.InlayHints.BlockEnd = Value;
   });
+if (F.LambdaCaptures)
+  Out.Apply.push_back(
+  [Value(**F.LambdaCaptures)](const Params &, Config ) {
+C.InlayHints.LambdaCaptures = Value;
+  });
+if (F.DefaultArguments)
+  Out.Apply.push_back(
+  [Value(**F.DefaultArguments)](const Params &, Config ) {
+C.InlayHints.DefaultArguments = Value;
+  });
 if (F.TypeNameLimit)
   Out.Apply.push_back(
   [Value(**F.TypeNameLimit)](const Params &, Config ) {
diff --git a/clang-tools-extra/clangd/ConfigFragment.h 
b/clang-tools-extra/clangd/ConfigFragment.h
index f3e51a9b6dbc4..ad30b43f34874 100644
--- a/clang-tools-extra/clangd/ConfigFragment.h
+++ b/clang-tools-extra/clangd/ConfigFragment.h
@@ -331,6 +331,11 @@ struct Fragment {
 std::optional> Designators;
 /// Show defined symbol names at the end of a definition block.
 std::optional> BlockEnd;
+/// Show names of captured variables by default capture groups in lambdas.
+std::optional> LambdaCaptures;
+/// Show parameter names and default values of default arguments after all
+/// of the explicit arguments.
+std::optional> DefaultArguments;
 /// Limit the length of type name hints. (0 means no limit)
 std::optional> TypeNameLimit;
   };
diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp 
b/clang-tools-extra/clangd/ConfigYAML.cpp
index 3e9b6a07d3b32..58e5f8bead101 100644
--- a/clang-tools-extra/clangd/ConfigYAML.cpp
+++ b/clang-tools-extra/clangd/ConfigYAML.cpp
@@ -14,7 +14,6 @@
 #include "llvm/Support/YAMLParser.h"
 #include 
 #include 
-#include 
 
 namespace clang {
 namespace clangd {
@@ -264,6 +263,14 @@ class Parser {
   if (auto Value = boolValue(N, "BlockEnd"))
 F.BlockEnd = *Value;
 });
+Dict.handle("LambdaCaptures", [&](Node ) {
+  if (auto Value = boolValue(N, "LambdaCaptures"))
+F.LambdaCaptures = *Value;
+});
+Dict.handle("DefaultArguments", [&](Node ) {
+  if (auto Value = boolValue(N, "DefaultArguments"))
+F.DefaultArguments = *Value;
+});
 Dict.handle("TypeNameLimit", [&](Node ) {
   if (auto Value = uint32Value(N, "TypeNameLimit"))
 F.TypeNameLimit = *Value;
diff --git a/clang-tools-extra/clangd/InlayHints.cpp 
b/clang-tools-extra/clangd/InlayHints.cpp
index cd4f1931b3ce1..2631c17409eab 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -11,27 +11,37 @@
 #include "Config.h"
 #include "HeuristicResolver.h"
 #include "ParsedAST.h"
+#include "Protocol.h"
 #include "SourceCode.h"
 #include "clang/AST/ASTDiagnostic.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/LambdaCapture.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/Builtins.h"
+#include "clang/Basic/Lambda.h"
 #include "clang/Basic/OperatorKinds.h"
+#include 

[clang-tools-extra] [clangd] Add inlay hints for default function arguments and implicit lambda captures (PR #95712)

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

https://github.com/torshepherd updated 
https://github.com/llvm/llvm-project/pull/95712

>From 77bb21454b2110bed491689a4c323cff1c745207 Mon Sep 17 00:00:00 2001
From: Tor Shepherd 
Date: Sun, 16 Jun 2024 10:54:43 -0400
Subject: [PATCH] Add default arguments and lambda captures only

---
 clang-tools-extra/clangd/Config.h |  2 +
 clang-tools-extra/clangd/ConfigCompile.cpp| 19 ++--
 clang-tools-extra/clangd/ConfigFragment.h |  5 ++
 clang-tools-extra/clangd/ConfigYAML.cpp   |  9 +-
 clang-tools-extra/clangd/InlayHints.cpp   | 87 ++-
 clang-tools-extra/clangd/Protocol.cpp |  8 +-
 clang-tools-extra/clangd/Protocol.h   | 33 +--
 .../clangd/unittests/InlayHintTests.cpp   | 66 +-
 8 files changed, 206 insertions(+), 23 deletions(-)

diff --git a/clang-tools-extra/clangd/Config.h 
b/clang-tools-extra/clangd/Config.h
index 41143b9ebc8d2..5100214244454 100644
--- a/clang-tools-extra/clangd/Config.h
+++ b/clang-tools-extra/clangd/Config.h
@@ -148,6 +148,8 @@ struct Config {
 bool DeducedTypes = true;
 bool Designators = true;
 bool BlockEnd = false;
+bool LambdaCaptures = false;
+bool DefaultArguments = false;
 // Limit the length of type names in inlay hints. (0 means no limit)
 uint32_t TypeNameLimit = 32;
   } InlayHints;
diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp 
b/clang-tools-extra/clangd/ConfigCompile.cpp
index f32f674443ffe..dcdb71ea01bb2 100644
--- a/clang-tools-extra/clangd/ConfigCompile.cpp
+++ b/clang-tools-extra/clangd/ConfigCompile.cpp
@@ -43,7 +43,6 @@
 #include "llvm/Support/Regex.h"
 #include "llvm/Support/SMLoc.h"
 #include "llvm/Support/SourceMgr.h"
-#include 
 #include 
 #include 
 #include 
@@ -504,10 +503,10 @@ struct FragmentCompiler {
   auto Fast = isFastTidyCheck(Str);
   if (!Fast.has_value()) {
 diag(Warning,
- llvm::formatv(
- "Latency of clang-tidy check '{0}' is not known. "
- "It will only run if ClangTidy.FastCheckFilter is Loose or 
None",
- Str)
+ llvm::formatv("Latency of clang-tidy check '{0}' is not known. "
+   "It will only run if ClangTidy.FastCheckFilter is "
+   "Loose or None",
+   Str)
  .str(),
  Arg.Range);
   } else if (!*Fast) {
@@ -654,6 +653,16 @@ struct FragmentCompiler {
   Out.Apply.push_back([Value(**F.BlockEnd)](const Params &, Config ) {
 C.InlayHints.BlockEnd = Value;
   });
+if (F.LambdaCaptures)
+  Out.Apply.push_back(
+  [Value(**F.LambdaCaptures)](const Params &, Config ) {
+C.InlayHints.LambdaCaptures = Value;
+  });
+if (F.DefaultArguments)
+  Out.Apply.push_back(
+  [Value(**F.DefaultArguments)](const Params &, Config ) {
+C.InlayHints.DefaultArguments = Value;
+  });
 if (F.TypeNameLimit)
   Out.Apply.push_back(
   [Value(**F.TypeNameLimit)](const Params &, Config ) {
diff --git a/clang-tools-extra/clangd/ConfigFragment.h 
b/clang-tools-extra/clangd/ConfigFragment.h
index f3e51a9b6dbc4..ad30b43f34874 100644
--- a/clang-tools-extra/clangd/ConfigFragment.h
+++ b/clang-tools-extra/clangd/ConfigFragment.h
@@ -331,6 +331,11 @@ struct Fragment {
 std::optional> Designators;
 /// Show defined symbol names at the end of a definition block.
 std::optional> BlockEnd;
+/// Show names of captured variables by default capture groups in lambdas.
+std::optional> LambdaCaptures;
+/// Show parameter names and default values of default arguments after all
+/// of the explicit arguments.
+std::optional> DefaultArguments;
 /// Limit the length of type name hints. (0 means no limit)
 std::optional> TypeNameLimit;
   };
diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp 
b/clang-tools-extra/clangd/ConfigYAML.cpp
index 3e9b6a07d3b32..58e5f8bead101 100644
--- a/clang-tools-extra/clangd/ConfigYAML.cpp
+++ b/clang-tools-extra/clangd/ConfigYAML.cpp
@@ -14,7 +14,6 @@
 #include "llvm/Support/YAMLParser.h"
 #include 
 #include 
-#include 
 
 namespace clang {
 namespace clangd {
@@ -264,6 +263,14 @@ class Parser {
   if (auto Value = boolValue(N, "BlockEnd"))
 F.BlockEnd = *Value;
 });
+Dict.handle("LambdaCaptures", [&](Node ) {
+  if (auto Value = boolValue(N, "LambdaCaptures"))
+F.LambdaCaptures = *Value;
+});
+Dict.handle("DefaultArguments", [&](Node ) {
+  if (auto Value = boolValue(N, "DefaultArguments"))
+F.DefaultArguments = *Value;
+});
 Dict.handle("TypeNameLimit", [&](Node ) {
   if (auto Value = uint32Value(N, "TypeNameLimit"))
 F.TypeNameLimit = *Value;
diff --git a/clang-tools-extra/clangd/InlayHints.cpp 
b/clang-tools-extra/clangd/InlayHints.cpp
index cd4f1931b3ce1..2631c17409eab 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ 

[clang-tools-extra] [clangd] Add inlay hints for default function arguments and implicit lambda captures (PR #95712)

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


@@ -504,10 +503,10 @@ struct FragmentCompiler {
   auto Fast = isFastTidyCheck(Str);
   if (!Fast.has_value()) {
 diag(Warning,
- llvm::formatv(
- "Latency of clang-tidy check '{0}' is not known. "
- "It will only run if ClangTidy.FastCheckFilter is Loose or 
None",
- Str)
+ llvm::formatv("Latency of clang-tidy check '{0}' is not known. "
+   "It will only run if ClangTidy.FastCheckFilter is "
+   "Loose or None",
+   Str)

torshepherd wrote:

Sorry, I think this was the formatter messing with it

https://github.com/llvm/llvm-project/pull/95712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Add inlay hints for default function arguments and implicit lambda captures (PR #95712)

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


@@ -1568,7 +1626,7 @@ TEST(TypeHints, SubstTemplateParameterAliases) {
   )cpp";
 
   llvm::StringRef VectorIntPtr = R"cpp(
-vector array;
+vector $init[[array]];

torshepherd wrote:

Another artifact, sorry

https://github.com/llvm/llvm-project/pull/95712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Add inlay hints for default function arguments and implicit lambda captures (PR #95712)

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


@@ -1458,13 +1463,66 @@ TEST(TypeHints, DefaultTemplateArgs) {
 struct A {};
 A foo();
 auto $var[[var]] = foo();
-A bar[1];
+A baz;
+A bar[1]{};

torshepherd wrote:

My fault, an artifact from a different set of inlay hints I decided not to 
include (show implicit default constructions with '{}')

https://github.com/llvm/llvm-project/pull/95712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Add inlay hints for default function arguments and implicit lambda captures (PR #95712)

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


@@ -15,9 +15,12 @@
 #include "support/Context.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include 
 #include 
+#include 

torshepherd wrote:

I believe std::move - let me check if they're still used in the latest revision 
though

https://github.com/llvm/llvm-project/pull/95712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Add inlay hints for default function arguments and implicit lambda captures (PR #95712)

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

torshepherd wrote:

Aha, that's a great suggestion. I agree that beyond the trivial case of 2 
captured variables it gets a bit noisy. On-hover would be quite nice though!

To clarify, you mean hovering over the default capture '=' or '&' right?

I'm happy to remove the lambdas from this PR in favor of the hover approach. 
Thoughts on the default arguments?

https://github.com/llvm/llvm-project/pull/95712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Add inlay hints for default function arguments and implicit lambda captures (PR #95712)

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

https://github.com/torshepherd created 
https://github.com/llvm/llvm-project/pull/95712

Title. This PR adds support for showing implicit lambda captures:

![image](https://github.com/llvm/llvm-project/assets/49597791/d0150817-f71e-4971-8d8b-e25d2b1e8bf8)

and defaulted function arguments:

![image](https://github.com/llvm/llvm-project/assets/49597791/2a6b83a9-b918-4363-be67-d79ce244b067)

as inlay hints.

If the list of captures (or default args) is too long (based on TypeNameLimit), 
shows "..." instead:

![image](https://github.com/llvm/llvm-project/assets/49597791/9bd5ee8c-ed73-44f9-821f-f2b29e612432)

![image](https://github.com/llvm/llvm-project/assets/49597791/c48acd28-091a-4d7d-9344-01e12c622024)

Note: I wrote this before https://github.com/llvm/llvm-project/pull/85497 
landed. With this we should probably also follow up with go-to-definition on 
the lambda captures at least; I could see that being helpful

>From 741921857955983e1bce2ba828ec369568948688 Mon Sep 17 00:00:00 2001
From: Tor Shepherd 
Date: Sun, 16 Jun 2024 10:54:43 -0400
Subject: [PATCH] Add default arguments and lambda captures only

---
 clang-tools-extra/clangd/Config.h |  2 +
 clang-tools-extra/clangd/ConfigCompile.cpp| 19 ++--
 clang-tools-extra/clangd/ConfigFragment.h |  5 ++
 clang-tools-extra/clangd/ConfigYAML.cpp   |  9 +-
 clang-tools-extra/clangd/InlayHints.cpp   | 87 ++-
 clang-tools-extra/clangd/Protocol.cpp |  8 +-
 clang-tools-extra/clangd/Protocol.h   | 33 +--
 .../clangd/unittests/InlayHintTests.cpp   | 66 +-
 8 files changed, 206 insertions(+), 23 deletions(-)

diff --git a/clang-tools-extra/clangd/Config.h 
b/clang-tools-extra/clangd/Config.h
index 41143b9ebc8d2..5100214244454 100644
--- a/clang-tools-extra/clangd/Config.h
+++ b/clang-tools-extra/clangd/Config.h
@@ -148,6 +148,8 @@ struct Config {
 bool DeducedTypes = true;
 bool Designators = true;
 bool BlockEnd = false;
+bool LambdaCaptures = false;
+bool DefaultArguments = false;
 // Limit the length of type names in inlay hints. (0 means no limit)
 uint32_t TypeNameLimit = 32;
   } InlayHints;
diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp 
b/clang-tools-extra/clangd/ConfigCompile.cpp
index f32f674443ffe..dcdb71ea01bb2 100644
--- a/clang-tools-extra/clangd/ConfigCompile.cpp
+++ b/clang-tools-extra/clangd/ConfigCompile.cpp
@@ -43,7 +43,6 @@
 #include "llvm/Support/Regex.h"
 #include "llvm/Support/SMLoc.h"
 #include "llvm/Support/SourceMgr.h"
-#include 
 #include 
 #include 
 #include 
@@ -504,10 +503,10 @@ struct FragmentCompiler {
   auto Fast = isFastTidyCheck(Str);
   if (!Fast.has_value()) {
 diag(Warning,
- llvm::formatv(
- "Latency of clang-tidy check '{0}' is not known. "
- "It will only run if ClangTidy.FastCheckFilter is Loose or 
None",
- Str)
+ llvm::formatv("Latency of clang-tidy check '{0}' is not known. "
+   "It will only run if ClangTidy.FastCheckFilter is "
+   "Loose or None",
+   Str)
  .str(),
  Arg.Range);
   } else if (!*Fast) {
@@ -654,6 +653,16 @@ struct FragmentCompiler {
   Out.Apply.push_back([Value(**F.BlockEnd)](const Params &, Config ) {
 C.InlayHints.BlockEnd = Value;
   });
+if (F.LambdaCaptures)
+  Out.Apply.push_back(
+  [Value(**F.LambdaCaptures)](const Params &, Config ) {
+C.InlayHints.LambdaCaptures = Value;
+  });
+if (F.DefaultArguments)
+  Out.Apply.push_back(
+  [Value(**F.DefaultArguments)](const Params &, Config ) {
+C.InlayHints.DefaultArguments = Value;
+  });
 if (F.TypeNameLimit)
   Out.Apply.push_back(
   [Value(**F.TypeNameLimit)](const Params &, Config ) {
diff --git a/clang-tools-extra/clangd/ConfigFragment.h 
b/clang-tools-extra/clangd/ConfigFragment.h
index f3e51a9b6dbc4..ad30b43f34874 100644
--- a/clang-tools-extra/clangd/ConfigFragment.h
+++ b/clang-tools-extra/clangd/ConfigFragment.h
@@ -331,6 +331,11 @@ struct Fragment {
 std::optional> Designators;
 /// Show defined symbol names at the end of a definition block.
 std::optional> BlockEnd;
+/// Show names of captured variables by default capture groups in lambdas.
+std::optional> LambdaCaptures;
+/// Show parameter names and default values of default arguments after all
+/// of the explicit arguments.
+std::optional> DefaultArguments;
 /// Limit the length of type name hints. (0 means no limit)
 std::optional> TypeNameLimit;
   };
diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp 
b/clang-tools-extra/clangd/ConfigYAML.cpp
index 3e9b6a07d3b32..58e5f8bead101 100644
--- a/clang-tools-extra/clangd/ConfigYAML.cpp
+++ b/clang-tools-extra/clangd/ConfigYAML.cpp
@@ -14,7 +14,6 @@
 #include 

[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
cfe-commits@lists.llvm.org
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 , const DiagBase 
,
   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  : 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  : AllDiagnostics) {
+for (const auto  : 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 ) {
+  llvm::DenseMap> CategorizedFixes;
+
+  for (auto  : 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{});
+  !DidEmplace)
+It->second.emplace_back();
+}
+  }
+
+  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 ) {
   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  : 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 ) {
 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 , const TextEdit 
) {
   return std::tie(L.newText, L.range, L.annotationId) ==
  std::tie(R.newText, R.range, L.annotationId);
 }
+inline bool operator<(const TextEdit , const TextEdit ) {
+  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 <<(llvm::raw_ostream &, const TextEdit &);
@@ -281,7 +285,7 @@ struct TextDocumentEdit {
   /// The text document to change.
   VersionedTextDocumentIdentifier textDocument;
 
-   /// The edits to be 

[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
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [llvm] [clangd] Add CodeAction to swap operands to binary operators (PR #78999)

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

torshepherd wrote:

Ping

https://github.com/llvm/llvm-project/pull/78999
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
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
cfe-commits@lists.llvm.org
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 , const DiagBase 
,
   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  : 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  : AllDiagnostics) {
+for (const auto  : 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 ) {
+  llvm::DenseMap> CategorizedFixes;
+
+  for (auto  : 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{});
+  !DidEmplace)
+It->second.emplace_back();
+}
+  }
+
+  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 ) {
   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  : 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 ) {
 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 , const TextEdit 
) {
   return std::tie(L.newText, L.range, L.annotationId) ==
  std::tie(R.newText, R.range, L.annotationId);
 }
+inline bool operator<(const TextEdit , const TextEdit ) {
+  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 <<(llvm::raw_ostream &, const TextEdit &);
@@ -281,7 +285,7 @@ struct TextDocumentEdit {
   /// The text document to change.
   VersionedTextDocumentIdentifier textDocument;
 
-   /// The edits to be 

[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
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [llvm] [clangd] Add CodeAction to swap operands to binary operators (PR #78999)

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

https://github.com/torshepherd updated 
https://github.com/llvm/llvm-project/pull/78999

>From 6abe2b5af090329bfca42d144597fbd5ca41d511 Mon Sep 17 00:00:00 2001
From: Tor Shepherd 
Date: Sun, 21 Jan 2024 17:53:31 -0500
Subject: [PATCH] [clangd] Swap binary operands

---
 .../clangd/refactor/tweaks/CMakeLists.txt |   1 +
 .../refactor/tweaks/SwapBinaryOperands.cpp| 210 ++
 .../clangd/unittests/CMakeLists.txt   |   1 +
 .../tweaks/SwapBinaryOperandsTests.cpp|  38 
 .../clangd/refactor/tweaks/BUILD.gn   |   1 +
 .../clangd/unittests/BUILD.gn |   1 +
 6 files changed, 252 insertions(+)
 create mode 100644 
clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp
 create mode 100644 
clang-tools-extra/clangd/unittests/tweaks/SwapBinaryOperandsTests.cpp

diff --git a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt 
b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
index 2e948c23569f68..59475b0dfd3d22 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
+++ b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
@@ -29,6 +29,7 @@ add_clang_library(clangDaemonTweaks OBJECT
   RemoveUsingNamespace.cpp
   ScopifyEnum.cpp
   SpecialMembers.cpp
+  SwapBinaryOperands.cpp
   SwapIfBranches.cpp
 
   LINK_LIBS
diff --git a/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp 
b/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp
new file mode 100644
index 00..2241b0ac37b52a
--- /dev/null
+++ b/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp
@@ -0,0 +1,210 @@
+//===--- SwapBinaryOperands.cpp --*- 
C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+#include "ParsedAST.h"
+#include "SourceCode.h"
+#include "refactor/Tweak.h"
+#include "support/Logger.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/OperationKinds.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+/// Check whether it makes logical sense to swap operands to an operator.
+/// Assignment or member access operators are rarely swappable
+/// while keeping the meaning intact, whereas comparison operators, 
mathematical
+/// operators, etc. are often desired to be swappable for readability, avoiding
+/// bugs by assigning to nullptr when comparison was desired, etc.
+auto isOpSwappable(const BinaryOperatorKind Opcode) -> bool {
+  switch (Opcode) {
+  case BinaryOperatorKind::BO_Mul:
+  case BinaryOperatorKind::BO_Add:
+  case BinaryOperatorKind::BO_Cmp:
+  case BinaryOperatorKind::BO_LT:
+  case BinaryOperatorKind::BO_GT:
+  case BinaryOperatorKind::BO_LE:
+  case BinaryOperatorKind::BO_GE:
+  case BinaryOperatorKind::BO_EQ:
+  case BinaryOperatorKind::BO_NE:
+  case BinaryOperatorKind::BO_And:
+  case BinaryOperatorKind::BO_Xor:
+  case BinaryOperatorKind::BO_Or:
+  case BinaryOperatorKind::BO_LAnd:
+  case BinaryOperatorKind::BO_LOr:
+  case BinaryOperatorKind::BO_Comma:
+return true;
+  // Noncommutative operators:
+  case BinaryOperatorKind::BO_Div:
+  case BinaryOperatorKind::BO_Sub:
+  case BinaryOperatorKind::BO_Shl:
+  case BinaryOperatorKind::BO_Shr:
+  case BinaryOperatorKind::BO_Rem:
+  // Member access:
+  case BinaryOperatorKind::BO_PtrMemD:
+  case BinaryOperatorKind::BO_PtrMemI:
+  // Assignment:
+  case BinaryOperatorKind::BO_Assign:
+  case BinaryOperatorKind::BO_MulAssign:
+  case BinaryOperatorKind::BO_DivAssign:
+  case BinaryOperatorKind::BO_RemAssign:
+  case BinaryOperatorKind::BO_AddAssign:
+  case BinaryOperatorKind::BO_SubAssign:
+  case BinaryOperatorKind::BO_ShlAssign:
+  case BinaryOperatorKind::BO_ShrAssign:
+  case BinaryOperatorKind::BO_AndAssign:
+  case BinaryOperatorKind::BO_XorAssign:
+  case BinaryOperatorKind::BO_OrAssign:
+return false;
+  }
+  return false;
+}
+
+/// Some operators are asymmetric and need to be flipped when swapping their
+/// operands
+/// @param[out] Opcode the opcode to potentially swap
+/// If the opcode does not need to be swapped or is not swappable, does nothing
+void swapOperator(BinaryOperatorKind ) {
+  switch (Opcode) {
+  case BinaryOperatorKind::BO_LT:
+Opcode = BinaryOperatorKind::BO_GT;
+return;
+  case BinaryOperatorKind::BO_GT:
+Opcode = BinaryOperatorKind::BO_LT;
+return;
+  case BinaryOperatorKind::BO_LE:
+Opcode = BinaryOperatorKind::BO_GE;
+return;
+  case 

[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 , const DiagBase 
,
   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  : 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  : AllDiagnostics) {
+for (const auto  : 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 ) {
+  llvm::DenseMap> CategorizedFixes;
+
+  for (auto  : 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{});
+  !DidEmplace)
+It->second.emplace_back();
+}
+  }
+
+  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 ) {
   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  : 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 ) {
 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 , const TextEdit 
) {
   return std::tie(L.newText, L.range, L.annotationId) ==
  std::tie(R.newText, R.range, L.annotationId);
 }
+inline bool operator<(const TextEdit , const TextEdit ) {
+  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 <<(llvm::raw_ostream &, const TextEdit &);
@@ -281,7 +285,7 @@ struct TextDocumentEdit {
   /// The text document to change.
   VersionedTextDocumentIdentifier textDocument;
 
-   /// The edits to be 

[llvm] [clang] [clang-tools-extra] [clangd] Add CodeAction to swap operands to binary operators (PR #78999)

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

https://github.com/torshepherd updated 
https://github.com/llvm/llvm-project/pull/78999

>From c22c4fc969af0860b1fb2c371d31c2757da70e01 Mon Sep 17 00:00:00 2001
From: Tor Shepherd 
Date: Sun, 21 Jan 2024 17:53:31 -0500
Subject: [PATCH 1/2] swap binary

---
 .../clangd/refactor/tweaks/CMakeLists.txt |   1 +
 .../refactor/tweaks/SwapBinaryOperands.cpp| 214 ++
 .../clangd/unittests/CMakeLists.txt   |   1 +
 .../tweaks/SwapBinaryOperandsTests.cpp|  29 +++
 .../clangd/refactor/tweaks/BUILD.gn   |   1 +
 .../clangd/unittests/BUILD.gn |   1 +
 6 files changed, 247 insertions(+)
 create mode 100644 
clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp
 create mode 100644 
clang-tools-extra/clangd/unittests/tweaks/SwapBinaryOperandsTests.cpp

diff --git a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt 
b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
index 2e948c23569f6..59475b0dfd3d2 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
+++ b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
@@ -29,6 +29,7 @@ add_clang_library(clangDaemonTweaks OBJECT
   RemoveUsingNamespace.cpp
   ScopifyEnum.cpp
   SpecialMembers.cpp
+  SwapBinaryOperands.cpp
   SwapIfBranches.cpp
 
   LINK_LIBS
diff --git a/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp 
b/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp
new file mode 100644
index 0..e457319dfc303
--- /dev/null
+++ b/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp
@@ -0,0 +1,214 @@
+//===--- SwapBinaryOperands.cpp --*- 
C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+#include "ParsedAST.h"
+#include "SourceCode.h"
+#include "refactor/Tweak.h"
+#include "support/Logger.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/OperationKinds.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+/// Check whether it makes logical sense to swap operands to an operator.
+/// Assignment or member access operators are rarely swappable
+/// while keeping the meaning intact, whereas comparison operators, 
mathematical
+/// operators, etc. are often desired to be swappable for readability, avoiding
+/// bugs by assigning to nullptr when comparison was desired, etc.
+auto isOpSwappable(const BinaryOperatorKind Opcode) -> bool {
+  switch (Opcode) {
+  case BinaryOperatorKind::BO_Mul:
+  case BinaryOperatorKind::BO_Add:
+  case BinaryOperatorKind::BO_Cmp:
+  case BinaryOperatorKind::BO_LT:
+  case BinaryOperatorKind::BO_GT:
+  case BinaryOperatorKind::BO_LE:
+  case BinaryOperatorKind::BO_GE:
+  case BinaryOperatorKind::BO_EQ:
+  case BinaryOperatorKind::BO_NE:
+  case BinaryOperatorKind::BO_And:
+  case BinaryOperatorKind::BO_Xor:
+  case BinaryOperatorKind::BO_Or:
+  case BinaryOperatorKind::BO_LAnd:
+  case BinaryOperatorKind::BO_LOr:
+  case BinaryOperatorKind::BO_Comma:
+return true;
+  // Noncommutative operators:
+  case BinaryOperatorKind::BO_Div:
+  case BinaryOperatorKind::BO_Sub:
+  case BinaryOperatorKind::BO_Shl:
+  case BinaryOperatorKind::BO_Shr:
+  case BinaryOperatorKind::BO_Rem:
+  // Member access:
+  case BinaryOperatorKind::BO_PtrMemD:
+  case BinaryOperatorKind::BO_PtrMemI:
+  // Assignment:
+  case BinaryOperatorKind::BO_Assign:
+  case BinaryOperatorKind::BO_MulAssign:
+  case BinaryOperatorKind::BO_DivAssign:
+  case BinaryOperatorKind::BO_RemAssign:
+  case BinaryOperatorKind::BO_AddAssign:
+  case BinaryOperatorKind::BO_SubAssign:
+  case BinaryOperatorKind::BO_ShlAssign:
+  case BinaryOperatorKind::BO_ShrAssign:
+  case BinaryOperatorKind::BO_AndAssign:
+  case BinaryOperatorKind::BO_XorAssign:
+  case BinaryOperatorKind::BO_OrAssign:
+return false;
+  }
+  return false;
+}
+
+/// Some operators are asymmetric and need to be flipped when swapping their
+/// operands
+/// @param[out] Opcode the opcode to potentially swap
+/// If the opcode does not need to be swapped or is not swappable, does nothing
+void swapOperator(BinaryOperatorKind ) {
+  switch (Opcode) {
+  case BinaryOperatorKind::BO_LT:
+Opcode = BinaryOperatorKind::BO_GT;
+return;
+  case BinaryOperatorKind::BO_GT:
+Opcode = BinaryOperatorKind::BO_LT;
+return;
+  case BinaryOperatorKind::BO_LE:
+Opcode = BinaryOperatorKind::BO_GE;
+return;
+  case BinaryOperatorKind::BO_GE:
+Opcode = 

[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
cfe-commits@lists.llvm.org
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 , const DiagBase 
,
   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  : 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  : AllDiagnostics) {
+for (const auto  : 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 ) {
+  llvm::DenseMap> CategorizedFixes;
+
+  for (auto  : 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{});
+  !DidEmplace)
+It->second.emplace_back();
+}
+  }
+
+  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 ) {
   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  : 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 ) {
 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 , const TextEdit 
) {
   return std::tie(L.newText, L.range, L.annotationId) ==
  std::tie(R.newText, R.range, L.annotationId);
 }
+inline bool operator<(const TextEdit , const TextEdit ) {
+  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 <<(llvm::raw_ostream &, const TextEdit &);
@@ -281,7 +285,7 @@ struct TextDocumentEdit {
   /// The text document to change.
   VersionedTextDocumentIdentifier textDocument;
 
-   /// The edits to be 

[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 , const DiagBase 
,
   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  : 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  : AllDiagnostics) {
+for (const auto  : 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 ) {
+  llvm::DenseMap> CategorizedFixes;
+  size_t FixableOverall = 0U;
+
+  for (auto  : 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{});
+  !DidEmplace)
+It->second.emplace_back();
+}
+  }
+
+  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 ) {
   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  : 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 ) {
 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 , const TextEdit 
) {
   return std::tie(L.newText, L.range, L.annotationId) ==
  std::tie(R.newText, R.range, L.annotationId);
 }
+inline bool operator<(const TextEdit , const TextEdit ) {
+  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 <<(llvm::raw_ostream &, const TextEdit &);
@@ -281,7 +285,7 @@ struct TextDocumentEdit {
   /// The text document to change.
   VersionedTextDocumentIdentifier textDocument;
 
-   

[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
cfe-commits@lists.llvm.org
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
cfe-commits@lists.llvm.org
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
cfe-commits@lists.llvm.org
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
cfe-commits@lists.llvm.org
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
cfe-commits@lists.llvm.org
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 , const DiagBase 
,
   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  : 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  : AllDiagnostics) {
+for (const auto  : 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 ) {
+  llvm::DenseMap> CategorizedFixes;
+  size_t FixableOverall = 0U;
+
+  for (auto  : 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{});
+  !DidEmplace)
+It->second.emplace_back();
+}
+  }
+
+  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 ) {
   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  : 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 

[clang-tools-extra] [clangd] Attempt to fix https://github.com/clangd/clangd/issues/1536 (PR #79448)

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

torshepherd wrote:

I'll close this patch and pursue the other things we discussed 

https://github.com/llvm/llvm-project/pull/79448
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Attempt to fix https://github.com/clangd/clangd/issues/1536 (PR #79448)

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

https://github.com/torshepherd closed 
https://github.com/llvm/llvm-project/pull/79448
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[llvm] [clang-tools-extra] [clangd] Add CodeAction to swap operands to binary operators (PR #78999)

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


@@ -0,0 +1,210 @@
+//===--- SwapBinaryOperands.cpp --*- 
C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+#include "ParsedAST.h"
+#include "SourceCode.h"
+#include "refactor/Tweak.h"
+#include "support/Logger.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/OperationKinds.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+/// Check whether it makes logical sense to swap operands to an operator.
+/// Assignment or member access operators are rarely swappable
+/// while keeping the meaning intact, whereas comparison operators, 
mathematical
+/// operators, etc. are often desired to be swappable for readability, avoiding
+/// bugs by assigning to nullptr when comparison was desired, etc.
+auto isOpSwappable(const BinaryOperatorKind Opcode) -> bool {
+  switch (Opcode) {
+  case BinaryOperatorKind::BO_Mul:
+  case BinaryOperatorKind::BO_Add:
+  case BinaryOperatorKind::BO_Cmp:
+  case BinaryOperatorKind::BO_LT:
+  case BinaryOperatorKind::BO_GT:
+  case BinaryOperatorKind::BO_LE:
+  case BinaryOperatorKind::BO_GE:
+  case BinaryOperatorKind::BO_EQ:
+  case BinaryOperatorKind::BO_NE:
+  case BinaryOperatorKind::BO_And:
+  case BinaryOperatorKind::BO_Xor:
+  case BinaryOperatorKind::BO_Or:
+  case BinaryOperatorKind::BO_LAnd:
+  case BinaryOperatorKind::BO_LOr:
+  case BinaryOperatorKind::BO_Comma:
+return true;
+  // Noncommutative operators:
+  case BinaryOperatorKind::BO_Div:
+  case BinaryOperatorKind::BO_Sub:
+  case BinaryOperatorKind::BO_Shl:
+  case BinaryOperatorKind::BO_Shr:
+  case BinaryOperatorKind::BO_Rem:
+  // Member access:
+  case BinaryOperatorKind::BO_PtrMemD:
+  case BinaryOperatorKind::BO_PtrMemI:
+  // Assignment:
+  case BinaryOperatorKind::BO_Assign:
+  case BinaryOperatorKind::BO_MulAssign:
+  case BinaryOperatorKind::BO_DivAssign:
+  case BinaryOperatorKind::BO_RemAssign:
+  case BinaryOperatorKind::BO_AddAssign:
+  case BinaryOperatorKind::BO_SubAssign:
+  case BinaryOperatorKind::BO_ShlAssign:
+  case BinaryOperatorKind::BO_ShrAssign:
+  case BinaryOperatorKind::BO_AndAssign:
+  case BinaryOperatorKind::BO_XorAssign:
+  case BinaryOperatorKind::BO_OrAssign:
+return false;
+  }
+  return false;
+}
+
+/// Some operators are asymmetric and need to be flipped when swapping their
+/// operands
+/// @param[out] Opcode the opcode to potentially swap
+/// If the opcode does not need to be swapped or is not swappable, does nothing
+void swapOperator(BinaryOperatorKind ) {
+  switch (Opcode) {
+  case BinaryOperatorKind::BO_LT:
+Opcode = BinaryOperatorKind::BO_GT;
+return;
+  case BinaryOperatorKind::BO_GT:
+Opcode = BinaryOperatorKind::BO_LT;
+return;
+  case BinaryOperatorKind::BO_LE:
+Opcode = BinaryOperatorKind::BO_GE;
+return;
+  case BinaryOperatorKind::BO_GE:
+Opcode = BinaryOperatorKind::BO_LE;
+return;
+  case BinaryOperatorKind::BO_Mul:
+  case BinaryOperatorKind::BO_Add:
+  case BinaryOperatorKind::BO_Cmp:
+  case BinaryOperatorKind::BO_EQ:
+  case BinaryOperatorKind::BO_NE:
+  case BinaryOperatorKind::BO_And:
+  case BinaryOperatorKind::BO_Xor:
+  case BinaryOperatorKind::BO_Or:
+  case BinaryOperatorKind::BO_LAnd:
+  case BinaryOperatorKind::BO_LOr:
+  case BinaryOperatorKind::BO_Comma:
+  case BinaryOperatorKind::BO_Div:
+  case BinaryOperatorKind::BO_Sub:
+  case BinaryOperatorKind::BO_Shl:
+  case BinaryOperatorKind::BO_Shr:
+  case BinaryOperatorKind::BO_Rem:
+  case BinaryOperatorKind::BO_PtrMemD:
+  case BinaryOperatorKind::BO_PtrMemI:
+  case BinaryOperatorKind::BO_Assign:
+  case BinaryOperatorKind::BO_MulAssign:
+  case BinaryOperatorKind::BO_DivAssign:
+  case BinaryOperatorKind::BO_RemAssign:
+  case BinaryOperatorKind::BO_AddAssign:
+  case BinaryOperatorKind::BO_SubAssign:
+  case BinaryOperatorKind::BO_ShlAssign:
+  case BinaryOperatorKind::BO_ShrAssign:
+  case BinaryOperatorKind::BO_AndAssign:
+  case BinaryOperatorKind::BO_XorAssign:
+  case BinaryOperatorKind::BO_OrAssign:
+return;
+  }
+}
+
+/// Swaps the operands to a binary operator
+/// Before:
+///   x != nullptr
+///   ^^^^
+/// After:
+///   nullptr != x
+class SwapBinaryOperands : public Tweak {
+public:
+  const char *id() const final;
+
+  bool prepare(const Selection ) override;
+  Expected apply(const Selection ) override;
+  std::string title() const override {
+return llvm::formatv("Swap operands to {0}",
+ 

[clang-tools-extra] [clangd] Attempt to fix https://github.com/clangd/clangd/issues/1536 (PR #79448)

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

https://github.com/torshepherd created 
https://github.com/llvm/llvm-project/pull/79448

This PR attempts to fix [1536.](https://github.com/clangd/clangd/issues/1536). 
See in the unit tests, when all quick fixes are of the same `code`, 
`isPreferred` will be true. However, this doesn't seem to change the behavior 
in vscode:

![image](https://github.com/llvm/llvm-project/assets/49597791/1d8236c3-3e43-4260-b655-d33d6cac6e42)


>From 6bb871f64073132683bab2318c228a4ef7723c8e Mon Sep 17 00:00:00 2001
From: Tor Shepherd 
Date: Wed, 24 Jan 2024 23:43:52 -0500
Subject: [PATCH 1/2] second try

---
 clang-tools-extra/clangd/ClangdLSPServer.cpp | 78 ++--
 1 file changed, 41 insertions(+), 37 deletions(-)

diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp 
b/clang-tools-extra/clangd/ClangdLSPServer.cpp
index a87da252b7a7e9b..5fbd09fdcfdf420 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -27,7 +27,9 @@
 #include "clang/Tooling/Core/Replacement.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/FunctionExtras.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/Allocator.h"
@@ -117,10 +119,9 @@ CodeAction toCodeAction(const Fix , const URIForFile 
,
 Edit.textDocument = VersionedTextDocumentIdentifier{{File}, Version};
 for (const auto  : F.Edits)
   Edit.edits.push_back(
-  {E.range, E.newText,
-   SupportChangeAnnotation ? E.annotationId : ""});
+  {E.range, E.newText, SupportChangeAnnotation ? E.annotationId : ""});
 if (SupportChangeAnnotation) {
-  for (const auto &[AID, Annotation]: F.Annotations)
+  for (const auto &[AID, Annotation] : F.Annotations)
 Action.edit->changeAnnotations[AID] = Annotation;
 }
   }
@@ -861,24 +862,24 @@ void ClangdLSPServer::onRename(const RenameParams ,
   if (!Server->getDraft(File))
 return Reply(llvm::make_error(
 "onRename called for non-added file", ErrorCode::InvalidParams));
-  Server->rename(File, Params.position, Params.newName, Opts.Rename,
- [File, Params, Reply = std::move(Reply),
-  this](llvm::Expected R) mutable {
-   if (!R)
- return Reply(R.takeError());
-   if (auto Err = validateEdits(*Server, R->GlobalChanges))
- return Reply(std::move(Err));
-   WorkspaceEdit Result;
-   // FIXME: use documentChanges if SupportDocumentChanges is
-   // true.
-   Result.changes.emplace();
-   for (const auto  : R->GlobalChanges) {
- (*Result
-   .changes)[URI::createFile(Rep.first()).toString()] =
- Rep.second.asTextEdits();
-   }
-   Reply(Result);
- });
+  Server->rename(
+  File, Params.position, Params.newName, Opts.Rename,
+  [File, Params, Reply = std::move(Reply),
+   this](llvm::Expected R) mutable {
+if (!R)
+  return Reply(R.takeError());
+if (auto Err = validateEdits(*Server, R->GlobalChanges))
+  return Reply(std::move(Err));
+WorkspaceEdit Result;
+// FIXME: use documentChanges if SupportDocumentChanges is
+// true.
+Result.changes.emplace();
+for (const auto  : R->GlobalChanges) {
+  (*Result.changes)[URI::createFile(Rep.first()).toString()] =
+  Rep.second.asTextEdits();
+}
+Reply(Result);
+  });
 }
 
 void ClangdLSPServer::onDocumentDidClose(
@@ -1014,7 +1015,7 @@ void ClangdLSPServer::onCodeAction(const CodeActionParams 
,
   std::map ToLSPDiags;
   ClangdServer::CodeActionInputs Inputs;
 
-  for (const auto& LSPDiag : Params.context.diagnostics) {
+  for (const auto  : Params.context.diagnostics) {
 if (auto DiagRef = getDiagRef(File.file(), LSPDiag)) {
   ToLSPDiags[*DiagRef] = LSPDiag;
   Inputs.Diagnostics.push_back(*DiagRef);
@@ -1023,13 +1024,9 @@ void ClangdLSPServer::onCodeAction(const 
CodeActionParams ,
   Inputs.File = File.file();
   Inputs.Selection = Params.range;
   Inputs.RequestedActionKinds = Params.context.only;
-  Inputs.TweakFilter = [this](const Tweak ) {
-return Opts.TweakFilter(T);
-  };
-  auto CB = [this,
- Reply = std::move(Reply),
- ToLSPDiags = std::move(ToLSPDiags), File,
- Selection = Params.range](
+  Inputs.TweakFilter = [this](const Tweak ) { return Opts.TweakFilter(T); };
+  auto CB = [this, Reply = std::move(Reply), ToLSPDiags = 
std::move(ToLSPDiags),
+ File, Selection = Params.range](
 llvm::Expected Fixits) mutable 
{
 if (!Fixits)
   return Reply(Fixits.takeError());
@@ -1038,27 +1035,34 @@ void 

[llvm] [clang-tools-extra] [clangd] Add CodeAction to swap operands to binary operators (PR #78999)

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

https://github.com/torshepherd updated 
https://github.com/llvm/llvm-project/pull/78999

>From c22c4fc969af0860b1fb2c371d31c2757da70e01 Mon Sep 17 00:00:00 2001
From: Tor Shepherd 
Date: Sun, 21 Jan 2024 17:53:31 -0500
Subject: [PATCH 1/2] swap binary

---
 .../clangd/refactor/tweaks/CMakeLists.txt |   1 +
 .../refactor/tweaks/SwapBinaryOperands.cpp| 214 ++
 .../clangd/unittests/CMakeLists.txt   |   1 +
 .../tweaks/SwapBinaryOperandsTests.cpp|  29 +++
 .../clangd/refactor/tweaks/BUILD.gn   |   1 +
 .../clangd/unittests/BUILD.gn |   1 +
 6 files changed, 247 insertions(+)
 create mode 100644 
clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp
 create mode 100644 
clang-tools-extra/clangd/unittests/tweaks/SwapBinaryOperandsTests.cpp

diff --git a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt 
b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
index 2e948c23569f686..59475b0dfd3d222 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
+++ b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
@@ -29,6 +29,7 @@ add_clang_library(clangDaemonTweaks OBJECT
   RemoveUsingNamespace.cpp
   ScopifyEnum.cpp
   SpecialMembers.cpp
+  SwapBinaryOperands.cpp
   SwapIfBranches.cpp
 
   LINK_LIBS
diff --git a/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp 
b/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp
new file mode 100644
index 000..e457319dfc30370
--- /dev/null
+++ b/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp
@@ -0,0 +1,214 @@
+//===--- SwapBinaryOperands.cpp --*- 
C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+#include "ParsedAST.h"
+#include "SourceCode.h"
+#include "refactor/Tweak.h"
+#include "support/Logger.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/OperationKinds.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+/// Check whether it makes logical sense to swap operands to an operator.
+/// Assignment or member access operators are rarely swappable
+/// while keeping the meaning intact, whereas comparison operators, 
mathematical
+/// operators, etc. are often desired to be swappable for readability, avoiding
+/// bugs by assigning to nullptr when comparison was desired, etc.
+auto isOpSwappable(const BinaryOperatorKind Opcode) -> bool {
+  switch (Opcode) {
+  case BinaryOperatorKind::BO_Mul:
+  case BinaryOperatorKind::BO_Add:
+  case BinaryOperatorKind::BO_Cmp:
+  case BinaryOperatorKind::BO_LT:
+  case BinaryOperatorKind::BO_GT:
+  case BinaryOperatorKind::BO_LE:
+  case BinaryOperatorKind::BO_GE:
+  case BinaryOperatorKind::BO_EQ:
+  case BinaryOperatorKind::BO_NE:
+  case BinaryOperatorKind::BO_And:
+  case BinaryOperatorKind::BO_Xor:
+  case BinaryOperatorKind::BO_Or:
+  case BinaryOperatorKind::BO_LAnd:
+  case BinaryOperatorKind::BO_LOr:
+  case BinaryOperatorKind::BO_Comma:
+return true;
+  // Noncommutative operators:
+  case BinaryOperatorKind::BO_Div:
+  case BinaryOperatorKind::BO_Sub:
+  case BinaryOperatorKind::BO_Shl:
+  case BinaryOperatorKind::BO_Shr:
+  case BinaryOperatorKind::BO_Rem:
+  // Member access:
+  case BinaryOperatorKind::BO_PtrMemD:
+  case BinaryOperatorKind::BO_PtrMemI:
+  // Assignment:
+  case BinaryOperatorKind::BO_Assign:
+  case BinaryOperatorKind::BO_MulAssign:
+  case BinaryOperatorKind::BO_DivAssign:
+  case BinaryOperatorKind::BO_RemAssign:
+  case BinaryOperatorKind::BO_AddAssign:
+  case BinaryOperatorKind::BO_SubAssign:
+  case BinaryOperatorKind::BO_ShlAssign:
+  case BinaryOperatorKind::BO_ShrAssign:
+  case BinaryOperatorKind::BO_AndAssign:
+  case BinaryOperatorKind::BO_XorAssign:
+  case BinaryOperatorKind::BO_OrAssign:
+return false;
+  }
+  return false;
+}
+
+/// Some operators are asymmetric and need to be flipped when swapping their
+/// operands
+/// @param[out] Opcode the opcode to potentially swap
+/// If the opcode does not need to be swapped or is not swappable, does nothing
+void swapOperator(BinaryOperatorKind ) {
+  switch (Opcode) {
+  case BinaryOperatorKind::BO_LT:
+Opcode = BinaryOperatorKind::BO_GT;
+return;
+  case BinaryOperatorKind::BO_GT:
+Opcode = BinaryOperatorKind::BO_LT;
+return;
+  case BinaryOperatorKind::BO_LE:
+Opcode = BinaryOperatorKind::BO_GE;
+return;
+  case BinaryOperatorKind::BO_GE:
+

[llvm] [clang-tools-extra] [clangd] Add CodeAction to swap operands to binary operators (PR #78999)

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

https://github.com/torshepherd created 
https://github.com/llvm/llvm-project/pull/78999

This MR resolves https://github.com/llvm/llvm-project/issues/78998

>From c22c4fc969af0860b1fb2c371d31c2757da70e01 Mon Sep 17 00:00:00 2001
From: Tor Shepherd 
Date: Sun, 21 Jan 2024 17:53:31 -0500
Subject: [PATCH 1/2] swap binary

---
 .../clangd/refactor/tweaks/CMakeLists.txt |   1 +
 .../refactor/tweaks/SwapBinaryOperands.cpp| 214 ++
 .../clangd/unittests/CMakeLists.txt   |   1 +
 .../tweaks/SwapBinaryOperandsTests.cpp|  29 +++
 .../clangd/refactor/tweaks/BUILD.gn   |   1 +
 .../clangd/unittests/BUILD.gn |   1 +
 6 files changed, 247 insertions(+)
 create mode 100644 
clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp
 create mode 100644 
clang-tools-extra/clangd/unittests/tweaks/SwapBinaryOperandsTests.cpp

diff --git a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt 
b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
index 2e948c23569f68..59475b0dfd3d22 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
+++ b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
@@ -29,6 +29,7 @@ add_clang_library(clangDaemonTweaks OBJECT
   RemoveUsingNamespace.cpp
   ScopifyEnum.cpp
   SpecialMembers.cpp
+  SwapBinaryOperands.cpp
   SwapIfBranches.cpp
 
   LINK_LIBS
diff --git a/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp 
b/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp
new file mode 100644
index 00..e457319dfc3037
--- /dev/null
+++ b/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp
@@ -0,0 +1,214 @@
+//===--- SwapBinaryOperands.cpp --*- 
C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+#include "ParsedAST.h"
+#include "SourceCode.h"
+#include "refactor/Tweak.h"
+#include "support/Logger.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/OperationKinds.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+/// Check whether it makes logical sense to swap operands to an operator.
+/// Assignment or member access operators are rarely swappable
+/// while keeping the meaning intact, whereas comparison operators, 
mathematical
+/// operators, etc. are often desired to be swappable for readability, avoiding
+/// bugs by assigning to nullptr when comparison was desired, etc.
+auto isOpSwappable(const BinaryOperatorKind Opcode) -> bool {
+  switch (Opcode) {
+  case BinaryOperatorKind::BO_Mul:
+  case BinaryOperatorKind::BO_Add:
+  case BinaryOperatorKind::BO_Cmp:
+  case BinaryOperatorKind::BO_LT:
+  case BinaryOperatorKind::BO_GT:
+  case BinaryOperatorKind::BO_LE:
+  case BinaryOperatorKind::BO_GE:
+  case BinaryOperatorKind::BO_EQ:
+  case BinaryOperatorKind::BO_NE:
+  case BinaryOperatorKind::BO_And:
+  case BinaryOperatorKind::BO_Xor:
+  case BinaryOperatorKind::BO_Or:
+  case BinaryOperatorKind::BO_LAnd:
+  case BinaryOperatorKind::BO_LOr:
+  case BinaryOperatorKind::BO_Comma:
+return true;
+  // Noncommutative operators:
+  case BinaryOperatorKind::BO_Div:
+  case BinaryOperatorKind::BO_Sub:
+  case BinaryOperatorKind::BO_Shl:
+  case BinaryOperatorKind::BO_Shr:
+  case BinaryOperatorKind::BO_Rem:
+  // Member access:
+  case BinaryOperatorKind::BO_PtrMemD:
+  case BinaryOperatorKind::BO_PtrMemI:
+  // Assignment:
+  case BinaryOperatorKind::BO_Assign:
+  case BinaryOperatorKind::BO_MulAssign:
+  case BinaryOperatorKind::BO_DivAssign:
+  case BinaryOperatorKind::BO_RemAssign:
+  case BinaryOperatorKind::BO_AddAssign:
+  case BinaryOperatorKind::BO_SubAssign:
+  case BinaryOperatorKind::BO_ShlAssign:
+  case BinaryOperatorKind::BO_ShrAssign:
+  case BinaryOperatorKind::BO_AndAssign:
+  case BinaryOperatorKind::BO_XorAssign:
+  case BinaryOperatorKind::BO_OrAssign:
+return false;
+  }
+  return false;
+}
+
+/// Some operators are asymmetric and need to be flipped when swapping their
+/// operands
+/// @param[out] Opcode the opcode to potentially swap
+/// If the opcode does not need to be swapped or is not swappable, does nothing
+void swapOperator(BinaryOperatorKind ) {
+  switch (Opcode) {
+  case BinaryOperatorKind::BO_LT:
+Opcode = BinaryOperatorKind::BO_GT;
+return;
+  case BinaryOperatorKind::BO_GT:
+Opcode = BinaryOperatorKind::BO_LT;
+return;
+  case BinaryOperatorKind::BO_LE:
+Opcode =