sammccall updated this revision to Diff 293863.
sammccall marked 2 inline comments as done.
sammccall added a comment.
Address review comments.
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/index/Index.cpp
clang-tools-extra/clangd/index/Index.h
llvm/include/llvm/Support/JSON.h
llvm/lib/Support/JSON.cpp
llvm/unittests/Support/JSONTest.cpp
Index: llvm/unittests/Support/JSONTest.cpp
===================================================================
--- llvm/unittests/Support/JSONTest.cpp
+++ llvm/unittests/Support/JSONTest.cpp
@@ -8,6 +8,7 @@
#include "llvm/Support/JSON.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -372,8 +373,8 @@
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);
@@ -403,16 +404,80 @@
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);
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()));
+}
+
+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, ErrorContext) {
+ Path::Root Root;
+ Value V = Array{
+ Object{{"str", "foo"}},
+ Object{
+ {"str", 43},
+ {"full", Array{nullptr}},
+ {"empty", Array{}},
+ {"short", "abc"},
+ {"long", "123456789012345678901234567890123456789012345"},
+ },
+ };
+ std::vector<CustomStruct> Out;
+
+ ASSERT_FALSE(fromJSON(V, Out, Root));
+ EXPECT_EQ("expected string at (root)[1].str", toString(Root.getError()));
+ const char *Expected = R"([
+ { ... },
+ {
+ "empty": [],
+ "full": [ ... ],
+ "long": "1234567890123456789012345678901234567...",
+ "short": "abc",
+ "str": /* error: expected string */ 43
+ }
+])";
+ EXPECT_EQ(errorContext(V, Root), Expected);
+}
+
+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) {
@@ -420,15 +485,19 @@
std::string S;
llvm::raw_string_ostream OS(S);
OStream J(OS, Indent);
+ J.comment("top*/level");
J.object([&] {
J.attributeArray("foo", [&] {
J.value(nullptr);
+ J.comment("element");
J.value(42.5);
J.arrayBegin();
J.value(43);
J.arrayEnd();
});
+ J.comment("attribute");
J.attributeBegin("bar");
+ J.comment("attribute value");
J.objectBegin();
J.objectEnd();
J.attributeEnd();
@@ -437,17 +506,21 @@
return OS.str();
};
- const char *Plain = R"({"foo":[null,42.5,[43]],"bar":{},"baz":"xyz"})";
+ const char *Plain =
+ R"(/*top* /level*/{"foo":[null,/*element*/42.5,[43]],/*attribute*/"bar":/*attribute value*/{},"baz":"xyz"})";
EXPECT_EQ(Plain, StreamStuff(0));
- const char *Pretty = R"({
+ const char *Pretty = R"(/* top* /level */
+{
"foo": [
null,
+ /* element */
42.5,
[
43
]
],
- "bar": {},
+ /* attribute */
+ "bar": /* attribute value */ {},
"baz": "xyz"
})";
EXPECT_EQ(Pretty, StreamStuff(2));
Index: llvm/lib/Support/JSON.cpp
===================================================================
--- llvm/lib/Support/JSON.cpp
+++ llvm/lib/Support/JSON.cpp
@@ -7,8 +7,12 @@
//===---------------------------------------------------------------------===//
#include "llvm/Support/JSON.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/raw_ostream.h"
#include <cctype>
namespace llvm {
@@ -198,6 +202,167 @@
llvm_unreachable("Unknown value kind");
}
+void Path::report(llvm::StringLiteral Msg) {
+ // Walk up to the root context, and count the number of segments.
+ unsigned Count = 0;
+ const Path *P;
+ for (P = this; P->Parent != nullptr; P = P->Parent)
+ ++Count;
+ Path::Root *R = P->Seg.root();
+ // Fill in the error message and copy the path (in reverse order).
+ R->ErrorMessage = Msg;
+ R->ErrorPath.resize(Count);
+ auto It = R->ErrorPath.begin();
+ for (P = this; P->Parent != nullptr; P = P->Parent)
+ *It++ = P->Seg;
+}
+
+Error Path::Root::getError() const {
+ std::string S;
+ raw_string_ostream OS(S);
+ OS << (ErrorMessage.empty() ? "invalid JSON contents" : ErrorMessage);
+ if (ErrorPath.empty()) {
+ if (!Name.empty())
+ OS << " when parsing " << Name;
+ } else {
+ OS << " at " << (Name.empty() ? "(root)" : Name);
+ for (const Path::Segment &S : llvm::reverse(ErrorPath)) {
+ if (S.isField())
+ OS << '.' << S.field();
+ else
+ OS << '[' << S.index() << ']';
+ }
+ }
+ return createStringError(llvm::inconvertibleErrorCode(), OS.str());
+}
+
+namespace {
+
+std::vector<const Object::value_type *> sortedElements(const Object &O) {
+ std::vector<const Object::value_type *> Elements;
+ for (const auto &E : O)
+ Elements.push_back(&E);
+ llvm::sort(Elements,
+ [](const Object::value_type *L, const Object::value_type *R) {
+ return L->first < R->first;
+ });
+ return Elements;
+}
+
+// Prints a one-line version of a value that isn't our main focus.
+// We interleave writes to OS and JOS, exploiting the lack of extra buffering.
+// This is OK as we own the implementation.
+// FIXME: once we have a "write custom serialized value" API, use it here.
+void abbreviate(const Value &V, OStream &JOS, raw_ostream &OS) {
+ switch (V.kind()) {
+ case Value::Array:
+ JOS.array([&] {
+ if (!V.getAsArray()->empty())
+ OS << " ... ";
+ });
+ break;
+ case Value::Object:
+ JOS.object([&] {
+ if (!V.getAsObject()->empty())
+ OS << " ... ";
+ });
+ break;
+ case Value::String: {
+ llvm::StringRef S = *V.getAsString();
+ if (S.size() < 40) {
+ JOS.value(V);
+ } else {
+ std::string Truncated = fixUTF8(S.take_front(37));
+ Truncated.append("...");
+ JOS.value(Truncated);
+ }
+ break;
+ }
+ default:
+ JOS.value(V);
+ }
+}
+
+// Prints a semi-expanded version of a value that is our main focus.
+// Array/Object entries are printed, but not recursively as they may be huge.
+void abbreviateChildren(const Value &V, OStream &JOS, raw_ostream &OS) {
+ switch (V.kind()) {
+ case Value::Array:
+ JOS.array([&] {
+ for (const auto &V : *V.getAsArray())
+ abbreviate(V, JOS, OS);
+ });
+ break;
+ case Value::Object:
+ JOS.object([&] {
+ for (const auto *KV : sortedElements(*V.getAsObject())) {
+ JOS.attributeBegin(KV->first);
+ abbreviate(KV->second, JOS, OS);
+ JOS.attributeEnd();
+ }
+ });
+ break;
+ default:
+ JOS.value(V);
+ }
+}
+
+} // namespace
+
+void Path::Root::printErrorContext(const Value &R, raw_ostream &OS) const {
+ OStream JOS(OS, /*IndentSize=*/2);
+ // PrintValue recurses down the path, printing the ancestors of our target.
+ // Siblings of nodes along the path are printed with abbreviate(), and the
+ // target itself is printed with the somewhat richer abbreviateChildren().
+ // 'Recurse' is the lambda itself, to allow recursive calls.
+ auto PrintValue = [&](const Value &V, ArrayRef<Segment> Path, auto &Recurse) {
+ // Print the target node itself, with the error as a comment.
+ // Also used if we can't follow our path, e.g. it names a field that
+ // *should* exist but doesn't.
+ auto HighlightCurrent = [&] {
+ std::string Comment = "error: ";
+ Comment.append(ErrorMessage.data(), ErrorMessage.size());
+ JOS.comment(Comment);
+ abbreviateChildren(V, JOS, OS);
+ };
+ if (Path.empty()) // We reached our target.
+ return HighlightCurrent();
+ const Segment &S = Path.back(); // Path is in reverse order.
+ if (S.isField()) {
+ // Current node is an object, path names a field.
+ llvm::StringRef FieldName = S.field();
+ const Object *O = V.getAsObject();
+ if (!O || !O->get(FieldName))
+ return HighlightCurrent();
+ JOS.object([&] {
+ for (const auto *KV : sortedElements(*O)) {
+ JOS.attributeBegin(KV->first);
+ if (FieldName.equals(KV->first))
+ Recurse(KV->second, Path.drop_back(), Recurse);
+ else
+ abbreviate(KV->second, JOS, OS);
+ JOS.attributeEnd();
+ }
+ });
+ } else {
+ // Current node is an array, path names an element.
+ const Array *A = V.getAsArray();
+ if (!A || S.index() >= A->size())
+ return HighlightCurrent();
+ JOS.array([&] {
+ unsigned Current = 0;
+ for (const auto &V : *A) {
+ if (Current++ == S.index())
+ Recurse(V, Path.drop_back(), Recurse);
+ else
+ abbreviate(V, JOS, OS);
+ }
+ });
+ }
+ };
+ PrintValue(R, ErrorPath, PrintValue);
+}
+
namespace {
// Simple recursive-descent JSON parser.
class Parser {
@@ -518,17 +683,6 @@
}
char ParseError::ID = 0;
-static std::vector<const Object::value_type *> sortedElements(const Object &O) {
- std::vector<const Object::value_type *> Elements;
- for (const auto &E : O)
- Elements.push_back(&E);
- llvm::sort(Elements,
- [](const Object::value_type *L, const Object::value_type *R) {
- return L->first < R->first;
- });
- return Elements;
-}
-
bool isUTF8(llvm::StringRef S, size_t *ErrOffset) {
// Fast-path for ASCII, which is valid UTF-8.
if (LLVM_LIKELY(isASCII(S)))
@@ -633,9 +787,40 @@
}
if (Stack.back().Ctx == Array)
newline();
+ flushComment();
Stack.back().HasValue = true;
}
+void OStream::comment(llvm::StringRef Comment) {
+ assert(PendingComment.empty() && "Only one comment per value!");
+ PendingComment = Comment;
+}
+
+void OStream::flushComment() {
+ if (PendingComment.empty())
+ return;
+ OS << (IndentSize ? "/* " : "/*");
+ // Be sure not to accidentally emit "*/". Transform to "* /".
+ while (!PendingComment.empty()) {
+ auto Pos = PendingComment.find("*/");
+ if (Pos == StringRef::npos) {
+ OS << PendingComment;
+ PendingComment = "";
+ } else {
+ OS << PendingComment.take_front(Pos) << "* /";
+ PendingComment = PendingComment.drop_front(Pos + 2);
+ }
+ }
+ OS << (IndentSize ? " */" : "*/");
+ // Comments are on their own line unless attached to an attribute value.
+ if (Stack.size() > 1 && Stack.back().Ctx == Singleton) {
+ if (IndentSize)
+ OS << ' ';
+ } else {
+ newline();
+ }
+}
+
void llvm::json::OStream::newline() {
if (IndentSize) {
OS.write('\n');
@@ -657,6 +842,7 @@
if (Stack.back().HasValue)
newline();
OS << ']';
+ assert(PendingComment.empty());
Stack.pop_back();
assert(!Stack.empty());
}
@@ -675,6 +861,7 @@
if (Stack.back().HasValue)
newline();
OS << '}';
+ assert(PendingComment.empty());
Stack.pop_back();
assert(!Stack.empty());
}
@@ -684,6 +871,7 @@
if (Stack.back().HasValue)
OS << ',';
newline();
+ flushComment();
Stack.back().HasValue = true;
Stack.emplace_back();
Stack.back().Ctx = Singleton;
@@ -701,6 +889,7 @@
void llvm::json::OStream::attributeEnd() {
assert(Stack.back().Ctx == Singleton);
assert(Stack.back().HasValue && "Attribute must have a value");
+ assert(PendingComment.empty());
Stack.pop_back();
assert(Stack.back().Ctx == Object);
}
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
@@ -557,81 +564,168 @@
return M.erase(ObjectKey(K));
}
+/// A "cursor" marking a position within a Value.
+/// The Value is a tree, and this is the path from the root to the current node.
+/// This is used to associate errors with particular subobjects.
+class Path {
+public:
+ class Root;
+
+ /// Records that the value at the current path is invalid.
+ /// Message is e.g. "expected number" and becomes part of the final error.
+ /// This overwrites any previously written error message in the root.
+ void report(llvm::StringLiteral Message);
+
+ /// The root may be treated as a Path.
+ Path(Root &R) : Parent(nullptr), Seg(&R) {}
+ /// Derives a path for an array element: this[Index]
+ Path index(unsigned Index) const { return Path(this, Segment(Index)); }
+ /// Derives a path for an object field: this.Field
+ Path field(StringRef Field) const { return Path(this, Segment(Field)); }
+
+private:
+ /// One element in a JSON path: an object field (.foo) or array index [27].
+ /// Exception: the root Path encodes a pointer to the Path::Root.
+ class Segment {
+ uintptr_t Pointer;
+ unsigned Offset;
+
+ public:
+ Segment(Root *R) : Pointer(reinterpret_cast<uintptr_t>(R)) {}
+ Segment(llvm::StringRef Field)
+ : Pointer(reinterpret_cast<uintptr_t>(Field.data())),
+ Offset(static_cast<unsigned>(Field.size())) {}
+ Segment(unsigned Index) : Pointer(0), Offset(Index) {}
+
+ bool isField() const { return Pointer != 0; }
+ StringRef field() const {
+ return StringRef(reinterpret_cast<const char *>(Pointer), Offset);
+ }
+ unsigned index() const { return Offset; }
+ Root *root() const { return reinterpret_cast<Root *>(Pointer); }
+ };
+
+ const Path *Parent;
+ Segment Seg;
+
+ Path(const Path *Parent, Segment S) : Parent(Parent), Seg(S) {}
+};
+
+/// The root is the trivial Path to the root value.
+/// It also stores the latest reported error and the path where it occurred.
+class Path::Root {
+ llvm::StringRef Name;
+ llvm::StringLiteral ErrorMessage;
+ std::vector<Path::Segment> ErrorPath; // Only valid in error state. Reversed.
+
+ friend void Path::report(llvm::StringLiteral Message);
+
+public:
+ Root(llvm::StringRef Name = "") : Name(Name), ErrorMessage("") {}
+ // No copy/move allowed as there are incoming pointers.
+ Root(Root &&) = delete;
+ Root &operator=(Root &&) = delete;
+ Root(const Root &) = delete;
+ Root &operator=(const Root &) = delete;
+
+ /// Returns the last error reported, or else a generic error.
+ Error getError() const;
+ /// Print the root value with the error shown inline as a comment.
+ /// Unrelated parts of the value are elided for brevity, e.g.
+ /// {
+ /// "id": 42,
+ /// "name": /* expected string */ null,
+ /// "properties": { ... }
+ /// }
+ void printErrorContext(const Value &, llvm::raw_ostream &) const;
+};
+
// 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;
}
@@ -644,42 +738,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.
@@ -703,9 +805,24 @@
}
};
+/// 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.
+/// It also allows emitting more constructs, such as comments.
///
/// Only one "top-level" object can be written to a stream.
/// Simplest usage involves passing lambdas (Blocks) to fill in containers:
@@ -791,6 +908,10 @@
Contents();
objectEnd();
}
+ /// Emit a JavaScript comment associated with the next printed value.
+ /// The string must be valid until the next attribute or value is emitted.
+ /// Comments are not part of standard JSON, and many parsers reject them!
+ void comment(llvm::StringRef);
// High level functions to output object attributes.
// Valid only within an object (any number of times).
@@ -826,6 +947,7 @@
}
void valueBegin();
+ void flushComment();
void newline();
enum Context {
@@ -838,6 +960,7 @@
bool HasValue = false;
};
llvm::SmallVector<State, 16> Stack; // Never empty.
+ llvm::StringRef PendingComment;
llvm::raw_ostream &OS;
unsigned IndentSize;
unsigned Indent = 0;
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/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);
};
}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits