[clang-tools-extra] r363680 - [clangd] Add hidden tweaks to dump AST/selection.

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Tue Jun 18 06:37:54 PDT 2019


Author: sammccall
Date: Tue Jun 18 06:37:54 2019
New Revision: 363680

URL: http://llvm.org/viewvc/llvm-project?rev=363680&view=rev
Log:
[clangd] Add hidden tweaks to dump AST/selection.

Summary:
This introduces a few new concepts:
 - tweaks have an Intent (they don't all advertise as refactorings)
 - tweaks may produce messages (for ShowMessage notification). Generalized
   Replacements -> Effect.
 - tweaks (and other features) may be hidden (clangd -hidden-features flag).
   We may choose to promote these one day. I'm not sure they're worth their own
   feature flags though.

Verified it in vim-clangd (not yet open source), curious if the UI is ok in VSCode.

Reviewers: ilya-biryukov

Subscribers: mgorny, MaskRay, jkorous, arphaman, kadircet, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D62538

Added:
    clang-tools-extra/trunk/clangd/refactor/tweaks/DumpAST.cpp
Modified:
    clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
    clang-tools-extra/trunk/clangd/ClangdServer.cpp
    clang-tools-extra/trunk/clangd/ClangdServer.h
    clang-tools-extra/trunk/clangd/Protocol.cpp
    clang-tools-extra/trunk/clangd/Protocol.h
    clang-tools-extra/trunk/clangd/Selection.cpp
    clang-tools-extra/trunk/clangd/refactor/Tweak.h
    clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt
    clang-tools-extra/trunk/clangd/refactor/tweaks/RawStringLiteral.cpp
    clang-tools-extra/trunk/clangd/refactor/tweaks/SwapIfBranches.cpp
    clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp
    clang-tools-extra/trunk/clangd/unittests/SelectionTests.cpp
    clang-tools-extra/trunk/clangd/unittests/TweakTests.cpp

Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=363680&r1=363679&r2=363680&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Tue Jun 18 06:37:54 2019
@@ -13,6 +13,7 @@
 #include "SourceCode.h"
 #include "Trace.h"
 #include "URI.h"
+#include "refactor/Tweak.h"
 #include "clang/Tooling/Core/Replacement.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/ScopeExit.h"
@@ -31,7 +32,14 @@ CodeAction toCodeAction(const ClangdServ
                         Range Selection) {
   CodeAction CA;
   CA.title = T.Title;
-  CA.kind = CodeAction::REFACTOR_KIND;
+  switch (T.Intent) {
+  case Tweak::Refactor:
+    CA.kind = CodeAction::REFACTOR_KIND;
+    break;
+  case Tweak::Info:
+    CA.kind = CodeAction::INFO_KIND;
+    break;
+  }
   // This tweak may have an expensive second stage, we only run it if the user
   // actually chooses it in the UI. We reply with a command that would run the
   // corresponding tweak.
@@ -481,18 +489,25 @@ void ClangdLSPServer::onCommand(const Ex
           llvm::inconvertibleErrorCode(),
           "trying to apply a code action for a non-added file"));
 
-    auto Action = [ApplyEdit](decltype(Reply) Reply, URIForFile File,
-                              std::string Code,
-                              llvm::Expected<std::vector<TextEdit>> R) {
+    auto Action = [this, ApplyEdit](decltype(Reply) Reply, URIForFile File,
+                                    std::string Code,
+                                    llvm::Expected<Tweak::Effect> R) {
       if (!R)
         return Reply(R.takeError());
 
-      WorkspaceEdit WE;
-      WE.changes.emplace();
-      (*WE.changes)[File.uri()] = std::move(*R);
-
-      Reply("Fix applied.");
-      ApplyEdit(std::move(WE));
+      if (R->ApplyEdit) {
+        WorkspaceEdit WE;
+        WE.changes.emplace();
+        (*WE.changes)[File.uri()] = replacementsToEdits(Code, *R->ApplyEdit);
+        ApplyEdit(std::move(WE));
+      }
+      if (R->ShowMessage) {
+        ShowMessageParams Msg;
+        Msg.message = *R->ShowMessage;
+        Msg.type = MessageType::Info;
+        notify("window/showMessage", Msg);
+      }
+      Reply("Tweak applied.");
     };
     Server->applyTweak(Params.tweakArgs->file.file(),
                        Params.tweakArgs->selection, Params.tweakArgs->tweakID,

Modified: clang-tools-extra/trunk/clangd/ClangdServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=363680&r1=363679&r2=363680&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdServer.cpp Tue Jun 18 06:37:54 2019
@@ -95,6 +95,7 @@ ClangdServer::ClangdServer(const GlobalC
                      : nullptr),
       GetClangTidyOptions(Opts.GetClangTidyOptions),
       SuggestMissingIncludes(Opts.SuggestMissingIncludes),
+      EnableHiddenFeatures(Opts.HiddenFeatures),
       WorkspaceRoot(Opts.WorkspaceRoot),
       // Pass a callback into `WorkScheduler` to extract symbols from a newly
       // parsed file and rebuild the file index synchronously each time an AST
@@ -303,16 +304,19 @@ tweakSelection(const Range &Sel, const I
 
 void ClangdServer::enumerateTweaks(PathRef File, Range Sel,
                                    Callback<std::vector<TweakRef>> CB) {
-  auto Action = [Sel](decltype(CB) CB, std::string File,
-                      Expected<InputsAndAST> InpAST) {
+  auto Action = [this, Sel](decltype(CB) CB, std::string File,
+                            Expected<InputsAndAST> InpAST) {
     if (!InpAST)
       return CB(InpAST.takeError());
     auto Selection = tweakSelection(Sel, *InpAST);
     if (!Selection)
       return CB(Selection.takeError());
     std::vector<TweakRef> Res;
-    for (auto &T : prepareTweaks(*Selection))
-      Res.push_back({T->id(), T->title()});
+    for (auto &T : prepareTweaks(*Selection)) {
+      if (T->hidden() && !EnableHiddenFeatures)
+        continue;
+      Res.push_back({T->id(), T->title(), T->intent()});
+    }
     CB(std::move(Res));
   };
 
@@ -321,7 +325,7 @@ void ClangdServer::enumerateTweaks(PathR
 }
 
 void ClangdServer::applyTweak(PathRef File, Range Sel, StringRef TweakID,
-                              Callback<std::vector<TextEdit>> CB) {
+                              Callback<Tweak::Effect> CB) {
   auto Action = [Sel](decltype(CB) CB, std::string File, std::string TweakID,
                       Expected<InputsAndAST> InpAST) {
     if (!InpAST)
@@ -332,17 +336,19 @@ void ClangdServer::applyTweak(PathRef Fi
     auto A = prepareTweak(TweakID, *Selection);
     if (!A)
       return CB(A.takeError());
-    auto Raw = (*A)->apply(*Selection);
-    if (!Raw)
-      return CB(Raw.takeError());
-    // FIXME: this function has I/O operations (find .clang-format file), figure
-    // out a way to cache the format style.
-    auto Style = getFormatStyleForFile(File, InpAST->Inputs.Contents,
-                                       InpAST->Inputs.FS.get());
-    auto Formatted = cleanupAndFormat(InpAST->Inputs.Contents, *Raw, Style);
-    if (!Formatted)
-      return CB(Formatted.takeError());
-    return CB(replacementsToEdits(InpAST->Inputs.Contents, *Formatted));
+    auto Effect = (*A)->apply(*Selection);
+    if (!Effect)
+      return CB(Effect.takeError());
+    if (Effect->ApplyEdit) {
+      // FIXME: this function has I/O operations (find .clang-format file),
+      // figure out a way to cache the format style.
+      auto Style = getFormatStyleForFile(File, InpAST->Inputs.Contents,
+                                         InpAST->Inputs.FS.get());
+      if (auto Formatted = cleanupAndFormat(InpAST->Inputs.Contents,
+                                            *Effect->ApplyEdit, Style))
+        Effect->ApplyEdit = std::move(*Formatted);
+    }
+    return CB(std::move(*Effect));
   };
   WorkScheduler.runWithAST(
       "ApplyTweak", File,

Modified: clang-tools-extra/trunk/clangd/ClangdServer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.h?rev=363680&r1=363679&r2=363680&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdServer.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdServer.h Tue Jun 18 06:37:54 2019
@@ -123,6 +123,10 @@ public:
         std::chrono::milliseconds(500);
 
     bool SuggestMissingIncludes = false;
+
+    /// Enable hidden features mostly useful to clangd developers.
+    /// e.g. tweaks to dump the AST.
+    bool HiddenFeatures = false;
   };
   // Sensible default options for use in tests.
   // Features like indexing must be enabled if desired.
@@ -227,6 +231,7 @@ public:
   struct TweakRef {
     std::string ID;    /// ID to pass for applyTweak.
     std::string Title; /// A single-line message to show in the UI.
+    Tweak::Intent Intent;
   };
   /// Enumerate the code tweaks available to the user at a specified point.
   void enumerateTweaks(PathRef File, Range Sel,
@@ -234,7 +239,7 @@ public:
 
   /// Apply the code tweak with a specified \p ID.
   void applyTweak(PathRef File, Range Sel, StringRef ID,
-                  Callback<std::vector<TextEdit>> CB);
+                  Callback<Tweak::Effect> CB);
 
   /// Only for testing purposes.
   /// Waits until all requests to worker thread are finished and dumps AST for
@@ -291,6 +296,7 @@ private:
   // If this is true, suggest include insertion fixes for diagnostic errors that
   // can be caused by missing includes (e.g. member access in incomplete type).
   bool SuggestMissingIncludes = false;
+  bool EnableHiddenFeatures = false;
 
   // GUARDED_BY(CachedCompletionFuzzyFindRequestMutex)
   llvm::StringMap<llvm::Optional<FuzzyFindRequest>>

Modified: clang-tools-extra/trunk/clangd/Protocol.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.cpp?rev=363680&r1=363679&r2=363680&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.cpp (original)
+++ clang-tools-extra/trunk/clangd/Protocol.cpp Tue Jun 18 06:37:54 2019
@@ -359,6 +359,14 @@ bool fromJSON(const llvm::json::Value &P
   return true;
 }
 
+llvm::json::Value toJSON(const MessageType &R) {
+  return static_cast<int64_t>(R);
+}
+
+llvm::json::Value toJSON(const ShowMessageParams &R) {
+  return llvm::json::Object{{"type", R.type}, {"message", R.message}};
+}
+
 bool fromJSON(const llvm::json::Value &Params, DidOpenTextDocumentParams &R) {
   llvm::json::ObjectMapper O(Params);
   return O && O.map("textDocument", R.textDocument);
@@ -593,6 +601,7 @@ llvm::json::Value toJSON(const Command &
 
 const llvm::StringLiteral CodeAction::QUICKFIX_KIND = "quickfix";
 const llvm::StringLiteral CodeAction::REFACTOR_KIND = "refactor";
+const llvm::StringLiteral CodeAction::INFO_KIND = "info";
 
 llvm::json::Value toJSON(const CodeAction &CA) {
   auto CodeAction = llvm::json::Object{{"title", CA.title}};

Modified: clang-tools-extra/trunk/clangd/Protocol.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=363680&r1=363679&r2=363680&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.h (original)
+++ clang-tools-extra/trunk/clangd/Protocol.h Tue Jun 18 06:37:54 2019
@@ -483,6 +483,28 @@ struct InitializeParams {
 };
 bool fromJSON(const llvm::json::Value &, InitializeParams &);
 
+enum class MessageType {
+  /// An error message.
+  Error = 1,
+  /// A warning message.
+  Warning = 1,
+  /// An information message.
+  Info = 1,
+  /// A log message.
+  Log = 1,
+};
+llvm::json::Value toJSON(const MessageType &);
+
+/// The show message notification is sent from a server to a client to ask the
+/// client to display a particular message in the user interface.
+struct ShowMessageParams {
+  /// The message type.
+  MessageType type = MessageType::Info;
+  /// The actual message.
+  std::string message;
+};
+llvm::json::Value toJSON(const ShowMessageParams &);
+
 struct DidOpenTextDocumentParams {
   /// The document that was opened.
   TextDocumentItem textDocument;
@@ -740,6 +762,7 @@ struct CodeAction {
   llvm::Optional<std::string> kind;
   const static llvm::StringLiteral QUICKFIX_KIND;
   const static llvm::StringLiteral REFACTOR_KIND;
+  const static llvm::StringLiteral INFO_KIND;
 
   /// The diagnostics that this code action resolves.
   llvm::Optional<std::vector<Diagnostic>> diagnostics;

Modified: clang-tools-extra/trunk/clangd/Selection.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Selection.cpp?rev=363680&r1=363679&r2=363680&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Selection.cpp (original)
+++ clang-tools-extra/trunk/clangd/Selection.cpp Tue Jun 18 06:37:54 2019
@@ -53,6 +53,9 @@ public:
   bool TraverseDecl(Decl *X) {
     if (X && isa<TranslationUnitDecl>(X))
       return Base::TraverseDecl(X); // Already pushed by constructor.
+    // Base::TraverseDecl will suppress children, but not this node itself.
+    if (X && X->isImplicit())
+      return true;
     return traverseNode(X, [&] { return Base::TraverseDecl(X); });
   }
   bool TraverseTypeLoc(TypeLoc X) {

Modified: clang-tools-extra/trunk/clangd/refactor/Tweak.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/refactor/Tweak.h?rev=363680&r1=363679&r2=363680&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/refactor/Tweak.h (original)
+++ clang-tools-extra/trunk/clangd/refactor/Tweak.h Tue Jun 18 06:37:54 2019
@@ -5,9 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// Tweaks are small refactoring-like actions that run over the AST and produce
-// the set of edits as a result. They are local, i.e. they should take the
-// current editor context, e.g. the cursor position and selection into account.
+// Tweaks are small actions that run over the AST and produce edits, messages
+// etc as a result. They are local, i.e. they should take the current editor
+// context, e.g. the cursor position and selection into account.
 // The actions are executed in two stages:
 //   - Stage 1 should check whether the action is available in a current
 //     context. It should be cheap and fast to compute as it is executed for all
@@ -47,10 +47,36 @@ public:
     ParsedAST &AST;
     /// A location of the cursor in the editor.
     SourceLocation Cursor;
-    // The AST nodes that were selected.
+    /// The AST nodes that were selected.
     SelectionTree ASTSelection;
     // FIXME: provide a way to get sources and ASTs for other files.
   };
+
+  /// Output of a tweak.
+  enum Intent {
+    /// Apply changes that preserve the behavior of the code.
+    Refactor,
+    /// Provide information to the user.
+    Info,
+  };
+  struct Effect {
+    /// A message to be displayed to the user.
+    llvm::Optional<std::string> ShowMessage;
+    /// An edit to apply to the input file.
+    llvm::Optional<tooling::Replacements> ApplyEdit;
+
+    static Effect applyEdit(tooling::Replacements R) {
+      Effect E;
+      E.ApplyEdit = std::move(R);
+      return E;
+    }
+    static Effect showMessage(StringRef S) {
+      Effect E;
+      E.ShowMessage = S;
+      return E;
+    }
+  };
+
   virtual ~Tweak() = default;
   /// A unique id of the action, it is always equal to the name of the class
   /// defining the Tweak. Definition is provided automatically by
@@ -63,13 +89,19 @@ public:
   /// should be moved into 'apply'.
   /// Returns true iff the action is available and apply() can be called on it.
   virtual bool prepare(const Selection &Sel) = 0;
-  /// Run the second stage of the action that would produce the actual changes.
+  /// Run the second stage of the action that would produce the actual effect.
   /// EXPECTS: prepare() was called and returned true.
-  virtual Expected<tooling::Replacements> apply(const Selection &Sel) = 0;
+  virtual Expected<Effect> apply(const Selection &Sel) = 0;
+
   /// A one-line title of the action that should be shown to the users in the
   /// UI.
   /// EXPECTS: prepare() was called and returned true.
   virtual std::string title() const = 0;
+  /// Describes what kind of action this is.
+  /// EXPECTS: prepare() was called and returned true.
+  virtual Intent intent() const = 0;
+  /// Is this a 'hidden' tweak, which are off by default.
+  virtual bool hidden() const { return false; }
 };
 
 // All tweaks must be registered in the .cpp file next to their definition.

Modified: clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt?rev=363680&r1=363679&r2=363680&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt Tue Jun 18 06:37:54 2019
@@ -12,6 +12,7 @@ set(LLVM_LINK_COMPONENTS
 # $<TARGET_OBJECTS:obj.clangDaemonTweaks> to a list of sources, see
 # clangd/tool/CMakeLists.txt for an example.
 add_clang_library(clangDaemonTweaks OBJECT
+  DumpAST.cpp
   RawStringLiteral.cpp
   SwapIfBranches.cpp
 

Added: clang-tools-extra/trunk/clangd/refactor/tweaks/DumpAST.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/refactor/tweaks/DumpAST.cpp?rev=363680&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/refactor/tweaks/DumpAST.cpp (added)
+++ clang-tools-extra/trunk/clangd/refactor/tweaks/DumpAST.cpp Tue Jun 18 06:37:54 2019
@@ -0,0 +1,139 @@
+//===--- DumpAST.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
+//
+//===----------------------------------------------------------------------===//
+// Defines a few tweaks that expose AST and related information.
+// Some of these are fairly clang-specific and hidden (e.g. textual AST dumps).
+// Others are more generally useful (class layout) and are exposed by default.
+//===----------------------------------------------------------------------===//
+#include "refactor/Tweak.h"
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/AST/Type.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+
+/// Dumps the AST of the selected node.
+/// Input:
+///   fcall("foo");
+///   ^^^^^
+/// Message:
+///   CallExpr
+///   |-DeclRefExpr fcall
+///   `-StringLiteral "foo"
+class DumpAST : public Tweak {
+public:
+  const char *id() const override final;
+
+  bool prepare(const Selection &Inputs) override {
+    for (auto N = Inputs.ASTSelection.commonAncestor(); N && !Node;
+         N = N->Parent)
+      if (dumpable(N->ASTNode))
+        Node = N->ASTNode;
+    return Node.hasValue();
+  }
+  Expected<Effect> apply(const Selection &Inputs) override;
+  std::string title() const override {
+    return llvm::formatv("Dump {0} AST", Node->getNodeKind().asStringRef());
+  }
+  Intent intent() const override { return Info; }
+  bool hidden() const override { return true; }
+
+private:
+  static bool dumpable(const ast_type_traits::DynTypedNode &N) {
+    // Sadly not all node types can be dumped, and there's no API to check.
+    // See DynTypedNode::dump().
+    return N.get<Decl>() || N.get<Stmt>() || N.get<Type>();
+  }
+
+  llvm::Optional<ast_type_traits::DynTypedNode> Node;
+};
+REGISTER_TWEAK(DumpAST)
+
+llvm::Expected<Tweak::Effect> DumpAST::apply(const Selection &Inputs) {
+  std::string Str;
+  llvm::raw_string_ostream OS(Str);
+  Node->dump(OS, Inputs.AST.getASTContext().getSourceManager());
+  return Effect::showMessage(std::move(OS.str()));
+}
+
+/// Dumps the SelectionTree.
+/// Input:
+/// int fcall(int);
+/// void foo() {
+///   fcall(2 + 2);
+///     ^^^^^
+/// }
+/// Message:
+/// TranslationUnitDecl
+///   FunctionDecl void foo()
+///     CompoundStmt {}
+///      .CallExpr fcall(2 + 2)
+///        ImplicitCastExpr fcall
+///         .DeclRefExpr fcall
+///        BinaryOperator 2 + 2
+///          *IntegerLiteral 2
+class ShowSelectionTree : public Tweak {
+public:
+  const char *id() const override final;
+
+  bool prepare(const Selection &Inputs) override {
+    return Inputs.ASTSelection.root() != nullptr;
+  }
+  Expected<Effect> apply(const Selection &Inputs) override {
+    return Effect::showMessage(llvm::to_string(Inputs.ASTSelection));
+  }
+  std::string title() const override { return "Show selection tree"; }
+  Intent intent() const override { return Info; }
+  bool hidden() const override { return true; }
+};
+REGISTER_TWEAK(ShowSelectionTree);
+
+/// Shows the layout of the RecordDecl under the cursor.
+/// Input:
+/// struct X { int foo; };
+/// ^^^^^^^^
+/// Message:
+///        0 | struct S
+///        0 |   int foo
+///          | [sizeof=4, dsize=4, align=4,
+///          |  nvsize=4, nvalign=4]
+class DumpRecordLayout : public Tweak {
+public:
+  const char *id() const override final;
+
+  bool prepare(const Selection &Inputs) override {
+    if (auto *Node = Inputs.ASTSelection.commonAncestor())
+      if (auto *D = Node->ASTNode.get<Decl>())
+        Record = dyn_cast<RecordDecl>(D);
+    return Record && Record->isThisDeclarationADefinition() &&
+           !Record->isDependentType();
+  }
+  Expected<Effect> apply(const Selection &Inputs) override {
+    std::string Str;
+    llvm::raw_string_ostream OS(Str);
+    Inputs.AST.getASTContext().DumpRecordLayout(Record, OS);
+    return Effect::showMessage(std::move(OS.str()));
+  }
+  std::string title() const override {
+    return llvm::formatv(
+        "Show {0} layout",
+        TypeWithKeyword::getTagTypeKindName(Record->getTagKind()));
+  }
+  Intent intent() const override { return Info; }
+
+private:
+  const RecordDecl *Record = nullptr;
+};
+REGISTER_TWEAK(DumpRecordLayout);
+
+} // namespace
+} // namespace clangd
+} // namespace clang

Modified: clang-tools-extra/trunk/clangd/refactor/tweaks/RawStringLiteral.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/refactor/tweaks/RawStringLiteral.cpp?rev=363680&r1=363679&r2=363680&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/refactor/tweaks/RawStringLiteral.cpp (original)
+++ clang-tools-extra/trunk/clangd/refactor/tweaks/RawStringLiteral.cpp Tue Jun 18 06:37:54 2019
@@ -39,8 +39,9 @@ public:
   const char *id() const override final;
 
   bool prepare(const Selection &Inputs) override;
-  Expected<tooling::Replacements> apply(const Selection &Inputs) override;
-  std::string title() const override;
+  Expected<Effect> apply(const Selection &Inputs) override;
+  std::string title() const override { return "Convert to raw string"; }
+  Intent intent() const override { return Refactor; }
 
 private:
   const clang::StringLiteral *Str = nullptr;
@@ -86,16 +87,13 @@ bool RawStringLiteral::prepare(const Sel
          needsRaw(Str->getBytes()) && canBeRaw(Str->getBytes());
 }
 
-Expected<tooling::Replacements>
-RawStringLiteral::apply(const Selection &Inputs) {
-  return tooling::Replacements(
+Expected<Tweak::Effect> RawStringLiteral::apply(const Selection &Inputs) {
+  return Effect::applyEdit(tooling::Replacements(
       tooling::Replacement(Inputs.AST.getSourceManager(), Str,
                            ("R\"(" + Str->getBytes() + ")\"").str(),
-                           Inputs.AST.getASTContext().getLangOpts()));
+                           Inputs.AST.getASTContext().getLangOpts())));
 }
 
-std::string RawStringLiteral::title() const { return "Convert to raw string"; }
-
 } // namespace
 } // namespace clangd
 } // namespace clang

Modified: clang-tools-extra/trunk/clangd/refactor/tweaks/SwapIfBranches.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/refactor/tweaks/SwapIfBranches.cpp?rev=363680&r1=363679&r2=363680&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/refactor/tweaks/SwapIfBranches.cpp (original)
+++ clang-tools-extra/trunk/clangd/refactor/tweaks/SwapIfBranches.cpp Tue Jun 18 06:37:54 2019
@@ -37,8 +37,9 @@ public:
   const char *id() const override final;
 
   bool prepare(const Selection &Inputs) override;
-  Expected<tooling::Replacements> apply(const Selection &Inputs) override;
-  std::string title() const override;
+  Expected<Effect> apply(const Selection &Inputs) override;
+  std::string title() const override { return "Swap if branches"; }
+  Intent intent() const override { return Refactor; }
 
 private:
   const IfStmt *If = nullptr;
@@ -60,7 +61,7 @@ bool SwapIfBranches::prepare(const Selec
          dyn_cast_or_null<CompoundStmt>(If->getElse());
 }
 
-Expected<tooling::Replacements> SwapIfBranches::apply(const Selection &Inputs) {
+Expected<Tweak::Effect> SwapIfBranches::apply(const Selection &Inputs) {
   auto &Ctx = Inputs.AST.getASTContext();
   auto &SrcMgr = Inputs.AST.getSourceManager();
 
@@ -89,11 +90,9 @@ Expected<tooling::Replacements> SwapIfBr
                                                  ElseRng->getBegin(),
                                                  ElseCode.size(), ThenCode)))
     return std::move(Err);
-  return Result;
+  return Effect::applyEdit(Result);
 }
 
-std::string SwapIfBranches::title() const { return "Swap if branches"; }
-
 } // namespace
 } // namespace clangd
 } // namespace clang

Modified: clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp?rev=363680&r1=363679&r2=363680&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp (original)
+++ clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp Tue Jun 18 06:37:54 2019
@@ -263,6 +263,11 @@ static llvm::cl::opt<CodeCompleteOptions
                                     "Always used text-based completion")),
         llvm::cl::init(CodeCompleteOptions().RunParser), llvm::cl::Hidden);
 
+static llvm::cl::opt<bool> HiddenFeatures(
+    "hidden-features",
+    llvm::cl::desc("Enable hidden features mostly useful to clangd developers"),
+    llvm::cl::init(false), llvm::cl::Hidden);
+
 namespace {
 
 /// \brief Supports a test URI scheme with relaxed constraints for lit tests.
@@ -459,6 +464,7 @@ int main(int argc, char *argv[]) {
   }
   Opts.StaticIndex = StaticIdx.get();
   Opts.AsyncThreadsCount = WorkerThreadsCount;
+  Opts.HiddenFeatures = HiddenFeatures;
 
   clangd::CodeCompleteOptions CCOpts;
   CCOpts.IncludeIneligibleResults = IncludeIneligibleResults;

Modified: clang-tools-extra/trunk/clangd/unittests/SelectionTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/unittests/SelectionTests.cpp?rev=363680&r1=363679&r2=363680&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/unittests/SelectionTests.cpp (original)
+++ clang-tools-extra/trunk/clangd/unittests/SelectionTests.cpp Tue Jun 18 06:37:54 2019
@@ -216,6 +216,16 @@ TEST(SelectionTest, CommonAncestor) {
   }
 }
 
+// Regression test: this used to match the injected X, not the outer X.
+TEST(SelectionTest, InjectedClassName) {
+  const char* Code = "struct ^X { int x; };";
+  auto AST = TestTU::withCode(Annotations(Code).code()).build();
+  auto T = makeSelectionTree(Code, AST);
+  ASSERT_EQ("CXXRecordDecl", nodeKind(T.commonAncestor())) << T;
+  auto *D = dyn_cast<CXXRecordDecl>(T.commonAncestor()->ASTNode.get<Decl>());
+  EXPECT_FALSE(D->isInjectedClassName());
+}
+
 TEST(SelectionTest, Selected) {
   // Selection with ^marks^.
   // Partially selected nodes marked with a [[range]].

Modified: clang-tools-extra/trunk/clangd/unittests/TweakTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/unittests/TweakTests.cpp?rev=363680&r1=363679&r2=363680&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/unittests/TweakTests.cpp (original)
+++ clang-tools-extra/trunk/clangd/unittests/TweakTests.cpp Tue Jun 18 06:37:54 2019
@@ -75,7 +75,8 @@ void checkAvailable(StringRef ID, llvm::
 void checkNotAvailable(StringRef ID, llvm::StringRef Input) {
   return checkAvailable(ID, Input, /*Available=*/false);
 }
-llvm::Expected<std::string> apply(StringRef ID, llvm::StringRef Input) {
+
+llvm::Expected<Tweak::Effect> apply(StringRef ID, llvm::StringRef Input) {
   Annotations Code(Input);
   Range SelectionRng;
   if (Code.points().size() != 0) {
@@ -97,15 +98,30 @@ llvm::Expected<std::string> apply(String
   auto T = prepareTweak(ID, S);
   if (!T)
     return T.takeError();
-  auto Replacements = (*T)->apply(S);
-  if (!Replacements)
-    return Replacements.takeError();
-  return applyAllReplacements(Code.code(), *Replacements);
+  return (*T)->apply(S);
+}
+
+llvm::Expected<std::string> applyEdit(StringRef ID, llvm::StringRef Input) {
+  auto Effect = apply(ID, Input);
+  if (!Effect)
+    return Effect.takeError();
+  if (!Effect->ApplyEdit)
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "No replacements");
+  Annotations Code(Input);
+  return applyAllReplacements(Code.code(), *Effect->ApplyEdit);
+}
+
+std::string getMessage(StringRef ID, llvm::StringRef Input) {
+  auto Effect = apply(ID, Input);
+  if (!Effect)
+    return "error: " + llvm::toString(Effect.takeError());
+  return Effect->ShowMessage.getValueOr("no message produced!");
 }
 
 void checkTransform(llvm::StringRef ID, llvm::StringRef Input,
                     std::string Output) {
-  auto Result = apply(ID, Input);
+  auto Result = applyEdit(ID, Input);
   ASSERT_TRUE(bool(Result)) << llvm::toString(Result.takeError()) << Input;
   EXPECT_EQ(Output, std::string(*Result)) << Input;
 }
@@ -216,6 +232,49 @@ literal)";
   checkTransform(ID, Input, Output);
 }
 
+TEST(TweakTest, DumpAST) {
+  llvm::StringLiteral ID = "DumpAST";
+
+  checkAvailable(ID, "^int f^oo() { re^turn 2 ^+ 2; }");
+  checkNotAvailable(ID, "/*c^omment*/ int foo() return 2 ^ + 2; }");
+
+  const char *Input = "int x = 2 ^+ 2;";
+  const char *Output = R"(BinaryOperator.*'\+'.*
+.*IntegerLiteral.*'int' 2.*
+.*IntegerLiteral.*'int' 2.*)";
+  EXPECT_THAT(getMessage(ID, Input), ::testing::MatchesRegex(Output));
+}
+
+TEST(TweakTest, ShowSelectionTree) {
+  llvm::StringLiteral ID = "ShowSelectionTree";
+
+  checkAvailable(ID, "^int f^oo() { re^turn 2 ^+ 2; }");
+  checkNotAvailable(ID, "/*c^omment*/ int foo() return 2 ^ + 2; }");
+
+  const char *Input = "int fcall(int); int x = fca[[ll(2 +]]2);";
+  const char *Output = R"(TranslationUnitDecl 
+  VarDecl int x = fcall(2 + 2)
+   .CallExpr fcall(2 + 2)
+      ImplicitCastExpr fcall
+       .DeclRefExpr fcall
+     .BinaryOperator 2 + 2
+       *IntegerLiteral 2
+)";
+  EXPECT_EQ(Output, getMessage(ID, Input));
+}
+
+TEST(TweakTest, DumpRecordLayout) {
+  llvm::StringLiteral ID = "DumpRecordLayout";
+  checkAvailable(ID, "^s^truct ^X ^{ int x; ^};");
+  checkNotAvailable(ID, "struct X { int ^a; };");
+  checkNotAvailable(ID, "struct ^X;");
+  checkNotAvailable(ID, "template <typename T> struct ^X { T t; };");
+  checkNotAvailable(ID, "enum ^X {};");
+
+  const char *Input = "struct ^X { int x; int y; }";
+  EXPECT_THAT(getMessage(ID, Input), ::testing::HasSubstr("0 |   int x"));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang




More information about the cfe-commits mailing list