Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -139,7 +139,7 @@ std::string Code = Server.getDocument(File); std::vector Edits = replacementsToEdits(Code, *Replacements); WorkspaceEdit WE; - WE.changes = {{llvm::yaml::escape(Params.textDocument.uri.uri), Edits}}; + WE.changes = {{Params.textDocument.uri.uri, Edits}}; C.reply(WorkspaceEdit::unparse(WE)); } @@ -249,7 +249,7 @@ // Set up JSONRPCDispatcher. JSONRPCDispatcher Dispatcher( - [](RequestContext Ctx, llvm::yaml::MappingNode *Params) { + [](RequestContext Ctx, const json::Expr &Params) { Ctx.replyError(ErrorCode::MethodNotFound, "method not found"); }); registerCallbackHandlers(Dispatcher, Out, /*Callbacks=*/*this); Index: clangd/JSONExpr.h =================================================================== --- clangd/JSONExpr.h +++ clangd/JSONExpr.h @@ -181,6 +181,16 @@ return as(); return llvm::None; } + llvm::Optional asInteger() const { + if (LLVM_LIKELY(Type == T_Number)) { + double D = as(); + if (LLVM_LIKELY(std::modf(D, &D) == 0 && + D >= std::numeric_limits::min() && + D <= std::numeric_limits::max())) + return D; + } + return llvm::None; + } llvm::Optional asString() const { if (Type == T_String) return llvm::StringRef(as()); @@ -324,6 +334,11 @@ return V->asNumber(); return llvm::None; } + llvm::Optional getInteger(const ObjectKey &K) const { + if (auto *V = get(K)) + return V->asInteger(); + return llvm::None; + } llvm::Optional getString(const ObjectKey &K) const { if (auto *V = get(K)) return V->asString(); @@ -374,6 +389,9 @@ llvm::Optional getNumber(size_t I) const { return (*this)[I].asNumber(); } + llvm::Optional getInteger(size_t I) const { + return (*this)[I].asInteger(); + } llvm::Optional getString(size_t I) const { return (*this)[I].asString(); } Index: clangd/JSONRPCDispatcher.h =================================================================== --- clangd/JSONRPCDispatcher.h +++ clangd/JSONRPCDispatcher.h @@ -17,7 +17,6 @@ #include "clang/Basic/LLVM.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" -#include "llvm/Support/YAMLParser.h" #include #include @@ -30,7 +29,7 @@ public: JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs, llvm::raw_ostream *InputMirror = nullptr, bool Pretty = false) - : Outs(Outs), Logs(Logs), InputMirror(InputMirror), Pretty(Pretty) {} + : Pretty(Pretty), Outs(Outs), Logs(Logs), InputMirror(InputMirror) {} /// Emit a JSONRPC message. void writeMessage(const json::Expr &Result); @@ -44,11 +43,13 @@ /// Unlike other methods of JSONOutput, mirrorInput is not thread-safe. void mirrorInput(const Twine &Message); + // Whether output should be pretty-printed. + const bool Pretty; + private: llvm::raw_ostream &Outs; llvm::raw_ostream &Logs; llvm::raw_ostream *InputMirror; - bool Pretty; std::mutex StreamMutex; }; @@ -84,8 +85,7 @@ class JSONRPCDispatcher { public: // A handler responds to requests for a particular method name. - using Handler = - std::function; + using Handler = std::function; /// Create a new JSONRPCDispatcher. UnknownHandler is called when an unknown /// method is received. @@ -96,7 +96,7 @@ void registerHandler(StringRef Method, Handler H); /// Parses a JSONRPC message and calls the Handler for it. - bool call(StringRef Content, JSONOutput &Out) const; + bool call(const json::Expr &Message, JSONOutput &Out) const; private: llvm::StringMap Handlers; Index: clangd/JSONRPCDispatcher.cpp =================================================================== --- clangd/JSONRPCDispatcher.cpp +++ clangd/JSONRPCDispatcher.cpp @@ -14,7 +14,6 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/SourceMgr.h" -#include "llvm/Support/YAMLParser.h" #include using namespace clang; @@ -101,88 +100,29 @@ Handlers[Method] = std::move(H); } -static void -callHandler(const llvm::StringMap &Handlers, - llvm::yaml::ScalarNode *Method, llvm::Optional ID, - llvm::yaml::MappingNode *Params, - const JSONRPCDispatcher::Handler &UnknownHandler, JSONOutput &Out) { - llvm::SmallString<64> MethodStorage; - llvm::StringRef MethodStr = Method->getValue(MethodStorage); - auto I = Handlers.find(MethodStr); - auto &Handler = I != Handlers.end() ? I->second : UnknownHandler; - Handler(RequestContext(Out, MethodStr, std::move(ID)), Params); -} - -bool JSONRPCDispatcher::call(StringRef Content, JSONOutput &Out) const { - llvm::SourceMgr SM; - llvm::yaml::Stream YAMLStream(Content, SM); - - auto Doc = YAMLStream.begin(); - if (Doc == YAMLStream.end()) +bool JSONRPCDispatcher::call(const json::Expr &Message, JSONOutput &Out) const { + // Message must be an object with "jsonrpc":"2.0". + auto *Object = Message.asObject(); + if (!Object || Object->getString("jsonrpc") != Optional("2.0")) return false; - - auto *Object = dyn_cast_or_null(Doc->getRoot()); - if (!Object) - return false; - - llvm::yaml::ScalarNode *Version = nullptr; - llvm::yaml::ScalarNode *Method = nullptr; - llvm::yaml::MappingNode *Params = nullptr; + // ID may be any JSON value. If absent, this is a notification. llvm::Optional ID; - for (auto &NextKeyValue : *Object) { - auto *KeyString = - dyn_cast_or_null(NextKeyValue.getKey()); - if (!KeyString) - return false; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - llvm::yaml::Node *Value = NextKeyValue.getValue(); - if (!Value) - return false; - - if (KeyValue == "jsonrpc") { - // This should be "2.0". Always. - Version = dyn_cast(Value); - if (!Version || Version->getRawValue() != "\"2.0\"") - return false; - } else if (KeyValue == "method") { - Method = dyn_cast(Value); - } else if (KeyValue == "id") { - // ID may be either a string or a number. - if (auto *IdNode = dyn_cast(Value)) { - llvm::SmallString<32> S; - llvm::StringRef V = IdNode->getValue(S); - if (IdNode->getRawValue().startswith("\"")) { - ID.emplace(V.str()); - } else { - double D; - // FIXME: this is locale-sensitive. - if (llvm::to_float(V, D)) - ID.emplace(D); - } - } - } else if (KeyValue == "params") { - if (!Method) - return false; - // We have to interleave the call of the function here, otherwise the - // YAMLParser will die because it can't go backwards. This is unfortunate - // because it will break clients that put the id after params. A possible - // fix would be to split the parsing and execution phases. - Params = dyn_cast(Value); - callHandler(Handlers, Method, std::move(ID), Params, UnknownHandler, Out); - return true; - } else { - return false; - } - } - - // In case there was a request with no params, call the handler on the - // leftovers. + if (auto *I = Object->get("id")) + ID = std::move(*I); + // Method must be given. + auto Method = Object->getString("method"); if (!Method) return false; - callHandler(Handlers, Method, std::move(ID), nullptr, UnknownHandler, Out); + // Params should be given, use null if not. + json::Expr Params = nullptr; + if (auto *P = Object->get("params")) + Params = std::move(*P); + auto I = Handlers.find(*Method); + auto &Handler = I != Handlers.end() ? I->second : UnknownHandler; + RequestContext Ctx(Out, *Method, std::move(ID)); + SPAN_ATTACH(Ctx.tracer(), "Params", Params); + Handler(std::move(Ctx), std::move(Params)); return true; } @@ -211,7 +151,7 @@ llvm::StringRef LineRef(Line); - // We allow YAML-style comments in headers. Technically this isn't part + // We allow comments in headers. Technically this isn't part // of the LSP specification, but makes writing tests easier. if (LineRef.startswith("#")) continue; @@ -251,11 +191,9 @@ } if (ContentLength > 0) { - std::vector JSON(ContentLength + 1, '\0'); + std::vector JSON(ContentLength); llvm::StringRef JSONRef; { - // Now read the JSON. Insert a trailing null byte as required by the - // YAML parser. In.read(JSON.data(), ContentLength); Out.mirrorInput(StringRef(JSON.data(), In.gcount())); @@ -271,12 +209,18 @@ JSONRef = StringRef(JSON.data(), ContentLength); } - // Log the message. - Out.log("<-- " + JSONRef + "\n"); - - // Finally, execute the action for this JSON message. - if (!Dispatcher.call(JSONRef, Out)) - Out.log("JSON dispatch failed!\n"); + if (auto Doc = json::parse(JSONRef)) { + // Log the formatted message. + Out.log(llvm::formatv(Out.Pretty ? "<-- {0:2}\n" : "<-- {0}\n", *Doc)); + // Finally, execute the action for this JSON message. + if (!Dispatcher.call(*Doc, Out)) + Out.log("JSON dispatch failed!\n"); + } else { + // Parse error. Log the raw message. + Out.log("<-- " + JSONRef + "\n"); + Out.log(llvm::Twine("JSON parse error: ") + + llvm::toString(Doc.takeError()) + "\n"); + } // If we're done, exit the loop. if (IsDone) Index: clangd/Protocol.h =================================================================== --- clangd/Protocol.h +++ clangd/Protocol.h @@ -23,15 +23,12 @@ #include "JSONExpr.h" #include "llvm/ADT/Optional.h" -#include "llvm/Support/YAMLParser.h" #include #include namespace clang { namespace clangd { -class Logger; - enum class ErrorCode { // Defined by JSON RPC. ParseError = -32700, @@ -54,7 +51,7 @@ static URI fromUri(llvm::StringRef uri); static URI fromFile(llvm::StringRef file); - static URI parse(llvm::yaml::ScalarNode *Param); + static URI parse(llvm::StringRef U) { return fromUri(U); } static json::Expr unparse(const URI &U); friend bool operator==(const URI &LHS, const URI &RHS) { @@ -74,8 +71,7 @@ /// The text document's URI. URI uri; - static llvm::Optional - parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger); + static llvm::Optional parse(const json::Expr &Params); }; struct Position { @@ -94,8 +90,7 @@ std::tie(RHS.line, RHS.character); } - static llvm::Optional parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger); + static llvm::Optional parse(const json::Expr &Params); static json::Expr unparse(const Position &P); }; @@ -113,8 +108,7 @@ return std::tie(LHS.start, LHS.end) < std::tie(RHS.start, RHS.end); } - static llvm::Optional parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger); + static llvm::Optional parse(const json::Expr &Params); static json::Expr unparse(const Range &P); }; @@ -141,8 +135,7 @@ struct Metadata { std::vector extraFlags; - static llvm::Optional parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger); + static llvm::Optional parse(const json::Expr &Params); }; struct TextEdit { @@ -154,8 +147,7 @@ /// empty string. std::string newText; - static llvm::Optional parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger); + static llvm::Optional parse(const json::Expr &Params); static json::Expr unparse(const TextEdit &P); }; @@ -172,8 +164,7 @@ /// The content of the opened text document. std::string text; - static llvm::Optional parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger); + static llvm::Optional parse(const json::Expr &Params); }; enum class TraceLevel { @@ -183,8 +174,7 @@ }; struct NoParams { - static llvm::Optional parse(llvm::yaml::MappingNode *Params, - Logger &Logger) { + static llvm::Optional parse(const json::Expr &Params) { return NoParams{}; } }; @@ -218,8 +208,7 @@ /// The initial trace setting. If omitted trace is disabled ('off'). llvm::Optional trace; - static llvm::Optional parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger); + static llvm::Optional parse(const json::Expr &Params); }; struct DidOpenTextDocumentParams { @@ -230,7 +219,7 @@ llvm::Optional metadata; static llvm::Optional - parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger); + parse(const json::Expr &Params); }; struct DidCloseTextDocumentParams { @@ -238,7 +227,7 @@ TextDocumentIdentifier textDocument; static llvm::Optional - parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger); + parse(const json::Expr &Params); }; struct TextDocumentContentChangeEvent { @@ -246,7 +235,7 @@ std::string text; static llvm::Optional - parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger); + parse(const json::Expr &Params); }; struct DidChangeTextDocumentParams { @@ -259,7 +248,7 @@ std::vector contentChanges; static llvm::Optional - parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger); + parse(const json::Expr &Params); }; enum class FileChangeType { @@ -277,8 +266,7 @@ /// The change type. FileChangeType type; - static llvm::Optional parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger); + static llvm::Optional parse(const json::Expr &Params); }; struct DidChangeWatchedFilesParams { @@ -286,7 +274,7 @@ std::vector changes; static llvm::Optional - parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger); + parse(const json::Expr &Params); }; struct FormattingOptions { @@ -296,8 +284,7 @@ /// Prefer spaces over tabs. bool insertSpaces; - static llvm::Optional - parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger); + static llvm::Optional parse(const json::Expr &Params); static json::Expr unparse(const FormattingOptions &P); }; @@ -312,7 +299,7 @@ FormattingOptions options; static llvm::Optional - parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger); + parse(const json::Expr &Params); }; struct DocumentOnTypeFormattingParams { @@ -329,7 +316,7 @@ FormattingOptions options; static llvm::Optional - parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger); + parse(const json::Expr &Params); }; struct DocumentFormattingParams { @@ -340,7 +327,7 @@ FormattingOptions options; static llvm::Optional - parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger); + parse(const json::Expr &Params); }; struct Diagnostic { @@ -372,16 +359,14 @@ std::tie(RHS.range, RHS.severity, RHS.message); } - static llvm::Optional parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger); + static llvm::Optional parse(const json::Expr &Params); }; struct CodeActionContext { /// An array of diagnostics. std::vector diagnostics; - static llvm::Optional - parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger); + static llvm::Optional parse(const json::Expr &Params); }; struct CodeActionParams { @@ -394,8 +379,7 @@ /// Context carrying additional information. CodeActionContext context; - static llvm::Optional parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger); + static llvm::Optional parse(const json::Expr &Params); }; struct WorkspaceEdit { @@ -405,8 +389,7 @@ /// Note: "documentChanges" is not currently used because currently there is /// no support for versioned edits. - static llvm::Optional parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger); + static llvm::Optional parse(const json::Expr &Params); static json::Expr unparse(const WorkspaceEdit &WE); }; @@ -429,8 +412,7 @@ llvm::Optional workspaceEdit; - static llvm::Optional - parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger); + static llvm::Optional parse(const json::Expr &Params); }; struct ApplyWorkspaceEditParams { @@ -446,7 +428,7 @@ Position position; static llvm::Optional - parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger); + parse(const json::Expr &Params); }; /// The kind of a completion entry. @@ -611,8 +593,7 @@ /// The new name of the symbol. std::string newName; - static llvm::Optional parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger); + static llvm::Optional parse(const json::Expr &Params); }; } // namespace clangd Index: clangd/Protocol.cpp =================================================================== --- clangd/Protocol.cpp +++ clangd/Protocol.cpp @@ -13,7 +13,6 @@ //===----------------------------------------------------------------------===// #include "Protocol.h" -#include "Logger.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/SmallString.h" @@ -25,12 +24,6 @@ using namespace clang; using namespace clang::clangd; -namespace { -void logIgnoredField(llvm::StringRef KeyValue, clangd::Logger &Logger) { - Logger.log(llvm::formatv("Ignored unknown field \"{0}\"\n", KeyValue)); -} -} // namespace - URI URI::fromUri(llvm::StringRef uri) { URI Result; Result.uri = uri; @@ -58,70 +51,31 @@ return Result; } -URI URI::parse(llvm::yaml::ScalarNode *Param) { - llvm::SmallString<10> Storage; - return URI::fromUri(Param->getValue(Storage)); -} - json::Expr URI::unparse(const URI &U) { return U.uri; } llvm::Optional -TextDocumentIdentifier::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { +TextDocumentIdentifier::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; + TextDocumentIdentifier Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - - if (KeyValue == "uri") { - Result.uri = URI::parse(Value); - } else if (KeyValue == "version") { - // FIXME: parse version, but only for VersionedTextDocumentIdentifiers. - } else { - logIgnoredField(KeyValue, Logger); - } - } + if (auto U = O->getString("uri")) + Result.uri = URI::parse(*U); + // FIXME: parse 'version', but only for VersionedTextDocumentIdentifiers. return Result; } -llvm::Optional Position::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { +llvm::Optional Position::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; + Position Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - - llvm::SmallString<10> Storage; - if (KeyValue == "line") { - long long Val; - if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val)) - return llvm::None; - Result.line = Val; - } else if (KeyValue == "character") { - long long Val; - if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val)) - return llvm::None; - Result.character = Val; - } else { - logIgnoredField(KeyValue, Logger); - } - } + if (auto L = O->getInteger("line")) + Result.line = *L; + if (auto C = O->getInteger("character")) + Result.character = *C; return Result; } @@ -132,35 +86,23 @@ }; } -llvm::Optional Range::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { +llvm::Optional Range::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; + Range Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - - llvm::SmallString<10> Storage; - if (KeyValue == "start") { - auto Parsed = Position::parse(Value, Logger); - if (!Parsed) - return llvm::None; - Result.start = std::move(*Parsed); - } else if (KeyValue == "end") { - auto Parsed = Position::parse(Value, Logger); - if (!Parsed) - return llvm::None; - Result.end = std::move(*Parsed); - } else { - logIgnoredField(KeyValue, Logger); - } + if (auto *S = O->get("start")) { + if (auto P = Position::parse(*S)) + Result.start = std::move(*P); + else + return None; + } + if (auto *E = O->get("end")) { + if (auto P = Position::parse(*E)) + Result.end = std::move(*P); + else + return None; } return Result; } @@ -180,100 +122,53 @@ } llvm::Optional -TextDocumentItem::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { +TextDocumentItem::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; + TextDocumentItem Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - - llvm::SmallString<10> Storage; - if (KeyValue == "uri") { - Result.uri = URI::parse(Value); - } else if (KeyValue == "languageId") { - Result.languageId = Value->getValue(Storage); - } else if (KeyValue == "version") { - long long Val; - if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val)) - return llvm::None; - Result.version = Val; - } else if (KeyValue == "text") { - Result.text = Value->getValue(Storage); - } else { - logIgnoredField(KeyValue, Logger); - } - } + if (auto U = O->getString("uri")) + Result.uri = URI::parse(*U); + if (auto L = O->getString("languageId")) + Result.languageId = *L; + if (auto V = O->getInteger("version")) + Result.version = *V; + if (auto T = O->getString("text")) + Result.text = *T; return Result; } -llvm::Optional Metadata::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { - Metadata Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; +llvm::Optional Metadata::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = NextKeyValue.getValue(); - - llvm::SmallString<10> Storage; - if (KeyValue == "extraFlags") { - auto *Seq = dyn_cast(Value); - if (!Seq) + Metadata Result; + if (auto *Flags = O->getArray("extraFlags")) + for (auto &F : *Flags) { + if (auto S = F.asString()) + Result.extraFlags.push_back(*S); + else return llvm::None; - for (auto &Item : *Seq) { - auto *Node = dyn_cast(&Item); - if (!Node) - return llvm::None; - Result.extraFlags.push_back(Node->getValue(Storage)); - } - } else { - logIgnoredField(KeyValue, Logger); } - } return Result; } -llvm::Optional TextEdit::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { - TextEdit Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; +llvm::Optional TextEdit::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = NextKeyValue.getValue(); - - llvm::SmallString<10> Storage; - if (KeyValue == "range") { - auto *Map = dyn_cast(Value); - if (!Map) - return llvm::None; - auto Parsed = Range::parse(Map, Logger); - if (!Parsed) - return llvm::None; + TextEdit Result; + if (auto *R = O->get("range")) { + if (auto Parsed = Range::parse(*R)) Result.range = std::move(*Parsed); - } else if (KeyValue == "newText") { - auto *Node = dyn_cast(Value); - if (!Node) - return llvm::None; - Result.newText = Node->getValue(Storage); - } else { - logIgnoredField(KeyValue, Logger); - } + else + return llvm::None; } + if (auto T = O->getString("newText")) + Result.newText = *T; return Result; } @@ -285,313 +180,154 @@ } namespace { -TraceLevel getTraceLevel(llvm::StringRef TraceLevelStr, - clangd::Logger &Logger) { +TraceLevel getTraceLevel(llvm::StringRef TraceLevelStr) { if (TraceLevelStr == "off") return TraceLevel::Off; else if (TraceLevelStr == "messages") return TraceLevel::Messages; else if (TraceLevelStr == "verbose") return TraceLevel::Verbose; - - Logger.log(llvm::formatv("Unknown trace level \"{0}\"\n", TraceLevelStr)); return TraceLevel::Off; } } // namespace llvm::Optional -InitializeParams::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { - // If we don't understand the params, proceed with default parameters. - auto ParseFailure = [&] { - Logger.log("Failed to decode InitializeParams\n"); - return InitializeParams(); - }; +InitializeParams::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; + InitializeParams Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return ParseFailure(); - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - continue; - - if (KeyValue == "processId") { - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return ParseFailure(); - long long Val; - if (llvm::getAsSignedInteger(Value->getValue(KeyStorage), 0, Val)) - return ParseFailure(); - Result.processId = Val; - } else if (KeyValue == "rootPath") { - Result.rootPath = Value->getValue(KeyStorage); - } else if (KeyValue == "rootUri") { - Result.rootUri = URI::parse(Value); - } else if (KeyValue == "initializationOptions") { - // Not used - } else if (KeyValue == "capabilities") { - // Not used - } else if (KeyValue == "trace") { - Result.trace = getTraceLevel(Value->getValue(KeyStorage), Logger); - } else { - logIgnoredField(KeyValue, Logger); - } - } + if (auto P = O->getInteger("processId")) + Result.processId = *P; + if (auto R = O->getString("rootPath")) + Result.rootPath = *R; + if (auto R = O->getString("rootUri")) + Result.rootUri = URI::parse(*R); + if (auto T = O->getString("trace")) + Result.trace = getTraceLevel(*T); + // initializationOptions, capabilities unused return Result; } llvm::Optional -DidOpenTextDocumentParams::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { +DidOpenTextDocumentParams::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; + DidOpenTextDocumentParams Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - - llvm::SmallString<10> Storage; - if (KeyValue == "textDocument") { - auto Parsed = TextDocumentItem::parse(Value, Logger); - if (!Parsed) - return llvm::None; + if (auto *D = O->get("textDocument")) { + if (auto Parsed = TextDocumentItem::parse(*D)) Result.textDocument = std::move(*Parsed); - } else if (KeyValue == "metadata") { - auto Parsed = Metadata::parse(Value, Logger); - if (!Parsed) - return llvm::None; + else + return llvm::None; + } + if (auto *M = O->get("metadata")) { + if (auto Parsed = Metadata::parse(*M)) Result.metadata = std::move(*Parsed); - } else { - logIgnoredField(KeyValue, Logger); - } + else + return llvm::None; } return Result; } llvm::Optional -DidCloseTextDocumentParams::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { - DidCloseTextDocumentParams Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; +DidCloseTextDocumentParams::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = NextKeyValue.getValue(); - - if (KeyValue == "textDocument") { - auto *Map = dyn_cast(Value); - if (!Map) - return llvm::None; - auto Parsed = TextDocumentIdentifier::parse(Map, Logger); - if (!Parsed) - return llvm::None; + DidCloseTextDocumentParams Result; + if (auto *D = O->get("textDocument")) { + if (auto Parsed = TextDocumentIdentifier::parse(*D)) Result.textDocument = std::move(*Parsed); - } else { - logIgnoredField(KeyValue, Logger); - } + else + return llvm::None; } return Result; } llvm::Optional -DidChangeTextDocumentParams::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { - DidChangeTextDocumentParams Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; +DidChangeTextDocumentParams::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = NextKeyValue.getValue(); - - llvm::SmallString<10> Storage; - if (KeyValue == "textDocument") { - auto *Map = dyn_cast(Value); - if (!Map) - return llvm::None; - auto Parsed = TextDocumentIdentifier::parse(Map, Logger); - if (!Parsed) - return llvm::None; + DidChangeTextDocumentParams Result; + if (auto *D = O->get("textDocument")) { + if (auto Parsed = TextDocumentIdentifier::parse(*D)) Result.textDocument = std::move(*Parsed); - } else if (KeyValue == "contentChanges") { - auto *Seq = dyn_cast(Value); - if (!Seq) - return llvm::None; - for (auto &Item : *Seq) { - auto *I = dyn_cast(&Item); - if (!I) - return llvm::None; - auto Parsed = TextDocumentContentChangeEvent::parse(I, Logger); - if (!Parsed) - return llvm::None; + else + return llvm::None; + } + if (auto *A = O->getArray("contentChanges")) + for (auto &E : *A) { + if (auto Parsed = TextDocumentContentChangeEvent::parse(E)) Result.contentChanges.push_back(std::move(*Parsed)); - } - } else { - logIgnoredField(KeyValue, Logger); + else + return llvm::None; } - } return Result; } -llvm::Optional FileEvent::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { - llvm::Optional Result = FileEvent(); - for (auto &NextKeyValue : *Params) { - // We have to consume the whole MappingNode because it doesn't support - // skipping and we want to be able to parse further valid events. - if (!Result) - continue; - - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) { - Result.reset(); - continue; - } +llvm::Optional FileEvent::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) { - Result.reset(); - continue; - } - llvm::SmallString<10> Storage; - if (KeyValue == "uri") { - Result->uri = URI::parse(Value); - } else if (KeyValue == "type") { - long long Val; - if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val)) { - Result.reset(); - continue; - } - Result->type = static_cast(Val); - if (Result->type < FileChangeType::Created || - Result->type > FileChangeType::Deleted) - Result.reset(); - } else { - logIgnoredField(KeyValue, Logger); - } + FileEvent Result; + if (auto U = O->getString("uri")) + Result.uri = URI::parse(*U); + if (auto T = O->getInteger("type")) { + if (*T < static_cast(FileChangeType::Created) || + *T > static_cast(FileChangeType::Deleted)) + return llvm::None; + Result.type = static_cast(*T); } return Result; } llvm::Optional -DidChangeWatchedFilesParams::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { - DidChangeWatchedFilesParams Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; +DidChangeWatchedFilesParams::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = NextKeyValue.getValue(); - - llvm::SmallString<10> Storage; - if (KeyValue == "changes") { - auto *Seq = dyn_cast(Value); - if (!Seq) + DidChangeWatchedFilesParams Result; + if (auto *C = O->getArray("changes")) + for (auto &E : *C) { + if (auto Parsed = FileEvent::parse(E)) + Result.changes.push_back(std::move(*Parsed)); + else return llvm::None; - for (auto &Item : *Seq) { - auto *I = dyn_cast(&Item); - if (!I) - return llvm::None; - auto Parsed = FileEvent::parse(I, Logger); - if (Parsed) - Result.changes.push_back(std::move(*Parsed)); - else - Logger.log("Failed to decode a FileEvent.\n"); - } - } else { - logIgnoredField(KeyValue, Logger); } - } return Result; } llvm::Optional -TextDocumentContentChangeEvent::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { +TextDocumentContentChangeEvent::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; + TextDocumentContentChangeEvent Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - - llvm::SmallString<10> Storage; - if (KeyValue == "text") { - Result.text = Value->getValue(Storage); - } else { - logIgnoredField(KeyValue, Logger); - } - } + if (auto T = O->getString("text")) + Result.text = *T; return Result; } llvm::Optional -FormattingOptions::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { +FormattingOptions::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; + FormattingOptions Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - - llvm::SmallString<10> Storage; - if (KeyValue == "tabSize") { - long long Val; - if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val)) - return llvm::None; - Result.tabSize = Val; - } else if (KeyValue == "insertSpaces") { - long long Val; - StringRef Str = Value->getValue(Storage); - if (llvm::getAsSignedInteger(Str, 0, Val)) { - if (Str == "true") - Val = 1; - else if (Str == "false") - Val = 0; - else - return llvm::None; - } - Result.insertSpaces = Val; - } else { - logIgnoredField(KeyValue, Logger); - } - } + if (auto T = O->getInteger("tabSize")) + Result.tabSize = *T; + if (auto I = O->getBoolean("insertSpaces")) + Result.insertSpaces = *I; return Result; } @@ -603,305 +339,182 @@ } llvm::Optional -DocumentRangeFormattingParams::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { +DocumentRangeFormattingParams::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; + DocumentRangeFormattingParams Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - - llvm::SmallString<10> Storage; - if (KeyValue == "textDocument") { - auto Parsed = TextDocumentIdentifier::parse(Value, Logger); - if (!Parsed) - return llvm::None; + if (auto *D = O->get("textDocument")) { + if (auto Parsed = TextDocumentIdentifier::parse(*D)) Result.textDocument = std::move(*Parsed); - } else if (KeyValue == "range") { - auto Parsed = Range::parse(Value, Logger); - if (!Parsed) - return llvm::None; + else + return llvm::None; + } + if (auto *R = O->get("range")) { + if (auto Parsed = Range::parse(*R)) Result.range = std::move(*Parsed); - } else if (KeyValue == "options") { - auto Parsed = FormattingOptions::parse(Value, Logger); - if (!Parsed) - return llvm::None; + else + return llvm::None; + } + if (auto *F = O->get("options")) { + if (auto Parsed = FormattingOptions::parse(*F)) Result.options = std::move(*Parsed); - } else { - logIgnoredField(KeyValue, Logger); - } + else + return llvm::None; } return Result; } llvm::Optional -DocumentOnTypeFormattingParams::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { - DocumentOnTypeFormattingParams Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; +DocumentOnTypeFormattingParams::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - - if (KeyValue == "ch") { - auto *ScalarValue = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!ScalarValue) - return llvm::None; - llvm::SmallString<10> Storage; - Result.ch = ScalarValue->getValue(Storage); - continue; - } - - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - if (KeyValue == "textDocument") { - auto Parsed = TextDocumentIdentifier::parse(Value, Logger); - if (!Parsed) - return llvm::None; + DocumentOnTypeFormattingParams Result; + if (auto Ch = O->getString("ch")) + Result.ch = *Ch; + if (auto *D = O->get("textDocument")) { + if (auto Parsed = TextDocumentIdentifier::parse(*D)) Result.textDocument = std::move(*Parsed); - } else if (KeyValue == "position") { - auto Parsed = Position::parse(Value, Logger); - if (!Parsed) - return llvm::None; + else + return llvm::None; + } + if (auto *P = O->get("position")) { + if (auto Parsed = Position::parse(*P)) Result.position = std::move(*Parsed); - } else if (KeyValue == "options") { - auto Parsed = FormattingOptions::parse(Value, Logger); - if (!Parsed) - return llvm::None; + else + return llvm::None; + } + if (auto *F = O->get("options")) { + if (auto Parsed = FormattingOptions::parse(*F)) Result.options = std::move(*Parsed); - } else { - logIgnoredField(KeyValue, Logger); - } + else + return llvm::None; } return Result; } llvm::Optional -DocumentFormattingParams::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { +DocumentFormattingParams::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; + DocumentFormattingParams Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - - llvm::SmallString<10> Storage; - if (KeyValue == "textDocument") { - auto Parsed = TextDocumentIdentifier::parse(Value, Logger); - if (!Parsed) - return llvm::None; + if (auto *D = O->get("textDocument")) { + if (auto Parsed = TextDocumentIdentifier::parse(*D)) Result.textDocument = std::move(*Parsed); - } else if (KeyValue == "options") { - auto Parsed = FormattingOptions::parse(Value, Logger); - if (!Parsed) - return llvm::None; + else + return llvm::None; + } + if (auto *F = O->get("options")) { + if (auto Parsed = FormattingOptions::parse(*F)) Result.options = std::move(*Parsed); - } else { - logIgnoredField(KeyValue, Logger); - } + else + return llvm::None; } return Result; } -llvm::Optional Diagnostic::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { - Diagnostic Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; +llvm::Optional Diagnostic::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - - llvm::SmallString<10> Storage; - if (KeyValue == "range") { - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - auto Parsed = Range::parse(Value, Logger); - if (!Parsed) - return llvm::None; + Diagnostic Result; + if (auto *R = O->get("range")) { + if (auto Parsed = Range::parse(*R)) Result.range = std::move(*Parsed); - } else if (KeyValue == "severity") { - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - long long Val; - if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val)) - return llvm::None; - Result.severity = Val; - } else if (KeyValue == "code") { - // Not currently used - } else if (KeyValue == "source") { - // Not currently used - } else if (KeyValue == "message") { - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - Result.message = Value->getValue(Storage); - } else { - logIgnoredField(KeyValue, Logger); - } + else + return llvm::None; } + if (auto S = O->getInteger("severity")) + Result.severity = *S; + if (auto M = O->getString("message")) + Result.message = *M; return Result; } llvm::Optional -CodeActionContext::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { - CodeActionContext Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; +CodeActionContext::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = NextKeyValue.getValue(); - - llvm::SmallString<10> Storage; - if (KeyValue == "diagnostics") { - auto *Seq = dyn_cast(Value); - if (!Seq) - return llvm::None; - for (auto &Item : *Seq) { - auto *I = dyn_cast(&Item); - if (!I) - return llvm::None; - auto Parsed = Diagnostic::parse(I, Logger); - if (!Parsed) - return llvm::None; + CodeActionContext Result; + if (auto *D = O->getArray("diagnostics")) + for (auto &E : *D) { + if (auto Parsed = Diagnostic::parse(E)) Result.diagnostics.push_back(std::move(*Parsed)); - } - } else { - logIgnoredField(KeyValue, Logger); + else + return llvm::None; } - } return Result; } llvm::Optional -CodeActionParams::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { +CodeActionParams::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; + CodeActionParams Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - - llvm::SmallString<10> Storage; - if (KeyValue == "textDocument") { - auto Parsed = TextDocumentIdentifier::parse(Value, Logger); - if (!Parsed) - return llvm::None; + if (auto *D = O->get("textDocument")) { + if (auto Parsed = TextDocumentIdentifier::parse(*D)) Result.textDocument = std::move(*Parsed); - } else if (KeyValue == "range") { - auto Parsed = Range::parse(Value, Logger); - if (!Parsed) - return llvm::None; + else + return llvm::None; + } + if (auto *R = O->get("range")) { + if (auto Parsed = Range::parse(*R)) Result.range = std::move(*Parsed); - } else if (KeyValue == "context") { - auto Parsed = CodeActionContext::parse(Value, Logger); - if (!Parsed) - return llvm::None; + else + return llvm::None; + } + if (auto *R = O->get("context")) { + if (auto Parsed = CodeActionContext::parse(*R)) Result.context = std::move(*Parsed); - } else { - logIgnoredField(KeyValue, Logger); - } + else + return llvm::None; } return Result; } -static llvm::Optional>> -parseWorkspaceEditChange(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { +llvm::Optional>> +parseWorkspaceEditChange(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; + std::map> Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) + for (const auto &KV : *O) { + auto &Values = Result[StringRef(KV.first)]; + if (auto *Edits = KV.second.asArray()) + for (auto &Edit : *Edits) { + if (auto Parsed = TextEdit::parse(Edit)) + Values.push_back(std::move(*Parsed)); + else + return llvm::None; + } + else return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - if (Result.count(KeyValue)) { - logIgnoredField(KeyValue, Logger); - continue; - } - - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - for (auto &Item : *Value) { - auto *ItemValue = dyn_cast_or_null(&Item); - if (!ItemValue) - return llvm::None; - auto Parsed = TextEdit::parse(ItemValue, Logger); - if (!Parsed) - return llvm::None; - - Result[KeyValue].push_back(*Parsed); - } } - return Result; } -llvm::Optional -WorkspaceEdit::parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger) { - WorkspaceEdit Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; +llvm::Optional WorkspaceEdit::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - - llvm::SmallString<10> Storage; - if (KeyValue == "changes") { - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - auto Parsed = parseWorkspaceEditChange(Value, Logger); - if (!Parsed) - return llvm::None; + WorkspaceEdit Result; + if (auto *C = O->get("changes")) { + if (auto Parsed = parseWorkspaceEditChange(*C)) Result.changes = std::move(*Parsed); - } else { - logIgnoredField(KeyValue, Logger); - } + else + return llvm::None; } return Result; } @@ -910,59 +523,25 @@ "clangd.applyFix"; llvm::Optional -ExecuteCommandParams::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { - ExecuteCommandParams Result; - // Depending on which "command" we parse, we will use this function to parse - // the command "arguments". - std::function ArgParser = nullptr; - - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - - // Note that "commands" has to be parsed before "arguments" for this to - // work properly. - if (KeyValue == "command") { - auto *ScalarValue = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!ScalarValue) - return llvm::None; - llvm::SmallString<10> Storage; - Result.command = ScalarValue->getValue(Storage); - if (Result.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND) { - ArgParser = [&Result, &Logger](llvm::yaml::MappingNode *Params) { - auto WE = WorkspaceEdit::parse(Params, Logger); - if (WE) - Result.workspaceEdit = WE; - return WE.hasValue(); - }; - } else { - return llvm::None; - } - } else if (KeyValue == "arguments") { - auto *Value = NextKeyValue.getValue(); - auto *Seq = dyn_cast(Value); - if (!Seq) - return llvm::None; - for (auto &Item : *Seq) { - auto *ItemValue = dyn_cast_or_null(&Item); - if (!ItemValue || !ArgParser) - return llvm::None; - if (!ArgParser(ItemValue)) - return llvm::None; - } - } else { - logIgnoredField(KeyValue, Logger); - } - } - if (Result.command.empty()) - return llvm::None; +ExecuteCommandParams::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; + ExecuteCommandParams Result; + if (auto Command = O->getString("command")) + Result.command = *Command; + auto Args = O->getArray("arguments"); + + if (Result.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND) { + if (!Args || Args->size() != 1) + return llvm::None; + if (auto Parsed = WorkspaceEdit::parse(Args->front())) + Result.workspaceEdit = std::move(*Parsed); + else + return llvm::None; + } else + return llvm::None; // Unrecognized command. return Result; } @@ -981,35 +560,23 @@ } llvm::Optional -TextDocumentPositionParams::parse(llvm::yaml::MappingNode *Params, - clangd::Logger &Logger) { +TextDocumentPositionParams::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; + TextDocumentPositionParams Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - continue; - - llvm::SmallString<10> Storage; - if (KeyValue == "textDocument") { - auto Parsed = TextDocumentIdentifier::parse(Value, Logger); - if (!Parsed) - return llvm::None; + if (auto *D = O->get("textDocument")) { + if (auto Parsed = TextDocumentIdentifier::parse(*D)) Result.textDocument = std::move(*Parsed); - } else if (KeyValue == "position") { - auto Parsed = Position::parse(Value, Logger); - if (!Parsed) - return llvm::None; + else + return llvm::None; + } + if (auto *P = O->get("position")) { + if (auto Parsed = Position::parse(*P)) Result.position = std::move(*Parsed); - } else { - logIgnoredField(KeyValue, Logger); - } + else + return llvm::None; } return Result; } @@ -1081,50 +648,25 @@ }; } -llvm::Optional -RenameParams::parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger) { - RenameParams Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; +llvm::Optional RenameParams::parse(const json::Expr &Params) { + const json::obj *O = Params.asObject(); + if (!O) + return None; - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - - if (KeyValue == "textDocument") { - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - continue; - auto *Map = dyn_cast(Value); - if (!Map) - return llvm::None; - auto Parsed = TextDocumentIdentifier::parse(Map, Logger); - if (!Parsed) - return llvm::None; + RenameParams Result; + if (auto *D = O->get("textDocument")) { + if (auto Parsed = TextDocumentIdentifier::parse(*D)) Result.textDocument = std::move(*Parsed); - } else if (KeyValue == "position") { - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - continue; - auto Parsed = Position::parse(Value, Logger); - if (!Parsed) - return llvm::None; + else + return llvm::None; + } + if (auto *P = O->get("position")) { + if (auto Parsed = Position::parse(*P)) Result.position = std::move(*Parsed); - } else if (KeyValue == "newName") { - auto *Value = NextKeyValue.getValue(); - if (!Value) - continue; - auto *Node = dyn_cast(Value); - if (!Node) - return llvm::None; - llvm::SmallString<10> Storage; - Result.newName = Node->getValue(Storage); - } else { - logIgnoredField(KeyValue, Logger); - } + else + return llvm::None; } + if (auto N = O->getString("newName")) + Result.newName = *N; return Result; } Index: clangd/ProtocolHandlers.cpp =================================================================== --- clangd/ProtocolHandlers.cpp +++ clangd/ProtocolHandlers.cpp @@ -21,7 +21,7 @@ // Helper for attaching ProtocolCallbacks methods to a JSONRPCDispatcher. // Invoke like: Registerer("foo", &ProtocolCallbacks::onFoo) // onFoo should be: void onFoo(Ctx &C, FooParams &Params) -// FooParams should have a static factory method: parse(yaml::MappingNode*). +// FooParams should have a static factory method: parse(const json::Expr&). struct HandlerRegisterer { template void operator()(StringRef Method, @@ -30,10 +30,10 @@ auto *Out = this->Out; auto *Callbacks = this->Callbacks; Dispatcher.registerHandler( - Method, [=](RequestContext C, llvm::yaml::MappingNode *RawParams) { + Method, [=](RequestContext C, const json::Expr &RawParams) { if (auto P = [&] { trace::Span Tracer("Parse"); - return std::decay::type::parse(RawParams, *Out); + return std::decay::type::parse(RawParams); }()) { (Callbacks->*Handler)(std::move(C), *P); } else { Index: clangd/Trace.cpp =================================================================== --- clangd/Trace.cpp +++ clangd/Trace.cpp @@ -14,7 +14,6 @@ #include "llvm/Support/FormatProviders.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Threading.h" -#include "llvm/Support/YAMLParser.h" #include namespace clang { Index: test/clangd/authority-less-uri.test =================================================================== --- test/clangd/authority-less-uri.test +++ test/clangd/authority-less-uri.test @@ -28,7 +28,7 @@ # CHECK-NEXT: "sortText": "{{.*}}fake" # CHECK: ] # CHECK-NEXT: } -Content-Length: 172 +Content-Length: 173 {"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"uri":"file:///main.cpp","position":{"line":3,"character":5}}} # Test params parsing in the presence of a 1.x-compatible client (inlined "uri") @@ -51,4 +51,4 @@ {"jsonrpc":"2.0","id":3,"method":"shutdown"} Content-Length: 33 -{"jsonrpc":"2.0":"method":"exit"} +{"jsonrpc":"2.0","method":"exit"} Index: test/clangd/definitions.test =================================================================== --- test/clangd/definitions.test +++ test/clangd/definitions.test @@ -101,7 +101,7 @@ # CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp" # CHECK-NEXT: } # CHECK-NEXT: ] -Content-Length: 187 +Content-Length: 188 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":4},"contentChanges":[{"text":"int main() {\n main();\n return 0;\n}"}]}} @@ -126,7 +126,7 @@ # CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp" # CHECK-NEXT: } # CHECK-NEXT: ] -Content-Length: 208 +Content-Length: 209 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"struct Foo {\n};\nint main() {\n Foo bar;\n return 0;\n}\n"}]}} @@ -151,7 +151,7 @@ # CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp" # CHECK-NEXT: } # CHECK-NEXT: ] -Content-Length: 231 +Content-Length: 232 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"namespace n1 {\nstruct Foo {\n};\n}\nint main() {\n n1::Foo bar;\n return 0;\n}\n"}]}} @@ -176,7 +176,7 @@ # CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp" # CHECK-NEXT: } # CHECK-NEXT: ] -Content-Length: 215 +Content-Length: 216 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":6},"contentChanges":[{"text":"struct Foo {\n int x;\n};\nint main() {\n Foo bar;\n bar.x;\n}\n"}]}} @@ -201,7 +201,7 @@ # CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp" # CHECK-NEXT: } # CHECK-NEXT: ] -Content-Length: 220 +Content-Length: 221 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n void x();\n};\nint main() {\n Foo bar;\n bar.x();\n}\n"}]}} @@ -226,7 +226,7 @@ # CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp" # CHECK-NEXT: } # CHECK-NEXT: ] -Content-Length: 240 +Content-Length: 241 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n};\ntypedef Foo TypedefFoo;\nint main() {\n TypedefFoo bar;\n return 0;\n}\n"}]}} @@ -251,7 +251,7 @@ # CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp" # CHECK-NEXT: } # CHECK-NEXT: ] -Content-Length: 254 +Content-Length: 253 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"template \nvoid foo() {\n MyTemplateParam a;\n}\nint main() {\n return 0;\n}\n"}]}} @@ -262,7 +262,7 @@ # CHECK: "id": 1, # CHECK-NEXT: "jsonrpc": "2.0", # CHECK-NEXT: "result": [] -Content-Length: 256 +Content-Length: 257 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace ns {\nstruct Foo {\nstatic void bar() {}\n};\n}\nint main() {\n ns::Foo::bar();\n return 0;\n}\n"}]}} @@ -418,4 +418,4 @@ {"jsonrpc":"2.0","id":10000,"method":"shutdown"} Content-Length: 33 -{"jsonrpc":"2.0":"method":"exit"} +{"jsonrpc":"2.0","method":"exit"} Index: test/clangd/diagnostics.test =================================================================== --- test/clangd/diagnostics.test +++ test/clangd/diagnostics.test @@ -47,4 +47,4 @@ {"jsonrpc":"2.0","id":5,"method":"shutdown"} Content-Length: 33 -{"jsonrpc":"2.0":"method":"exit"} +{"jsonrpc":"2.0","method":"exit"} Index: test/clangd/did-change-watch-files.test =================================================================== --- test/clangd/did-change-watch-files.test +++ test/clangd/did-change-watch-files.test @@ -1,53 +0,0 @@ -# RUN: clangd -run-synchronously < %s 2>&1 | FileCheck -check-prefix=STDERR %s -# It is absolutely vital that this file has CRLF line endings. -# -# Test initialize request parameters with rootUri -Content-Length: 143 - -{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootUri":"file:///path/to/workspace","capabilities":{},"trace":"off"}} -# Normal case. -Content-Length: 217 - -{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":[{"uri":"file:///path/to/file.cpp","type":1},{"uri":"file:///path/to/file2.cpp","type":2},{"uri":"file:///path/to/file3.cpp","type":3}]}} - -# Wrong event type, integer -Content-Length: 173 - -{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":[{"uri":"file:///path/to/file2.cpp","type":0},{"uri":"file:///path/to/file3.cpp","type":4}]}} -# STDERR: Failed to decode a FileEvent. -# Wrong event type, string -Content-Length: 132 - -{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":[{"uri":"file:///path/to/file2.cpp","type":"foo"}]}} -# STDERR: Failed to decode a FileEvent. -#Custom event field -Content-Length: 143 - -{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":[{"uri":"file:///path/to/file2.cpp","type":1,"custom":"foo"}]}} -# STDERR: Failed to decode a FileEvent. -#Event field with object -Content-Length: 140 - -{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":[{"uri":"file:///path/to/file2.cpp","type":{"foo":"bar"}}]}} -# STDERR: Failed to decode a FileEvent. -# Changes field with sequence but no object -Content-Length: 86 - -{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":[""]}} -# STDERR: Failed to decode workspace/didChangeWatchedFiles request. -# Changes field with no sequence -Content-Length: 84 - -{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes":""}} -# STDERR: Failed to decode workspace/didChangeWatchedFiles request. -# Custom field -Content-Length: 86 - -{"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"custom":"foo"}} -# STDERR: Ignored unknown field "custom" -Content-Length: 44 - -{"jsonrpc":"2.0","id":3,"method":"shutdown"} -Content-Length: 33 - -{"jsonrpc":"2.0":"method":"exit"} Index: test/clangd/protocol.test =================================================================== --- test/clangd/protocol.test +++ test/clangd/protocol.test @@ -79,7 +79,7 @@ {"jsonrpc":"2.0","id":4,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}} # Test message with malformed Content-Length # -# STDERR: JSON dispatch failed! +# STDERR: JSON parse error # Ensure we recover by sending another (valid) message Content-Length: 146 Index: unittests/clangd/JSONExprTests.cpp =================================================================== --- unittests/clangd/JSONExprTests.cpp +++ unittests/clangd/JSONExprTests.cpp @@ -205,6 +205,7 @@ EXPECT_TRUE(O->getNull("null")); EXPECT_EQ(O->getNumber("number"), llvm::Optional(2.78)); + EXPECT_FALSE(O->getInteger("number")); EXPECT_EQ(O->getString("string"), llvm::Optional("json")); ASSERT_FALSE(O->getObject("missing")); ASSERT_FALSE(O->getObject("array")); @@ -216,6 +217,7 @@ EXPECT_EQ(A->getBoolean(1), llvm::Optional(true)); ASSERT_TRUE(A->getArray(4)); EXPECT_EQ(*A->getArray(4), (ary{1, 2, 3})); + EXPECT_EQ(A->getArray(4)->getInteger(1), llvm::Optional(2)); int I = 0; for (Expr &E : *A) { if (I++ == 5) {