[clang-tools-extra] r344637 - Revert "[clangd] Refactor JSON-over-stdin/stdout code into Transport abstraction."

Krasimir Georgiev via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 16 11:44:41 PDT 2018


Author: krasimir
Date: Tue Oct 16 11:44:41 2018
New Revision: 344637

URL: http://llvm.org/viewvc/llvm-project?rev=344637&view=rev
Log:
Revert "[clangd] Refactor JSON-over-stdin/stdout code into Transport abstraction."

This reverts commit r344620.
Breaks upstream bots.

Removed:
    clang-tools-extra/trunk/clangd/JSONTransport.cpp
    clang-tools-extra/trunk/clangd/Transport.h
    clang-tools-extra/trunk/unittests/clangd/JSONTransportTests.cpp
Modified:
    clang-tools-extra/trunk/clangd/CMakeLists.txt
    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/tool/ClangdMain.cpp
    clang-tools-extra/trunk/test/clangd/compile-commands-path-in-initialize.test
    clang-tools-extra/trunk/test/clangd/completion-snippets.test
    clang-tools-extra/trunk/test/clangd/completion.test
    clang-tools-extra/trunk/test/clangd/crash-non-added-files.test
    clang-tools-extra/trunk/test/clangd/execute-command.test
    clang-tools-extra/trunk/test/clangd/input-mirror.test
    clang-tools-extra/trunk/test/clangd/signature-help.test
    clang-tools-extra/trunk/test/clangd/textdocument-didchange-fail.test
    clang-tools-extra/trunk/test/clangd/trace.test
    clang-tools-extra/trunk/test/clangd/xrefs.test
    clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt

Modified: clang-tools-extra/trunk/clangd/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CMakeLists.txt?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clangd/CMakeLists.txt Tue Oct 16 11:44:41 2018
@@ -26,7 +26,6 @@ add_clang_library(clangDaemon
   GlobalCompilationDatabase.cpp
   Headers.cpp
   JSONRPCDispatcher.cpp
-  JSONTransport.cpp
   Logger.cpp
   Protocol.cpp
   ProtocolHandlers.cpp

Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Tue Oct 16 11:44:41 2018
@@ -162,10 +162,7 @@ void ClangdLSPServer::onShutdown(Shutdow
   reply(nullptr);
 }
 
-void ClangdLSPServer::onExit(ExitParams &Params) {
-  // No work to do.
-  // JSONRPCDispatcher shuts down the transport after this notification.
-}
+void ClangdLSPServer::onExit(ExitParams &Params) { IsDone = true; }
 
 void ClangdLSPServer::onDocumentDidOpen(DidOpenTextDocumentParams &Params) {
   PathRef File = Params.textDocument.uri.file();
@@ -500,41 +497,39 @@ void ClangdLSPServer::onReference(Refere
                          });
 }
 
-ClangdLSPServer::ClangdLSPServer(class Transport &Transport,
+ClangdLSPServer::ClangdLSPServer(JSONOutput &Out,
                                  const clangd::CodeCompleteOptions &CCOpts,
                                  llvm::Optional<Path> CompileCommandsDir,
                                  bool ShouldUseInMemoryCDB,
                                  const ClangdServer::Options &Opts)
-    : Transport(Transport),
-      CDB(ShouldUseInMemoryCDB ? CompilationDB::makeInMemory()
-                               : CompilationDB::makeDirectoryBased(
-                                     std::move(CompileCommandsDir))),
+    : Out(Out), CDB(ShouldUseInMemoryCDB ? CompilationDB::makeInMemory()
+                                         : CompilationDB::makeDirectoryBased(
+                                               std::move(CompileCommandsDir))),
       CCOpts(CCOpts), SupportedSymbolKinds(defaultSymbolKinds()),
       SupportedCompletionItemKinds(defaultCompletionItemKinds()),
       Server(new ClangdServer(CDB.getCDB(), FSProvider, /*DiagConsumer=*/*this,
                               Opts)) {}
 
-bool ClangdLSPServer::run() {
+bool ClangdLSPServer::run(std::FILE *In, JSONStreamStyle InputStyle) {
+  assert(!IsDone && "Run was called before");
   assert(Server);
 
   // Set up JSONRPCDispatcher.
   JSONRPCDispatcher Dispatcher([](const json::Value &Params) {
     replyError(ErrorCode::MethodNotFound, "method not found");
-    return true;
   });
   registerCallbackHandlers(Dispatcher, /*Callbacks=*/*this);
 
   // Run the Language Server loop.
-  bool CleanExit = true;
-  if (auto Err = Dispatcher.runLanguageServerLoop(Transport)) {
-    elog("Transport error: {0}", std::move(Err));
-    CleanExit = false;
-  }
+  runLanguageServerLoop(In, Out, InputStyle, Dispatcher, IsDone);
 
+  // Make sure IsDone is set to true after this method exits to ensure assertion
+  // at the start of the method fires if it's ever executed again.
+  IsDone = true;
   // Destroy ClangdServer to ensure all worker threads finish.
   Server.reset();
 
-  return CleanExit && ShutdownRequestReceived;
+  return ShutdownRequestReceived;
 }
 
 std::vector<Fix> ClangdLSPServer::getFixes(StringRef File,
@@ -594,11 +589,15 @@ void ClangdLSPServer::onDiagnosticsReady
   }
 
   // Publish diagnostics.
-  Transport.notify("textDocument/publishDiagnostics",
-                   json::Object{
-                       {"uri", URIForFile{File}},
-                       {"diagnostics", std::move(DiagnosticsJSON)},
-                   });
+  Out.writeMessage(json::Object{
+      {"jsonrpc", "2.0"},
+      {"method", "textDocument/publishDiagnostics"},
+      {"params",
+       json::Object{
+           {"uri", URIForFile{File}},
+           {"diagnostics", std::move(DiagnosticsJSON)},
+       }},
+  });
 }
 
 void ClangdLSPServer::reparseOpenedFiles() {

Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.h?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.h Tue Oct 16 11:44:41 2018
@@ -37,16 +37,18 @@ public:
   /// If \p CompileCommandsDir has a value, compile_commands.json will be
   /// loaded only from \p CompileCommandsDir. Otherwise, clangd will look
   /// for compile_commands.json in all parent directories of each file.
-  ClangdLSPServer(Transport &Transport,
-                  const clangd::CodeCompleteOptions &CCOpts,
+  ClangdLSPServer(JSONOutput &Out, const clangd::CodeCompleteOptions &CCOpts,
                   llvm::Optional<Path> CompileCommandsDir,
                   bool ShouldUseInMemoryCDB, const ClangdServer::Options &Opts);
 
-  /// Run LSP server loop, communicating with the Transport provided in the
-  /// constructor. This method must not be executed more than once.
+  /// Run LSP server loop, receiving input for it from \p In. \p In must be
+  /// opened in binary mode. Output will be written using Out variable passed to
+  /// class constructor. This method must not be executed more than once for
+  /// each instance of ClangdLSPServer.
   ///
-  /// \return Whether we shut down cleanly with a 'shutdown' -> 'exit' sequence.
-  bool run();
+  /// \return Whether we received a 'shutdown' request before an 'exit' request.
+  bool run(std::FILE *In,
+           JSONStreamStyle InputStyle = JSONStreamStyle::Standard);
 
 private:
   // Implement DiagnosticsConsumer.
@@ -87,10 +89,16 @@ private:
   void reparseOpenedFiles();
   void applyConfiguration(const ClangdConfigurationParamsChange &Settings);
 
+  JSONOutput &Out;
   /// Used to indicate that the 'shutdown' request was received from the
   /// Language Server client.
   bool ShutdownRequestReceived = false;
 
+  /// Used to indicate that the 'exit' notification was received from the
+  /// Language Server client.
+  /// It's used to break out of the LSP parsing loop.
+  bool IsDone = false;
+
   std::mutex FixItsMutex;
   typedef std::map<clangd::Diagnostic, std::vector<Fix>, LSPDiagnosticCompare>
       DiagnosticToReplacementMap;
@@ -145,7 +153,6 @@ private:
     bool IsDirectoryBased;
   };
 
-  clangd::Transport &Transport;
   // Various ClangdServer parameters go here. It's important they're created
   // before ClangdServer.
   CompilationDB CDB;

Modified: clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp (original)
+++ clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp Tue Oct 16 11:44:41 2018
@@ -11,7 +11,6 @@
 #include "Cancellation.h"
 #include "ProtocolHandlers.h"
 #include "Trace.h"
-#include "Transport.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
@@ -29,7 +28,7 @@ using namespace clangd;
 
 namespace {
 static Key<json::Value> RequestID;
-static Key<Transport *> CurrentTransport;
+static Key<JSONOutput *> RequestOut;
 
 // When tracing, we trace a request and attach the response in reply().
 // Because the Span isn't available, we find the current request using Context.
@@ -59,6 +58,23 @@ public:
 Key<std::unique_ptr<RequestSpan>> RequestSpan::RSKey;
 } // namespace
 
+void JSONOutput::writeMessage(const json::Value &Message) {
+  std::string S;
+  llvm::raw_string_ostream OS(S);
+  if (Pretty)
+    OS << llvm::formatv("{0:2}", Message);
+  else
+    OS << Message;
+  OS.flush();
+
+  {
+    std::lock_guard<std::mutex> Guard(StreamMutex);
+    Outs << "Content-Length: " << S.size() << "\r\n\r\n" << S;
+    Outs.flush();
+  }
+  vlog(">>> {0}\n", S);
+}
+
 void JSONOutput::log(Logger::Level Level,
                      const llvm::formatv_object_base &Message) {
   if (Level < MinLevel)
@@ -71,6 +87,14 @@ void JSONOutput::log(Logger::Level Level
   Logs.flush();
 }
 
+void JSONOutput::mirrorInput(const Twine &Message) {
+  if (!InputMirror)
+    return;
+
+  *InputMirror << Message;
+  InputMirror->flush();
+}
+
 void clangd::reply(json::Value &&Result) {
   auto ID = getRequestId();
   if (!ID) {
@@ -80,8 +104,12 @@ void clangd::reply(json::Value &&Result)
   RequestSpan::attach([&](json::Object &Args) { Args["Reply"] = Result; });
   log("--> reply({0})", *ID);
   Context::current()
-      .getExisting(CurrentTransport)
-      ->reply(std::move(*ID), std::move(Result));
+      .getExisting(RequestOut)
+      ->writeMessage(json::Object{
+          {"jsonrpc", "2.0"},
+          {"id", *ID},
+          {"result", std::move(Result)},
+      });
 }
 
 void clangd::replyError(ErrorCode Code, const llvm::StringRef &Message) {
@@ -94,8 +122,13 @@ void clangd::replyError(ErrorCode Code,
   if (auto ID = getRequestId()) {
     log("--> reply({0}) error: {1}", *ID, Message);
     Context::current()
-        .getExisting(CurrentTransport)
-        ->reply(std::move(*ID), make_error<LSPError>(Message, Code));
+        .getExisting(RequestOut)
+        ->writeMessage(json::Object{
+            {"jsonrpc", "2.0"},
+            {"id", *ID},
+            {"error", json::Object{{"code", static_cast<int>(Code)},
+                                   {"message", Message}}},
+        });
   }
 }
 
@@ -118,20 +151,22 @@ void clangd::call(StringRef Method, json
   auto ID = 1;
   log("--> {0}({1})", Method, ID);
   Context::current()
-      .getExisting(CurrentTransport)
-      ->call(Method, std::move(Params), ID);
+      .getExisting(RequestOut)
+      ->writeMessage(json::Object{
+          {"jsonrpc", "2.0"},
+          {"id", ID},
+          {"method", Method},
+          {"params", std::move(Params)},
+      });
 }
 
 JSONRPCDispatcher::JSONRPCDispatcher(Handler UnknownHandler)
     : UnknownHandler(std::move(UnknownHandler)) {
   registerHandler("$/cancelRequest", [this](const json::Value &Params) {
     if (auto *O = Params.getAsObject())
-      if (auto *ID = O->get("id")) {
-        cancelRequest(*ID);
-        return true;
-      }
+      if (auto *ID = O->get("id"))
+        return cancelRequest(*ID);
     log("Bad cancellation request: {0}", Params);
-    return true;
   });
 }
 
@@ -140,48 +175,64 @@ void JSONRPCDispatcher::registerHandler(
   Handlers[Method] = std::move(H);
 }
 
-bool JSONRPCDispatcher::onCall(StringRef Method, json::Value Params,
-                               json::Value ID) {
-  log("<-- {0}({1})", Method, ID);
-  auto I = Handlers.find(Method);
+static void logIncomingMessage(const llvm::Optional<json::Value> &ID,
+                               llvm::Optional<StringRef> Method,
+                               const json::Object *Error) {
+  if (Method) { // incoming request
+    if (ID)     // call
+      log("<-- {0}({1})", *Method, *ID);
+    else // notification
+      log("<-- {0}", *Method);
+  } else if (ID) { // response, ID must be provided
+    if (Error)
+      log("<-- reply({0}) error: {1}", *ID,
+          Error->getString("message").getValueOr("<no message>"));
+    else
+      log("<-- reply({0})", *ID);
+  }
+}
+
+bool JSONRPCDispatcher::call(const json::Value &Message, JSONOutput &Out) {
+  // Message must be an object with "jsonrpc":"2.0".
+  auto *Object = Message.getAsObject();
+  if (!Object || Object->getString("jsonrpc") != Optional<StringRef>("2.0"))
+    return false;
+  // ID may be any JSON value. If absent, this is a notification.
+  llvm::Optional<json::Value> ID;
+  if (auto *I = Object->get("id"))
+    ID = std::move(*I);
+  auto Method = Object->getString("method");
+  logIncomingMessage(ID, Method, Object->getObject("error"));
+  if (!Method) // We only handle incoming requests, and ignore responses.
+    return false;
+  // Params should be given, use null if not.
+  json::Value Params = nullptr;
+  if (auto *P = Object->get("params"))
+    Params = std::move(*P);
+
+  auto I = Handlers.find(*Method);
   auto &Handler = I != Handlers.end() ? I->second : UnknownHandler;
 
   // Create a Context that contains request information.
-  WithContextValue WithID(RequestID, ID);
+  WithContextValue WithRequestOut(RequestOut, &Out);
+  llvm::Optional<WithContextValue> WithID;
+  if (ID)
+    WithID.emplace(RequestID, *ID);
 
   // Create a tracing Span covering the whole request lifetime.
-  trace::Span Tracer(Method);
-  SPAN_ATTACH(Tracer, "ID", ID);
+  trace::Span Tracer(*Method);
+  if (ID)
+    SPAN_ATTACH(Tracer, "ID", *ID);
   SPAN_ATTACH(Tracer, "Params", Params);
 
-  // Calls can be canceled by the client. Add cancellation context.
-  WithContext WithCancel(cancelableRequestContext(ID));
-
-  // Stash a reference to the span args, so later calls can add metadata.
-  WithContext WithRequestSpan(RequestSpan::stash(Tracer));
-  return Handler(std::move(Params));
-}
-
-bool JSONRPCDispatcher::onNotify(StringRef Method, json::Value Params) {
-  log("<-- {0}", Method);
-  auto I = Handlers.find(Method);
-  auto &Handler = I != Handlers.end() ? I->second : UnknownHandler;
-
-  // Create a tracing Span covering the whole request lifetime.
-  trace::Span Tracer(Method);
-  SPAN_ATTACH(Tracer, "Params", Params);
+  // Requests with IDs can be canceled by the client. Add cancellation context.
+  llvm::Optional<WithContext> WithCancel;
+  if (ID)
+    WithCancel.emplace(cancelableRequestContext(*ID));
 
   // Stash a reference to the span args, so later calls can add metadata.
   WithContext WithRequestSpan(RequestSpan::stash(Tracer));
-  return Handler(std::move(Params));
-}
-
-bool JSONRPCDispatcher::onReply(json::Value ID, Expected<json::Value> Result) {
-  // We ignore replies, just log them.
-  if (Result)
-    log("<-- reply({0})", ID);
-  else
-    log("<-- reply({0}) error: {1}", ID, llvm::toString(Result.takeError()));
+  Handler(std::move(Params));
   return true;
 }
 
@@ -215,10 +266,162 @@ void JSONRPCDispatcher::cancelRequest(co
     It->second.first(); // Invoke the canceler.
 }
 
-llvm::Error JSONRPCDispatcher::runLanguageServerLoop(Transport &Transport) {
-  // Propagate transport to all handlers so they can reply.
-  WithContextValue WithTransport(CurrentTransport, &Transport);
-  return Transport.loop(*this);
+// Tries to read a line up to and including \n.
+// If failing, feof() or ferror() will be set.
+static bool readLine(std::FILE *In, std::string &Out) {
+  static constexpr int BufSize = 1024;
+  size_t Size = 0;
+  Out.clear();
+  for (;;) {
+    Out.resize(Size + BufSize);
+    // Handle EINTR which is sent when a debugger attaches on some platforms.
+    if (!llvm::sys::RetryAfterSignal(nullptr, ::fgets, &Out[Size], BufSize, In))
+      return false;
+    clearerr(In);
+    // If the line contained null bytes, anything after it (including \n) will
+    // be ignored. Fortunately this is not a legal header or JSON.
+    size_t Read = std::strlen(&Out[Size]);
+    if (Read > 0 && Out[Size + Read - 1] == '\n') {
+      Out.resize(Size + Read);
+      return true;
+    }
+    Size += Read;
+  }
+}
+
+// Returns None when:
+//  - ferror() or feof() are set.
+//  - Content-Length is missing or empty (protocol error)
+static llvm::Optional<std::string> readStandardMessage(std::FILE *In,
+                                                       JSONOutput &Out) {
+  // A Language Server Protocol message starts with a set of HTTP headers,
+  // delimited  by \r\n, and terminated by an empty line (\r\n).
+  unsigned long long ContentLength = 0;
+  std::string Line;
+  while (true) {
+    if (feof(In) || ferror(In) || !readLine(In, Line))
+      return llvm::None;
+
+    Out.mirrorInput(Line);
+    llvm::StringRef LineRef(Line);
+
+    // We allow comments in headers. Technically this isn't part
+    // of the LSP specification, but makes writing tests easier.
+    if (LineRef.startswith("#"))
+      continue;
+
+    // Content-Length is a mandatory header, and the only one we handle.
+    if (LineRef.consume_front("Content-Length: ")) {
+      if (ContentLength != 0) {
+        elog("Warning: Duplicate Content-Length header received. "
+             "The previous value for this message ({0}) was ignored.",
+             ContentLength);
+      }
+      llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength);
+      continue;
+    } else if (!LineRef.trim().empty()) {
+      // It's another header, ignore it.
+      continue;
+    } else {
+      // An empty line indicates the end of headers.
+      // Go ahead and read the JSON.
+      break;
+    }
+  }
+
+  // The fuzzer likes crashing us by sending "Content-Length: 9999999999999999"
+  if (ContentLength > 1 << 30) { // 1024M
+    elog("Refusing to read message with long Content-Length: {0}. "
+         "Expect protocol errors",
+         ContentLength);
+    return llvm::None;
+  }
+  if (ContentLength == 0) {
+    log("Warning: Missing Content-Length header, or zero-length message.");
+    return llvm::None;
+  }
+
+  std::string JSON(ContentLength, '\0');
+  for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) {
+    // Handle EINTR which is sent when a debugger attaches on some platforms.
+    Read = llvm::sys::RetryAfterSignal(0u, ::fread, &JSON[Pos], 1,
+                                       ContentLength - Pos, In);
+    Out.mirrorInput(StringRef(&JSON[Pos], Read));
+    if (Read == 0) {
+      elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos,
+           ContentLength);
+      return llvm::None;
+    }
+    clearerr(In); // If we're done, the error was transient. If we're not done,
+                  // either it was transient or we'll see it again on retry.
+    Pos += Read;
+  }
+  return std::move(JSON);
+}
+
+// For lit tests we support a simplified syntax:
+// - messages are delimited by '---' on a line by itself
+// - lines starting with # are ignored.
+// This is a testing path, so favor simplicity over performance here.
+// When returning None, feof() or ferror() will be set.
+static llvm::Optional<std::string> readDelimitedMessage(std::FILE *In,
+                                                        JSONOutput &Out) {
+  std::string JSON;
+  std::string Line;
+  while (readLine(In, Line)) {
+    auto LineRef = llvm::StringRef(Line).trim();
+    if (LineRef.startswith("#")) // comment
+      continue;
+
+    // found a delimiter
+    if (LineRef.rtrim() == "---")
+      break;
+
+    JSON += Line;
+  }
+
+  if (ferror(In)) {
+    elog("Input error while reading message!");
+    return llvm::None;
+  } else { // Including EOF
+    Out.mirrorInput(
+        llvm::formatv("Content-Length: {0}\r\n\r\n{1}", JSON.size(), JSON));
+    return std::move(JSON);
+  }
+}
+
+// The use of C-style std::FILE* IO deserves some explanation.
+// Previously, std::istream was used. When a debugger attached on MacOS, the
+// process received EINTR, the stream went bad, and clangd exited.
+// A retry-on-EINTR loop around reads solved this problem, but caused clangd to
+// sometimes hang rather than exit on other OSes. The interaction between
+// istreams and signals isn't well-specified, so it's hard to get this right.
+// The C APIs seem to be clearer in this respect.
+void clangd::runLanguageServerLoop(std::FILE *In, JSONOutput &Out,
+                                   JSONStreamStyle InputStyle,
+                                   JSONRPCDispatcher &Dispatcher,
+                                   bool &IsDone) {
+  auto &ReadMessage =
+      (InputStyle == Delimited) ? readDelimitedMessage : readStandardMessage;
+  while (!IsDone && !feof(In)) {
+    if (ferror(In)) {
+      elog("IO error: {0}", llvm::sys::StrError());
+      return;
+    }
+    if (auto JSON = ReadMessage(In, Out)) {
+      if (auto Doc = json::parse(*JSON)) {
+        // Log the formatted message.
+        vlog(Out.Pretty ? "<<< {0:2}\n" : "<<< {0}\n", *Doc);
+        // Finally, execute the action for this JSON message.
+        if (!Dispatcher.call(*Doc, Out))
+          elog("JSON dispatch failed!");
+      } else {
+        // Parse error. Log the raw message.
+        vlog("<<< {0}\n", *JSON);
+        elog("JSON parse error: {0}", llvm::toString(Doc.takeError()));
+      }
+    }
+  }
 }
 
 const json::Value *clangd::getRequestId() {

Modified: clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h (original)
+++ clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h Tue Oct 16 11:44:41 2018
@@ -14,7 +14,6 @@
 #include "Logger.h"
 #include "Protocol.h"
 #include "Trace.h"
-#include "Transport.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringMap.h"
@@ -25,19 +24,37 @@
 namespace clang {
 namespace clangd {
 
-// Logs to an output stream, such as stderr.
-// FIXME: Rename to StreamLogger or such, and move to Logger.h.
+/// Encapsulates output and logs streams and provides thread-safe access to
+/// them.
 class JSONOutput : public Logger {
+  // FIXME(ibiryukov): figure out if we can shrink the public interface of
+  // JSONOutput now that we pass Context everywhere.
 public:
-  JSONOutput(llvm::raw_ostream &Logs, Logger::Level MinLevel)
-      : MinLevel(MinLevel), Logs(Logs) {}
+  JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs,
+             Logger::Level MinLevel, llvm::raw_ostream *InputMirror = nullptr,
+             bool Pretty = false)
+      : Pretty(Pretty), MinLevel(MinLevel), Outs(Outs), Logs(Logs),
+        InputMirror(InputMirror) {}
+
+  /// Emit a JSONRPC message.
+  void writeMessage(const llvm::json::Value &Result);
 
   /// Write a line to the logging stream.
   void log(Level, const llvm::formatv_object_base &Message) override;
 
+  /// Mirror \p Message into InputMirror stream. Does nothing if InputMirror is
+  /// null.
+  /// Unlike other methods of JSONOutput, mirrorInput is not thread-safe.
+  void mirrorInput(const Twine &Message);
+
+  // Whether output should be pretty-printed.
+  const bool Pretty;
+
 private:
   Logger::Level MinLevel;
+  llvm::raw_ostream &Outs;
   llvm::raw_ostream &Logs;
+  llvm::raw_ostream *InputMirror;
 
   std::mutex StreamMutex;
 };
@@ -64,15 +81,14 @@ void call(llvm::StringRef Method, llvm::
 ///
 /// The `$/cancelRequest` notification is handled by the dispatcher itself.
 /// It marks the matching request as cancelled, if it's still running.
-class JSONRPCDispatcher : private Transport::MessageHandler {
+class JSONRPCDispatcher {
 public:
   /// A handler responds to requests for a particular method name.
-  /// It returns false if the server should now shut down.
   ///
   /// JSONRPCDispatcher will mark the handler's context as cancelled if a
   /// matching cancellation request is received. Handlers are encouraged to
   /// check for cancellation and fail quickly in this case.
-  using Handler = std::function<bool(const llvm::json::Value &)>;
+  using Handler = std::function<void(const llvm::json::Value &)>;
 
   /// Create a new JSONRPCDispatcher. UnknownHandler is called when an unknown
   /// method is received.
@@ -81,22 +97,10 @@ public:
   /// Registers a Handler for the specified Method.
   void registerHandler(StringRef Method, Handler H);
 
-  /// Parses input queries from LSP client (coming from \p In) and runs call
-  /// method for each query.
-  ///
-  /// Input stream(\p In) must be opened in binary mode to avoid
-  /// preliminary replacements of \r\n with \n. We use C-style FILE* for reading
-  /// as std::istream has unclear interaction with signals, which are sent by
-  /// debuggers on some OSs.
-  llvm::Error runLanguageServerLoop(Transport &);
+  /// Parses a JSONRPC message and calls the Handler for it.
+  bool call(const llvm::json::Value &Message, JSONOutput &Out);
 
 private:
-  bool onReply(llvm::json::Value ID,
-               llvm::Expected<llvm::json::Value> Result) override;
-  bool onNotify(llvm::StringRef Method, llvm::json::Value Message) override;
-  bool onCall(llvm::StringRef Method, llvm::json::Value Message,
-              llvm::json::Value ID) override;
-
   // Tracking cancellations needs a mutex: handlers may finish on a different
   // thread, and that's when we clean up entries in the map.
   mutable std::mutex RequestCancelersMutex;
@@ -109,6 +113,25 @@ private:
   Handler UnknownHandler;
 };
 
+/// Controls the way JSON-RPC messages are encoded (both input and output).
+enum JSONStreamStyle {
+  /// Encoding per the LSP specification, with mandatory Content-Length header.
+  Standard,
+  /// Messages are delimited by a '---' line. Comment lines start with #.
+  Delimited
+};
+
+/// Parses input queries from LSP client (coming from \p In) and runs call
+/// method of \p Dispatcher for each query.
+/// After handling each query checks if \p IsDone is set true and exits the loop
+/// if it is.
+/// Input stream(\p In) must be opened in binary mode to avoid preliminary
+/// replacements of \r\n with \n.
+/// We use C-style FILE* for reading as std::istream has unclear interaction
+/// with signals, which are sent by debuggers on some OSs.
+void runLanguageServerLoop(std::FILE *In, JSONOutput &Out,
+                           JSONStreamStyle InputStyle,
+                           JSONRPCDispatcher &Dispatcher, bool &IsDone);
 } // namespace clangd
 } // namespace clang
 

Removed: clang-tools-extra/trunk/clangd/JSONTransport.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/JSONTransport.cpp?rev=344636&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/JSONTransport.cpp (original)
+++ clang-tools-extra/trunk/clangd/JSONTransport.cpp (removed)
@@ -1,298 +0,0 @@
-//===--- JSONTransport.cpp - sending and receiving LSP messages over JSON -===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-#include "Logger.h"
-#include "Protocol.h" // For LSPError
-#include "Transport.h"
-#include "llvm/Support/Errno.h"
-
-using namespace llvm;
-namespace clang {
-namespace clangd {
-namespace {
-
-json::Object encodeError(Error E) {
-  std::string Message;
-  ErrorCode Code = ErrorCode::UnknownErrorCode;
-  if (Error Unhandled =
-          handleErrors(std::move(E), [&](const LSPError &L) -> Error {
-            Message = L.Message;
-            Code = L.Code;
-            return Error::success();
-          }))
-    Message = llvm::toString(std::move(Unhandled));
-
-  return json::Object{
-      {"message", std::move(Message)},
-      {"code", int64_t(Code)},
-  };
-}
-
-Error decodeError(const json::Object &O) {
-  std::string Msg = O.getString("message").getValueOr("Unspecified error");
-  if (auto Code = O.getInteger("code"))
-    return make_error<LSPError>(std::move(Msg), ErrorCode(*Code));
-  return make_error<StringError>(std::move(Msg), inconvertibleErrorCode());
-}
-
-class JSONTransport : public Transport {
-public:
-  JSONTransport(std::FILE *In, llvm::raw_ostream &Out,
-                llvm::raw_ostream *InMirror, bool Pretty, JSONStreamStyle Style)
-      : In(In), Out(Out), InMirror(InMirror ? *InMirror : nulls()),
-        Pretty(Pretty), Style(Style) {}
-
-  void notify(StringRef Method, json::Value Params) override {
-    sendMessage(json::Object{
-        {"jsonrpc", "2.0"},
-        {"method", Method},
-        {"params", std::move(Params)},
-    });
-  }
-  void call(StringRef Method, json::Value Params, json::Value ID) override {
-    sendMessage(json::Object{
-        {"jsonrpc", "2.0"},
-        {"id", std::move(ID)},
-        {"method", Method},
-        {"params", std::move(Params)},
-    });
-  }
-  void reply(json::Value ID, Expected<json::Value> Result) override {
-    if (Result) {
-      sendMessage(json::Object{
-          {"jsonrpc", "2.0"},
-          {"id", std::move(ID)},
-          {"result", std::move(*Result)},
-      });
-    } else {
-      sendMessage(json::Object{
-          {"jsonrpc", "2.0"},
-          {"id", std::move(ID)},
-          {"error", encodeError(Result.takeError())},
-      });
-    }
-  }
-
-  Error loop(MessageHandler &Handler) override {
-    while (!feof(In)) {
-      if (ferror(In))
-        return errorCodeToError(std::error_code(errno, std::system_category()));
-      if (auto JSON = readRawMessage()) {
-        if (auto Doc = json::parse(*JSON)) {
-          vlog(Pretty ? "<<< {0:2}\n" : "<<< {0}\n", *Doc);
-          if (!handleMessage(std::move(*Doc), Handler))
-            return Error::success(); // we saw the "exit" notification.
-        } else {
-          // Parse error. Log the raw message.
-          vlog("<<< {0}\n", *JSON);
-          elog("JSON parse error: {0}", llvm::toString(Doc.takeError()));
-        }
-      }
-    }
-    return errorCodeToError(std::make_error_code(std::errc::io_error));
-  }
-
-private:
-  // Dispatches incoming message to Handler onNotify/onCall/onReply.
-  bool handleMessage(llvm::json::Value Message, MessageHandler &Handler);
-  // Writes outgoing message to Out stream.
-  void sendMessage(llvm::json::Value Message) {
-    std::string S;
-    llvm::raw_string_ostream OS(S);
-    OS << llvm::formatv(Pretty ? "{0:2}" : "{0}", Message);
-    OS.flush();
-    Out << "Content-Length: " << S.size() << "\r\n\r\n" << S;
-    Out.flush();
-    vlog(">>> {0}\n", S);
-  }
-
-  // Read raw string messages from input stream.
-  llvm::Optional<std::string> readRawMessage() {
-    return Style == JSONStreamStyle::Delimited ? readDelimitedMessage()
-                                               : readStandardMessage();
-  }
-  llvm::Optional<std::string> readDelimitedMessage();
-  llvm::Optional<std::string> readStandardMessage();
-
-  std::FILE *In;
-  llvm::raw_ostream &Out;
-  llvm::raw_ostream &InMirror;
-  bool Pretty;
-  JSONStreamStyle Style;
-};
-
-bool JSONTransport::handleMessage(llvm::json::Value Message,
-                                  MessageHandler &Handler) {
-  // Message must be an object with "jsonrpc":"2.0".
-  auto *Object = Message.getAsObject();
-  if (!Object || Object->getString("jsonrpc") != Optional<StringRef>("2.0")) {
-    elog("Not a JSON-RPC 2.0 message: {0:2}", Message);
-    return false;
-  }
-  // ID may be any JSON value. If absent, this is a notification.
-  llvm::Optional<json::Value> ID;
-  if (auto *I = Object->get("id"))
-    ID = std::move(*I);
-  auto Method = Object->getString("method");
-  if (!Method) { // This is a response.
-    if (!ID) {
-      elog("No method and no response ID: {0:2}", Message);
-      return false;
-    }
-    if (auto *Err = Object->getObject("error"))
-      return Handler.onReply(std::move(*ID), decodeError(*Err));
-    // Result should be given, use null if not.
-    json::Value Result = nullptr;
-    if (auto *R = Object->get("result"))
-      Result = std::move(*R);
-    return Handler.onReply(std::move(*ID), std::move(Result));
-  }
-  // Params should be given, use null if not.
-  json::Value Params = nullptr;
-  if (auto *P = Object->get("params"))
-    Params = std::move(*P);
-
-  if (ID)
-    return Handler.onCall(*Method, std::move(Params), std::move(*ID));
-  else
-    return Handler.onNotify(*Method, std::move(Params));
-}
-
-// Tries to read a line up to and including \n.
-// If failing, feof() or ferror() will be set.
-bool readLine(std::FILE *In, std::string &Out) {
-  static constexpr int BufSize = 1024;
-  size_t Size = 0;
-  Out.clear();
-  for (;;) {
-    Out.resize(Size + BufSize);
-    // Handle EINTR which is sent when a debugger attaches on some platforms.
-    if (!llvm::sys::RetryAfterSignal(nullptr, ::fgets, &Out[Size], BufSize, In))
-      return false;
-    clearerr(In);
-    // If the line contained null bytes, anything after it (including \n) will
-    // be ignored. Fortunately this is not a legal header or JSON.
-    size_t Read = std::strlen(&Out[Size]);
-    if (Read > 0 && Out[Size + Read - 1] == '\n') {
-      Out.resize(Size + Read);
-      return true;
-    }
-    Size += Read;
-  }
-}
-
-// Returns None when:
-//  - ferror() or feof() are set.
-//  - Content-Length is missing or empty (protocol error)
-llvm::Optional<std::string> JSONTransport::readStandardMessage() {
-  // A Language Server Protocol message starts with a set of HTTP headers,
-  // delimited  by \r\n, and terminated by an empty line (\r\n).
-  unsigned long long ContentLength = 0;
-  std::string Line;
-  while (true) {
-    if (feof(In) || ferror(In) || !readLine(In, Line))
-      return llvm::None;
-    InMirror << Line;
-
-    llvm::StringRef LineRef(Line);
-
-    // We allow comments in headers. Technically this isn't part
-
-    // of the LSP specification, but makes writing tests easier.
-    if (LineRef.startswith("#"))
-      continue;
-
-    // Content-Length is a mandatory header, and the only one we handle.
-    if (LineRef.consume_front("Content-Length: ")) {
-      if (ContentLength != 0) {
-        elog("Warning: Duplicate Content-Length header received. "
-             "The previous value for this message ({0}) was ignored.",
-             ContentLength);
-      }
-      llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength);
-      continue;
-    } else if (!LineRef.trim().empty()) {
-      // It's another header, ignore it.
-      continue;
-    } else {
-      // An empty line indicates the end of headers.
-      // Go ahead and read the JSON.
-      break;
-    }
-  }
-
-  // The fuzzer likes crashing us by sending "Content-Length: 9999999999999999"
-  if (ContentLength > 1 << 30) { // 1024M
-    elog("Refusing to read message with long Content-Length: {0}. "
-         "Expect protocol errors",
-         ContentLength);
-    return llvm::None;
-  }
-  if (ContentLength == 0) {
-    log("Warning: Missing Content-Length header, or zero-length message.");
-    return llvm::None;
-  }
-
-  std::string JSON(ContentLength, '\0');
-  for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) {
-    // Handle EINTR which is sent when a debugger attaches on some platforms.
-    Read = llvm::sys::RetryAfterSignal(0u, ::fread, &JSON[Pos], 1,
-                                       ContentLength - Pos, In);
-    if (Read == 0) {
-      elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos,
-           ContentLength);
-      return llvm::None;
-    }
-    InMirror << StringRef(&JSON[Pos], Read);
-    clearerr(In); // If we're done, the error was transient. If we're not done,
-                  // either it was transient or we'll see it again on retry.
-    Pos += Read;
-  }
-  return std::move(JSON);
-}
-
-// For lit tests we support a simplified syntax:
-// - messages are delimited by '---' on a line by itself
-// - lines starting with # are ignored.
-// This is a testing path, so favor simplicity over performance here.
-// When returning None, feof() or ferror() will be set.
-llvm::Optional<std::string> JSONTransport::readDelimitedMessage() {
-  std::string JSON;
-  std::string Line;
-  while (readLine(In, Line)) {
-    InMirror << Line;
-    auto LineRef = llvm::StringRef(Line).trim();
-    if (LineRef.startswith("#")) // comment
-      continue;
-
-    // found a delimiter
-    if (LineRef.rtrim() == "---")
-      break;
-
-    JSON += Line;
-  }
-
-  if (ferror(In)) {
-    elog("Input error while reading message!");
-    return llvm::None;
-  }
-  return std::move(JSON); // Including at EOF
-}
-
-} // namespace
-
-std::unique_ptr<Transport> newJSONTransport(std::FILE *In,
-                                            llvm::raw_ostream &Out,
-                                            llvm::raw_ostream *InMirror,
-                                            bool Pretty,
-                                            JSONStreamStyle Style) {
-  return llvm::make_unique<JSONTransport>(In, Out, InMirror, Pretty, Style);
-}
-
-} // namespace clangd
-} // namespace clang

Modified: clang-tools-extra/trunk/clangd/Protocol.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.cpp?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.cpp (original)
+++ clang-tools-extra/trunk/clangd/Protocol.cpp Tue Oct 16 11:44:41 2018
@@ -25,8 +25,6 @@ namespace clang {
 namespace clangd {
 using namespace llvm;
 
-char LSPError::ID;
-
 URIForFile::URIForFile(std::string AbsPath) {
   assert(llvm::sys::path::is_absolute(AbsPath) && "the path is relative");
   File = std::move(AbsPath);

Modified: clang-tools-extra/trunk/clangd/Protocol.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.h (original)
+++ clang-tools-extra/trunk/clangd/Protocol.h Tue Oct 16 11:44:41 2018
@@ -48,23 +48,6 @@ enum class ErrorCode {
   // Defined by the protocol.
   RequestCancelled = -32800,
 };
-// Models an LSP error as an llvm::Error.
-class LSPError : public llvm::ErrorInfo<LSPError> {
-public:
-  std::string Message;
-  ErrorCode Code;
-  static char ID;
-
-  LSPError(std::string Message, ErrorCode Code)
-      : Message(std::move(Message)), Code(Code) {}
-
-  void log(llvm::raw_ostream &OS) const override {
-    OS << int(Code) << ": " << Message;
-  }
-  std::error_code convertToErrorCode() const override {
-    return llvm::inconvertibleErrorCode();
-  }
-};
 
 struct URIForFile {
   URIForFile() = default;

Modified: clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp (original)
+++ clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp Tue Oct 16 11:44:41 2018
@@ -35,7 +35,6 @@ struct HandlerRegisterer {
       } else {
         elog("Failed to decode {0} request.", Method);
       }
-      return Method != "exit"; // Shut down after exit notification.
     });
   }
 

Removed: clang-tools-extra/trunk/clangd/Transport.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Transport.h?rev=344636&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/Transport.h (original)
+++ clang-tools-extra/trunk/clangd/Transport.h (removed)
@@ -1,92 +0,0 @@
-//===--- Transport.h - sending and receiving LSP messages -------*- C++ -*-===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// The language server protocol is usually implemented by writing messages as
-// JSON-RPC over the stdin/stdout of a subprocess. However other communications
-// mechanisms are possible, such as XPC on mac.
-//
-// The Transport interface allows the mechanism to be replaced, and the JSONRPC
-// Transport is the standard implementation.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_TRANSPORT_H_
-#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_TRANSPORT_H_
-
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/JSON.h"
-#include "llvm/Support/raw_ostream.h"
-
-namespace clang {
-namespace clangd {
-
-// A transport is responsible for maintaining the connection to a client
-// application, and reading/writing structured messages to it.
-//
-// Transports have limited thread safety requirements:
-//  - messages will not be sent concurrently
-//  - messages MAY be sent while loop() is reading, or its callback is active
-class Transport {
-public:
-  virtual ~Transport() = default;
-
-  // Called by Clangd to send messages to the client.
-  virtual void notify(llvm::StringRef Method, llvm::json::Value Params) = 0;
-  virtual void call(llvm::StringRef Method, llvm::json::Value Params,
-                    llvm::json::Value ID) = 0;
-  virtual void reply(llvm::json::Value ID,
-                     llvm::Expected<llvm::json::Value> Result) = 0;
-
-  // Implemented by Clangd to handle incoming messages. (See loop() below).
-  class MessageHandler {
-  public:
-    virtual ~MessageHandler() = default;
-    // Handler returns true to keep processing messages, or false to shut down.
-    virtual bool onNotify(llvm::StringRef Method, llvm::json::Value) = 0;
-    virtual bool onCall(llvm::StringRef Method, llvm::json::Value Params,
-                        llvm::json::Value ID) = 0;
-    virtual bool onReply(llvm::json::Value ID,
-                         llvm::Expected<llvm::json::Value> Result) = 0;
-  };
-  // Called by Clangd to receive messages from the client.
-  // The transport should in turn invoke the handler to process messages.
-  // If handler returns false, the transport should immedately exit the loop.
-  // (This is used to implement the `exit` notification).
-  // Otherwise, it returns an error when the transport becomes unusable.
-  virtual llvm::Error loop(MessageHandler &) = 0;
-};
-
-// Controls the way JSON-RPC messages are encoded (both input and output).
-enum JSONStreamStyle {
-  // Encoding per the LSP specification, with mandatory Content-Length header.
-  Standard,
-  // Messages are delimited by a '---' line. Comment lines start with #.
-  Delimited
-};
-
-// Returns a Transport that speaks JSON-RPC over a pair of streams.
-// The input stream must be opened in binary mode.
-// If InMirror is set, data read will be echoed to it.
-//
-// The use of C-style std::FILE* input deserves some explanation.
-// Previously, std::istream was used. When a debugger attached on MacOS, the
-// process received EINTR, the stream went bad, and clangd exited.
-// A retry-on-EINTR loop around reads solved this problem, but caused clangd to
-// sometimes hang rather than exit on other OSes. The interaction between
-// istreams and signals isn't well-specified, so it's hard to get this right.
-// The C APIs seem to be clearer in this respect.
-std::unique_ptr<Transport>
-newJSONTransport(std::FILE *In, llvm::raw_ostream &Out,
-                 llvm::raw_ostream *InMirror, bool Pretty,
-                 JSONStreamStyle = JSONStreamStyle::Standard);
-
-} // namespace clangd
-} // namespace clang
-
-#endif

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=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp (original)
+++ clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp Tue Oct 16 11:44:41 2018
@@ -253,8 +253,11 @@ int main(int argc, char *argv[]) {
   // Use buffered stream to stderr (we still flush each log message). Unbuffered
   // stream can cause significant (non-deterministic) latency for the logger.
   llvm::errs().SetBuffered();
-  JSONOutput Logger(llvm::errs(), LogLevel);
-  clangd::LoggingSession LoggingSession(Logger);
+  JSONOutput Out(llvm::outs(), llvm::errs(), LogLevel,
+                 InputMirrorStream ? InputMirrorStream.getPointer() : nullptr,
+                 PrettyPrint);
+
+  clangd::LoggingSession LoggingSession(Out);
 
   // If --compile-commands-dir arg was invoked, check value and override default
   // path.
@@ -314,16 +317,12 @@ int main(int argc, char *argv[]) {
   CCOpts.AllScopes = AllScopesCompletion;
 
   // Initialize and run ClangdLSPServer.
-  // Change stdin to binary to not lose \r\n on windows.
-  llvm::sys::ChangeStdinToBinary();
-  auto Transport = newJSONTransport(
-      stdin, llvm::outs(),
-      InputMirrorStream ? InputMirrorStream.getPointer() : nullptr, PrettyPrint,
-      InputStyle);
   ClangdLSPServer LSPServer(
-      *Transport, CCOpts, CompileCommandsDirPath,
+      Out, CCOpts, CompileCommandsDirPath,
       /*ShouldUseInMemoryCDB=*/CompileArgsFrom == LSPCompileArgs, Opts);
   constexpr int NoShutdownRequestErrorCode = 1;
   llvm::set_thread_name("clangd.main");
-  return LSPServer.run() ? 0 : NoShutdownRequestErrorCode;
+  // Change stdin to binary to not lose \r\n on windows.
+  llvm::sys::ChangeStdinToBinary();
+  return LSPServer.run(stdin, InputStyle) ? 0 : NoShutdownRequestErrorCode;
 }

Modified: clang-tools-extra/trunk/test/clangd/compile-commands-path-in-initialize.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/compile-commands-path-in-initialize.test?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/compile-commands-path-in-initialize.test (original)
+++ clang-tools-extra/trunk/test/clangd/compile-commands-path-in-initialize.test Tue Oct 16 11:44:41 2018
@@ -24,5 +24,3 @@
 # CHECK-NEXT:         "message": "MACRO is one",
 ---
 {"jsonrpc":"2.0","id":10000,"method":"shutdown"}
----
-{"jsonrpc":"2.0","method":"exit"}

Modified: clang-tools-extra/trunk/test/clangd/completion-snippets.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/completion-snippets.test?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/completion-snippets.test (original)
+++ clang-tools-extra/trunk/test/clangd/completion-snippets.test Tue Oct 16 11:44:41 2018
@@ -52,5 +52,3 @@
 # CHECK-NEXT:  }
 ---
 {"jsonrpc":"2.0","id":4,"method":"shutdown"}
----
-{"jsonrpc":"2.0","method":"exit"}

Modified: clang-tools-extra/trunk/test/clangd/completion.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/completion.test?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/completion.test (original)
+++ clang-tools-extra/trunk/test/clangd/completion.test Tue Oct 16 11:44:41 2018
@@ -68,5 +68,3 @@
 # CHECK-NEXT:  ]
 ---
 {"jsonrpc":"2.0","id":4,"method":"shutdown"}
----
-{"jsonrpc":"2.0","method":"exit"}

Modified: clang-tools-extra/trunk/test/clangd/crash-non-added-files.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/crash-non-added-files.test?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/crash-non-added-files.test (original)
+++ clang-tools-extra/trunk/test/clangd/crash-non-added-files.test Tue Oct 16 11:44:41 2018
@@ -32,5 +32,3 @@
 {"jsonrpc":"2.0","id":6,"method":"shutdown"}
 ---
 {"jsonrpc":"2.0","method":"exit"}
----
-{"jsonrpc":"2.0","method":"exit"}

Modified: clang-tools-extra/trunk/test/clangd/execute-command.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/execute-command.test?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/execute-command.test (original)
+++ clang-tools-extra/trunk/test/clangd/execute-command.test Tue Oct 16 11:44:41 2018
@@ -62,5 +62,3 @@
 {"jsonrpc":"2.0","id":9,"method":"workspace/executeCommand","params":{"arguments":[{"custom":"foo", "changes":{"test:///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":")"}]}}],"command":"clangd.applyFix"}}
 ---
 {"jsonrpc":"2.0","id":3,"method":"shutdown"}
----
-{"jsonrpc":"2.0","method":"exit"}

Modified: clang-tools-extra/trunk/test/clangd/input-mirror.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/input-mirror.test?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/input-mirror.test (original)
+++ clang-tools-extra/trunk/test/clangd/input-mirror.test Tue Oct 16 11:44:41 2018
@@ -12,6 +12,3 @@ Content-Length: 172
 Content-Length: 44
 
 {"jsonrpc":"2.0","id":3,"method":"shutdown"}
-Content-Length: 33
-
-{"jsonrpc":"2.0","method":"exit"}

Modified: clang-tools-extra/trunk/test/clangd/signature-help.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/signature-help.test?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/signature-help.test (original)
+++ clang-tools-extra/trunk/test/clangd/signature-help.test Tue Oct 16 11:44:41 2018
@@ -23,5 +23,3 @@
 # CHECK-NEXT: }
 ---
 {"jsonrpc":"2.0","id":100000,"method":"shutdown"}
----
-{"jsonrpc":"2.0","method":"exit"}

Modified: clang-tools-extra/trunk/test/clangd/textdocument-didchange-fail.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/textdocument-didchange-fail.test?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/textdocument-didchange-fail.test (original)
+++ clang-tools-extra/trunk/test/clangd/textdocument-didchange-fail.test Tue Oct 16 11:44:41 2018
@@ -35,5 +35,3 @@
 # CHECK-NEXT:}
 ---
 {"jsonrpc":"2.0","id":4,"method":"shutdown"}
----
-{"jsonrpc":"2.0","method":"exit"}

Modified: clang-tools-extra/trunk/test/clangd/trace.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/trace.test?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/trace.test (original)
+++ clang-tools-extra/trunk/test/clangd/trace.test Tue Oct 16 11:44:41 2018
@@ -21,5 +21,3 @@
 # CHECK: },
 ---
 {"jsonrpc":"2.0","id":5,"method":"shutdown"}
----
-{"jsonrpc":"2.0","method":"exit"}

Modified: clang-tools-extra/trunk/test/clangd/xrefs.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/xrefs.test?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/xrefs.test (original)
+++ clang-tools-extra/trunk/test/clangd/xrefs.test Tue Oct 16 11:44:41 2018
@@ -55,5 +55,3 @@
 # CHECK-NEXT: ]
 ---
 {"jsonrpc":"2.0","id":10000,"method":"shutdown"}
----
-{"jsonrpc":"2.0","method":"exit"}

Modified: clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt?rev=344637&r1=344636&r2=344637&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt Tue Oct 16 11:44:41 2018
@@ -27,7 +27,6 @@ add_extra_unittest(ClangdTests
   GlobalCompilationDatabaseTests.cpp
   HeadersTests.cpp
   IndexTests.cpp
-  JSONTransportTests.cpp
   QualityTests.cpp
   RIFFTests.cpp
   SerializationTests.cpp

Removed: clang-tools-extra/trunk/unittests/clangd/JSONTransportTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/JSONTransportTests.cpp?rev=344636&view=auto
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/JSONTransportTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/JSONTransportTests.cpp (removed)
@@ -1,199 +0,0 @@
-//===-- JSONTransportTests.cpp  -------------------------------------------===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-#include "Protocol.h"
-#include "Transport.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include <stdio.h>
-
-using namespace llvm;
-namespace clang {
-namespace clangd {
-namespace {
-
-// No fmemopen on windows, so we can't easily run this test.
-#ifndef WIN32
-
-// Fixture takes care of managing the input/output buffers for the transport.
-class JSONTransportTest : public ::testing::Test {
-  std::string InBuf, OutBuf, MirrorBuf;
-  llvm::raw_string_ostream Out, Mirror;
-  std::unique_ptr<FILE, int (*)(FILE *)> In;
-
-protected:
-  JSONTransportTest() : Out(OutBuf), Mirror(MirrorBuf), In(nullptr, nullptr) {}
-
-  template <typename... Args>
-  std::unique_ptr<Transport> transport(std::string InData, bool Pretty,
-                                       JSONStreamStyle Style) {
-    InBuf = std::move(InData);
-    In = {fmemopen(&InBuf[0], InBuf.size(), "r"), &fclose};
-    return newJSONTransport(In.get(), Out, &Mirror, Pretty, Style);
-  }
-
-  std::string input() const { return InBuf; }
-  std::string output() { return Out.str(); }
-  std::string input_mirror() { return Mirror.str(); }
-};
-
-// Echo is a simple server running on a transport:
-//   - logs each message it gets.
-//   - when it gets a call, replies to it
-//   - when it gets a notification for method "call", makes a call on Target
-// Hangs up when it gets an exit notification.
-class Echo : public Transport::MessageHandler {
-  Transport &Target;
-  std::string LogBuf;
-  raw_string_ostream Log;
-
-public:
-  Echo(Transport &Target) : Target(Target), Log(LogBuf) {}
-
-  std::string log() { return Log.str(); }
-
-  bool onNotify(StringRef Method, json::Value Params) override {
-    Log << "Notification " << Method << ": " << Params << "\n";
-    if (Method == "call")
-      Target.call("echo call", std::move(Params), 42);
-    return Method != "exit";
-  }
-
-  bool onCall(StringRef Method, json::Value Params, json::Value ID) override {
-    Log << "Call " << Method << "(" << ID << "): " << Params << "\n";
-    if (Method == "err")
-      Target.reply(ID, make_error<LSPError>("trouble at mill", ErrorCode(88)));
-    else
-      Target.reply(ID, std::move(Params));
-    return true;
-  }
-
-  bool onReply(json::Value ID, Expected<json::Value> Params) override {
-    if (Params)
-      Log << "Reply(" << ID << "): " << *Params << "\n";
-    else
-      Log << "Reply(" << ID
-          << "): error = " << llvm::toString(Params.takeError()) << "\n";
-    return true;
-  }
-};
-
-std::string trim(StringRef S) { return S.trim().str(); }
-
-// Runs an Echo session using the standard JSON-RPC format we use in production.
-TEST_F(JSONTransportTest, StandardDense) {
-  auto T = transport(
-      "Content-Length: 52\r\n\r\n"
-      R"({"jsonrpc": "2.0", "method": "call", "params": 1234})"
-      "Content-Length: 46\r\n\r\n"
-      R"({"jsonrpc": "2.0", "id": 1234, "result": 5678})"
-      "Content-Length: 67\r\n\r\n"
-      R"({"jsonrpc": "2.0", "method": "foo", "id": "abcd", "params": "efgh"})"
-      "Content-Length: 73\r\n\r\n"
-      R"({"jsonrpc": "2.0", "id": "xyz", "error": {"code": 99, "message": "bad!"}})"
-      "Content-Length: 68\r\n\r\n"
-      R"({"jsonrpc": "2.0", "method": "err", "id": "wxyz", "params": "boom!"})"
-      "Content-Length: 36\r\n\r\n"
-      R"({"jsonrpc": "2.0", "method": "exit"})",
-      /*Pretty=*/false, JSONStreamStyle::Standard);
-  Echo E(*T);
-  auto Err = T->loop(E);
-  EXPECT_FALSE(bool(Err)) << llvm::toString(std::move(Err));
-
-  EXPECT_EQ(trim(E.log()), trim(R"(
-Notification call: 1234
-Reply(1234): 5678
-Call foo("abcd"): "efgh"
-Reply("xyz"): error = 99: bad!
-Call err("wxyz"): "boom!"
-Notification exit: null
-  )"));
-  EXPECT_EQ(
-      trim(output()),
-      "Content-Length: 60\r\n\r\n"
-      R"({"id":42,"jsonrpc":"2.0","method":"echo call","params":1234})"
-      "Content-Length: 45\r\n\r\n"
-      R"({"id":"abcd","jsonrpc":"2.0","result":"efgh"})"
-      "Content-Length: 77\r\n\r\n"
-      R"({"error":{"code":88,"message":"trouble at mill"},"id":"wxyz","jsonrpc":"2.0"})");
-  EXPECT_EQ(trim(input_mirror()), trim(input()));
-}
-
-// Runs an Echo session using the "delimited" input and pretty-printed output
-// that we use in lit tests.
-TEST_F(JSONTransportTest, DelimitedPretty) {
-  auto T = transport(R"jsonrpc(
-{"jsonrpc": "2.0", "method": "call", "params": 1234}
----
-{"jsonrpc": "2.0", "id": 1234, "result": 5678}
----
-{"jsonrpc": "2.0", "method": "foo", "id": "abcd", "params": "efgh"}
----
-{"jsonrpc": "2.0", "id": "xyz", "error": {"code": 99, "message": "bad!"}}
----
-{"jsonrpc": "2.0", "method": "err", "id": "wxyz", "params": "boom!"}
----
-{"jsonrpc": "2.0", "method": "exit"}
-  )jsonrpc",
-                     /*Pretty=*/true, JSONStreamStyle::Delimited);
-  Echo E(*T);
-  auto Err = T->loop(E);
-  EXPECT_FALSE(bool(Err)) << llvm::toString(std::move(Err));
-
-  EXPECT_EQ(trim(E.log()), trim(R"(
-Notification call: 1234
-Reply(1234): 5678
-Call foo("abcd"): "efgh"
-Reply("xyz"): error = 99: bad!
-Call err("wxyz"): "boom!"
-Notification exit: null
-  )"));
-  EXPECT_EQ(trim(output()), "Content-Length: 77\r\n\r\n"
-                            R"({
-  "id": 42,
-  "jsonrpc": "2.0",
-  "method": "echo call",
-  "params": 1234
-})"
-                            "Content-Length: 58\r\n\r\n"
-                            R"({
-  "id": "abcd",
-  "jsonrpc": "2.0",
-  "result": "efgh"
-})"
-                            "Content-Length: 105\r\n\r\n"
-                            R"({
-  "error": {
-    "code": 88,
-    "message": "trouble at mill"
-  },
-  "id": "wxyz",
-  "jsonrpc": "2.0"
-})");
-  EXPECT_EQ(trim(input_mirror()), trim(input()));
-}
-
-// IO errors such as EOF ane reported.
-// The only successful return from loop() is if a handler returned false.
-TEST_F(JSONTransportTest, EndOfFile) {
-  auto T = transport("Content-Length: 52\r\n\r\n"
-                     R"({"jsonrpc": "2.0", "method": "call", "params": 1234})",
-                     /*Pretty=*/false, JSONStreamStyle::Standard);
-  Echo E(*T);
-  auto Err = T->loop(E);
-  EXPECT_EQ(trim(E.log()), "Notification call: 1234");
-  EXPECT_TRUE(bool(Err)); // Ran into EOF with no handler signalling done.
-  consumeError(std::move(Err));
-  EXPECT_EQ(trim(input_mirror()), trim(input()));
-}
-
-#endif
-
-} // namespace
-} // namespace clangd
-} // namespace clang




More information about the cfe-commits mailing list