[clang-tools-extra] r315577 - [clangd] less boilerplate in RPC dispatch

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 12 06:29:58 PDT 2017


Author: sammccall
Date: Thu Oct 12 06:29:58 2017
New Revision: 315577

URL: http://llvm.org/viewvc/llvm-project?rev=315577&view=rev
Log:
[clangd] less boilerplate in RPC dispatch

Summary:
Make the ProtocolHandlers glue between JSONRPCDispatcher and
ClangdLSPServer generic.
Eliminate small differences between methods, de-emphasize the unimportant
distinction between notifications and methods.

ClangdLSPServer is no longer responsible for producing a complete
JSON-RPC response, just the JSON of the result object. (In future, we
should move that JSON serialization out, too).
Handler methods now take a context object that we may hang more
functionality off in the future.

Added documentation to ProtocolHandlers.

Reviewers: ilya-biryukov, bkramer

Reviewed By: ilya-biryukov

Subscribers: cfe-commits

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

Modified:
    clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
    clang-tools-extra/trunk/clangd/ClangdLSPServer.h
    clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp
    clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h
    clang-tools-extra/trunk/clangd/Protocol.cpp
    clang-tools-extra/trunk/clangd/Protocol.h
    clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp
    clang-tools-extra/trunk/clangd/ProtocolHandlers.h
    clang-tools-extra/trunk/test/clangd/did-change-watch-files.test
    clang-tools-extra/trunk/test/clangd/fixits.test

Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=315577&r1=315576&r2=315577&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Thu Oct 12 06:29:58 2017
@@ -37,11 +37,9 @@ replacementsToEdits(StringRef Code,
 
 } // namespace
 
-void ClangdLSPServer::onInitialize(StringRef ID, InitializeParams IP,
-                                   JSONOutput &Out) {
-  Out.writeMessage(
-      R"({"jsonrpc":"2.0","id":)" + ID +
-      R"(,"result":{"capabilities":{
+void ClangdLSPServer::onInitialize(Ctx C, InitializeParams &Params) {
+  C.reply(
+      R"({"capabilities":{
           "textDocumentSync": 1,
           "documentFormattingProvider": true,
           "documentRangeFormattingProvider": true,
@@ -50,73 +48,68 @@ void ClangdLSPServer::onInitialize(Strin
           "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">",":"]},
           "signatureHelpProvider": {"triggerCharacters": ["(",","]},
           "definitionProvider": true
-        }}})");
-  if (IP.rootUri && !IP.rootUri->file.empty())
-    Server.setRootPath(IP.rootUri->file);
-  else if (IP.rootPath && !IP.rootPath->empty())
-    Server.setRootPath(*IP.rootPath);
+        }})");
+  if (Params.rootUri && !Params.rootUri->file.empty())
+    Server.setRootPath(Params.rootUri->file);
+  else if (Params.rootPath && !Params.rootPath->empty())
+    Server.setRootPath(*Params.rootPath);
 }
 
-void ClangdLSPServer::onShutdown(JSONOutput &Out) { IsDone = true; }
+void ClangdLSPServer::onShutdown(Ctx C, ShutdownParams &Params) {
+  IsDone = true;
+}
 
-void ClangdLSPServer::onDocumentDidOpen(DidOpenTextDocumentParams Params,
-                                        JSONOutput &Out) {
+void ClangdLSPServer::onDocumentDidOpen(Ctx C,
+                                        DidOpenTextDocumentParams &Params) {
   if (Params.metadata && !Params.metadata->extraFlags.empty())
     CDB.setExtraFlagsForFile(Params.textDocument.uri.file,
                              std::move(Params.metadata->extraFlags));
   Server.addDocument(Params.textDocument.uri.file, Params.textDocument.text);
 }
 
-void ClangdLSPServer::onDocumentDidChange(DidChangeTextDocumentParams Params,
-                                          JSONOutput &Out) {
+void ClangdLSPServer::onDocumentDidChange(Ctx C,
+                                          DidChangeTextDocumentParams &Params) {
   // We only support full syncing right now.
   Server.addDocument(Params.textDocument.uri.file,
                      Params.contentChanges[0].text);
 }
 
-void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
+void ClangdLSPServer::onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) {
   Server.onFileEvent(Params);
 }
 
-void ClangdLSPServer::onDocumentDidClose(DidCloseTextDocumentParams Params,
-                                         JSONOutput &Out) {
+void ClangdLSPServer::onDocumentDidClose(Ctx C,
+                                         DidCloseTextDocumentParams &Params) {
   Server.removeDocument(Params.textDocument.uri.file);
 }
 
 void ClangdLSPServer::onDocumentOnTypeFormatting(
-    DocumentOnTypeFormattingParams Params, StringRef ID, JSONOutput &Out) {
+    Ctx C, DocumentOnTypeFormattingParams &Params) {
   auto File = Params.textDocument.uri.file;
   std::string Code = Server.getDocument(File);
   std::string Edits =
       replacementsToEdits(Code, Server.formatOnType(File, Params.position));
-
-  Out.writeMessage(R"({"jsonrpc":"2.0","id":)" + ID.str() +
-                   R"(,"result":[)" + Edits + R"(]})");
+  C.reply("[" + Edits + "]");
 }
 
 void ClangdLSPServer::onDocumentRangeFormatting(
-    DocumentRangeFormattingParams Params, StringRef ID, JSONOutput &Out) {
+    Ctx C, DocumentRangeFormattingParams &Params) {
   auto File = Params.textDocument.uri.file;
   std::string Code = Server.getDocument(File);
   std::string Edits =
       replacementsToEdits(Code, Server.formatRange(File, Params.range));
-
-  Out.writeMessage(R"({"jsonrpc":"2.0","id":)" + ID.str() +
-                   R"(,"result":[)" + Edits + R"(]})");
+  C.reply("[" + Edits + "]");
 }
 
-void ClangdLSPServer::onDocumentFormatting(DocumentFormattingParams Params,
-                                           StringRef ID, JSONOutput &Out) {
+void ClangdLSPServer::onDocumentFormatting(Ctx C,
+                                           DocumentFormattingParams &Params) {
   auto File = Params.textDocument.uri.file;
   std::string Code = Server.getDocument(File);
   std::string Edits = replacementsToEdits(Code, Server.formatFile(File));
-
-  Out.writeMessage(R"({"jsonrpc":"2.0","id":)" + ID.str() +
-                   R"(,"result":[)" + Edits + R"(]})");
+  C.reply("[" + Edits + "]");
 }
 
-void ClangdLSPServer::onCodeAction(CodeActionParams Params, StringRef ID,
-                                   JSONOutput &Out) {
+void ClangdLSPServer::onCodeAction(Ctx C, CodeActionParams &Params) {
   // We provide a code action for each diagnostic at the requested location
   // which has FixIts available.
   std::string Code = Server.getDocument(Params.textDocument.uri.file);
@@ -136,16 +129,10 @@ void ClangdLSPServer::onCodeAction(CodeA
   }
   if (!Commands.empty())
     Commands.pop_back();
-
-  Out.writeMessage(
-      R"({"jsonrpc":"2.0","id":)" + ID.str() +
-      R"(, "result": [)" + Commands +
-      R"(]})");
+  C.reply("[" + Commands + "]");
 }
 
-void ClangdLSPServer::onCompletion(TextDocumentPositionParams Params,
-                                   StringRef ID, JSONOutput &Out) {
-
+void ClangdLSPServer::onCompletion(Ctx C, TextDocumentPositionParams &Params) {
   auto Items = Server
                    .codeComplete(Params.textDocument.uri.file,
                                  Position{Params.position.line,
@@ -162,26 +149,21 @@ void ClangdLSPServer::onCompletion(TextD
   }
   if (!Completions.empty())
     Completions.pop_back();
-  Out.writeMessage(
-      R"({"jsonrpc":"2.0","id":)" + ID.str() +
-      R"(,"result":[)" + Completions + R"(]})");
+  C.reply("[" + Completions + "]");
 }
 
-void ClangdLSPServer::onSignatureHelp(TextDocumentPositionParams Params,
-                                      StringRef ID, JSONOutput &Out) {
-  const auto SigHelp = SignatureHelp::unparse(
+void ClangdLSPServer::onSignatureHelp(Ctx C,
+                                      TextDocumentPositionParams &Params) {
+  C.reply(SignatureHelp::unparse(
       Server
           .signatureHelp(
               Params.textDocument.uri.file,
               Position{Params.position.line, Params.position.character})
-          .Value);
-  Out.writeMessage(R"({"jsonrpc":"2.0","id":)" + ID.str() + R"(,"result":)" +
-                   SigHelp + "}");
+          .Value));
 }
 
-void ClangdLSPServer::onGoToDefinition(TextDocumentPositionParams Params,
-                                       StringRef ID, JSONOutput &Out) {
-
+void ClangdLSPServer::onGoToDefinition(Ctx C,
+                                       TextDocumentPositionParams &Params) {
   auto Items = Server
                    .findDefinitions(Params.textDocument.uri.file,
                                     Position{Params.position.line,
@@ -195,23 +177,14 @@ void ClangdLSPServer::onGoToDefinition(T
   }
   if (!Locations.empty())
     Locations.pop_back();
-  Out.writeMessage(
-      R"({"jsonrpc":"2.0","id":)" + ID.str() +
-      R"(,"result":[)" + Locations + R"(]})");
+  C.reply("[" + Locations + "]");
 }
 
-void ClangdLSPServer::onSwitchSourceHeader(TextDocumentIdentifier Params,
-                                           StringRef ID, JSONOutput &Out) {
+void ClangdLSPServer::onSwitchSourceHeader(Ctx C,
+                                           TextDocumentIdentifier &Params) {
   llvm::Optional<Path> Result = Server.switchSourceHeader(Params.uri.file);
   std::string ResultUri;
-  if (Result)
-    ResultUri = URI::unparse(URI::fromFile(*Result));
-  else
-    ResultUri = "\"\"";
-
-  Out.writeMessage(
-      R"({"jsonrpc":"2.0","id":)" + ID.str() +
-      R"(,"result":)" + ResultUri + R"(})");
+  C.reply(Result ? URI::unparse(URI::fromFile(*Result)) : R"("")");
 }
 
 ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
@@ -226,7 +199,10 @@ void ClangdLSPServer::run(std::istream &
   assert(!IsDone && "Run was called before");
 
   // Set up JSONRPCDispatcher.
-  JSONRPCDispatcher Dispatcher(llvm::make_unique<Handler>(Out));
+  JSONRPCDispatcher Dispatcher(
+      [](RequestContext Ctx, llvm::yaml::MappingNode *Params) {
+        Ctx.replyError(-32601, "method not found");
+      });
   registerCallbackHandlers(Dispatcher, Out, /*Callbacks=*/*this);
 
   // Run the Language Server loop.

Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.h?rev=315577&r1=315576&r2=315577&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.h Thu Oct 12 06:29:58 2017
@@ -48,32 +48,24 @@ private:
                      Tagged<std::vector<DiagWithFixIts>> Diagnostics) override;
 
   // Implement ProtocolCallbacks.
-  void onInitialize(StringRef ID, InitializeParams IP,
-                    JSONOutput &Out) override;
-  void onShutdown(JSONOutput &Out) override;
-  void onDocumentDidOpen(DidOpenTextDocumentParams Params,
-                         JSONOutput &Out) override;
-  void onDocumentDidChange(DidChangeTextDocumentParams Params,
-                           JSONOutput &Out) override;
-  void onDocumentDidClose(DidCloseTextDocumentParams Params,
-                          JSONOutput &Out) override;
-  void onDocumentOnTypeFormatting(DocumentOnTypeFormattingParams Params,
-                                  StringRef ID, JSONOutput &Out) override;
-  void onDocumentRangeFormatting(DocumentRangeFormattingParams Params,
-                                 StringRef ID, JSONOutput &Out) override;
-  void onDocumentFormatting(DocumentFormattingParams Params, StringRef ID,
-                            JSONOutput &Out) override;
-  void onCodeAction(CodeActionParams Params, StringRef ID,
-                    JSONOutput &Out) override;
-  void onCompletion(TextDocumentPositionParams Params, StringRef ID,
-                    JSONOutput &Out) override;
-  void onSignatureHelp(TextDocumentPositionParams Params, StringRef ID,
-                       JSONOutput &Out) override;
-  void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID,
-                        JSONOutput &Out) override;
-  void onSwitchSourceHeader(TextDocumentIdentifier Params, StringRef ID,
-                            JSONOutput &Out) override;
-  void onFileEvent(const DidChangeWatchedFilesParams &Params) override;
+  void onInitialize(Ctx C, InitializeParams &Params) override;
+  void onShutdown(Ctx C, ShutdownParams &Params) override;
+  void onDocumentDidOpen(Ctx C, DidOpenTextDocumentParams &Params) override;
+  void onDocumentDidChange(Ctx C, DidChangeTextDocumentParams &Params) override;
+  void onDocumentDidClose(Ctx C, DidCloseTextDocumentParams &Params) override;
+  void
+  onDocumentOnTypeFormatting(Ctx C,
+                             DocumentOnTypeFormattingParams &Params) override;
+  void
+  onDocumentRangeFormatting(Ctx C,
+                            DocumentRangeFormattingParams &Params) override;
+  void onDocumentFormatting(Ctx C, DocumentFormattingParams &Params) override;
+  void onCodeAction(Ctx C, CodeActionParams &Params) override;
+  void onCompletion(Ctx C, TextDocumentPositionParams &Params) override;
+  void onSignatureHelp(Ctx C, TextDocumentPositionParams &Params) override;
+  void onGoToDefinition(Ctx C, TextDocumentPositionParams &Params) override;
+  void onSwitchSourceHeader(Ctx C, TextDocumentIdentifier &Params) override;
+  void onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) override;
 
   std::vector<clang::tooling::Replacement>
   getFixIts(StringRef File, const clangd::Diagnostic &D);

Modified: clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp?rev=315577&r1=315576&r2=315577&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp (original)
+++ clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp Thu Oct 12 06:29:58 2017
@@ -45,38 +45,42 @@ void JSONOutput::mirrorInput(const Twine
   InputMirror->flush();
 }
 
-void Handler::handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) {
-  Output.log("Method ignored.\n");
-  // Return that this method is unsupported.
-  writeMessage(
-      R"({"jsonrpc":"2.0","id":)" + ID +
-      R"(,"error":{"code":-32601,"message":"method not found"}})");
+void RequestContext::reply(const llvm::Twine &Result) {
+  if (ID.empty()) {
+    Out.log("Attempted to reply to a notification!\n");
+    return;
+  }
+  Out.writeMessage(llvm::Twine(R"({"jsonrpc":"2.0","id":)") + ID +
+                   R"(,"result":)" + Result + "}");
 }
 
-void Handler::handleNotification(llvm::yaml::MappingNode *Params) {
-  Output.log("Notification ignored.\n");
+void RequestContext::replyError(int code, const llvm::StringRef &Message) {
+  Out.log("Error " + llvm::Twine(code) + ": " + Message + "\n");
+  if (!ID.empty()) {
+    Out.writeMessage(llvm::Twine(R"({"jsonrpc":"2.0","id":)") + ID +
+                     R"(,"error":{"code":)" + llvm::Twine(code) +
+                     R"(,"message":")" + llvm::yaml::escape(Message) +
+                     R"("}})");
+  }
 }
 
-void JSONRPCDispatcher::registerHandler(StringRef Method,
-                                        std::unique_ptr<Handler> H) {
+void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) {
   assert(!Handlers.count(Method) && "Handler already registered!");
   Handlers[Method] = std::move(H);
 }
 
 static void
-callHandler(const llvm::StringMap<std::unique_ptr<Handler>> &Handlers,
+callHandler(const llvm::StringMap<JSONRPCDispatcher::Handler> &Handlers,
             llvm::yaml::ScalarNode *Method, llvm::yaml::ScalarNode *Id,
-            llvm::yaml::MappingNode *Params, Handler *UnknownHandler) {
-  llvm::SmallString<10> MethodStorage;
+            llvm::yaml::MappingNode *Params,
+            const JSONRPCDispatcher::Handler &UnknownHandler, JSONOutput &Out) {
+  llvm::SmallString<64> MethodStorage;
   auto I = Handlers.find(Method->getValue(MethodStorage));
-  auto *Handler = I != Handlers.end() ? I->second.get() : UnknownHandler;
-  if (Id)
-    Handler->handleMethod(Params, Id->getRawValue());
-  else
-    Handler->handleNotification(Params);
+  auto &Handler = I != Handlers.end() ? I->second : UnknownHandler;
+  Handler(RequestContext(Out, Id ? Id->getRawValue() : ""), Params);
 }
 
-bool JSONRPCDispatcher::call(StringRef Content) const {
+bool JSONRPCDispatcher::call(StringRef Content, JSONOutput &Out) const {
   llvm::SourceMgr SM;
   llvm::yaml::Stream YAMLStream(Content, SM);
 
@@ -124,7 +128,7 @@ bool JSONRPCDispatcher::call(StringRef C
       // because it will break clients that put the id after params. A possible
       // fix would be to split the parsing and execution phases.
       Params = dyn_cast<llvm::yaml::MappingNode>(Value);
-      callHandler(Handlers, Method, Id, Params, UnknownHandler.get());
+      callHandler(Handlers, Method, Id, Params, UnknownHandler, Out);
       return true;
     } else {
       return false;
@@ -135,7 +139,7 @@ bool JSONRPCDispatcher::call(StringRef C
   // leftovers.
   if (!Method)
     return false;
-  callHandler(Handlers, Method, Id, nullptr, UnknownHandler.get());
+  callHandler(Handlers, Method, Id, nullptr, UnknownHandler, Out);
 
   return true;
 }
@@ -215,7 +219,7 @@ void clangd::runLanguageServerLoop(std::
       Out.log("<-- " + JSONRef + "\n");
 
       // Finally, execute the action for this JSON message.
-      if (!Dispatcher.call(JSONRef))
+      if (!Dispatcher.call(JSONRef, Out))
         Out.log("JSON dispatch failed!\n");
 
       // If we're done, exit the loop.

Modified: clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h?rev=315577&r1=315576&r2=315577&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h (original)
+++ clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h Thu Oct 12 06:29:58 2017
@@ -12,6 +12,7 @@
 
 #include "Logger.h"
 #include "clang/Basic/LLVM.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/Support/YAMLParser.h"
 #include <iosfwd>
@@ -32,6 +33,7 @@ public:
   void writeMessage(const Twine &Message);
 
   /// Write to the logging stream.
+  /// No newline is implicitly added. (TODO: we should fix this!)
   void log(const Twine &Message) override;
 
   /// Mirror \p Message into InputMirror stream. Does nothing if InputMirror is
@@ -47,45 +49,43 @@ private:
   std::mutex StreamMutex;
 };
 
-/// Callback for messages sent to the server, called by the JSONRPCDispatcher.
-class Handler {
+/// Context object passed to handlers to allow replies.
+class RequestContext {
 public:
-  Handler(JSONOutput &Output) : Output(Output) {}
-  virtual ~Handler() = default;
+  RequestContext(JSONOutput &Out, StringRef ID) : Out(Out), ID(ID) {}
 
-  /// Called when the server receives a method call. This is supposed to return
-  /// a result on Outs. The default implementation returns an "unknown method"
-  /// error to the client and logs a warning.
-  virtual void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID);
-  /// Called when the server receives a notification. No result should be
-  /// written to Outs. The default implemetation logs a warning.
-  virtual void handleNotification(llvm::yaml::MappingNode *Params);
+  /// Sends a successful reply. Result should be well-formed JSON.
+  void reply(const Twine &Result);
+  /// Sends an error response to the client, and logs it.
+  void replyError(int code, const llvm::StringRef &Message);
 
-protected:
-  JSONOutput &Output;
-
-  /// Helper to write a JSONRPC result to Output.
-  void writeMessage(const Twine &Message) { Output.writeMessage(Message); }
+private:
+  JSONOutput &Out;
+  llvm::SmallString<64> ID; // Valid JSON, or empty for a notification.
 };
 
 /// Main JSONRPC entry point. This parses the JSONRPC "header" and calls the
 /// registered Handler for the method received.
 class JSONRPCDispatcher {
 public:
+  // A handler responds to requests for a particular method name.
+  using Handler =
+      std::function<void(RequestContext, llvm::yaml::MappingNode *)>;
+
   /// Create a new JSONRPCDispatcher. UnknownHandler is called when an unknown
   /// method is received.
-  JSONRPCDispatcher(std::unique_ptr<Handler> UnknownHandler)
+  JSONRPCDispatcher(Handler UnknownHandler)
       : UnknownHandler(std::move(UnknownHandler)) {}
 
   /// Registers a Handler for the specified Method.
-  void registerHandler(StringRef Method, std::unique_ptr<Handler> H);
+  void registerHandler(StringRef Method, Handler H);
 
   /// Parses a JSONRPC message and calls the Handler for it.
-  bool call(StringRef Content) const;
+  bool call(StringRef Content, JSONOutput &Out) const;
 
 private:
-  llvm::StringMap<std::unique_ptr<Handler>> Handlers;
-  std::unique_ptr<Handler> UnknownHandler;
+  llvm::StringMap<Handler> Handlers;
+  Handler UnknownHandler;
 };
 
 /// Parses input queries from LSP client (coming from \p In) and runs call

Modified: clang-tools-extra/trunk/clangd/Protocol.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.cpp?rev=315577&r1=315576&r2=315577&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.cpp (original)
+++ clang-tools-extra/trunk/clangd/Protocol.cpp Thu Oct 12 06:29:58 2017
@@ -305,11 +305,16 @@ TraceLevel getTraceLevel(llvm::StringRef
 llvm::Optional<InitializeParams>
 InitializeParams::parse(llvm::yaml::MappingNode *Params,
                         clangd::Logger &Logger) {
+  // If we don't understand the params, proceed with default parameters.
+  auto ParseFailure = [&] {
+    Logger.log("Failed to decode InitializeParams\n");
+    return InitializeParams();
+  };
   InitializeParams Result;
   for (auto &NextKeyValue : *Params) {
     auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
     if (!KeyString)
-      return llvm::None;
+      return ParseFailure();
 
     llvm::SmallString<10> KeyStorage;
     StringRef KeyValue = KeyString->getValue(KeyStorage);
@@ -322,10 +327,10 @@ InitializeParams::parse(llvm::yaml::Mapp
       auto *Value =
           dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
       if (!Value)
-        return llvm::None;
+        return ParseFailure();
       long long Val;
       if (llvm::getAsSignedInteger(Value->getValue(KeyStorage), 0, Val))
-        return llvm::None;
+        return ParseFailure();
       Result.processId = Val;
     } else if (KeyValue == "rootPath") {
       Result.rootPath = Value->getValue(KeyStorage);

Modified: clang-tools-extra/trunk/clangd/Protocol.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=315577&r1=315576&r2=315577&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.h (original)
+++ clang-tools-extra/trunk/clangd/Protocol.h Thu Oct 12 06:29:58 2017
@@ -166,6 +166,14 @@ enum class TraceLevel {
   Verbose = 2,
 };
 
+struct NoParams {
+  static llvm::Optional<NoParams> parse(llvm::yaml::MappingNode *Params,
+                                        Logger &Logger) {
+    return NoParams{};
+  }
+};
+using ShutdownParams = NoParams;
+
 struct InitializeParams {
   /// The process Id of the parent process that started
   /// the server. Is null if the process has not been started by another

Modified: clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp?rev=315577&r1=315576&r2=315577&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp (original)
+++ clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp Thu Oct 12 06:29:58 2017
@@ -11,255 +11,36 @@
 #include "ClangdLSPServer.h"
 #include "ClangdServer.h"
 #include "DraftStore.h"
+
 using namespace clang;
-using namespace clangd;
+using namespace clang::clangd;
 
 namespace {
 
-struct InitializeHandler : Handler {
-  InitializeHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
-      : Handler(Output), Callbacks(Callbacks) {}
-
-  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
-    auto IP = InitializeParams::parse(Params, Output);
-    if (!IP) {
-      Output.log("Failed to decode InitializeParams!\n");
-      IP = InitializeParams();
-    }
-
-    Callbacks.onInitialize(ID, *IP, Output);
-  }
-
-private:
-  ProtocolCallbacks &Callbacks;
-};
-
-struct ShutdownHandler : Handler {
-  ShutdownHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
-      : Handler(Output), Callbacks(Callbacks) {}
-
-  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
-    Callbacks.onShutdown(Output);
-  }
-
-private:
-  ProtocolCallbacks &Callbacks;
-};
-
-struct TextDocumentDidOpenHandler : Handler {
-  TextDocumentDidOpenHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
-      : Handler(Output), Callbacks(Callbacks) {}
-
-  void handleNotification(llvm::yaml::MappingNode *Params) override {
-    auto DOTDP = DidOpenTextDocumentParams::parse(Params, Output);
-    if (!DOTDP) {
-      Output.log("Failed to decode DidOpenTextDocumentParams!\n");
-      return;
-    }
-    Callbacks.onDocumentDidOpen(*DOTDP, Output);
-  }
-
-private:
-  ProtocolCallbacks &Callbacks;
-};
-
-struct TextDocumentDidChangeHandler : Handler {
-  TextDocumentDidChangeHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
-      : Handler(Output), Callbacks(Callbacks) {}
-
-  void handleNotification(llvm::yaml::MappingNode *Params) override {
-    auto DCTDP = DidChangeTextDocumentParams::parse(Params, Output);
-    if (!DCTDP || DCTDP->contentChanges.size() != 1) {
-      Output.log("Failed to decode DidChangeTextDocumentParams!\n");
-      return;
-    }
-
-    Callbacks.onDocumentDidChange(*DCTDP, Output);
-  }
-
-private:
-  ProtocolCallbacks &Callbacks;
-};
-
-struct TextDocumentDidCloseHandler : Handler {
-  TextDocumentDidCloseHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
-      : Handler(Output), Callbacks(Callbacks) {}
-
-  void handleNotification(llvm::yaml::MappingNode *Params) override {
-    auto DCTDP = DidCloseTextDocumentParams::parse(Params, Output);
-    if (!DCTDP) {
-      Output.log("Failed to decode DidCloseTextDocumentParams!\n");
-      return;
-    }
-
-    Callbacks.onDocumentDidClose(*DCTDP, Output);
-  }
-
-private:
-  ProtocolCallbacks &Callbacks;
-};
-
-struct TextDocumentOnTypeFormattingHandler : Handler {
-  TextDocumentOnTypeFormattingHandler(JSONOutput &Output,
-                                      ProtocolCallbacks &Callbacks)
-      : Handler(Output), Callbacks(Callbacks) {}
-
-  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
-    auto DOTFP = DocumentOnTypeFormattingParams::parse(Params, Output);
-    if (!DOTFP) {
-      Output.log("Failed to decode DocumentOnTypeFormattingParams!\n");
-      return;
-    }
-
-    Callbacks.onDocumentOnTypeFormatting(*DOTFP, ID, Output);
-  }
-
-private:
-  ProtocolCallbacks &Callbacks;
-};
-
-struct TextDocumentRangeFormattingHandler : Handler {
-  TextDocumentRangeFormattingHandler(JSONOutput &Output,
-                                     ProtocolCallbacks &Callbacks)
-      : Handler(Output), Callbacks(Callbacks) {}
-
-  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
-    auto DRFP = DocumentRangeFormattingParams::parse(Params, Output);
-    if (!DRFP) {
-      Output.log("Failed to decode DocumentRangeFormattingParams!\n");
-      return;
-    }
-
-    Callbacks.onDocumentRangeFormatting(*DRFP, ID, Output);
-  }
-
-private:
-  ProtocolCallbacks &Callbacks;
-};
-
-struct TextDocumentFormattingHandler : Handler {
-  TextDocumentFormattingHandler(JSONOutput &Output,
-                                ProtocolCallbacks &Callbacks)
-      : Handler(Output), Callbacks(Callbacks) {}
-
-  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
-    auto DFP = DocumentFormattingParams::parse(Params, Output);
-    if (!DFP) {
-      Output.log("Failed to decode DocumentFormattingParams!\n");
-      return;
-    }
-
-    Callbacks.onDocumentFormatting(*DFP, ID, Output);
-  }
-
-private:
-  ProtocolCallbacks &Callbacks;
-};
-
-struct CodeActionHandler : Handler {
-  CodeActionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
-      : Handler(Output), Callbacks(Callbacks) {}
-
-  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
-    auto CAP = CodeActionParams::parse(Params, Output);
-    if (!CAP) {
-      Output.log("Failed to decode CodeActionParams!\n");
-      return;
-    }
-
-    Callbacks.onCodeAction(*CAP, ID, Output);
-  }
-
-private:
-  ProtocolCallbacks &Callbacks;
-};
-
-struct CompletionHandler : Handler {
-  CompletionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
-      : Handler(Output), Callbacks(Callbacks) {}
-
-  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
-    auto TDPP = TextDocumentPositionParams::parse(Params, Output);
-    if (!TDPP) {
-      Output.log("Failed to decode TextDocumentPositionParams!\n");
-      return;
-    }
-
-    Callbacks.onCompletion(*TDPP, ID, Output);
-  }
-
-private:
-  ProtocolCallbacks &Callbacks;
-};
-
-struct SignatureHelpHandler : Handler {
-  SignatureHelpHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
-      : Handler(Output), Callbacks(Callbacks) {}
-
-  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
-    auto TDPP = TextDocumentPositionParams::parse(Params, Output);
-    if (!TDPP) {
-      Output.log("Failed to decode TextDocumentPositionParams!\n");
-      return;
-    }
-    Callbacks.onSignatureHelp(*TDPP, ID, Output);
-  }
-
-private:
-  ProtocolCallbacks &Callbacks;
-};
-
-struct GotoDefinitionHandler : Handler {
-  GotoDefinitionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
-      : Handler(Output), Callbacks(Callbacks) {}
-
-  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
-    auto TDPP = TextDocumentPositionParams::parse(Params, Output);
-    if (!TDPP) {
-      Output.log("Failed to decode TextDocumentPositionParams!\n");
-      return;
-    }
-
-    Callbacks.onGoToDefinition(*TDPP, ID, Output);
-  }
-
-private:
-  ProtocolCallbacks &Callbacks;
-};
-
-struct SwitchSourceHeaderHandler : Handler {
-  SwitchSourceHeaderHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
-      : Handler(Output), Callbacks(Callbacks) {}
-
-  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
-    auto TDPP = TextDocumentIdentifier::parse(Params, Output);
-    if (!TDPP)
-      return;
-
-    Callbacks.onSwitchSourceHeader(*TDPP, ID, Output);
-  }
-
-private:
-  ProtocolCallbacks &Callbacks;
-};
-
-struct WorkspaceDidChangeWatchedFilesHandler : Handler {
-  WorkspaceDidChangeWatchedFilesHandler(JSONOutput &Output,
-                                        ProtocolCallbacks &Callbacks)
-      : Handler(Output), Callbacks(Callbacks) {}
-
-  void handleNotification(llvm::yaml::MappingNode *Params) {
-    auto DCWFP = DidChangeWatchedFilesParams::parse(Params, Output);
-    if (!DCWFP) {
-      Output.log("Failed to decode DidChangeWatchedFilesParams.\n");
-      return;
-    }
-
-    Callbacks.onFileEvent(*DCWFP);
-  }
-
-private:
-  ProtocolCallbacks &Callbacks;
+// Helper for attaching ProtocolCallbacks methods to a JSONRPCDispatcher.
+// Invoke like: Registerer("foo", &ProtocolCallbacks::onFoo)
+// onFoo should be: void onFoo(Ctx &C, FooParams &Params)
+// FooParams should have a static factory method: parse(yaml::MappingNode*).
+struct HandlerRegisterer {
+  template <typename Param>
+  void operator()(StringRef Method,
+                  void (ProtocolCallbacks::*Handler)(RequestContext, Param)) {
+    // Capture pointers by value, as the lambda will outlive this object.
+    auto *Out = this->Out;
+    auto *Callbacks = this->Callbacks;
+    Dispatcher.registerHandler(
+        Method, [=](RequestContext C, llvm::yaml::MappingNode *RawParams) {
+          if (auto P = std::decay<Param>::type::parse(RawParams, *Out)) {
+            (Callbacks->*Handler)(std::move(C), *P);
+          } else {
+            Out->log("Failed to decode " + Method + " request.\n");
+          }
+        });
+  }
+
+  JSONRPCDispatcher &Dispatcher;
+  JSONOutput *Out;
+  ProtocolCallbacks *Callbacks;
 };
 
 } // namespace
@@ -267,44 +48,23 @@ private:
 void clangd::registerCallbackHandlers(JSONRPCDispatcher &Dispatcher,
                                       JSONOutput &Out,
                                       ProtocolCallbacks &Callbacks) {
-  Dispatcher.registerHandler(
-      "initialize", llvm::make_unique<InitializeHandler>(Out, Callbacks));
-  Dispatcher.registerHandler(
-      "shutdown", llvm::make_unique<ShutdownHandler>(Out, Callbacks));
-  Dispatcher.registerHandler(
-      "textDocument/didOpen",
-      llvm::make_unique<TextDocumentDidOpenHandler>(Out, Callbacks));
-  Dispatcher.registerHandler(
-      "textDocument/didClose",
-      llvm::make_unique<TextDocumentDidCloseHandler>(Out, Callbacks));
-  Dispatcher.registerHandler(
-      "textDocument/didChange",
-      llvm::make_unique<TextDocumentDidChangeHandler>(Out, Callbacks));
-  Dispatcher.registerHandler(
-      "textDocument/rangeFormatting",
-      llvm::make_unique<TextDocumentRangeFormattingHandler>(Out, Callbacks));
-  Dispatcher.registerHandler(
-      "textDocument/onTypeFormatting",
-      llvm::make_unique<TextDocumentOnTypeFormattingHandler>(Out, Callbacks));
-  Dispatcher.registerHandler(
-      "textDocument/formatting",
-      llvm::make_unique<TextDocumentFormattingHandler>(Out, Callbacks));
-  Dispatcher.registerHandler(
-      "textDocument/codeAction",
-      llvm::make_unique<CodeActionHandler>(Out, Callbacks));
-  Dispatcher.registerHandler(
-      "textDocument/completion",
-      llvm::make_unique<CompletionHandler>(Out, Callbacks));
-  Dispatcher.registerHandler(
-      "textDocument/signatureHelp",
-      llvm::make_unique<SignatureHelpHandler>(Out, Callbacks));
-  Dispatcher.registerHandler(
-      "textDocument/definition",
-      llvm::make_unique<GotoDefinitionHandler>(Out, Callbacks));
-  Dispatcher.registerHandler(
-      "textDocument/switchSourceHeader",
-      llvm::make_unique<SwitchSourceHeaderHandler>(Out, Callbacks));
-  Dispatcher.registerHandler(
-      "workspace/didChangeWatchedFiles",
-      llvm::make_unique<WorkspaceDidChangeWatchedFilesHandler>(Out, Callbacks));
+  HandlerRegisterer Register{Dispatcher, &Out, &Callbacks};
+
+  Register("initialize", &ProtocolCallbacks::onInitialize);
+  Register("shutdown", &ProtocolCallbacks::onShutdown);
+  Register("textDocument/didOpen", &ProtocolCallbacks::onDocumentDidOpen);
+  Register("textDocument/didClose", &ProtocolCallbacks::onDocumentDidClose);
+  Register("textDocument/didChange", &ProtocolCallbacks::onDocumentDidChange);
+  Register("textDocument/rangeFormatting",
+           &ProtocolCallbacks::onDocumentRangeFormatting);
+  Register("textDocument/onTypeFormatting",
+           &ProtocolCallbacks::onDocumentOnTypeFormatting);
+  Register("textDocument/formatting", &ProtocolCallbacks::onDocumentFormatting);
+  Register("textDocument/codeAction", &ProtocolCallbacks::onCodeAction);
+  Register("textDocument/completion", &ProtocolCallbacks::onCompletion);
+  Register("textDocument/signatureHelp", &ProtocolCallbacks::onSignatureHelp);
+  Register("textDocument/definition", &ProtocolCallbacks::onGoToDefinition);
+  Register("textDocument/switchSourceHeader",
+           &ProtocolCallbacks::onSwitchSourceHeader);
+  Register("workspace/didChangeWatchedFiles", &ProtocolCallbacks::onFileEvent);
 }

Modified: clang-tools-extra/trunk/clangd/ProtocolHandlers.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.h?rev=315577&r1=315576&r2=315577&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ProtocolHandlers.h (original)
+++ clang-tools-extra/trunk/clangd/ProtocolHandlers.h Thu Oct 12 06:29:58 2017
@@ -7,8 +7,11 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This file contains the actions performed when the server gets a specific
-// request.
+// ProtocolHandlers translates incoming JSON requests from JSONRPCDispatcher
+// into method calls on ClangLSPServer.
+//
+// Currently it parses requests into objects, but the ClangLSPServer is
+// responsible for producing JSON responses. We should move that here, too.
 //
 //===----------------------------------------------------------------------===//
 
@@ -23,37 +26,31 @@
 namespace clang {
 namespace clangd {
 
+// The interface implemented by ClangLSPServer to handle incoming requests.
 class ProtocolCallbacks {
 public:
+  using Ctx = RequestContext;
   virtual ~ProtocolCallbacks() = default;
 
-  virtual void onInitialize(StringRef ID, InitializeParams IP,
-                            JSONOutput &Out) = 0;
-  virtual void onShutdown(JSONOutput &Out) = 0;
-  virtual void onDocumentDidOpen(DidOpenTextDocumentParams Params,
-                                 JSONOutput &Out) = 0;
-  virtual void onDocumentDidChange(DidChangeTextDocumentParams Params,
-                                   JSONOutput &Out) = 0;
-
-  virtual void onDocumentDidClose(DidCloseTextDocumentParams Params,
-                                  JSONOutput &Out) = 0;
-  virtual void onDocumentFormatting(DocumentFormattingParams Params,
-                                    StringRef ID, JSONOutput &Out) = 0;
-  virtual void onDocumentOnTypeFormatting(DocumentOnTypeFormattingParams Params,
-                                          StringRef ID, JSONOutput &Out) = 0;
-  virtual void onDocumentRangeFormatting(DocumentRangeFormattingParams Params,
-                                         StringRef ID, JSONOutput &Out) = 0;
-  virtual void onCodeAction(CodeActionParams Params, StringRef ID,
-                            JSONOutput &Out) = 0;
-  virtual void onCompletion(TextDocumentPositionParams Params, StringRef ID,
-                            JSONOutput &Out) = 0;
-  virtual void onSignatureHelp(TextDocumentPositionParams Params, StringRef ID,
-                               JSONOutput &Out) = 0;
-  virtual void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID,
-                                JSONOutput &Out) = 0;
-  virtual void onSwitchSourceHeader(TextDocumentIdentifier Params, StringRef ID,
-                                    JSONOutput &Out) = 0;
-  virtual void onFileEvent(const DidChangeWatchedFilesParams &Params) = 0;
+  virtual void onInitialize(Ctx C, InitializeParams &Params) = 0;
+  virtual void onShutdown(Ctx C, ShutdownParams &Params) = 0;
+  virtual void onDocumentDidOpen(Ctx C, DidOpenTextDocumentParams &Params) = 0;
+  virtual void onDocumentDidChange(Ctx C,
+                                   DidChangeTextDocumentParams &Params) = 0;
+  virtual void onDocumentDidClose(Ctx C,
+                                  DidCloseTextDocumentParams &Params) = 0;
+  virtual void onDocumentFormatting(Ctx C,
+                                    DocumentFormattingParams &Params) = 0;
+  virtual void
+  onDocumentOnTypeFormatting(Ctx C, DocumentOnTypeFormattingParams &Params) = 0;
+  virtual void
+  onDocumentRangeFormatting(Ctx C, DocumentRangeFormattingParams &Params) = 0;
+  virtual void onCodeAction(Ctx C, CodeActionParams &Params) = 0;
+  virtual void onCompletion(Ctx C, TextDocumentPositionParams &Params) = 0;
+  virtual void onSignatureHelp(Ctx C, TextDocumentPositionParams &Params) = 0;
+  virtual void onGoToDefinition(Ctx C, TextDocumentPositionParams &Params) = 0;
+  virtual void onSwitchSourceHeader(Ctx C, TextDocumentIdentifier &Params) = 0;
+  virtual void onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) = 0;
 };
 
 void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,

Modified: clang-tools-extra/trunk/test/clangd/did-change-watch-files.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/did-change-watch-files.test?rev=315577&r1=315576&r2=315577&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/did-change-watch-files.test (original)
+++ clang-tools-extra/trunk/test/clangd/did-change-watch-files.test Thu Oct 12 06:29:58 2017
@@ -45,12 +45,12 @@ Content-Length: 140
 Content-Length: 86
 
 {"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":[""]}}
-# STDERR: Failed to decode DidChangeWatchedFilesParams.
+# STDERR: Failed to decode workspace/didChangeWatchedFiles request.
 # Changes field with no sequence
 Content-Length: 84
 
 {"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":""}}
-# STDERR: Failed to decode DidChangeWatchedFilesParams.
+# STDERR: Failed to decode workspace/didChangeWatchedFiles request.
 # Custom field
 Content-Length: 86
 

Modified: clang-tools-extra/trunk/test/clangd/fixits.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/fixits.test?rev=315577&r1=315576&r2=315577&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/fixits.test (original)
+++ clang-tools-extra/trunk/test/clangd/fixits.test Thu Oct 12 06:29:58 2017
@@ -15,13 +15,13 @@ Content-Length: 746
 
  {"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":2,"message":"using the result of an assignment as a condition without parentheses"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"place parentheses around the assignment to silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this assignment into an equality comparison"}]}}}
 #
-# CHECK: {"jsonrpc":"2.0","id":2, "result": [{"title":"Apply FixIt 'place parentheses around the assignment to silence this warning'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 32}}, "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end": {"line": 0, "character": 37}}, "newText": ")"}]]},{"title":"Apply FixIt 'use '==' to turn this assignment into an equality comparison'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character": 35}}, "newText": "=="}]]}]
+# CHECK: {"jsonrpc":"2.0","id":2,"result":[{"title":"Apply FixIt 'place parentheses around the assignment to silence this warning'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 32}}, "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end": {"line": 0, "character": 37}}, "newText": ")"}]]},{"title":"Apply FixIt 'use '==' to turn this assignment into an equality comparison'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character": 35}}, "newText": "=="}]]}]
 #
 Content-Length: 771
 
 {"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":2,"code":"1","source":"foo","message":"using the result of an assignment as a condition without parentheses"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"place parentheses around the assignment to silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this assignment into an equality comparison"}]}}}
 # Make sure unused "code" and "source" fields ignored gracefully
-# CHECK: {"jsonrpc":"2.0","id":2, "result": [{"title":"Apply FixIt 'place parentheses around the assignment to silence this warning'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 32}}, "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end": {"line": 0, "character": 37}}, "newText": ")"}]]},{"title":"Apply FixIt 'use '==' to turn this assignment into an equality comparison'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character": 35}}, "newText": "=="}]]}]
+# CHECK: {"jsonrpc":"2.0","id":2,"result":[{"title":"Apply FixIt 'place parentheses around the assignment to silence this warning'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 32}}, "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end": {"line": 0, "character": 37}}, "newText": ")"}]]},{"title":"Apply FixIt 'use '==' to turn this assignment into an equality comparison'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character": 35}}, "newText": "=="}]]}]
 #
 Content-Length: 44
 




More information about the cfe-commits mailing list