VitaNuo created this revision.
Herald added subscribers: kadircet, arphaman.
Herald added a project: All.
VitaNuo requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D153769

Files:
  clang-tools-extra/clangd/ClangdLSPServer.cpp
  clang-tools-extra/clangd/Protocol.cpp
  clang-tools-extra/clangd/Protocol.h
  clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
  clang-tools-extra/clangd/refactor/tweaks/OrganizeImports.cpp
  clang-tools-extra/clangd/test/code-action-request.test
  clang-tools-extra/clangd/test/fixits-codeaction-documentchanges.test
  clang-tools-extra/clangd/test/fixits-codeaction.test
  clang-tools-extra/clangd/test/fixits-command-documentchanges.test
  clang-tools-extra/clangd/test/fixits-command.test
  clang-tools-extra/clangd/test/include-cleaner-batch-fix.test

Index: clang-tools-extra/clangd/test/include-cleaner-batch-fix.test
===================================================================
--- clang-tools-extra/clangd/test/include-cleaner-batch-fix.test
+++ clang-tools-extra/clangd/test/include-cleaner-batch-fix.test
@@ -300,6 +300,26 @@
 # CHECK-NEXT:      ],
 # CHECK-NEXT:      "command": "clangd.applyFix",
 # CHECK-NEXT:      "title": "Apply fix: fix all includes"
+# CHECK-NEXT:    },
+# CHECK-NEXT:    {
+# CHECK-NEXT:      "arguments": [
+# CHECK-NEXT:        {
+# CHECK-NEXT:          "file": "file:///clangd-test/simple.cpp",
+# CHECK-NEXT:            "selection": {
+# CHECK-NEXT:            "end": {
+# CHECK-NEXT:              "character": 4,
+# CHECK-NEXT:              "line": 2
+# CHECK-NEXT:            },
+# CHECK-NEXT:            "start": {
+# CHECK-NEXT:              "character": 1,
+# CHECK-NEXT:              "line": 2
+# CHECK-NEXT:            }
+# CHECK-NEXT:          },
+# CHECK-NEXT:          "tweakID": "OrganizeImports"
+# CHECK-NEXT:        }
+# CHECK-NEXT:      ],
+# CHECK-NEXT:      "command": "clangd.applyTweak",
+# CHECK-NEXT:      "title": "Remove unused and add missing includes"
 # CHECK-NEXT:    }
 # CHECK-NEXT:  ]
 ---
@@ -485,6 +505,26 @@
 # CHECK-NEXT:      ],
 # CHECK-NEXT:      "command": "clangd.applyFix",
 # CHECK-NEXT:      "title": "Apply fix: fix all includes"
+# CHECK-NEXT:    },
+# CHECK-NEXT: {
+# CHECK-NEXT:      "arguments": [
+# CHECK-NEXT:        {
+# CHECK-NEXT:          "file": "file:///clangd-test/simple.cpp",
+# CHECK-NEXT:          "selection": {
+# CHECK-NEXT:            "end": {
+# CHECK-NEXT:              "character": 17,
+# CHECK-NEXT:              "line": 0
+# CHECK-NEXT:            },
+# CHECK-NEXT:            "start": {
+# CHECK-NEXT:              "character": 0,
+# CHECK-NEXT:              "line": 0
+# CHECK-NEXT:            }
+# CHECK-NEXT:          },
+# CHECK-NEXT:          "tweakID": "OrganizeImports"
+# CHECK-NEXT:        }
+# CHECK-NEXT:      ],
+# CHECK-NEXT:      "command": "clangd.applyTweak",
+# CHECK-NEXT:      "title": "Remove unused and add missing includes"
 # CHECK-NEXT:    }
 # CHECK-NEXT:  ]
 ---
Index: clang-tools-extra/clangd/test/fixits-command.test
===================================================================
--- clang-tools-extra/clangd/test/fixits-command.test
+++ clang-tools-extra/clangd/test/fixits-command.test
@@ -92,6 +92,26 @@
 # CHECK-NEXT:      ],
 # CHECK-NEXT:      "command": "clangd.applyFix",
 # CHECK-NEXT:      "title": "Apply fix: use '==' to turn this assignment into an equality comparison"
+# CHECK-NEXT:    },
+# CHECK-NEXT:    {
+# CHECK-NEXT:      "arguments": [
+# CHECK-NEXT:        {
+# CHECK-NEXT:          "file": "file:///clangd-test/foo.c",
+# CHECK-NEXT:          "selection": {
+# CHECK-NEXT:            "end": {
+# CHECK-NEXT:              "character": 35,
+# CHECK-NEXT:              "line": 0
+# CHECK-NEXT:            },
+# CHECK-NEXT:            "start": {
+# CHECK-NEXT:              "character": 13,
+# CHECK-NEXT:              "line": 0
+# CHECK-NEXT:            }
+# CHECK-NEXT:          },
+# CHECK-NEXT:          "tweakID": "OrganizeImports"
+# CHECK-NEXT:        }
+# CHECK-NEXT:      ],
+# CHECK-NEXT:      "command": "clangd.applyTweak",
+# CHECK-NEXT:      "title": "Remove unused and add missing includes"
 # CHECK-NEXT:    }
 # CHECK-NEXT:  ]
 ---
@@ -162,6 +182,26 @@
 # CHECK-NEXT:      ],
 # CHECK-NEXT:      "command": "clangd.applyFix",
 # CHECK-NEXT:      "title": "Apply fix: use '==' to turn this assignment into an equality comparison"
+# CHECK-NEXT:    },
+# CHECK-NEXT:    {
+# CHECK-NEXT:      "arguments": [
+# CHECK-NEXT:        {
+# CHECK-NEXT:          "file": "file:///clangd-test/foo.c",
+# CHECK-NEXT:          "selection": {
+# CHECK-NEXT:            "end": {
+# CHECK-NEXT:              "character": 35,
+# CHECK-NEXT:              "line": 0
+# CHECK-NEXT:            },
+# CHECK-NEXT:            "start": {
+# CHECK-NEXT:              "character": 13,
+# CHECK-NEXT:              "line": 0
+# CHECK-NEXT:            }
+# CHECK-NEXT:          },
+# CHECK-NEXT:          "tweakID": "OrganizeImports"
+# CHECK-NEXT:        }
+# CHECK-NEXT:      ],
+# CHECK-NEXT:      "command": "clangd.applyTweak",
+# CHECK-NEXT:      "title": "Remove unused and add missing includes"
 # CHECK-NEXT:    }
 # CHECK-NEXT:  ]
 ---
Index: clang-tools-extra/clangd/test/fixits-command-documentchanges.test
===================================================================
--- clang-tools-extra/clangd/test/fixits-command-documentchanges.test
+++ clang-tools-extra/clangd/test/fixits-command-documentchanges.test
@@ -104,7 +104,27 @@
 # CHECK-NEXT:      ],
 # CHECK-NEXT:      "command": "clangd.applyFix",
 # CHECK-NEXT:      "title": "Apply fix: use '==' to turn this assignment into an equality comparison"
-# CHECK-NEXT:    }
+# CHECK-NEXT:    },
+# CHECK-NEXT:    {
+# CHECK-NEXT:       "arguments": [
+# CHECK-NEXT:         {
+# CHECK-NEXT:           "file": "file:///clangd-test/foo.c",
+# CHECK-NEXT:           "selection": {
+# CHECK-NEXT:             "end": {
+# CHECK-NEXT:               "character": 35,
+# CHECK-NEXT:               "line": 0
+# CHECK-NEXT:             },
+# CHECK-NEXT:             "start": {
+# CHECK-NEXT:               "character": 13,
+# CHECK-NEXT:               "line": 0
+# CHECK-NEXT:             }
+# CHECK-NEXT:           },
+# CHECK-NEXT:           "tweakID": "OrganizeImports"
+# CHECK-NEXT:         }
+# CHECK-NEXT:       ],
+# CHECK-NEXT:       "command": "clangd.applyTweak",
+# CHECK-NEXT:       "title": "Remove unused and add missing includes"
+# CHECK-NEXT:     }
 # CHECK-NEXT:  ]
 ---
 {"jsonrpc":"2.0","id":3,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":0,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses (fixes available)"}]}}}
@@ -186,6 +206,26 @@
 # CHECK-NEXT:      ],
 # CHECK-NEXT:      "command": "clangd.applyFix",
 # CHECK-NEXT:      "title": "Apply fix: use '==' to turn this assignment into an equality comparison"
+# CHECK-NEXT:    },
+# CHECK-NEXT:    {
+# CHECK-NEXT:      "arguments": [
+# CHECK-NEXT:        {
+# CHECK-NEXT:          "file": "file:///clangd-test/foo.c",
+# CHECK-NEXT:          "selection": {
+# CHECK-NEXT:            "end": {
+# CHECK-NEXT:              "character": 35,
+# CHECK-NEXT:              "line": 0
+# CHECK-NEXT:            },
+# CHECK-NEXT:            "start": {
+# CHECK-NEXT:              "character": 13,
+# CHECK-NEXT:              "line": 0
+# CHECK-NEXT:            }
+# CHECK-NEXT:          },
+# CHECK-NEXT:          "tweakID": "OrganizeImports"
+# CHECK-NEXT:        }
+# CHECK-NEXT:      ],
+# CHECK-NEXT:      "command": "clangd.applyTweak",
+# CHECK-NEXT:      "title": "Remove unused and add missing includes"
 # CHECK-NEXT:    }
 # CHECK-NEXT:  ]
 ---
Index: clang-tools-extra/clangd/test/fixits-codeaction.test
===================================================================
--- clang-tools-extra/clangd/test/fixits-codeaction.test
+++ clang-tools-extra/clangd/test/fixits-codeaction.test
@@ -124,6 +124,30 @@
 # CHECK-NEXT:      },
 # CHECK-NEXT:      "kind": "quickfix",
 # CHECK-NEXT:      "title": "use '==' to turn this assignment into an equality comparison"
+# CHECK-NEXT:    },
+# CHECK-NEXT:    {
+# CHECK-NEXT:      "command": {
+# CHECK-NEXT:        "arguments": [
+# CHECK-NEXT:          {
+# CHECK-NEXT:            "file": "file:///clangd-test/foo.c",
+# CHECK-NEXT:            "selection": {
+# CHECK-NEXT:              "end": {
+# CHECK-NEXT:                "character": 35,
+# CHECK-NEXT:                "line": 0
+# CHECK-NEXT:              },
+# CHECK-NEXT:              "start": {
+# CHECK-NEXT:                "character": 13,
+# CHECK-NEXT:                "line": 0
+# CHECK-NEXT:              }
+# CHECK-NEXT:            },
+# CHECK-NEXT:            "tweakID": "OrganizeImports"
+# CHECK-NEXT:          }
+# CHECK-NEXT:        ],
+# CHECK-NEXT:        "command": "clangd.applyTweak",
+# CHECK-NEXT:        "title": "Remove unused and add missing includes"
+# CHECK-NEXT:      },
+# CHECK-NEXT:      "kind": "source.organizeImports",
+# CHECK-NEXT:      "title": "Remove unused and add missing includes"
 # CHECK-NEXT:    }
 # CHECK-NEXT:  ]
 ---
Index: clang-tools-extra/clangd/test/fixits-codeaction-documentchanges.test
===================================================================
--- clang-tools-extra/clangd/test/fixits-codeaction-documentchanges.test
+++ clang-tools-extra/clangd/test/fixits-codeaction-documentchanges.test
@@ -136,6 +136,30 @@
 # CHECK-NEXT:      },
 # CHECK-NEXT:      "kind": "quickfix",
 # CHECK-NEXT:      "title": "use '==' to turn this assignment into an equality comparison"
+# CHECK-NEXT:    },
+# CHECK-NEXT:    {
+# CHECK-NEXT:      "command": {
+# CHECK-NEXT:        "arguments": [
+# CHECK-NEXT:          {
+# CHECK-NEXT:            "file": "file:///clangd-test/foo.c",
+# CHECK-NEXT:            "selection": {
+# CHECK-NEXT:              "end": {
+# CHECK-NEXT:                "character": 35,
+# CHECK-NEXT:                "line": 0
+# CHECK-NEXT:              },
+# CHECK-NEXT:              "start": {
+# CHECK-NEXT:                "character": 13,
+# CHECK-NEXT:                "line": 0
+# CHECK-NEXT:              }
+# CHECK-NEXT:            },
+# CHECK-NEXT:            "tweakID": "OrganizeImports"
+# CHECK-NEXT:          }
+# CHECK-NEXT:        ],
+# CHECK-NEXT:        "command": "clangd.applyTweak",
+# CHECK-NEXT:        "title": "Remove unused and add missing includes"
+# CHECK-NEXT:      },
+# CHECK-NEXT:      "kind": "source.organizeImports",
+# CHECK-NEXT:      "title": "Remove unused and add missing includes"
 # CHECK-NEXT:    }
 # CHECK-NEXT:  ]
 ---
Index: clang-tools-extra/clangd/test/code-action-request.test
===================================================================
--- clang-tools-extra/clangd/test/code-action-request.test
+++ clang-tools-extra/clangd/test/code-action-request.test
@@ -48,6 +48,26 @@
 # CHECK-NEXT:      ],
 # CHECK-NEXT:      "command": "clangd.applyTweak",
 # CHECK-NEXT:      "title": "Replace with deduced type"
+# CHECK-NEXT:    },
+# CHECK-NEXT:    {
+# CHECK-NEXT:      "arguments": [
+# CHECK-NEXT:        {
+# CHECK-NEXT:          "file": "file:///clangd-test/main.cpp",
+# CHECK-NEXT:          "selection": {
+# CHECK-NEXT:            "end": {
+# CHECK-NEXT:              "character": 4,
+# CHECK-NEXT:              "line": 0
+# CHECK-NEXT:            },
+# CHECK-NEXT:            "start": {
+# CHECK-NEXT:              "character": 0,
+# CHECK-NEXT:              "line": 0
+# CHECK-NEXT:            }
+# CHECK-NEXT:          },
+# CHECK-NEXT:          "tweakID": "OrganizeImports"
+# CHECK-NEXT:        }
+# CHECK-NEXT:      ],
+# CHECK-NEXT:      "command": "clangd.applyTweak",
+# CHECK-NEXT:      "title": "Remove unused and add missing includes"
 # CHECK-NEXT:    }
 # CHECK-NEXT:  ]
 ---
Index: clang-tools-extra/clangd/refactor/tweaks/OrganizeImports.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/refactor/tweaks/OrganizeImports.cpp
@@ -0,0 +1,88 @@
+//===--- OrganizeImports.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 "IncludeCleaner.h"
+#include "Protocol.h"
+#include "SourceCode.h"
+#include "clang-include-cleaner/IncludeSpeller.h"
+#include "refactor/Tweak.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Format/Format.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <climits>
+#include <set>
+#include <string>
+
+namespace clang {
+namespace clangd {
+namespace {
+
+// Tweak for applying IWYU-related changes (removing unused and adding missing
+// includes) in batch. The tweak can be triggered via the "Organize Imports"
+// source action (VS Code). The set of changes applied fully corresponds to the
+// findings from the clang-include-cleaner tool.
+class OrganizeImports : public Tweak {
+public:
+  const char *id() const override;
+
+  bool prepare(const Selection &Inputs) override;
+  Expected<Effect> apply(const Selection &Inputs) override;
+  std::string title() const override;
+  llvm::StringLiteral kind() const override {
+    return CodeAction::SOURCE_ORGANIZE_IMPORTS;
+  }
+};
+REGISTER_TWEAK(OrganizeImports)
+
+std::string OrganizeImports::title() const {
+  return "Remove unused and add missing includes";
+}
+
+bool OrganizeImports::prepare(const Selection &Inputs) { return true; }
+
+Expected<OrganizeImports::Effect>
+OrganizeImports::apply(const Selection &Inputs) {
+  IncludeCleanerFindings Findings = computeIncludeCleanerFindings(*Inputs.AST);
+  const auto MainFilePath = Inputs.AST->tuPath();
+  tooling::Replacements Replacements;
+  for (const auto *Inc : Findings.UnusedIncludes)
+    if (auto Err = Replacements.add(
+            tooling::Replacement{MainFilePath, UINT_MAX, 1, Inc->Written}))
+      return Err;
+
+  const auto &SM = Inputs.AST->getSourceManager();
+  std::set<std::string> Spellings;
+  for (const auto &Missing : Findings.MissingIncludes)
+    for (const auto &P : Missing.Providers)
+      Spellings.insert(include_cleaner::spellHeader(
+          {P, Inputs.AST->getPreprocessor().getHeaderSearchInfo(),
+           SM.getFileEntryForID(SM.getMainFileID())}));
+
+  for (const auto &Spelling : Spellings)
+    if (auto Err = Replacements.add(tooling::Replacement{
+            MainFilePath, UINT_MAX, 0, "#include " + Spelling}))
+      return Err;
+
+  auto FileStyle =
+      format::getStyle(format::DefaultFormatStyle, Inputs.AST->tuPath(),
+                       format::DefaultFallbackStyle, Inputs.Code, Inputs.FS);
+  if (!FileStyle)
+    FileStyle = format::getLLVMStyle();
+  if (auto Final = format::cleanupAroundReplacements(Inputs.Code, Replacements,
+                                                     *FileStyle))
+    return Effect::mainFileEdit(SM, *Final);
+  else
+    return Final.takeError();
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
Index: clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
+++ clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
@@ -24,6 +24,7 @@
   MemberwiseConstructor.cpp
   ObjCLocalizeStringLiteral.cpp
   ObjCMemberwiseInitializer.cpp
+  OrganizeImports.cpp
   PopulateSwitch.cpp
   RawStringLiteral.cpp
   RemoveUsingNamespace.cpp
Index: clang-tools-extra/clangd/Protocol.h
===================================================================
--- clang-tools-extra/clangd/Protocol.h
+++ clang-tools-extra/clangd/Protocol.h
@@ -28,6 +28,7 @@
 #include "support/MemoryTree.h"
 #include "clang/Index/IndexSymbol.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
 #include "llvm/Support/JSON.h"
 #include "llvm/Support/raw_ostream.h"
 #include <bitset>
@@ -1080,6 +1081,7 @@
   const static llvm::StringLiteral QUICKFIX_KIND;
   const static llvm::StringLiteral REFACTOR_KIND;
   const static llvm::StringLiteral INFO_KIND;
+  const static llvm::StringLiteral SOURCE_ORGANIZE_IMPORTS;
 
   /// The diagnostics that this code action resolves.
   std::optional<std::vector<Diagnostic>> diagnostics;
Index: clang-tools-extra/clangd/Protocol.cpp
===================================================================
--- clang-tools-extra/clangd/Protocol.cpp
+++ clang-tools-extra/clangd/Protocol.cpp
@@ -868,6 +868,8 @@
 const llvm::StringLiteral CodeAction::QUICKFIX_KIND = "quickfix";
 const llvm::StringLiteral CodeAction::REFACTOR_KIND = "refactor";
 const llvm::StringLiteral CodeAction::INFO_KIND = "info";
+const llvm::StringLiteral CodeAction::SOURCE_ORGANIZE_IMPORTS =
+    "source.organizeImports";
 
 llvm::json::Value toJSON(const CodeAction &CA) {
   auto CodeAction = llvm::json::Object{{"title", CA.title}};
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -647,7 +647,8 @@
           ? llvm::json::Object{{"codeActionKinds",
                                 {CodeAction::QUICKFIX_KIND,
                                  CodeAction::REFACTOR_KIND,
-                                 CodeAction::INFO_KIND}}}
+                                 CodeAction::INFO_KIND,
+                                 CodeAction::SOURCE_ORGANIZE_IMPORTS}}}
           : llvm::json::Value(true);
 
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to