This revision was automatically updated to reflect the committed changes.
Closed by commit rGfa69b608063e: [JSON] Add error reporting to fromJSON and 
ObjectMapper (authored by sammccall).
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

Changed prior to commit:
  https://reviews.llvm.org/D88103?vs=293879&id=293891#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D88103/new/

https://reviews.llvm.org/D88103

Files:
  clang-tools-extra/clangd/ClangdLSPServer.cpp
  clang-tools-extra/clangd/ClangdLSPServer.h
  clang-tools-extra/clangd/Protocol.cpp
  clang-tools-extra/clangd/Protocol.h
  clang-tools-extra/clangd/benchmarks/IndexBenchmark.cpp
  clang-tools-extra/clangd/index/Index.cpp
  clang-tools-extra/clangd/index/Index.h
  lldb/source/Utility/StructuredData.cpp
  llvm/include/llvm/Support/JSON.h
  llvm/lib/Analysis/TFUtils.cpp
  llvm/unittests/Support/JSONTest.cpp

Index: llvm/unittests/Support/JSONTest.cpp
===================================================================
--- llvm/unittests/Support/JSONTest.cpp
+++ llvm/unittests/Support/JSONTest.cpp
@@ -373,14 +373,21 @@
   return OS << "(" << S.S << ", " << (S.I ? std::to_string(*S.I) : "None")
             << ", " << S.B << ")";
 }
-bool fromJSON(const Value &E, CustomStruct &R) {
-  ObjectMapper O(E);
+bool fromJSON(const Value &E, CustomStruct &R, Path P) {
+  ObjectMapper O(E, P);
   if (!O || !O.map("str", R.S) || !O.map("int", R.I))
     return false;
   O.map("bool", R.B);
   return true;
 }
 
+static std::string errorContext(const Value &V, const Path::Root &R) {
+  std::string Context;
+  llvm::raw_string_ostream OS(Context);
+  R.printErrorContext(V, OS);
+  return OS.str();
+}
+
 TEST(JSONTest, Deserialize) {
   std::map<std::string, std::vector<CustomStruct>> R;
   CustomStruct ExpectedStruct = {"foo", 42, true};
@@ -404,16 +411,58 @@
       CustomStruct("bar", llvm::None, false),
       CustomStruct("baz", llvm::None, false),
   };
-  ASSERT_TRUE(fromJSON(J, R));
+  Path::Root Root("CustomStruct");
+  ASSERT_TRUE(fromJSON(J, R, Root));
   EXPECT_EQ(R, Expected);
 
+  (*J.getAsObject()->getArray("foo"))[0] = 123;
+  ASSERT_FALSE(fromJSON(J, R, Root));
+  EXPECT_EQ("expected object at CustomStruct.foo[0]",
+            toString(Root.getError()));
+  const char *ExpectedDump = R"({
+  "foo": [
+    /* error: expected object */
+    123,
+    { ... },
+    { ... }
+  ]
+})";
+  EXPECT_EQ(ExpectedDump, errorContext(J, Root));
+
   CustomStruct V;
-  EXPECT_FALSE(fromJSON(nullptr, V)) << "Not an object " << V;
-  EXPECT_FALSE(fromJSON(Object{}, V)) << "Missing required field " << V;
-  EXPECT_FALSE(fromJSON(Object{{"str", 1}}, V)) << "Wrong type " << V;
+  EXPECT_FALSE(fromJSON(nullptr, V, Root));
+  EXPECT_EQ("expected object when parsing CustomStruct",
+            toString(Root.getError()));
+
+  EXPECT_FALSE(fromJSON(Object{}, V, Root));
+  EXPECT_EQ("missing value at CustomStruct.str", toString(Root.getError()));
+
+  EXPECT_FALSE(fromJSON(Object{{"str", 1}}, V, Root));
+  EXPECT_EQ("expected string at CustomStruct.str", toString(Root.getError()));
+
   // Optional<T> must parse as the correct type if present.
-  EXPECT_FALSE(fromJSON(Object{{"str", 1}, {"int", "string"}}, V))
-      << "Wrong type for Optional<T> " << V;
+  EXPECT_FALSE(fromJSON(Object{{"str", "1"}, {"int", "string"}}, V, Root));
+  EXPECT_EQ("expected integer at CustomStruct.int", toString(Root.getError()));
+}
+
+TEST(JSONTest, ParseDeserialize) {
+  auto E = parse<std::vector<CustomStruct>>(R"json(
+    [{"str": "foo", "int": 42}, {"int": 42}]
+  )json");
+  EXPECT_THAT_EXPECTED(E, FailedWithMessage("missing value at (root)[1].str"));
+
+  E = parse<std::vector<CustomStruct>>(R"json(
+    [{"str": "foo", "int": 42}, {"str": "bar"}
+  )json");
+  EXPECT_THAT_EXPECTED(
+      E,
+      FailedWithMessage("[3:2, byte=50]: Expected , or ] after array element"));
+
+  E = parse<std::vector<CustomStruct>>(R"json(
+    [{"str": "foo", "int": 42}]
+  )json");
+  EXPECT_THAT_EXPECTED(E, Succeeded());
+  EXPECT_THAT(*E, testing::SizeIs(1));
 }
 
 TEST(JSONTest, Stream) {
@@ -481,9 +530,6 @@
                    {"f", "a moderately long string: 48 characters in total"},
                }}}},
   };
-  std::string Err;
-  raw_string_ostream OS(Err);
-  R.printErrorContext(V, OS);
   const char *Expected = R"({
   "a": [ ... ],
   "b": {
@@ -496,7 +542,7 @@
     }
   }
 })";
-  EXPECT_EQ(Expected, OS.str());
+  EXPECT_EQ(Expected, errorContext(V, R));
 }
 
 } // namespace
Index: llvm/lib/Analysis/TFUtils.cpp
===================================================================
--- llvm/lib/Analysis/TFUtils.cpp
+++ llvm/lib/Analysis/TFUtils.cpp
@@ -104,7 +104,9 @@
     Ctx.emitError("Unable to parse JSON Value as spec (" + Message + "): " + S);
     return None;
   };
-  json::ObjectMapper Mapper(Value);
+  // FIXME: accept a Path as a parameter, and use it for error reporting.
+  json::Path::Root Root("tensor_spec");
+  json::ObjectMapper Mapper(Value, Root);
   if (!Mapper)
     return EmitError("Value is not a dict");
 
Index: llvm/include/llvm/Support/JSON.h
===================================================================
--- llvm/include/llvm/Support/JSON.h
+++ llvm/include/llvm/Support/JSON.h
@@ -253,7 +253,14 @@
 /// === Converting JSON values to C++ types ===
 ///
 /// The convention is to have a deserializer function findable via ADL:
-///     fromJSON(const json::Value&, T&)->bool
+///     fromJSON(const json::Value&, T&, Path) -> bool
+///
+/// The return value indicates overall success, and Path is used for precise
+/// error reporting. (The Path::Root passed in at the top level fromJSON call
+/// captures any nested error and can render it in context).
+/// If conversion fails, fromJSON calls Path::report() and immediately returns.
+/// This ensures that the first fatal error survives.
+///
 /// Deserializers are provided for:
 ///   - bool
 ///   - int and int64_t
@@ -636,79 +643,90 @@
 
 // Standard deserializers are provided for primitive types.
 // See comments on Value.
-inline bool fromJSON(const Value &E, std::string &Out) {
+inline bool fromJSON(const Value &E, std::string &Out, Path P) {
   if (auto S = E.getAsString()) {
     Out = std::string(*S);
     return true;
   }
+  P.report("expected string");
   return false;
 }
-inline bool fromJSON(const Value &E, int &Out) {
+inline bool fromJSON(const Value &E, int &Out, Path P) {
   if (auto S = E.getAsInteger()) {
     Out = *S;
     return true;
   }
+  P.report("expected integer");
   return false;
 }
-inline bool fromJSON(const Value &E, int64_t &Out) {
+inline bool fromJSON(const Value &E, int64_t &Out, Path P) {
   if (auto S = E.getAsInteger()) {
     Out = *S;
     return true;
   }
+  P.report("expected integer");
   return false;
 }
-inline bool fromJSON(const Value &E, double &Out) {
+inline bool fromJSON(const Value &E, double &Out, Path P) {
   if (auto S = E.getAsNumber()) {
     Out = *S;
     return true;
   }
+  P.report("expected number");
   return false;
 }
-inline bool fromJSON(const Value &E, bool &Out) {
+inline bool fromJSON(const Value &E, bool &Out, Path P) {
   if (auto S = E.getAsBoolean()) {
     Out = *S;
     return true;
   }
+  P.report("expected boolean");
   return false;
 }
-inline bool fromJSON(const Value &E, std::nullptr_t &Out) {
+inline bool fromJSON(const Value &E, std::nullptr_t &Out, Path P) {
   if (auto S = E.getAsNull()) {
     Out = *S;
     return true;
   }
+  P.report("expected null");
   return false;
 }
-template <typename T> bool fromJSON(const Value &E, llvm::Optional<T> &Out) {
+template <typename T>
+bool fromJSON(const Value &E, llvm::Optional<T> &Out, Path P) {
   if (E.getAsNull()) {
     Out = llvm::None;
     return true;
   }
   T Result;
-  if (!fromJSON(E, Result))
+  if (!fromJSON(E, Result, P))
     return false;
   Out = std::move(Result);
   return true;
 }
-template <typename T> bool fromJSON(const Value &E, std::vector<T> &Out) {
+template <typename T>
+bool fromJSON(const Value &E, std::vector<T> &Out, Path P) {
   if (auto *A = E.getAsArray()) {
     Out.clear();
     Out.resize(A->size());
     for (size_t I = 0; I < A->size(); ++I)
-      if (!fromJSON((*A)[I], Out[I]))
+      if (!fromJSON((*A)[I], Out[I], P.index(I)))
         return false;
     return true;
   }
+  P.report("expected array");
   return false;
 }
 template <typename T>
-bool fromJSON(const Value &E, std::map<std::string, T> &Out) {
+bool fromJSON(const Value &E, std::map<std::string, T> &Out, Path P) {
   if (auto *O = E.getAsObject()) {
     Out.clear();
     for (const auto &KV : *O)
-      if (!fromJSON(KV.second, Out[std::string(llvm::StringRef(KV.first))]))
+      if (!fromJSON(KV.second, Out[std::string(llvm::StringRef(KV.first))],
+                    P.field(KV.first)))
         return false;
     return true;
   }
+  P.report("expected object");
   return false;
 }
 
@@ -721,42 +739,50 @@
 ///
 /// Example:
 /// \code
-///   bool fromJSON(const Value &E, MyStruct &R) {
-///     ObjectMapper O(E);
+///   bool fromJSON(const Value &E, MyStruct &R, Path P) {
+///     ObjectMapper O(E, P);
 ///     if (!O || !O.map("mandatory_field", R.MandatoryField))
-///       return false;
+///       return false; // error details are already reported
 ///     O.map("optional_field", R.OptionalField);
 ///     return true;
 ///   }
 /// \endcode
 class ObjectMapper {
 public:
-  ObjectMapper(const Value &E) : O(E.getAsObject()) {}
+  /// If O is not an object, this mapper is invalid and an error is reported.
+  ObjectMapper(const Value &E, Path P) : O(E.getAsObject()), P(P) {
+    if (!O)
+      P.report("expected object");
+  }
 
   /// True if the expression is an object.
   /// Must be checked before calling map().
-  operator bool() { return O; }
+  operator bool() const { return O; }
 
-  /// Maps a property to a field, if it exists.
-  template <typename T> bool map(StringRef Prop, T &Out) {
+  /// Maps a property to a field.
+  /// If the property is missing or invalid, reports an error.
+  template <typename T> bool map(StringLiteral Prop, T &Out) {
     assert(*this && "Must check this is an object before calling map()");
     if (const Value *E = O->get(Prop))
-      return fromJSON(*E, Out);
+      return fromJSON(*E, Out, P.field(Prop));
+    P.field(Prop).report("missing value");
     return false;
   }
 
   /// Maps a property to a field, if it exists.
+  /// If the property exists and is invalid, reports an error.
   /// (Optional requires special handling, because missing keys are OK).
-  template <typename T> bool map(StringRef Prop, llvm::Optional<T> &Out) {
+  template <typename T> bool map(StringLiteral Prop, llvm::Optional<T> &Out) {
     assert(*this && "Must check this is an object before calling map()");
     if (const Value *E = O->get(Prop))
-      return fromJSON(*E, Out);
+      return fromJSON(*E, Out, P.field(Prop));
     Out = llvm::None;
     return true;
   }
 
 private:
   const Object *O;
+  Path P;
 };
 
 /// Parses the provided JSON source, or returns a ParseError.
@@ -780,6 +806,20 @@
   }
 };
 
+/// Version of parse() that converts the parsed value to the type T.
+/// RootName describes the root object and is used in error messages.
+template <typename T>
+Expected<T> parse(const llvm::StringRef &JSON, const char *RootName = "") {
+  auto V = parse(JSON);
+  if (!V)
+    return V.takeError();
+  Path::Root R(RootName);
+  T Result;
+  if (fromJSON(*V, Result, R))
+    return std::move(Result);
+  return R.getError();
+}
+
 /// json::OStream allows writing well-formed JSON without materializing
 /// all structures as json::Value ahead of time.
 /// It's faster, lower-level, and less safe than OS << json::Value.
Index: lldb/source/Utility/StructuredData.cpp
===================================================================
--- lldb/source/Utility/StructuredData.cpp
+++ lldb/source/Utility/StructuredData.cpp
@@ -57,21 +57,17 @@
   if (json::Array *A = value.getAsArray())
     return ParseJSONArray(A);
 
-  std::string s;
-  if (json::fromJSON(value, s))
-    return std::make_shared<StructuredData::String>(s);
+  if (auto s = value.getAsString())
+    return std::make_shared<StructuredData::String>(*s);
 
-  bool b;
-  if (json::fromJSON(value, b))
-    return std::make_shared<StructuredData::Boolean>(b);
+  if (auto b = value.getAsBoolean())
+    return std::make_shared<StructuredData::Boolean>(*b);
 
-  int64_t i;
-  if (json::fromJSON(value, i))
-    return std::make_shared<StructuredData::Integer>(i);
+  if (auto i = value.getAsInteger(i))
+    return std::make_shared<StructuredData::Integer>(*i);
 
-  double d;
-  if (json::fromJSON(value, d))
-    return std::make_shared<StructuredData::Float>(d);
+  if (auto d = value.getAsNumber())
+    return std::make_shared<StructuredData::Float>(*d);
 
   return StructuredData::ObjectSP();
 }
Index: clang-tools-extra/clangd/index/Index.h
===================================================================
--- clang-tools-extra/clangd/index/Index.h
+++ clang-tools-extra/clangd/index/Index.h
@@ -57,7 +57,8 @@
   }
   bool operator!=(const FuzzyFindRequest &Req) const { return !(*this == Req); }
 };
-bool fromJSON(const llvm::json::Value &Value, FuzzyFindRequest &Request);
+bool fromJSON(const llvm::json::Value &Value, FuzzyFindRequest &Request,
+              llvm::json::Path);
 llvm::json::Value toJSON(const FuzzyFindRequest &Request);
 
 struct LookupRequest {
Index: clang-tools-extra/clangd/index/Index.cpp
===================================================================
--- clang-tools-extra/clangd/index/Index.cpp
+++ clang-tools-extra/clangd/index/Index.cpp
@@ -31,8 +31,9 @@
   return Index;
 }
 
-bool fromJSON(const llvm::json::Value &Parameters, FuzzyFindRequest &Request) {
-  llvm::json::ObjectMapper O(Parameters);
+bool fromJSON(const llvm::json::Value &Parameters, FuzzyFindRequest &Request,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Parameters, P);
   int64_t Limit;
   bool OK =
       O && O.map("Query", Request.Query) && O.map("Scopes", Request.Scopes) &&
Index: clang-tools-extra/clangd/benchmarks/IndexBenchmark.cpp
===================================================================
--- clang-tools-extra/clangd/benchmarks/IndexBenchmark.cpp
+++ clang-tools-extra/clangd/benchmarks/IndexBenchmark.cpp
@@ -56,8 +56,10 @@
   for (const auto &Item : *JSONArray->getAsArray()) {
     FuzzyFindRequest Request;
     // Panic if the provided file couldn't be parsed.
-    if (!fromJSON(Item, Request)) {
-      llvm::errs() << "Error when deserializing request: " << Item << '\n';
+    llvm::json::Path::Root Root("FuzzyFindRequest");
+    if (!fromJSON(Item, Request, Root)) {
+      llvm::errs() << llvm::toString(Root.getError()) << "\n";
+      Root.printErrorContext(Item, llvm::errs());
       exit(1);
     }
     Requests.push_back(Request);
Index: clang-tools-extra/clangd/Protocol.h
===================================================================
--- clang-tools-extra/clangd/Protocol.h
+++ clang-tools-extra/clangd/Protocol.h
@@ -116,14 +116,15 @@
 
 /// Serialize/deserialize \p URIForFile to/from a string URI.
 llvm::json::Value toJSON(const URIForFile &U);
-bool fromJSON(const llvm::json::Value &, URIForFile &);
+bool fromJSON(const llvm::json::Value &, URIForFile &, llvm::json::Path);
 
 struct TextDocumentIdentifier {
   /// The text document's URI.
   URIForFile uri;
 };
 llvm::json::Value toJSON(const TextDocumentIdentifier &);
-bool fromJSON(const llvm::json::Value &, TextDocumentIdentifier &);
+bool fromJSON(const llvm::json::Value &, TextDocumentIdentifier &,
+              llvm::json::Path);
 
 struct VersionedTextDocumentIdentifier : public TextDocumentIdentifier {
   /// The version number of this document. If a versioned text document
@@ -139,7 +140,8 @@
   llvm::Optional<std::int64_t> version;
 };
 llvm::json::Value toJSON(const VersionedTextDocumentIdentifier &);
-bool fromJSON(const llvm::json::Value &, VersionedTextDocumentIdentifier &);
+bool fromJSON(const llvm::json::Value &, VersionedTextDocumentIdentifier &,
+              llvm::json::Path);
 
 struct Position {
   /// Line position in a document (zero-based).
@@ -166,7 +168,7 @@
            std::tie(RHS.line, RHS.character);
   }
 };
-bool fromJSON(const llvm::json::Value &, Position &);
+bool fromJSON(const llvm::json::Value &, Position &, llvm::json::Path);
 llvm::json::Value toJSON(const Position &);
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Position &);
 
@@ -192,7 +194,7 @@
     return start <= Rng.start && Rng.end <= end;
   }
 };
-bool fromJSON(const llvm::json::Value &, Range &);
+bool fromJSON(const llvm::json::Value &, Range &, llvm::json::Path);
 llvm::json::Value toJSON(const Range &);
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Range &);
 
@@ -228,7 +230,7 @@
 inline bool operator==(const TextEdit &L, const TextEdit &R) {
   return std::tie(L.newText, L.range) == std::tie(R.newText, R.range);
 }
-bool fromJSON(const llvm::json::Value &, TextEdit &);
+bool fromJSON(const llvm::json::Value &, TextEdit &, llvm::json::Path);
 llvm::json::Value toJSON(const TextEdit &);
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TextEdit &);
 
@@ -248,17 +250,19 @@
   /// The content of the opened text document.
   std::string text;
 };
-bool fromJSON(const llvm::json::Value &, TextDocumentItem &);
+bool fromJSON(const llvm::json::Value &, TextDocumentItem &, llvm::json::Path);
 
 enum class TraceLevel {
   Off = 0,
   Messages = 1,
   Verbose = 2,
 };
-bool fromJSON(const llvm::json::Value &E, TraceLevel &Out);
+bool fromJSON(const llvm::json::Value &E, TraceLevel &Out, llvm::json::Path);
 
 struct NoParams {};
-inline bool fromJSON(const llvm::json::Value &, NoParams &) { return true; }
+inline bool fromJSON(const llvm::json::Value &, NoParams &, llvm::json::Path) {
+  return true;
+}
 using InitializedParams = NoParams;
 using ShutdownParams = NoParams;
 using ExitParams = NoParams;
@@ -306,13 +310,15 @@
   Operator = 24,
   TypeParameter = 25,
 };
-bool fromJSON(const llvm::json::Value &, CompletionItemKind &);
+bool fromJSON(const llvm::json::Value &, CompletionItemKind &,
+              llvm::json::Path);
 constexpr auto CompletionItemKindMin =
     static_cast<size_t>(CompletionItemKind::Text);
 constexpr auto CompletionItemKindMax =
     static_cast<size_t>(CompletionItemKind::TypeParameter);
 using CompletionItemKindBitset = std::bitset<CompletionItemKindMax + 1>;
-bool fromJSON(const llvm::json::Value &, CompletionItemKindBitset &);
+bool fromJSON(const llvm::json::Value &, CompletionItemKindBitset &,
+              llvm::json::Path);
 CompletionItemKind
 adjustKindToCapability(CompletionItemKind Kind,
                        CompletionItemKindBitset &SupportedCompletionItemKinds);
@@ -346,11 +352,11 @@
   Operator = 25,
   TypeParameter = 26
 };
-bool fromJSON(const llvm::json::Value &, SymbolKind &);
+bool fromJSON(const llvm::json::Value &, SymbolKind &, llvm::json::Path);
 constexpr auto SymbolKindMin = static_cast<size_t>(SymbolKind::File);
 constexpr auto SymbolKindMax = static_cast<size_t>(SymbolKind::TypeParameter);
 using SymbolKindBitset = std::bitset<SymbolKindMax + 1>;
-bool fromJSON(const llvm::json::Value &, SymbolKindBitset &);
+bool fromJSON(const llvm::json::Value &, SymbolKindBitset &, llvm::json::Path);
 SymbolKind adjustKindToCapability(SymbolKind Kind,
                                   SymbolKindBitset &supportedSymbolKinds);
 
@@ -372,7 +378,7 @@
   UTF32,
 };
 llvm::json::Value toJSON(const OffsetEncoding &);
-bool fromJSON(const llvm::json::Value &, OffsetEncoding &);
+bool fromJSON(const llvm::json::Value &, OffsetEncoding &, llvm::json::Path);
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, OffsetEncoding);
 
 // Describes the content type that a client supports in various result literals
@@ -381,7 +387,7 @@
   PlainText,
   Markdown,
 };
-bool fromJSON(const llvm::json::Value &, MarkupKind &);
+bool fromJSON(const llvm::json::Value &, MarkupKind &, llvm::json::Path);
 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, MarkupKind);
 
 // This struct doesn't mirror LSP!
@@ -470,7 +476,8 @@
   /// window.implicitWorkDoneProgressCreate
   bool ImplicitProgressCreation = false;
 };
-bool fromJSON(const llvm::json::Value &, ClientCapabilities &);
+bool fromJSON(const llvm::json::Value &, ClientCapabilities &,
+              llvm::json::Path);
 
 /// Clangd extension that's used in the 'compilationDatabaseChanges' in
 /// workspace/didChangeConfiguration to record updates to the in-memory
@@ -479,7 +486,8 @@
   std::string workingDirectory;
   std::vector<std::string> compilationCommand;
 };
-bool fromJSON(const llvm::json::Value &, ClangdCompileCommand &);
+bool fromJSON(const llvm::json::Value &, ClangdCompileCommand &,
+              llvm::json::Path);
 
 /// Clangd extension: parameters configurable at any time, via the
 /// `workspace/didChangeConfiguration` notification.
@@ -489,7 +497,8 @@
   // The key of the map is a file name.
   std::map<std::string, ClangdCompileCommand> compilationDatabaseChanges;
 };
-bool fromJSON(const llvm::json::Value &, ConfigurationSettings &);
+bool fromJSON(const llvm::json::Value &, ConfigurationSettings &,
+              llvm::json::Path);
 
 /// Clangd extension: parameters configurable at `initialize` time.
 /// LSP defines this type as `any`.
@@ -507,7 +516,8 @@
   /// Clients supports show file status for textDocument/clangd.fileStatus.
   bool FileStatus = false;
 };
-bool fromJSON(const llvm::json::Value &, InitializationOptions &);
+bool fromJSON(const llvm::json::Value &, InitializationOptions &,
+              llvm::json::Path);
 
 struct InitializeParams {
   /// The process Id of the parent process that started
@@ -539,7 +549,7 @@
   /// User-provided initialization options.
   InitializationOptions initializationOptions;
 };
-bool fromJSON(const llvm::json::Value &, InitializeParams &);
+bool fromJSON(const llvm::json::Value &, InitializeParams &, llvm::json::Path);
 
 struct WorkDoneProgressCreateParams {
   /// The token to be used to report progress.
@@ -650,19 +660,22 @@
   /// The document that was opened.
   TextDocumentItem textDocument;
 };
-bool fromJSON(const llvm::json::Value &, DidOpenTextDocumentParams &);
+bool fromJSON(const llvm::json::Value &, DidOpenTextDocumentParams &,
+              llvm::json::Path);
 
 struct DidCloseTextDocumentParams {
   /// The document that was closed.
   TextDocumentIdentifier textDocument;
 };
-bool fromJSON(const llvm::json::Value &, DidCloseTextDocumentParams &);
+bool fromJSON(const llvm::json::Value &, DidCloseTextDocumentParams &,
+              llvm::json::Path);
 
 struct DidSaveTextDocumentParams {
   /// The document that was saved.
   TextDocumentIdentifier textDocument;
 };
-bool fromJSON(const llvm::json::Value &, DidSaveTextDocumentParams &);
+bool fromJSON(const llvm::json::Value &, DidSaveTextDocumentParams &,
+              llvm::json::Path);
 
 struct TextDocumentContentChangeEvent {
   /// The range of the document that changed.
@@ -674,7 +687,8 @@
   /// The new text of the range/document.
   std::string text;
 };
-bool fromJSON(const llvm::json::Value &, TextDocumentContentChangeEvent &);
+bool fromJSON(const llvm::json::Value &, TextDocumentContentChangeEvent &,
+              llvm::json::Path);
 
 struct DidChangeTextDocumentParams {
   /// The document that did change. The version number points
@@ -697,7 +711,8 @@
   /// This is a clangd extension.
   bool forceRebuild = false;
 };
-bool fromJSON(const llvm::json::Value &, DidChangeTextDocumentParams &);
+bool fromJSON(const llvm::json::Value &, DidChangeTextDocumentParams &,
+              llvm::json::Path);
 
 enum class FileChangeType {
   /// The file got created.
@@ -707,7 +722,8 @@
   /// The file got deleted.
   Deleted = 3
 };
-bool fromJSON(const llvm::json::Value &E, FileChangeType &Out);
+bool fromJSON(const llvm::json::Value &E, FileChangeType &Out,
+              llvm::json::Path);
 
 struct FileEvent {
   /// The file's URI.
@@ -715,18 +731,20 @@
   /// The change type.
   FileChangeType type = FileChangeType::Created;
 };
-bool fromJSON(const llvm::json::Value &, FileEvent &);
+bool fromJSON(const llvm::json::Value &, FileEvent &, llvm::json::Path);
 
 struct DidChangeWatchedFilesParams {
   /// The actual file events.
   std::vector<FileEvent> changes;
 };
-bool fromJSON(const llvm::json::Value &, DidChangeWatchedFilesParams &);
+bool fromJSON(const llvm::json::Value &, DidChangeWatchedFilesParams &,
+              llvm::json::Path);
 
 struct DidChangeConfigurationParams {
   ConfigurationSettings settings;
 };
-bool fromJSON(const llvm::json::Value &, DidChangeConfigurationParams &);
+bool fromJSON(const llvm::json::Value &, DidChangeConfigurationParams &,
+              llvm::json::Path);
 
 // Note: we do not parse FormattingOptions for *FormattingParams.
 // In general, we use a clang-format style detected from common mechanisms
@@ -743,7 +761,8 @@
   /// The range to format
   Range range;
 };
-bool fromJSON(const llvm::json::Value &, DocumentRangeFormattingParams &);
+bool fromJSON(const llvm::json::Value &, DocumentRangeFormattingParams &,
+              llvm::json::Path);
 
 struct DocumentOnTypeFormattingParams {
   /// The document to format.
@@ -755,19 +774,22 @@
   /// The character that has been typed.
   std::string ch;
 };
-bool fromJSON(const llvm::json::Value &, DocumentOnTypeFormattingParams &);
+bool fromJSON(const llvm::json::Value &, DocumentOnTypeFormattingParams &,
+              llvm::json::Path);
 
 struct DocumentFormattingParams {
   /// The document to format.
   TextDocumentIdentifier textDocument;
 };
-bool fromJSON(const llvm::json::Value &, DocumentFormattingParams &);
+bool fromJSON(const llvm::json::Value &, DocumentFormattingParams &,
+              llvm::json::Path);
 
 struct DocumentSymbolParams {
   // The text document to find symbols in.
   TextDocumentIdentifier textDocument;
 };
-bool fromJSON(const llvm::json::Value &, DocumentSymbolParams &);
+bool fromJSON(const llvm::json::Value &, DocumentSymbolParams &,
+              llvm::json::Path);
 
 /// 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
@@ -826,7 +848,7 @@
     return std::tie(LHS.range, LHS.message) < std::tie(RHS.range, RHS.message);
   }
 };
-bool fromJSON(const llvm::json::Value &, Diagnostic &);
+bool fromJSON(const llvm::json::Value &, Diagnostic &, llvm::json::Path);
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Diagnostic &);
 
 struct PublishDiagnosticsParams {
@@ -843,7 +865,7 @@
   /// An array of diagnostics.
   std::vector<Diagnostic> diagnostics;
 };
-bool fromJSON(const llvm::json::Value &, CodeActionContext &);
+bool fromJSON(const llvm::json::Value &, CodeActionContext &, llvm::json::Path);
 
 struct CodeActionParams {
   /// The document in which the command was invoked.
@@ -855,7 +877,7 @@
   /// Context carrying additional information.
   CodeActionContext context;
 };
-bool fromJSON(const llvm::json::Value &, CodeActionParams &);
+bool fromJSON(const llvm::json::Value &, CodeActionParams &, llvm::json::Path);
 
 struct WorkspaceEdit {
   /// Holds changes to existing resources.
@@ -864,7 +886,7 @@
   /// Note: "documentChanges" is not currently used because currently there is
   /// no support for versioned edits.
 };
-bool fromJSON(const llvm::json::Value &, WorkspaceEdit &);
+bool fromJSON(const llvm::json::Value &, WorkspaceEdit &, llvm::json::Path);
 llvm::json::Value toJSON(const WorkspaceEdit &WE);
 
 /// Arguments for the 'applyTweak' command. The server sends these commands as a
@@ -879,7 +901,7 @@
   /// ID of the tweak that should be executed. Corresponds to Tweak::id().
   std::string tweakID;
 };
-bool fromJSON(const llvm::json::Value &, TweakArgs &);
+bool fromJSON(const llvm::json::Value &, TweakArgs &, llvm::json::Path);
 llvm::json::Value toJSON(const TweakArgs &A);
 
 /// Exact commands are not specified in the protocol so we define the
@@ -903,7 +925,8 @@
   llvm::Optional<WorkspaceEdit> workspaceEdit;
   llvm::Optional<TweakArgs> tweakArgs;
 };
-bool fromJSON(const llvm::json::Value &, ExecuteCommandParams &);
+bool fromJSON(const llvm::json::Value &, ExecuteCommandParams &,
+              llvm::json::Path);
 
 struct Command : public ExecuteCommandParams {
   std::string title;
@@ -1014,7 +1037,8 @@
   /// A non-empty query string
   std::string query;
 };
-bool fromJSON(const llvm::json::Value &, WorkspaceSymbolParams &);
+bool fromJSON(const llvm::json::Value &, WorkspaceSymbolParams &,
+              llvm::json::Path);
 
 struct ApplyWorkspaceEditParams {
   WorkspaceEdit edit;
@@ -1025,7 +1049,8 @@
   bool applied = true;
   llvm::Optional<std::string> failureReason;
 };
-bool fromJSON(const llvm::json::Value &, ApplyWorkspaceEditResponse &);
+bool fromJSON(const llvm::json::Value &, ApplyWorkspaceEditResponse &,
+              llvm::json::Path);
 
 struct TextDocumentPositionParams {
   /// The text document.
@@ -1034,7 +1059,8 @@
   /// The position inside the text document.
   Position position;
 };
-bool fromJSON(const llvm::json::Value &, TextDocumentPositionParams &);
+bool fromJSON(const llvm::json::Value &, TextDocumentPositionParams &,
+              llvm::json::Path);
 
 enum class CompletionTriggerKind {
   /// Completion was triggered by typing an identifier (24x7 code
@@ -1054,12 +1080,12 @@
   /// Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter`
   std::string triggerCharacter;
 };
-bool fromJSON(const llvm::json::Value &, CompletionContext &);
+bool fromJSON(const llvm::json::Value &, CompletionContext &, llvm::json::Path);
 
 struct CompletionParams : TextDocumentPositionParams {
   CompletionContext context;
 };
-bool fromJSON(const llvm::json::Value &, CompletionParams &);
+bool fromJSON(const llvm::json::Value &, CompletionParams &, llvm::json::Path);
 
 struct MarkupContent {
   MarkupKind kind = MarkupKind::PlainText;
@@ -1237,7 +1263,7 @@
   /// The new name of the symbol.
   std::string newName;
 };
-bool fromJSON(const llvm::json::Value &, RenameParams &);
+bool fromJSON(const llvm::json::Value &, RenameParams &, llvm::json::Path);
 
 enum class DocumentHighlightKind { Text = 1, Read = 2, Write = 3 };
 
@@ -1268,7 +1294,8 @@
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DocumentHighlight &);
 
 enum class TypeHierarchyDirection { Children = 0, Parents = 1, Both = 2 };
-bool fromJSON(const llvm::json::Value &E, TypeHierarchyDirection &Out);
+bool fromJSON(const llvm::json::Value &E, TypeHierarchyDirection &Out,
+              llvm::json::Path);
 
 /// The type hierarchy params is an extension of the
 /// `TextDocumentPositionsParams` with optional properties which can be used to
@@ -1280,7 +1307,8 @@
   /// The direction of the hierarchy levels to resolve.
   TypeHierarchyDirection direction = TypeHierarchyDirection::Parents;
 };
-bool fromJSON(const llvm::json::Value &, TypeHierarchyParams &);
+bool fromJSON(const llvm::json::Value &, TypeHierarchyParams &,
+              llvm::json::Path);
 
 struct TypeHierarchyItem {
   /// The human readable name of the hierarchy item.
@@ -1326,7 +1354,7 @@
 };
 llvm::json::Value toJSON(const TypeHierarchyItem &);
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TypeHierarchyItem &);
-bool fromJSON(const llvm::json::Value &, TypeHierarchyItem &);
+bool fromJSON(const llvm::json::Value &, TypeHierarchyItem &, llvm::json::Path);
 
 /// Parameters for the `typeHierarchy/resolve` request.
 struct ResolveTypeHierarchyItemParams {
@@ -1339,12 +1367,13 @@
   /// The direction of the hierarchy levels to resolve.
   TypeHierarchyDirection direction;
 };
-bool fromJSON(const llvm::json::Value &, ResolveTypeHierarchyItemParams &);
+bool fromJSON(const llvm::json::Value &, ResolveTypeHierarchyItemParams &,
+              llvm::json::Path);
 
 struct ReferenceParams : public TextDocumentPositionParams {
   // For now, no options like context.includeDeclaration are supported.
 };
-bool fromJSON(const llvm::json::Value &, ReferenceParams &);
+bool fromJSON(const llvm::json::Value &, ReferenceParams &, llvm::json::Path);
 
 /// Clangd extension: indicates the current state of the file in clangd,
 /// sent from server via the `textDocument/clangd.fileStatus` notification.
@@ -1394,7 +1423,8 @@
   /// The text document.
   TextDocumentIdentifier textDocument;
 };
-bool fromJSON(const llvm::json::Value &, SemanticTokensParams &);
+bool fromJSON(const llvm::json::Value &, SemanticTokensParams &,
+              llvm::json::Path);
 
 /// Body of textDocument/semanticTokens/full/delta request.
 /// Requests the changes in semantic tokens since a previous response.
@@ -1404,7 +1434,8 @@
   /// The previous result id.
   std::string previousResultId;
 };
-bool fromJSON(const llvm::json::Value &Params, SemanticTokensDeltaParams &R);
+bool fromJSON(const llvm::json::Value &Params, SemanticTokensDeltaParams &R,
+              llvm::json::Path);
 
 /// Describes a a replacement of a contiguous range of semanticTokens.
 struct SemanticTokensEdit {
@@ -1462,7 +1493,8 @@
   /// The positions inside the text document.
   std::vector<Position> positions;
 };
-bool fromJSON(const llvm::json::Value &, SelectionRangeParams &);
+bool fromJSON(const llvm::json::Value &, SelectionRangeParams &,
+              llvm::json::Path);
 
 struct SelectionRange {
   /**
@@ -1482,7 +1514,8 @@
   /// The document to provide document links for.
   TextDocumentIdentifier textDocument;
 };
-bool fromJSON(const llvm::json::Value &, DocumentLinkParams &);
+bool fromJSON(const llvm::json::Value &, DocumentLinkParams &,
+              llvm::json::Path);
 
 /// A range in a text document that links to an internal or external resource,
 /// like another text document or a web site.
@@ -1515,7 +1548,8 @@
 struct FoldingRangeParams {
   TextDocumentIdentifier textDocument;
 };
-bool fromJSON(const llvm::json::Value &, FoldingRangeParams &);
+bool fromJSON(const llvm::json::Value &, FoldingRangeParams &,
+              llvm::json::Path);
 
 /// Stores information about a region of code that can be folded.
 struct FoldingRange {
Index: clang-tools-extra/clangd/Protocol.cpp
===================================================================
--- clang-tools-extra/clangd/Protocol.cpp
+++ clang-tools-extra/clangd/Protocol.cpp
@@ -51,22 +51,22 @@
   return URIForFile(std::move(*Resolved));
 }
 
-bool fromJSON(const llvm::json::Value &E, URIForFile &R) {
+bool fromJSON(const llvm::json::Value &E, URIForFile &R, llvm::json::Path P) {
   if (auto S = E.getAsString()) {
     auto Parsed = URI::parse(*S);
     if (!Parsed) {
-      elog("Failed to parse URI {0}: {1}", *S, Parsed.takeError());
+      P.report("failed to parse URI");
       return false;
     }
     if (Parsed->scheme() != "file" && Parsed->scheme() != "test") {
-      elog("Clangd only supports 'file' URI scheme for workspace files: {0}",
-           *S);
+      P.report("clangd only supports 'file' URI scheme for workspace files");
       return false;
     }
     // "file" and "test" schemes do not require hint path.
     auto U = URIForFile::fromURI(*Parsed, /*HintPath=*/"");
     if (!U) {
-      elog("{0}", U.takeError());
+      P.report("unresolvable URI");
+      consumeError(U.takeError());
       return false;
     }
     R = std::move(*U);
@@ -85,8 +85,9 @@
   return llvm::json::Object{{"uri", R.uri}};
 }
 
-bool fromJSON(const llvm::json::Value &Params, TextDocumentIdentifier &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, TextDocumentIdentifier &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("uri", R.uri);
 }
 
@@ -97,14 +98,15 @@
 }
 
 bool fromJSON(const llvm::json::Value &Params,
-              VersionedTextDocumentIdentifier &R) {
-  llvm::json::ObjectMapper O(Params);
-  return fromJSON(Params, static_cast<TextDocumentIdentifier &>(R)) && O &&
+              VersionedTextDocumentIdentifier &R, llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return fromJSON(Params, static_cast<TextDocumentIdentifier &>(R), P) && O &&
          O.map("version", R.version);
 }
 
-bool fromJSON(const llvm::json::Value &Params, Position &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, Position &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("line", R.line) && O.map("character", R.character);
 }
 
@@ -119,8 +121,8 @@
   return OS << P.line << ':' << P.character;
 }
 
-bool fromJSON(const llvm::json::Value &Params, Range &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, Range &R, llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("start", R.start) && O.map("end", R.end);
 }
 
@@ -146,14 +148,16 @@
   return OS << L.range << '@' << L.uri;
 }
 
-bool fromJSON(const llvm::json::Value &Params, TextDocumentItem &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, TextDocumentItem &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("uri", R.uri) && O.map("languageId", R.languageId) &&
          O.map("version", R.version) && O.map("text", R.text);
 }
 
-bool fromJSON(const llvm::json::Value &Params, TextEdit &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, TextEdit &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("range", R.range) && O.map("newText", R.newText);
 }
 
@@ -170,7 +174,7 @@
   return OS << '"';
 }
 
-bool fromJSON(const llvm::json::Value &E, TraceLevel &Out) {
+bool fromJSON(const llvm::json::Value &E, TraceLevel &Out, llvm::json::Path P) {
   if (auto S = E.getAsString()) {
     if (*S == "off") {
       Out = TraceLevel::Off;
@@ -186,7 +190,7 @@
   return false;
 }
 
-bool fromJSON(const llvm::json::Value &E, SymbolKind &Out) {
+bool fromJSON(const llvm::json::Value &E, SymbolKind &Out, llvm::json::Path P) {
   if (auto T = E.getAsInteger()) {
     if (*T < static_cast<int>(SymbolKind::File) ||
         *T > static_cast<int>(SymbolKind::TypeParameter))
@@ -197,11 +201,12 @@
   return false;
 }
 
-bool fromJSON(const llvm::json::Value &E, SymbolKindBitset &Out) {
+bool fromJSON(const llvm::json::Value &E, SymbolKindBitset &Out,
+              llvm::json::Path P) {
   if (auto *A = E.getAsArray()) {
     for (size_t I = 0; I < A->size(); ++I) {
       SymbolKind KindOut;
-      if (fromJSON((*A)[I], KindOut))
+      if (fromJSON((*A)[I], KindOut, P.index(I)))
         Out.set(size_t(KindOut));
     }
     return true;
@@ -286,10 +291,13 @@
   llvm_unreachable("invalid symbol kind");
 }
 
-bool fromJSON(const llvm::json::Value &Params, ClientCapabilities &R) {
+bool fromJSON(const llvm::json::Value &Params, ClientCapabilities &R,
+              llvm::json::Path P) {
   const llvm::json::Object *O = Params.getAsObject();
-  if (!O)
+  if (!O) {
+    P.report("expected object");
     return false;
+  }
   if (auto *TextDocument = O->getObject("textDocument")) {
     if (auto *SemanticHighlighting =
             TextDocument->getObject("semanticHighlightingCapabilities")) {
@@ -313,7 +321,7 @@
           R.CompletionSnippets = *SnippetSupport;
         if (auto DocumentationFormat = Item->getArray("documentationFormat")) {
           for (const auto &Format : *DocumentationFormat) {
-            if (fromJSON(Format, R.CompletionDocumentationFormat))
+            if (fromJSON(Format, R.CompletionDocumentationFormat, P))
               break;
           }
         }
@@ -321,7 +329,11 @@
       if (auto *ItemKind = Completion->getObject("completionItemKind")) {
         if (auto *ValueSet = ItemKind->get("valueSet")) {
           R.CompletionItemKinds.emplace();
-          if (!fromJSON(*ValueSet, *R.CompletionItemKinds))
+          if (!fromJSON(*ValueSet, *R.CompletionItemKinds,
+                        P.field("textDocument")
+                            .field("completion")
+                            .field("completionItemKind")
+                            .field("valueSet")))
             return false;
         }
       }
@@ -340,7 +352,7 @@
     if (auto *Hover = TextDocument->getObject("hover")) {
       if (auto *ContentFormat = Hover->getArray("contentFormat")) {
         for (const auto &Format : *ContentFormat) {
-          if (fromJSON(Format, R.HoverContentFormat))
+          if (fromJSON(Format, R.HoverContentFormat, P))
             break;
         }
       }
@@ -364,7 +376,11 @@
       if (auto *SymbolKind = Symbol->getObject("symbolKind")) {
         if (auto *ValueSet = SymbolKind->get("valueSet")) {
           R.WorkspaceSymbolKinds.emplace();
-          if (!fromJSON(*ValueSet, *R.WorkspaceSymbolKinds))
+          if (!fromJSON(*ValueSet, *R.WorkspaceSymbolKinds,
+                        P.field("workspace")
+                            .field("symbol")
+                            .field("symbolKind")
+                            .field("valueSet")))
             return false;
         }
       }
@@ -378,14 +394,16 @@
   }
   if (auto *OffsetEncoding = O->get("offsetEncoding")) {
     R.offsetEncoding.emplace();
-    if (!fromJSON(*OffsetEncoding, *R.offsetEncoding))
+    if (!fromJSON(*OffsetEncoding, *R.offsetEncoding,
+                  P.field("offsetEncoding")))
       return false;
   }
   return true;
 }
 
-bool fromJSON(const llvm::json::Value &Params, InitializeParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, InitializeParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   if (!O)
     return false;
   // We deliberately don't fail if we can't parse individual fields.
@@ -445,23 +463,27 @@
   return llvm::json::Object{{"type", R.type}, {"message", R.message}};
 }
 
-bool fromJSON(const llvm::json::Value &Params, DidOpenTextDocumentParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, DidOpenTextDocumentParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument);
 }
 
-bool fromJSON(const llvm::json::Value &Params, DidCloseTextDocumentParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, DidCloseTextDocumentParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument);
 }
 
-bool fromJSON(const llvm::json::Value &Params, DidSaveTextDocumentParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, DidSaveTextDocumentParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument);
 }
 
-bool fromJSON(const llvm::json::Value &Params, DidChangeTextDocumentParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, DidChangeTextDocumentParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   if (!O)
     return false;
   O.map("forceRebuild", R.forceRebuild);  // Optional clangd extension.
@@ -470,7 +492,8 @@
          O.map("wantDiagnostics", R.wantDiagnostics);
 }
 
-bool fromJSON(const llvm::json::Value &E, FileChangeType &Out) {
+bool fromJSON(const llvm::json::Value &E, FileChangeType &Out,
+              llvm::json::Path P) {
   if (auto T = E.getAsInteger()) {
     if (*T < static_cast<int>(FileChangeType::Created) ||
         *T > static_cast<int>(FileChangeType::Deleted))
@@ -481,43 +504,47 @@
   return false;
 }
 
-bool fromJSON(const llvm::json::Value &Params, FileEvent &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, FileEvent &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("uri", R.uri) && O.map("type", R.type);
 }
 
-bool fromJSON(const llvm::json::Value &Params, DidChangeWatchedFilesParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, DidChangeWatchedFilesParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("changes", R.changes);
 }
 
 bool fromJSON(const llvm::json::Value &Params,
-              TextDocumentContentChangeEvent &R) {
-  llvm::json::ObjectMapper O(Params);
+              TextDocumentContentChangeEvent &R, llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("range", R.range) && O.map("rangeLength", R.rangeLength) &&
          O.map("text", R.text);
 }
 
-bool fromJSON(const llvm::json::Value &Params,
-              DocumentRangeFormattingParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, DocumentRangeFormattingParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument) && O.map("range", R.range);
 }
 
 bool fromJSON(const llvm::json::Value &Params,
-              DocumentOnTypeFormattingParams &R) {
-  llvm::json::ObjectMapper O(Params);
+              DocumentOnTypeFormattingParams &R, llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument) &&
          O.map("position", R.position) && O.map("ch", R.ch);
 }
 
-bool fromJSON(const llvm::json::Value &Params, DocumentFormattingParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, DocumentFormattingParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument);
 }
 
-bool fromJSON(const llvm::json::Value &Params, DocumentSymbolParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, DocumentSymbolParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument);
 }
 
@@ -548,8 +575,9 @@
   return std::move(Diag);
 }
 
-bool fromJSON(const llvm::json::Value &Params, Diagnostic &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, Diagnostic &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   if (!O || !O.map("range", R.range) || !O.map("message", R.message))
     return false;
   O.map("severity", R.severity);
@@ -569,8 +597,9 @@
   return std::move(Result);
 }
 
-bool fromJSON(const llvm::json::Value &Params, CodeActionContext &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, CodeActionContext &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("diagnostics", R.diagnostics);
 }
 
@@ -596,14 +625,16 @@
   return OS << '(' << D.severity << "): " << D.message << "]";
 }
 
-bool fromJSON(const llvm::json::Value &Params, CodeActionParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, CodeActionParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument) &&
          O.map("range", R.range) && O.map("context", R.context);
 }
 
-bool fromJSON(const llvm::json::Value &Params, WorkspaceEdit &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, WorkspaceEdit &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("changes", R.changes);
 }
 
@@ -612,18 +643,21 @@
 const llvm::StringLiteral ExecuteCommandParams::CLANGD_APPLY_TWEAK =
     "clangd.applyTweak";
 
-bool fromJSON(const llvm::json::Value &Params, ExecuteCommandParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, ExecuteCommandParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   if (!O || !O.map("command", R.command))
     return false;
 
   auto Args = Params.getAsObject()->getArray("arguments");
   if (R.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND) {
     return Args && Args->size() == 1 &&
-           fromJSON(Args->front(), R.workspaceEdit);
+           fromJSON(Args->front(), R.workspaceEdit,
+                    P.field("arguments").index(0));
   }
   if (R.command == ExecuteCommandParams::CLANGD_APPLY_TWEAK)
-    return Args && Args->size() == 1 && fromJSON(Args->front(), R.tweakArgs);
+    return Args && Args->size() == 1 &&
+           fromJSON(Args->front(), R.tweakArgs, P.field("arguments").index(0));
   return false; // Unrecognized command.
 }
 
@@ -681,8 +715,9 @@
   return O;
 }
 
-bool fromJSON(const llvm::json::Value &Params, WorkspaceSymbolParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, WorkspaceSymbolParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("query", R.query);
 }
 
@@ -741,8 +776,9 @@
   return llvm::json::Object{{"changes", std::move(FileChanges)}};
 }
 
-bool fromJSON(const llvm::json::Value &Params, TweakArgs &A) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, TweakArgs &A,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("file", A.file) && O.map("selection", A.selection) &&
          O.map("tweakID", A.tweakID);
 }
@@ -756,23 +792,25 @@
   return llvm::json::Object{{"edit", Params.edit}};
 }
 
-bool fromJSON(const llvm::json::Value &Response,
-              ApplyWorkspaceEditResponse &R) {
-  llvm::json::ObjectMapper O(Response);
+bool fromJSON(const llvm::json::Value &Response, ApplyWorkspaceEditResponse &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Response, P);
   if (!O || !O.map("applied", R.applied))
     return false;
   O.map("failureReason", R.failureReason);
   return true;
 }
 
-bool fromJSON(const llvm::json::Value &Params, TextDocumentPositionParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, TextDocumentPositionParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument) &&
          O.map("position", R.position);
 }
 
-bool fromJSON(const llvm::json::Value &Params, CompletionContext &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, CompletionContext &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   if (!O)
     return false;
 
@@ -782,15 +820,16 @@
   R.triggerKind = static_cast<CompletionTriggerKind>(TriggerKind);
 
   if (auto *TC = Params.getAsObject()->get("triggerCharacter"))
-    return fromJSON(*TC, R.triggerCharacter);
+    return fromJSON(*TC, R.triggerCharacter, P.field("triggerCharacter"));
   return true;
 }
 
-bool fromJSON(const llvm::json::Value &Params, CompletionParams &R) {
-  if (!fromJSON(Params, static_cast<TextDocumentPositionParams &>(R)))
+bool fromJSON(const llvm::json::Value &Params, CompletionParams &R,
+              llvm::json::Path P) {
+  if (!fromJSON(Params, static_cast<TextDocumentPositionParams &>(R), P))
     return false;
   if (auto *Context = Params.getAsObject()->get("context"))
-    return fromJSON(*Context, R.context);
+    return fromJSON(*Context, R.context, P.field("context"));
   return true;
 }
 
@@ -804,7 +843,7 @@
   llvm_unreachable("Invalid MarkupKind");
 }
 
-bool fromJSON(const llvm::json::Value &V, MarkupKind &K) {
+bool fromJSON(const llvm::json::Value &V, MarkupKind &K, llvm::json::Path P) {
   auto Str = V.getAsString();
   if (!Str) {
     elog("Failed to parse markup kind: expected a string");
@@ -844,7 +883,8 @@
   return std::move(Result);
 }
 
-bool fromJSON(const llvm::json::Value &E, CompletionItemKind &Out) {
+bool fromJSON(const llvm::json::Value &E, CompletionItemKind &Out,
+              llvm::json::Path P) {
   if (auto T = E.getAsInteger()) {
     if (*T < static_cast<int>(CompletionItemKind::Text) ||
         *T > static_cast<int>(CompletionItemKind::TypeParameter))
@@ -877,11 +917,12 @@
   }
 }
 
-bool fromJSON(const llvm::json::Value &E, CompletionItemKindBitset &Out) {
+bool fromJSON(const llvm::json::Value &E, CompletionItemKindBitset &Out,
+              llvm::json::Path P) {
   if (auto *A = E.getAsArray()) {
     for (size_t I = 0; I < A->size(); ++I) {
       CompletionItemKind KindOut;
-      if (fromJSON((*A)[I], KindOut))
+      if (fromJSON((*A)[I], KindOut, P.index(I)))
         Out.set(size_t(KindOut));
     }
     return true;
@@ -976,8 +1017,9 @@
   };
 }
 
-bool fromJSON(const llvm::json::Value &Params, RenameParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, RenameParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument) &&
          O.map("position", R.position) && O.map("newName", R.newName);
 }
@@ -1038,13 +1080,15 @@
   return std::move(Result);
 }
 
-bool fromJSON(const llvm::json::Value &Params, SemanticTokensParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, SemanticTokensParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument);
 }
 
-bool fromJSON(const llvm::json::Value &Params, SemanticTokensDeltaParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, SemanticTokensDeltaParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument) &&
          O.map("previousResultId", R.previousResultId);
 }
@@ -1060,39 +1104,42 @@
 }
 
 bool fromJSON(const llvm::json::Value &Params,
-              DidChangeConfigurationParams &CCP) {
-  llvm::json::ObjectMapper O(Params);
+              DidChangeConfigurationParams &CCP, llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("settings", CCP.settings);
 }
 
-bool fromJSON(const llvm::json::Value &Params,
-              ClangdCompileCommand &CDbUpdate) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, ClangdCompileCommand &CDbUpdate,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("workingDirectory", CDbUpdate.workingDirectory) &&
          O.map("compilationCommand", CDbUpdate.compilationCommand);
 }
 
-bool fromJSON(const llvm::json::Value &Params, ConfigurationSettings &S) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, ConfigurationSettings &S,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   if (!O)
     return true; // 'any' type in LSP.
   O.map("compilationDatabaseChanges", S.compilationDatabaseChanges);
   return true;
 }
 
-bool fromJSON(const llvm::json::Value &Params, InitializationOptions &Opts) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, InitializationOptions &Opts,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   if (!O)
     return true; // 'any' type in LSP.
 
-  fromJSON(Params, Opts.ConfigSettings);
+  fromJSON(Params, Opts.ConfigSettings, P);
   O.map("compilationDatabasePath", Opts.compilationDatabasePath);
   O.map("fallbackFlags", Opts.fallbackFlags);
   O.map("clangdFileStatus", Opts.FileStatus);
   return true;
 }
 
-bool fromJSON(const llvm::json::Value &E, TypeHierarchyDirection &Out) {
+bool fromJSON(const llvm::json::Value &E, TypeHierarchyDirection &Out,
+              llvm::json::Path P) {
   auto T = E.getAsInteger();
   if (!T)
     return false;
@@ -1103,8 +1150,9 @@
   return true;
 }
 
-bool fromJSON(const llvm::json::Value &Params, TypeHierarchyParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, TypeHierarchyParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument) &&
          O.map("position", R.position) && O.map("resolve", R.resolve) &&
          O.map("direction", R.direction);
@@ -1135,8 +1183,9 @@
   return std::move(Result);
 }
 
-bool fromJSON(const llvm::json::Value &Params, TypeHierarchyItem &I) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, TypeHierarchyItem &I,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
 
   // Required fields.
   if (!(O && O.map("name", I.name) && O.map("kind", I.kind) &&
@@ -1156,15 +1205,16 @@
 }
 
 bool fromJSON(const llvm::json::Value &Params,
-              ResolveTypeHierarchyItemParams &P) {
-  llvm::json::ObjectMapper O(Params);
-  return O && O.map("item", P.item) && O.map("resolve", P.resolve) &&
-         O.map("direction", P.direction);
+              ResolveTypeHierarchyItemParams &R, llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O && O.map("item", R.item) && O.map("resolve", R.resolve) &&
+         O.map("direction", R.direction);
 }
 
-bool fromJSON(const llvm::json::Value &Params, ReferenceParams &R) {
+bool fromJSON(const llvm::json::Value &Params, ReferenceParams &R,
+              llvm::json::Path P) {
   TextDocumentPositionParams &Base = R;
-  return fromJSON(Params, Base);
+  return fromJSON(Params, Base, P);
 }
 
 static const char *toString(OffsetEncoding OE) {
@@ -1181,7 +1231,8 @@
   llvm_unreachable("Unknown clang.clangd.OffsetEncoding");
 }
 llvm::json::Value toJSON(const OffsetEncoding &OE) { return toString(OE); }
-bool fromJSON(const llvm::json::Value &V, OffsetEncoding &OE) {
+bool fromJSON(const llvm::json::Value &V, OffsetEncoding &OE,
+              llvm::json::Path P) {
   auto Str = V.getAsString();
   if (!Str)
     return false;
@@ -1215,10 +1266,11 @@
   };
 }
 
-bool fromJSON(const llvm::json::Value &Params, SelectionRangeParams &P) {
-  llvm::json::ObjectMapper O(Params);
-  return O && O.map("textDocument", P.textDocument) &&
-         O.map("positions", P.positions);
+bool fromJSON(const llvm::json::Value &Params, SelectionRangeParams &S,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O && O.map("textDocument", S.textDocument) &&
+         O.map("positions", S.positions);
 }
 
 llvm::json::Value toJSON(const SelectionRange &Out) {
@@ -1229,8 +1281,9 @@
   return llvm::json::Object{{"range", Out.range}};
 }
 
-bool fromJSON(const llvm::json::Value &Params, DocumentLinkParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, DocumentLinkParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument);
 }
 
@@ -1241,8 +1294,9 @@
   };
 }
 
-bool fromJSON(const llvm::json::Value &Params, FoldingRangeParams &R) {
-  llvm::json::ObjectMapper O(Params);
+bool fromJSON(const llvm::json::Value &Params, FoldingRangeParams &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
   return O && O.map("textDocument", R.textDocument);
 }
 
Index: clang-tools-extra/clangd/ClangdLSPServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.h
+++ clang-tools-extra/clangd/ClangdLSPServer.h
@@ -180,22 +180,37 @@
   std::unique_ptr<MessageHandler> MsgHandler;
   std::mutex TranspWriter;
 
+  template <typename T>
+  static Expected<T> parse(const llvm::json::Value &Raw,
+                           llvm::StringRef PayloadName,
+                           llvm::StringRef PayloadKind) {
+    T Result;
+    llvm::json::Path::Root Root;
+    if (!fromJSON(Raw, Result, Root)) {
+      elog("Failed to decode {0} {1}", PayloadName, PayloadKind);
+      // Dump the relevant parts of the broken message.
+      std::string Context;
+      llvm::raw_string_ostream OS(Context);
+      Root.printErrorContext(Raw, OS);
+      vlog("{0}", OS.str());
+      // Report the error (e.g. to the client).
+      return llvm::make_error<LSPError>(
+          llvm::formatv("failed to decode {0} {1}", PayloadName, PayloadKind),
+          ErrorCode::InvalidParams);
+    }
+    return std::move(Result);
+  }
+
   template <typename Response>
   void call(StringRef Method, llvm::json::Value Params, Callback<Response> CB) {
     // Wrap the callback with LSP conversion and error-handling.
     auto HandleReply =
-        [CB = std::move(CB), Ctx = Context::current().clone()](
+        [CB = std::move(CB), Ctx = Context::current().clone(),
+         Method = Method.str()](
             llvm::Expected<llvm::json::Value> RawResponse) mutable {
-          Response Rsp;
-          if (!RawResponse) {
-            CB(RawResponse.takeError());
-          } else if (fromJSON(*RawResponse, Rsp)) {
-            CB(std::move(Rsp));
-          } else {
-            elog("Failed to decode {0} response", *RawResponse);
-            CB(llvm::make_error<LSPError>("failed to decode response",
-                                          ErrorCode::InvalidParams));
-          }
+          if (!RawResponse)
+            return CB(RawResponse.takeError());
+          CB(parse<Response>(*RawResponse, Method, "response"));
         };
     callRaw(Method, std::move(Params), std::move(HandleReply));
   }
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -247,14 +247,10 @@
             void (ClangdLSPServer::*Handler)(const Param &, Callback<Result>)) {
     Calls[Method] = [Method, Handler, this](llvm::json::Value RawParams,
                                             ReplyOnce Reply) {
-      Param P;
-      if (fromJSON(RawParams, P)) {
-        (Server.*Handler)(P, std::move(Reply));
-      } else {
-        elog("Failed to decode {0} request.", Method);
-        Reply(llvm::make_error<LSPError>("failed to decode request",
-                                         ErrorCode::InvalidRequest));
-      }
+      auto P = parse<Param>(RawParams, Method, "request");
+      if (!P)
+        return Reply(P.takeError());
+      (Server.*Handler)(*P, std::move(Reply));
     };
   }
 
@@ -292,14 +288,12 @@
             void (ClangdLSPServer::*Handler)(const Param &)) {
     Notifications[Method] = [Method, Handler,
                              this](llvm::json::Value RawParams) {
-      Param P;
-      if (!fromJSON(RawParams, P)) {
-        elog("Failed to decode {0} request.", Method);
-        return;
-      }
+      llvm::Expected<Param> P = parse<Param>(RawParams, Method, "request");
+      if (!P)
+        return llvm::consumeError(P.takeError());
       trace::Span Tracer(Method, LSPLatency);
       SPAN_ATTACH(Tracer, "Params", RawParams);
-      (Server.*Handler)(P);
+      (Server.*Handler)(*P);
     };
   }
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
  • [Lldb-commits] [PATCH] D88103:... Sam McCall via Phabricator via lldb-commits

Reply via email to