Translating between JSON objects and C++ strutctures is common.
From experience in clangd, fromJSON/ObjectMapper work well and save a lot of
code, but aren't adopted elsewhere at least partly due to total lack of error
reporting beyond "ok"/"bad".
The recently-added error model should be rich enough for most applications.
It requires tracking the path within the root object and reporting local
errors at appropriate places.
To do this, we exploit the fact that the call graph of recursive
parse functions mirror the structure of the JSON itself.
The current path is represented as a linked list of segments, each of which is
on the stack as a parameter. Concretely, fromJSON now looks like:
bool fromJSON(const Value&, T&, Path);
Beyond the signature change, this is reasonably unobtrusive: building
the path segments is mostly handled by ObjectMapper and the vector<T> fromJSON.
However the root caller of fromJSON must now create a Root object to
store the errors, which is a little clunky.
I've added high-level parse<T>(StringRef) -> Expected<T>, but it's not
general enough to be the primary interface I think (at least, not usable in
clangd).
All existing users (mostly just clangd) are updated in this patch,
making this change backwards-compatible is a bit hairy.
Differential Revision: https://reviews.llvm.org/D88103
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));
};
}
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);
};
}
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));
}
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);
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);
}
}
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);
}
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);
}
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);
}
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;
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))
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;
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")) {
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;
}
}
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;
}
}
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;
}
}
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;
}
}
}
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.
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.
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))
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);
}
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);
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);
}
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);
}
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.
}
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);
}
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);
}
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;
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;
}
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");
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))
}
}
-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;
};
}
-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);
}
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);
}
}
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;
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);
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) &&
}
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) {
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;
};
}
-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) {
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);
}
};
}
-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);
}
/// 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
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).
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 &);
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 &);
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 &);
/// 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;
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);
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);
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
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!
/// 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
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.
// 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`.
/// 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
/// 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.
/// 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.
/// 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
/// 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.
/// 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.
/// 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
/// 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.
/// 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
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 {
/// 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.
/// 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.
/// 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
/// 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
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;
/// 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;
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.
/// 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
/// 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;
/// 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 };
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
/// 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.
};
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 {
/// 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.
/// 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.
/// 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 {
/// 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 {
/**
/// 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.
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 {
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);
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) &&
}
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 {
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();
}
/// === 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
// 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;
}
///
/// 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.
}
};
+/// 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.
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");
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};
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) {
{"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": {
}
}
})";
- EXPECT_EQ(Expected, OS.str());
+ EXPECT_EQ(Expected, errorContext(V, R));
}
} // namespace