[PATCH] D40564: [clangd] Simplify common JSON-parsing patterns in Protocol.

2017-11-29 Thread Sam McCall via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL319309: [clangd] Simplify common JSON-parsing patterns in 
Protocol. (authored by sammccall).

Changed prior to commit:
  https://reviews.llvm.org/D40564?vs=124585=124713#toc

Repository:
  rL LLVM

https://reviews.llvm.org/D40564

Files:
  clang-tools-extra/trunk/clangd/Protocol.cpp
  clang-tools-extra/trunk/clangd/Protocol.h
  clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test

Index: clang-tools-extra/trunk/clangd/Protocol.h
===
--- clang-tools-extra/trunk/clangd/Protocol.h
+++ clang-tools-extra/trunk/clangd/Protocol.h
@@ -51,7 +51,7 @@
   static URI fromUri(llvm::StringRef uri);
   static URI fromFile(llvm::StringRef file);
 
-  static URI parse(llvm::StringRef U) { return fromUri(U); }
+  static llvm::Optional parse(const json::Expr );
   static json::Expr unparse(const URI );
 
   friend bool operator==(const URI , const URI ) {
Index: clang-tools-extra/trunk/clangd/Protocol.cpp
===
--- clang-tools-extra/trunk/clangd/Protocol.cpp
+++ clang-tools-extra/trunk/clangd/Protocol.cpp
@@ -24,6 +24,149 @@
 using namespace clang;
 using namespace clang::clangd;
 
+namespace {
+// Helper for mapping JSON objects onto our protocol structs. Intended use:
+// Optional parse(json::Expr E) {
+//   ObjectParser O(E);
+//   if (!O || !O.parse("mandatory_field", Result.MandatoryField))
+// return None;
+//   O.parse("optional_field", Result.OptionalField);
+//   return Result;
+// }
+// FIXME: the static methods here should probably become the public parse()
+// extension point. Overloading free functions allows us to uniformly handle
+// enums, vectors, etc.
+class ObjectParser {
+public:
+  ObjectParser(const json::Expr ) : O(E.asObject()) {}
+
+  // True if the expression is an object.
+  operator bool() { return O; }
+
+  template  bool parse(const char *Prop, T ) {
+assert(*this && "Must check this is an object before calling parse()");
+if (const json::Expr *E = O->get(Prop))
+  return parse(*E, Out);
+return false;
+  }
+
+  // Optional requires special handling, because missing keys are OK.
+  template  bool parse(const char *Prop, llvm::Optional ) {
+assert(*this && "Must check this is an object before calling parse()");
+if (const json::Expr *E = O->get(Prop))
+  return parse(*E, Out);
+Out = None;
+return true;
+  }
+
+private:
+  // Primitives.
+  static bool parse(const json::Expr , std::string ) {
+if (auto S = E.asString()) {
+  Out = *S;
+  return true;
+}
+return false;
+  }
+
+  static bool parse(const json::Expr , int ) {
+if (auto S = E.asInteger()) {
+  Out = *S;
+  return true;
+}
+return false;
+  }
+
+  static bool parse(const json::Expr , bool ) {
+if (auto S = E.asBoolean()) {
+  Out = *S;
+  return true;
+}
+return false;
+  }
+
+  // Types with a parse() function.
+  template  static bool parse(const json::Expr , T ) {
+if (auto Parsed = std::remove_reference::type::parse(E)) {
+  Out = std::move(*Parsed);
+  return true;
+}
+return false;
+  }
+
+  // Nullable values as Optional.
+  template 
+  static bool parse(const json::Expr , llvm::Optional ) {
+if (E.asNull()) {
+  Out = None;
+  return true;
+}
+T Result;
+if (!parse(E, Result))
+  return false;
+Out = std::move(Result);
+return true;
+  }
+
+  // Array values with std::vector type.
+  template 
+  static bool parse(const json::Expr , std::vector ) {
+if (auto *A = E.asArray()) {
+  Out.clear();
+  Out.resize(A->size());
+  for (size_t I = 0; I < A->size(); ++I)
+if (!parse((*A)[I], Out[I]))
+  return false;
+  return true;
+}
+return false;
+  }
+
+  // Object values with std::map
+  template 
+  static bool parse(const json::Expr , std::map ) {
+if (auto *O = E.asObject()) {
+  for (const auto  : *O)
+if (!parse(KV.second, Out[StringRef(KV.first)]))
+  return false;
+  return true;
+}
+return false;
+  }
+
+  // Special cased enums, which can't have T::parse() functions.
+  // FIXME: make everything free functions so there's no special casing.
+  static bool parse(const json::Expr , TraceLevel ) {
+if (auto S = E.asString()) {
+  if (*S == "off") {
+Out = TraceLevel::Off;
+return true;
+  } else if (*S == "messages") {
+Out = TraceLevel::Messages;
+return true;
+  } else if (*S == "verbose") {
+Out = TraceLevel::Verbose;
+return true;
+  }
+}
+return false;
+  }
+
+  static bool parse(const json::Expr , FileChangeType ) {
+if (auto T = E.asInteger()) {
+  if (*T < static_cast(FileChangeType::Created) ||
+  *T > 

[PATCH] D40564: [clangd] Simplify common JSON-parsing patterns in Protocol.

2017-11-29 Thread Eric Liu via Phabricator via cfe-commits
ioeric accepted this revision.
ioeric added a comment.
This revision is now accepted and ready to land.

lgtm




Comment at: clangd/Protocol.cpp:56
+assert(*this && "Must check this is an object before calling parse()");
+if (const json::Expr *E = O->get(Prop)) {
+  return parse(*E, Out);

nit: no braces around one liners.



Comment at: clangd/Protocol.cpp:101
+  static bool parse(const json::Expr , llvm::Optional ) {
+if (E.asNull())
+  return true;

Should we set `Out` to `None` in this case?


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D40564



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits