[Mlir-commits] [mlir] b3911cd - [mlir-lsp-server] Add support for sending diagnostics to the client

River Riddle llvmlistbot at llvm.org
Wed May 12 13:12:31 PDT 2021


Author: River Riddle
Date: 2021-05-12T13:02:25-07:00
New Revision: b3911cdfc89f3b560fc00048d6f99280268fa63c

URL: https://github.com/llvm/llvm-project/commit/b3911cdfc89f3b560fc00048d6f99280268fa63c
DIFF: https://github.com/llvm/llvm-project/commit/b3911cdfc89f3b560fc00048d6f99280268fa63c.diff

LOG: [mlir-lsp-server] Add support for sending diagnostics to the client

This allows for diagnostics emitted during parsing/verification to be surfaced to the user by the language client, as opposed to just being emitted to the logs like they are now.

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

Added: 
    mlir/test/mlir-lsp-server/diagnostics.test

Modified: 
    mlir/include/mlir/Parser/AsmParserState.h
    mlir/lib/Parser/AsmParserState.cpp
    mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp
    mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp
    mlir/lib/Tools/mlir-lsp-server/MLIRServer.h
    mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.cpp
    mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.h
    mlir/lib/Tools/mlir-lsp-server/lsp/Transport.cpp
    mlir/lib/Tools/mlir-lsp-server/lsp/Transport.h

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Parser/AsmParserState.h b/mlir/include/mlir/Parser/AsmParserState.h
index b468f582f4964..0b9e2553a8054 100644
--- a/mlir/include/mlir/Parser/AsmParserState.h
+++ b/mlir/include/mlir/Parser/AsmParserState.h
@@ -82,6 +82,7 @@ class AsmParserState {
 
   AsmParserState();
   ~AsmParserState();
+  AsmParserState &operator=(AsmParserState &&other);
 
   //===--------------------------------------------------------------------===//
   // Access State

diff  --git a/mlir/lib/Parser/AsmParserState.cpp b/mlir/lib/Parser/AsmParserState.cpp
index 1c629351b73eb..8655cd1f963d2 100644
--- a/mlir/lib/Parser/AsmParserState.cpp
+++ b/mlir/lib/Parser/AsmParserState.cpp
@@ -52,6 +52,10 @@ struct AsmParserState::Impl {
 
 AsmParserState::AsmParserState() : impl(std::make_unique<Impl>()) {}
 AsmParserState::~AsmParserState() {}
+AsmParserState &AsmParserState::operator=(AsmParserState &&other) {
+  impl = std::move(other.impl);
+  return *this;
+}
 
 //===----------------------------------------------------------------------===//
 // Access State

diff  --git a/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp b/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp
index 44d6f3da99f19..954482f48b5e1 100644
--- a/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp
+++ b/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp
@@ -59,6 +59,10 @@ struct LSPServer::Impl {
   MLIRServer &server;
   JSONTransport &transport;
 
+  /// An outgoing notification used to send diagnostics to the client when they
+  /// are ready to be processed.
+  OutgoingNotification<PublishDiagnosticsParams> publishDiagnostics;
+
   /// Used to indicate that the 'shutdown' request was received from the
   /// Language Server client.
   bool shutdownRequestReceived = false;
@@ -99,11 +103,21 @@ void LSPServer::Impl::onShutdown(const NoParams &,
 
 void LSPServer::Impl::onDocumentDidOpen(
     const DidOpenTextDocumentParams &params) {
-  server.addOrUpdateDocument(params.textDocument.uri, params.textDocument.text);
+  PublishDiagnosticsParams diagParams(params.textDocument.uri);
+  server.addOrUpdateDocument(params.textDocument.uri, params.textDocument.text,
+                             diagParams.diagnostics);
+
+  // Publish any recorded diagnostics.
+  publishDiagnostics(diagParams);
 }
 void LSPServer::Impl::onDocumentDidClose(
     const DidCloseTextDocumentParams &params) {
   server.removeDocument(params.textDocument.uri);
+
+  // Empty out the diagnostics shown for this document. This will clear out
+  // anything currently displayed by the client for this document (e.g. in the
+  // "Problems" pane of VSCode).
+  publishDiagnostics(PublishDiagnosticsParams(params.textDocument.uri));
 }
 void LSPServer::Impl::onDocumentDidChange(
     const DidChangeTextDocumentParams &params) {
@@ -111,8 +125,13 @@ void LSPServer::Impl::onDocumentDidChange(
   // to avoid this.
   if (params.contentChanges.size() != 1)
     return;
+  PublishDiagnosticsParams diagParams(params.textDocument.uri);
   server.addOrUpdateDocument(params.textDocument.uri,
-                             params.contentChanges.front().text);
+                             params.contentChanges.front().text,
+                             diagParams.diagnostics);
+
+  // Publish any recorded diagnostics.
+  publishDiagnostics(diagParams);
 }
 
 //===----------------------------------------------------------------------===//
@@ -173,6 +192,11 @@ LogicalResult LSPServer::run() {
   // Hover
   messageHandler.method("textDocument/hover", impl.get(), &Impl::onHover);
 
+  // Diagnostics
+  impl->publishDiagnostics =
+      messageHandler.outgoingNotification<PublishDiagnosticsParams>(
+          "textDocument/publishDiagnostics");
+
   // Run the main loop of the transport.
   LogicalResult result = success();
   if (llvm::Error error = impl->transport.run(messageHandler)) {

diff  --git a/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp b/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp
index 807e1864ed3ba..f9b0bfb526353 100644
--- a/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp
+++ b/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp
@@ -60,7 +60,28 @@ static Optional<lsp::Location> getLocationFromLoc(FileLineColLoc loc) {
   lsp::Position position;
   position.line = loc.getLine() - 1;
   position.character = loc.getColumn();
-  return lsp::Location{*sourceURI, lsp::Range{position, position}};
+  return lsp::Location{*sourceURI, lsp::Range(position)};
+}
+
+/// Returns a language server location from the given MLIR location, or None if
+/// one couldn't be created. `uri` is an optional additional filter that, when
+/// present, is used to filter sub locations that do not share the same uri.
+static Optional<lsp::Location>
+getLocationFromLoc(Location loc, const lsp::URIForFile *uri = nullptr) {
+  Optional<lsp::Location> location;
+  loc->walk([&](Location nestedLoc) {
+    FileLineColLoc fileLoc = nestedLoc.dyn_cast<FileLineColLoc>();
+    if (!fileLoc)
+      return WalkResult::advance();
+
+    Optional<lsp::Location> sourceLoc = getLocationFromLoc(fileLoc);
+    if (sourceLoc && (!uri || sourceLoc->uri == *uri)) {
+      location = *sourceLoc;
+      return WalkResult::interrupt();
+    }
+    return WalkResult::advance();
+  });
+  return location;
 }
 
 /// Collect all of the locations from the given MLIR location that are not
@@ -173,6 +194,56 @@ static void printDefBlockName(raw_ostream &os,
   printDefBlockName(os, def.block, def.definition.loc);
 }
 
+/// Convert the given MLIR diagnostic to the LSP form.
+static lsp::Diagnostic getLspDiagnoticFromDiag(Diagnostic &diag,
+                                               const lsp::URIForFile &uri) {
+  lsp::Diagnostic lspDiag;
+  lspDiag.source = "mlir";
+
+  // Note: Right now all of the diagnostics are treated as parser issues, but
+  // some are parser and some are verifier.
+  lspDiag.category = "Parse Error";
+
+  // Try to grab a file location for this diagnostic.
+  // TODO: For simplicity, we just grab the first one. It may be likely that we
+  // will need a more interesting heuristic here.'
+  Optional<lsp::Location> lspLocation =
+      getLocationFromLoc(diag.getLocation(), &uri);
+  if (lspLocation)
+    lspDiag.range = lspLocation->range;
+
+  // Convert the severity for the diagnostic.
+  switch (diag.getSeverity()) {
+  case DiagnosticSeverity::Note:
+    llvm_unreachable("expected notes to be handled separately");
+  case DiagnosticSeverity::Warning:
+    lspDiag.severity = lsp::DiagnosticSeverity::Warning;
+    break;
+  case DiagnosticSeverity::Error:
+    lspDiag.severity = lsp::DiagnosticSeverity::Error;
+    break;
+  case DiagnosticSeverity::Remark:
+    lspDiag.severity = lsp::DiagnosticSeverity::Information;
+    break;
+  }
+  lspDiag.message = diag.str();
+
+  // Attach any notes to the main diagnostic as related information.
+  std::vector<lsp::DiagnosticRelatedInformation> relatedDiags;
+  for (Diagnostic &note : diag.getNotes()) {
+    lsp::Location noteLoc;
+    if (Optional<lsp::Location> loc = getLocationFromLoc(note.getLocation()))
+      noteLoc = *loc;
+    else
+      noteLoc.uri = uri;
+    relatedDiags.emplace_back(noteLoc, note.str());
+  }
+  if (!relatedDiags.empty())
+    lspDiag.relatedInformation = std::move(relatedDiags);
+
+  return lspDiag;
+}
+
 //===----------------------------------------------------------------------===//
 // MLIRDocument
 //===----------------------------------------------------------------------===//
@@ -182,7 +253,8 @@ namespace {
 /// document.
 struct MLIRDocument {
   MLIRDocument(const lsp::URIForFile &uri, StringRef contents,
-               DialectRegistry &registry);
+               DialectRegistry &registry,
+               std::vector<lsp::Diagnostic> &diagnostics);
 
   //===--------------------------------------------------------------------===//
   // Definitions and References
@@ -227,15 +299,12 @@ struct MLIRDocument {
 } // namespace
 
 MLIRDocument::MLIRDocument(const lsp::URIForFile &uri, StringRef contents,
-                           DialectRegistry &registry)
+                           DialectRegistry &registry,
+                           std::vector<lsp::Diagnostic> &diagnostics)
     : context(registry) {
   context.allowUnregisteredDialects();
   ScopedDiagnosticHandler handler(&context, [&](Diagnostic &diag) {
-    // TODO: What should we do with these diagnostics?
-    //  * Cache and show to the user?
-    //  * Ignore?
-    lsp::Logger::error("Error when parsing MLIR document `{0}`: `{1}`",
-                       uri.file(), diag.str());
+    diagnostics.push_back(getLspDiagnoticFromDiag(diag, uri));
   });
 
   // Try to parsed the given IR string.
@@ -246,9 +315,13 @@ MLIRDocument::MLIRDocument(const lsp::URIForFile &uri, StringRef contents,
   }
 
   sourceMgr.AddNewSourceBuffer(std::move(memBuffer), llvm::SMLoc());
-  if (failed(
-          parseSourceFile(sourceMgr, &parsedIR, &context, nullptr, &asmState)))
+  if (failed(parseSourceFile(sourceMgr, &parsedIR, &context, nullptr,
+                             &asmState))) {
+    // If parsing failed, clear out any of the current state.
+    parsedIR.clear();
+    asmState = AsmParserState();
     return;
+  }
 }
 
 //===----------------------------------------------------------------------===//
@@ -495,10 +568,11 @@ lsp::MLIRServer::MLIRServer(DialectRegistry &registry)
     : impl(std::make_unique<Impl>(registry)) {}
 lsp::MLIRServer::~MLIRServer() {}
 
-void lsp::MLIRServer::addOrUpdateDocument(const URIForFile &uri,
-                                          StringRef contents) {
-  impl->documents[uri.file()] =
-      std::make_unique<MLIRDocument>(uri, contents, impl->registry);
+void lsp::MLIRServer::addOrUpdateDocument(
+    const URIForFile &uri, StringRef contents,
+    std::vector<Diagnostic> &diagnostics) {
+  impl->documents[uri.file()] = std::make_unique<MLIRDocument>(
+      uri, contents, impl->registry, diagnostics);
 }
 
 void lsp::MLIRServer::removeDocument(const URIForFile &uri) {

diff  --git a/mlir/lib/Tools/mlir-lsp-server/MLIRServer.h b/mlir/lib/Tools/mlir-lsp-server/MLIRServer.h
index 6b974a5c95884..f3af2a67453d5 100644
--- a/mlir/lib/Tools/mlir-lsp-server/MLIRServer.h
+++ b/mlir/lib/Tools/mlir-lsp-server/MLIRServer.h
@@ -16,6 +16,7 @@ namespace mlir {
 class DialectRegistry;
 
 namespace lsp {
+struct Diagnostic;
 struct Hover;
 struct Location;
 struct Position;
@@ -30,8 +31,10 @@ class MLIRServer {
   MLIRServer(DialectRegistry &registry);
   ~MLIRServer();
 
-  /// Add or update the document at the given URI.
-  void addOrUpdateDocument(const URIForFile &uri, StringRef contents);
+  /// Add or update the document at the given URI. Any diagnostics emitted for
+  /// this document should be added to `diagnostics`
+  void addOrUpdateDocument(const URIForFile &uri, StringRef contents,
+                           std::vector<Diagnostic> &diagnostics);
 
   /// Remove the document with the given uri.
   void removeDocument(const URIForFile &uri);

diff  --git a/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.cpp b/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.cpp
index 9b906243e0fa6..8d4fbe1807915 100644
--- a/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.cpp
+++ b/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.cpp
@@ -473,3 +473,44 @@ llvm::json::Value mlir::lsp::toJSON(const Hover &hover) {
     result["range"] = toJSON(*hover.range);
   return std::move(result);
 }
+
+//===----------------------------------------------------------------------===//
+// DiagnosticRelatedInformation
+//===----------------------------------------------------------------------===//
+
+llvm::json::Value mlir::lsp::toJSON(const DiagnosticRelatedInformation &info) {
+  return llvm::json::Object{
+      {"location", info.location},
+      {"message", info.message},
+  };
+}
+
+//===----------------------------------------------------------------------===//
+// Diagnostic
+//===----------------------------------------------------------------------===//
+
+llvm::json::Value mlir::lsp::toJSON(const Diagnostic &diag) {
+  llvm::json::Object result{
+      {"range", diag.range},
+      {"severity", (int)diag.severity},
+      {"message", diag.message},
+  };
+  if (diag.category)
+    result["category"] = *diag.category;
+  if (!diag.source.empty())
+    result["source"] = diag.source;
+  if (diag.relatedInformation)
+    result["relatedInformation"] = *diag.relatedInformation;
+  return std::move(result);
+}
+
+//===----------------------------------------------------------------------===//
+// PublishDiagnosticsParams
+//===----------------------------------------------------------------------===//
+
+llvm::json::Value mlir::lsp::toJSON(const PublishDiagnosticsParams &params) {
+  return llvm::json::Object{
+      {"uri", params.uri},
+      {"diagnostics", params.diagnostics},
+  };
+}

diff  --git a/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.h b/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.h
index 78d313dfa9518..de08fa247bd84 100644
--- a/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.h
+++ b/mlir/lib/Tools/mlir-lsp-server/lsp/Protocol.h
@@ -228,6 +228,10 @@ raw_ostream &operator<<(raw_ostream &os, const Position &value);
 //===----------------------------------------------------------------------===//
 
 struct Range {
+  Range() = default;
+  Range(Position start, Position end) : start(start), end(end) {}
+  Range(Position loc) : Range(loc, loc) {}
+
   /// The range's start position.
   Position start;
 
@@ -393,6 +397,79 @@ struct Hover {
 };
 llvm::json::Value toJSON(const Hover &hover);
 
+//===----------------------------------------------------------------------===//
+// DiagnosticRelatedInformation
+//===----------------------------------------------------------------------===//
+
+/// Represents a related message and source code location for a diagnostic.
+/// This should be used to point to code locations that cause or related to a
+/// diagnostics, e.g. when duplicating a symbol in a scope.
+struct DiagnosticRelatedInformation {
+  DiagnosticRelatedInformation(Location location, std::string message)
+      : location(location), message(std::move(message)) {}
+
+  /// The location of this related diagnostic information.
+  Location location;
+  /// The message of this related diagnostic information.
+  std::string message;
+};
+llvm::json::Value toJSON(const DiagnosticRelatedInformation &info);
+
+//===----------------------------------------------------------------------===//
+// Diagnostic
+//===----------------------------------------------------------------------===//
+
+enum class DiagnosticSeverity {
+  /// It is up to the client to interpret diagnostics as error, warning, info or
+  /// hint.
+  Undetermined = 0,
+  Error = 1,
+  Warning = 2,
+  Information = 3,
+  Hint = 4
+};
+
+struct Diagnostic {
+  /// The source range where the message applies.
+  Range range;
+
+  /// The diagnostic's severity. Can be omitted. If omitted it is up to the
+  /// client to interpret diagnostics as error, warning, info or hint.
+  DiagnosticSeverity severity = DiagnosticSeverity::Undetermined;
+
+  /// A human-readable string describing the source of this diagnostic, e.g.
+  /// 'typescript' or 'super lint'.
+  std::string source;
+
+  /// The diagnostic's message.
+  std::string message;
+
+  /// An array of related diagnostic information, e.g. when symbol-names within
+  /// a scope collide all definitions can be marked via this property.
+  Optional<std::vector<DiagnosticRelatedInformation>> relatedInformation;
+
+  /// The diagnostic's category. Can be omitted.
+  /// An LSP extension that's used to send the name of the category over to the
+  /// client. The category typically describes the compilation stage during
+  /// which the issue was produced, e.g. "Semantic Issue" or "Parse Issue".
+  Optional<std::string> category;
+};
+llvm::json::Value toJSON(const Diagnostic &diag);
+
+//===----------------------------------------------------------------------===//
+// PublishDiagnosticsParams
+//===----------------------------------------------------------------------===//
+
+struct PublishDiagnosticsParams {
+  PublishDiagnosticsParams(URIForFile uri) : uri(uri) {}
+
+  /// The URI for which diagnostic information is reported.
+  URIForFile uri;
+  /// The list of reported diagnostics.
+  std::vector<Diagnostic> diagnostics;
+};
+llvm::json::Value toJSON(const PublishDiagnosticsParams &params);
+
 } // namespace lsp
 } // namespace mlir
 

diff  --git a/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.cpp b/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.cpp
index c13fc85bb1d7d..9468d39f273b7 100644
--- a/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.cpp
+++ b/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.cpp
@@ -21,6 +21,30 @@ using namespace mlir::lsp;
 // Reply
 //===----------------------------------------------------------------------===//
 
+namespace {
+/// Function object to reply to an LSP call.
+/// Each instance must be called exactly once, otherwise:
+///  - if there was no reply, an error reply is sent
+///  - if there were multiple replies, only the first is sent
+class Reply {
+public:
+  Reply(const llvm::json::Value &id, StringRef method,
+        JSONTransport &transport);
+  Reply(Reply &&other);
+  Reply &operator=(Reply &&) = delete;
+  Reply(const Reply &) = delete;
+  Reply &operator=(const Reply &) = delete;
+
+  void operator()(llvm::Expected<llvm::json::Value> reply);
+
+private:
+  StringRef method;
+  std::atomic<bool> replied = {false};
+  llvm::json::Value id;
+  JSONTransport *transport;
+};
+} // namespace
+
 Reply::Reply(const llvm::json::Value &id, llvm::StringRef method,
              JSONTransport &transport)
     : id(id), transport(&transport) {}

diff  --git a/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.h b/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.h
index 2cbf8f1ddb235..23b565c7dce5a 100644
--- a/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.h
+++ b/mlir/lib/Tools/mlir-lsp-server/lsp/Transport.h
@@ -28,32 +28,61 @@
 
 namespace mlir {
 namespace lsp {
-class JSONTransport;
+class MessageHandler;
 
 //===----------------------------------------------------------------------===//
-// Reply
+// JSONTransport
 //===----------------------------------------------------------------------===//
 
-/// Function object to reply to an LSP call.
-/// Each instance must be called exactly once, otherwise:
-///  - if there was no reply, an error reply is sent
-///  - if there were multiple replies, only the first is sent
-class Reply {
+/// The encoding style of the JSON-RPC messages (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
+};
+
+/// A transport class that performs the JSON-RPC communication with the LSP
+/// client.
+class JSONTransport {
 public:
-  Reply(const llvm::json::Value &id, StringRef method,
-        JSONTransport &transport);
-  Reply(Reply &&other);
-  Reply &operator=(Reply &&) = delete;
-  Reply(const Reply &) = delete;
-  Reply &operator=(const Reply &) = delete;
+  JSONTransport(std::FILE *in, raw_ostream &out,
+                JSONStreamStyle style = JSONStreamStyle::Standard,
+                bool prettyOutput = false)
+      : in(in), out(out), style(style), prettyOutput(prettyOutput) {}
 
-  void operator()(llvm::Expected<llvm::json::Value> reply);
+  /// The following methods are used to send a message to the LSP client.
+  void notify(StringRef method, llvm::json::Value params);
+  void call(StringRef method, llvm::json::Value params, llvm::json::Value id);
+  void reply(llvm::json::Value id, llvm::Expected<llvm::json::Value> result);
+
+  /// Start executing the JSON-RPC transport.
+  llvm::Error run(MessageHandler &handler);
 
 private:
-  StringRef method;
-  std::atomic<bool> replied = {false};
-  llvm::json::Value id;
-  JSONTransport *transport;
+  /// Dispatches the given incoming json message to the message handler.
+  bool handleMessage(llvm::json::Value msg, MessageHandler &handler);
+  /// Writes the given message to the output stream.
+  void sendMessage(llvm::json::Value msg);
+
+  /// Read in a message from the input stream.
+  LogicalResult readMessage(std::string &json) {
+    return style == JSONStreamStyle::Delimited ? readDelimitedMessage(json)
+                                               : readStandardMessage(json);
+  }
+  LogicalResult readDelimitedMessage(std::string &json);
+  LogicalResult readStandardMessage(std::string &json);
+
+  /// An output buffer used when building output messages.
+  SmallVector<char, 0> outputBuffer;
+  /// The input file stream.
+  std::FILE *in;
+  /// The output file stream.
+  raw_ostream &out;
+  /// The JSON stream style to use.
+  JSONStreamStyle style;
+  /// If the output JSON should be formatted for easier readability.
+  bool prettyOutput;
 };
 
 //===----------------------------------------------------------------------===//
@@ -65,6 +94,11 @@ class Reply {
 template <typename T>
 using Callback = llvm::unique_function<void(llvm::Expected<T>)>;
 
+/// An OutgoingNotification<T> is a function used for outgoing notifications
+/// send to the client.
+template <typename T>
+using OutgoingNotification = llvm::unique_function<void(const T &)>;
+
 /// A handler used to process the incoming transport messages.
 class MessageHandler {
 public:
@@ -119,6 +153,14 @@ class MessageHandler {
     };
   }
 
+  /// Create an OutgoingNotification object used for the given method.
+  template <typename T>
+  OutgoingNotification<T> outgoingNotification(llvm::StringLiteral method) {
+    return [&, method](const T &params) {
+      transport.notify(method, llvm::json::Value(params));
+    };
+  }
+
 private:
   template <typename HandlerT>
   using HandlerMap = llvm::StringMap<llvm::unique_function<HandlerT>>;
@@ -130,61 +172,6 @@ class MessageHandler {
   JSONTransport &transport;
 };
 
-//===----------------------------------------------------------------------===//
-// JSONTransport
-//===----------------------------------------------------------------------===//
-
-/// The encoding style of the JSON-RPC messages (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
-};
-
-/// A transport class that performs the JSON-RPC communication with the LSP
-/// client.
-class JSONTransport {
-public:
-  JSONTransport(std::FILE *in, raw_ostream &out,
-                JSONStreamStyle style = JSONStreamStyle::Standard,
-                bool prettyOutput = false)
-      : in(in), out(out), style(style), prettyOutput(prettyOutput) {}
-
-  /// The following methods are used to send a message to the LSP client.
-  void notify(StringRef method, llvm::json::Value params);
-  void call(StringRef method, llvm::json::Value params, llvm::json::Value id);
-  void reply(llvm::json::Value id, llvm::Expected<llvm::json::Value> result);
-
-  /// Start executing the JSON-RPC transport.
-  llvm::Error run(MessageHandler &handler);
-
-private:
-  /// Dispatches the given incoming json message to the message handler.
-  bool handleMessage(llvm::json::Value msg, MessageHandler &handler);
-  /// Writes the given message to the output stream.
-  void sendMessage(llvm::json::Value msg);
-
-  /// Read in a message from the input stream.
-  LogicalResult readMessage(std::string &json) {
-    return style == JSONStreamStyle::Delimited ? readDelimitedMessage(json)
-                                               : readStandardMessage(json);
-  }
-  LogicalResult readDelimitedMessage(std::string &json);
-  LogicalResult readStandardMessage(std::string &json);
-
-  /// An output buffer used when building output messages.
-  SmallVector<char, 0> outputBuffer;
-  /// The input file stream.
-  std::FILE *in;
-  /// The output file stream.
-  raw_ostream &out;
-  /// The JSON stream style to use.
-  JSONStreamStyle style;
-  /// If the output JSON should be formatted for easier readability.
-  bool prettyOutput;
-};
-
 } // namespace lsp
 } // namespace mlir
 

diff  --git a/mlir/test/mlir-lsp-server/diagnostics.test b/mlir/test/mlir-lsp-server/diagnostics.test
new file mode 100644
index 0000000000000..d04183e60c942
--- /dev/null
+++ b/mlir/test/mlir-lsp-server/diagnostics.test
@@ -0,0 +1,35 @@
+// RUN: mlir-lsp-server -lit-test < %s | FileCheck -strict-whitespace %s
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"mlir","capabilities":{},"trace":"off"}}
+// -----
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{
+  "uri":"test:///foo.mlir",
+  "languageId":"mlir",
+  "version":1,
+  "text":"func ()"
+}}}
+// CHECK: "method": "textDocument/publishDiagnostics",
+// CHECK-NEXT: "params": {
+// CHECK-NEXT:     "diagnostics": [
+// CHECK-NEXT:       {
+// CHECK-NEXT:         "category": "Parse Error",
+// CHECK-NEXT:         "message": "custom op 'func' expected valid '@'-identifier for symbol name",
+// CHECK-NEXT:         "range": {
+// CHECK-NEXT:           "end": {
+// CHECK-NEXT:             "character": 6,
+// CHECK-NEXT:             "line": 0
+// CHECK-NEXT:           },
+// CHECK-NEXT:           "start": {
+// CHECK-NEXT:             "character": 6,
+// CHECK-NEXT:             "line": 0
+// CHECK-NEXT:           }
+// CHECK-NEXT:         },
+// CHECK-NEXT:         "severity": 1,
+// CHECK-NEXT:         "source": "mlir"
+// CHECK-NEXT:       }
+// CHECK-NEXT:     ],
+// CHECK-NEXT:     "uri": "test:///foo.mlir"
+// CHECK-NEXT:   }
+// -----
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+// -----
+{"jsonrpc":"2.0","method":"exit"}


        


More information about the Mlir-commits mailing list