Index: clangd/CMakeLists.txt =================================================================== --- clangd/CMakeLists.txt +++ clangd/CMakeLists.txt @@ -23,7 +23,6 @@ FuzzyMatch.cpp GlobalCompilationDatabase.cpp Headers.cpp - JSONExpr.cpp JSONRPCDispatcher.cpp Logger.cpp Protocol.cpp Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -18,6 +18,7 @@ using namespace clang::clangd; using namespace clang; +using namespace llvm; namespace { @@ -87,25 +88,25 @@ } } - reply(json::obj{ + reply(json::Object{ {{"capabilities", - json::obj{ + json::Object{ {"textDocumentSync", (int)TextDocumentSyncKind::Incremental}, {"documentFormattingProvider", true}, {"documentRangeFormattingProvider", true}, {"documentOnTypeFormattingProvider", - json::obj{ + json::Object{ {"firstTriggerCharacter", "}"}, {"moreTriggerCharacter", {}}, }}, {"codeActionProvider", true}, {"completionProvider", - json::obj{ + json::Object{ {"resolveProvider", false}, {"triggerCharacters", {".", ">", ":"}}, }}, {"signatureHelpProvider", - json::obj{ + json::Object{ {"triggerCharacters", {"(", ","}}, }}, {"definitionProvider", true}, @@ -115,7 +116,7 @@ {"documentSymbolProvider", true}, {"workspaceSymbolProvider", true}, {"executeCommandProvider", - json::obj{ + json::Object{ {"commands", {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}}, }}, }}}}); @@ -212,7 +213,7 @@ for (auto &Sym : *Items) Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds); - reply(json::ary(*Items)); + reply(json::Array(*Items)); }); } @@ -258,7 +259,7 @@ auto ReplacementsOrError = Server.formatOnType(*Code, File, Params.position); if (ReplacementsOrError) - reply(json::ary(replacementsToEdits(*Code, ReplacementsOrError.get()))); + reply(json::Array(replacementsToEdits(*Code, ReplacementsOrError.get()))); else replyError(ErrorCode::UnknownErrorCode, llvm::toString(ReplacementsOrError.takeError())); @@ -274,7 +275,7 @@ auto ReplacementsOrError = Server.formatRange(*Code, File, Params.range); if (ReplacementsOrError) - reply(json::ary(replacementsToEdits(*Code, ReplacementsOrError.get()))); + reply(json::Array(replacementsToEdits(*Code, ReplacementsOrError.get()))); else replyError(ErrorCode::UnknownErrorCode, llvm::toString(ReplacementsOrError.takeError())); @@ -289,7 +290,7 @@ auto ReplacementsOrError = Server.formatFile(*Code, File); if (ReplacementsOrError) - reply(json::ary(replacementsToEdits(*Code, ReplacementsOrError.get()))); + reply(json::Array(replacementsToEdits(*Code, ReplacementsOrError.get()))); else replyError(ErrorCode::UnknownErrorCode, llvm::toString(ReplacementsOrError.takeError())); @@ -304,7 +305,7 @@ llvm::toString(Items.takeError())); for (auto &Sym : *Items) Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds); - reply(json::ary(*Items)); + reply(json::Array(*Items)); }); } @@ -316,13 +317,13 @@ return replyError(ErrorCode::InvalidParams, "onCodeAction called for non-added file"); - json::ary Commands; + json::Array Commands; for (Diagnostic &D : Params.context.diagnostics) { for (auto &F : getFixes(Params.textDocument.uri.file(), D)) { WorkspaceEdit WE; std::vector Edits(F.Edits.begin(), F.Edits.end()); WE.changes = {{Params.textDocument.uri.uri(), std::move(Edits)}}; - Commands.push_back(json::obj{ + Commands.push_back(json::Object{ {"title", llvm::formatv("Apply fix: {0}", F.Message)}, {"command", ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}, {"arguments", {WE}}, @@ -364,7 +365,7 @@ if (!Items) return replyError(ErrorCode::InvalidParams, llvm::toString(Items.takeError())); - reply(json::ary(*Items)); + reply(json::Array(*Items)); }); } @@ -380,7 +381,7 @@ if (!Highlights) return replyError(ErrorCode::InternalError, llvm::toString(Highlights.takeError())); - reply(json::ary(*Highlights)); + reply(json::Array(*Highlights)); }); } @@ -424,7 +425,7 @@ assert(!IsDone && "Run was called before"); // Set up JSONRPCDispatcher. - JSONRPCDispatcher Dispatcher([](const json::Expr &Params) { + JSONRPCDispatcher Dispatcher([](const json::Value &Params) { replyError(ErrorCode::MethodNotFound, "method not found"); }); registerCallbackHandlers(Dispatcher, /*Callbacks=*/*this); @@ -456,12 +457,12 @@ void ClangdLSPServer::onDiagnosticsReady(PathRef File, std::vector Diagnostics) { - json::ary DiagnosticsJSON; + json::Array DiagnosticsJSON; DiagnosticToReplacementMap LocalFixIts; // Temporary storage for (auto &Diag : Diagnostics) { toLSPDiags(Diag, [&](clangd::Diagnostic Diag, llvm::ArrayRef Fixes) { - DiagnosticsJSON.push_back(json::obj{ + DiagnosticsJSON.push_back(json::Object{ {"range", Diag.range}, {"severity", Diag.severity}, {"message", Diag.message}, @@ -481,11 +482,11 @@ } // Publish diagnostics. - Out.writeMessage(json::obj{ + Out.writeMessage(json::Object{ {"jsonrpc", "2.0"}, {"method", "textDocument/publishDiagnostics"}, {"params", - json::obj{ + json::Object{ {"uri", URIForFile{File}}, {"diagnostics", std::move(DiagnosticsJSON)}, }}, Index: clangd/CodeComplete.cpp =================================================================== --- clangd/CodeComplete.cpp +++ clangd/CodeComplete.cpp @@ -1014,7 +1014,7 @@ SPAN_ATTACH(Tracer, "sema_results", NSema); SPAN_ATTACH(Tracer, "index_results", NIndex); SPAN_ATTACH(Tracer, "merged_results", NBoth); - SPAN_ATTACH(Tracer, "returned_results", Output.Completions.size()); + SPAN_ATTACH(Tracer, "returned_results", int64_t(Output.Completions.size())); SPAN_ATTACH(Tracer, "incomplete", Output.HasMore); log(llvm::formatv("Code complete: {0} results from Sema, {1} from Index, " "{2} matched, {3} returned{4}.", @@ -1056,7 +1056,7 @@ SymbolSlab queryIndex() { trace::Span Tracer("Query index"); - SPAN_ATTACH(Tracer, "limit", Opts.Limit); + SPAN_ATTACH(Tracer, "limit", int64_t(Opts.Limit)); SymbolSlab::Builder ResultsBuilder; // Build the query. Index: clangd/JSONExpr.h =================================================================== --- clangd/JSONExpr.h +++ clangd/JSONExpr.h @@ -1,586 +0,0 @@ -//===--- JSONExpr.h - JSON expressions, parsing and serialization - C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===---------------------------------------------------------------------===// - -// FIXME: rename to JSON.h now that the scope is wider? - -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSON_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSON_H - -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/FormatVariadic.h" -#include "llvm/Support/raw_ostream.h" -#include - -namespace clang { -namespace clangd { -namespace json { -class Expr; -template Expr toJSON(const llvm::Optional &Opt); - -// An Expr is an JSON value of unknown type. -// They can be copied, but should generally be moved. -// -// === Composing expressions === -// -// You can implicitly construct Exprs from: -// - strings: std::string, SmallString, formatv, StringRef, char* -// (char*, and StringRef are references, not copies!) -// - numbers -// - booleans -// - null: nullptr -// - arrays: {"foo", 42.0, false} -// - serializable things: types with toJSON(const T&)->Expr, found by ADL -// -// They can also be constructed from object/array helpers: -// - json::obj is a type like map -// - json::ary is a type like vector -// These can be list-initialized, or used to build up collections in a loop. -// json::ary(Collection) converts all items in a collection to Exprs. -// -// === Inspecting expressions === -// -// Each Expr is one of the JSON kinds: -// null (nullptr_t) -// boolean (bool) -// number (double) -// string (StringRef) -// array (json::ary) -// object (json::obj) -// -// The kind can be queried directly, or implicitly via the typed accessors: -// if (Optional S = E.asString() -// assert(E.kind() == Expr::String); -// -// Array and Object also have typed indexing accessors for easy traversal: -// Expected E = parse(R"( {"options": {"font": "sans-serif"}} )"); -// if (json::obj* O = E->asObject()) -// if (json::obj* Opts = O->getObject("options")) -// if (Optional Font = Opts->getString("font")) -// assert(Opts->at("font").kind() == Expr::String); -// -// === Converting expressions to objects === -// -// The convention is to have a deserializer function findable via ADL: -// fromJSON(const json::Expr&, T&)->bool -// Deserializers are provided for: -// - bool -// - int -// - double -// - std::string -// - vector, where T is deserializable -// - map, where T is deserializable -// - Optional, where T is deserializable -// -// ObjectMapper can help writing fromJSON() functions for object types: -// bool fromJSON(const Expr &E, MyStruct &R) { -// ObjectMapper O(E); -// if (!O || !O.map("mandatory_field", R.MandatoryField)) -// return false; -// O.map("optional_field", R.OptionalField); -// return true; -// } -// -// === Serialization === -// -// Exprs can be serialized to JSON: -// 1) raw_ostream << Expr // Basic formatting. -// 2) raw_ostream << formatv("{0}", Expr) // Basic formatting. -// 3) raw_ostream << formatv("{0:2}", Expr) // Pretty-print with indent 2. -// -// And parsed: -// Expected E = json::parse("[1, 2, null]"); -// assert(E && E->kind() == Expr::Array); -class Expr { -public: - enum Kind { - Null, - Boolean, - Number, - String, - Array, - Object, - }; - class ObjectExpr; - class ObjectKey; - class ArrayExpr; - - // It would be nice to have Expr() be null. But that would make {} null too... - Expr(const Expr &M) { copyFrom(M); } - Expr(Expr &&M) { moveFrom(std::move(M)); } - // "cheating" move-constructor for moving from initializer_list. - Expr(const Expr &&M) { moveFrom(std::move(M)); } - Expr(std::initializer_list Elements) : Expr(ArrayExpr(Elements)) {} - Expr(ArrayExpr &&Elements) : Type(T_Array) { - create(std::move(Elements)); - } - Expr(ObjectExpr &&Properties) : Type(T_Object) { - create(std::move(Properties)); - } - // Strings: types with value semantics. - Expr(std::string &&V) : Type(T_String) { create(std::move(V)); } - Expr(const std::string &V) : Type(T_String) { create(V); } - Expr(const llvm::SmallVectorImpl &V) : Type(T_String) { - create(V.begin(), V.end()); - } - Expr(const llvm::formatv_object_base &V) : Expr(V.str()){}; - // Strings: types with reference semantics. - Expr(llvm::StringRef V) : Type(T_StringRef) { create(V); } - Expr(const char *V) : Type(T_StringRef) { create(V); } - Expr(std::nullptr_t) : Type(T_Null) {} - // Prevent implicit conversions to boolean. - template ::value>::type> - Expr(T B) : Type(T_Boolean) { - create(B); - } - // Numbers: arithmetic types that are not boolean. - template < - typename T, - typename = typename std::enable_if::value>::type, - typename = typename std::enable_if::value>::value>::type> - Expr(T D) : Type(T_Number) { - create(D); - } - // Types with a toJSON(const T&)->Expr function, found by ADL. - template ::value>> - Expr(const T &V) : Expr(toJSON(V)) {} - - Expr &operator=(const Expr &M) { - destroy(); - copyFrom(M); - return *this; - } - Expr &operator=(Expr &&M) { - destroy(); - moveFrom(std::move(M)); - return *this; - } - ~Expr() { destroy(); } - - Kind kind() const { - switch (Type) { - case T_Null: - return Null; - case T_Boolean: - return Boolean; - case T_Number: - return Number; - case T_String: - case T_StringRef: - return String; - case T_Object: - return Object; - case T_Array: - return Array; - } - llvm_unreachable("Unknown kind"); - } - - // Typed accessors return None/nullptr if the Expr is not of this type. - llvm::Optional asNull() const { - if (LLVM_LIKELY(Type == T_Null)) - return nullptr; - return llvm::None; - } - llvm::Optional asBoolean() const { - if (LLVM_LIKELY(Type == T_Boolean)) - return as(); - return llvm::None; - } - llvm::Optional asNumber() const { - if (LLVM_LIKELY(Type == T_Number)) - 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()); - if (LLVM_LIKELY(Type == T_StringRef)) - return as(); - return llvm::None; - } - const ObjectExpr *asObject() const { - return LLVM_LIKELY(Type == T_Object) ? &as() : nullptr; - } - ObjectExpr *asObject() { - return LLVM_LIKELY(Type == T_Object) ? &as() : nullptr; - } - const ArrayExpr *asArray() const { - return LLVM_LIKELY(Type == T_Array) ? &as() : nullptr; - } - ArrayExpr *asArray() { - return LLVM_LIKELY(Type == T_Array) ? &as() : nullptr; - } - - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Expr &); - -private: - void destroy(); - void copyFrom(const Expr &M); - // We allow moving from *const* Exprs, by marking all members as mutable! - // This hack is needed to support initializer-list syntax efficiently. - // (std::initializer_list is a container of const T). - void moveFrom(const Expr &&M); - - template void create(U &&... V) { - new (&as()) T(std::forward(V)...); - } - template T &as() const { - return *reinterpret_cast(Union.buffer); - } - - template - void print(llvm::raw_ostream &, const Indenter &) const; - friend struct llvm::format_provider; - - enum ExprType : char { - T_Null, - T_Boolean, - T_Number, - T_StringRef, - T_String, - T_Object, - T_Array, - }; - mutable ExprType Type; - -public: - // ObjectKey is a used to capture keys in Expr::ObjectExpr. Like Expr but: - // - only strings are allowed - // - it's optimized for the string literal case (Owned == nullptr) - class ObjectKey { - public: - ObjectKey(const char *S) : Data(S) {} - ObjectKey(llvm::StringRef S) : Data(S) {} - ObjectKey(std::string &&V) - : Owned(new std::string(std::move(V))), Data(*Owned) {} - ObjectKey(const std::string &V) : Owned(new std::string(V)), Data(*Owned) {} - ObjectKey(const llvm::SmallVectorImpl &V) - : ObjectKey(std::string(V.begin(), V.end())) {} - ObjectKey(const llvm::formatv_object_base &V) : ObjectKey(V.str()) {} - - ObjectKey(const ObjectKey &C) { *this = C; } - ObjectKey(ObjectKey &&C) : ObjectKey(static_cast(C)) {} - ObjectKey &operator=(const ObjectKey &C) { - if (C.Owned) { - Owned.reset(new std::string(*C.Owned)); - Data = *Owned; - } else { - Data = C.Data; - } - return *this; - } - ObjectKey &operator=(ObjectKey &&) = default; - - operator llvm::StringRef() const { return Data; } - - friend bool operator<(const ObjectKey &L, const ObjectKey &R) { - return L.Data < R.Data; - } - - // "cheating" move-constructor for moving from initializer_list. - ObjectKey(const ObjectKey &&V) { - Owned = std::move(V.Owned); - Data = V.Data; - } - - private: - mutable std::unique_ptr Owned; // mutable for cheating. - llvm::StringRef Data; - }; - - class ObjectExpr : public std::map { - public: - explicit ObjectExpr() {} - // Use a custom struct for list-init, because pair forces extra copies. - struct KV; - explicit ObjectExpr(std::initializer_list Properties); - - // Allow [] as if Expr was default-constructible as null. - Expr &operator[](const ObjectKey &K) { - return emplace(K, Expr(nullptr)).first->second; - } - Expr &operator[](ObjectKey &&K) { - return emplace(std::move(K), Expr(nullptr)).first->second; - } - - // Look up a property, returning nullptr if it doesn't exist. - json::Expr *get(const ObjectKey &K) { - auto I = find(K); - if (I == end()) - return nullptr; - return &I->second; - } - const json::Expr *get(const ObjectKey &K) const { - auto I = find(K); - if (I == end()) - return nullptr; - return &I->second; - } - // Typed accessors return None/nullptr if - // - the property doesn't exist - // - or it has the wrong type - llvm::Optional getNull(const ObjectKey &K) const { - if (auto *V = get(K)) - return V->asNull(); - return llvm::None; - } - llvm::Optional getBoolean(const ObjectKey &K) const { - if (auto *V = get(K)) - return V->asBoolean(); - return llvm::None; - } - llvm::Optional getNumber(const ObjectKey &K) const { - if (auto *V = get(K)) - 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(); - return llvm::None; - } - const ObjectExpr *getObject(const ObjectKey &K) const { - if (auto *V = get(K)) - return V->asObject(); - return nullptr; - } - ObjectExpr *getObject(const ObjectKey &K) { - if (auto *V = get(K)) - return V->asObject(); - return nullptr; - } - const ArrayExpr *getArray(const ObjectKey &K) const { - if (auto *V = get(K)) - return V->asArray(); - return nullptr; - } - ArrayExpr *getArray(const ObjectKey &K) { - if (auto *V = get(K)) - return V->asArray(); - return nullptr; - } - }; - - class ArrayExpr : public std::vector { - public: - explicit ArrayExpr() {} - explicit ArrayExpr(std::initializer_list Elements) { - reserve(Elements.size()); - for (const Expr &V : Elements) - emplace_back(std::move(V)); - }; - template explicit ArrayExpr(const Collection &C) { - for (const auto &V : C) - emplace_back(V); - } - - // Typed accessors return None/nullptr if the element has the wrong type. - llvm::Optional getNull(size_t I) const { - return (*this)[I].asNull(); - } - llvm::Optional getBoolean(size_t I) const { - return (*this)[I].asBoolean(); - } - 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(); - } - const ObjectExpr *getObject(size_t I) const { - return (*this)[I].asObject(); - } - ObjectExpr *getObject(size_t I) { return (*this)[I].asObject(); } - const ArrayExpr *getArray(size_t I) const { return (*this)[I].asArray(); } - ArrayExpr *getArray(size_t I) { return (*this)[I].asArray(); } - }; - -private: - mutable llvm::AlignedCharArrayUnion - Union; -}; - -bool operator==(const Expr &, const Expr &); -inline bool operator!=(const Expr &L, const Expr &R) { return !(L == R); } -inline bool operator==(const Expr::ObjectKey &L, const Expr::ObjectKey &R) { - return llvm::StringRef(L) == llvm::StringRef(R); -} -inline bool operator!=(const Expr::ObjectKey &L, const Expr::ObjectKey &R) { - return !(L == R); -} - -struct Expr::ObjectExpr::KV { - ObjectKey K; - Expr V; -}; - -inline Expr::ObjectExpr::ObjectExpr(std::initializer_list Properties) { - for (const auto &P : Properties) - emplace(std::move(P.K), std::move(P.V)); -} - -// Give Expr::{Object,Array} more convenient names for literal use. -using obj = Expr::ObjectExpr; -using ary = Expr::ArrayExpr; - -// Standard deserializers. -inline bool fromJSON(const json::Expr &E, std::string &Out) { - if (auto S = E.asString()) { - Out = *S; - return true; - } - return false; -} -inline bool fromJSON(const json::Expr &E, int &Out) { - if (auto S = E.asInteger()) { - Out = *S; - return true; - } - return false; -} -inline bool fromJSON(const json::Expr &E, double &Out) { - if (auto S = E.asNumber()) { - Out = *S; - return true; - } - return false; -} -inline bool fromJSON(const json::Expr &E, bool &Out) { - if (auto S = E.asBoolean()) { - Out = *S; - return true; - } - return false; -} -template -bool fromJSON(const json::Expr &E, llvm::Optional &Out) { - if (E.asNull()) { - Out = llvm::None; - return true; - } - T Result; - if (!fromJSON(E, Result)) - return false; - Out = std::move(Result); - return true; -} -template bool fromJSON(const json::Expr &E, std::vector &Out) { - if (auto *A = E.asArray()) { - Out.clear(); - Out.resize(A->size()); - for (size_t I = 0; I < A->size(); ++I) - if (!fromJSON((*A)[I], Out[I])) - return false; - return true; - } - return false; -} -template -bool fromJSON(const json::Expr &E, std::map &Out) { - if (auto *O = E.asObject()) { - Out.clear(); - for (const auto &KV : *O) - if (!fromJSON(KV.second, Out[llvm::StringRef(KV.first)])) - return false; - return true; - } - return false; -} - -template -json::Expr toJSON(const llvm::Optional& Opt) { - return Opt ? json::Expr(*Opt) : json::Expr(nullptr); -} - -// Helper for mapping JSON objects onto protocol structs. -// See file header for example. -class ObjectMapper { -public: - ObjectMapper(const json::Expr &E) : O(E.asObject()) {} - - // True if the expression is an object. - // Must be checked before calling map(). - operator bool() { return O; } - - // Maps a property to a field, if it exists. - template bool map(const char *Prop, T &Out) { - assert(*this && "Must check this is an object before calling map()"); - if (const json::Expr *E = O->get(Prop)) - return fromJSON(*E, Out); - return false; - } - - // Optional requires special handling, because missing keys are OK. - template bool map(const char *Prop, llvm::Optional &Out) { - assert(*this && "Must check this is an object before calling map()"); - if (const json::Expr *E = O->get(Prop)) - return fromJSON(*E, Out); - Out = llvm::None; - return true; - } - -private: - const json::obj *O; -}; - -llvm::Expected parse(llvm::StringRef JSON); - -class ParseError : public llvm::ErrorInfo { - const char *Msg; - unsigned Line, Column, Offset; - -public: - static char ID; - ParseError(const char *Msg, unsigned Line, unsigned Column, unsigned Offset) - : Msg(Msg), Line(Line), Column(Column), Offset(Offset) {} - void log(llvm::raw_ostream &OS) const override { - OS << llvm::formatv("[{0}:{1}, byte={2}]: {3}", Line, Column, Offset, Msg); - } - std::error_code convertToErrorCode() const override { - return llvm::inconvertibleErrorCode(); - } -}; - -} // namespace json -} // namespace clangd -} // namespace clang - -namespace llvm { -template <> struct format_provider { - static void format(const clang::clangd::json::Expr &, raw_ostream &, - StringRef); -}; -} // namespace llvm - -#endif Index: clangd/JSONExpr.cpp =================================================================== --- clangd/JSONExpr.cpp +++ clangd/JSONExpr.cpp @@ -1,554 +0,0 @@ -//=== JSONExpr.cpp - JSON expressions, parsing and serialization - C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===---------------------------------------------------------------------===// - -#include "JSONExpr.h" -#include "llvm/Support/Format.h" -#include - -using namespace llvm; -namespace clang { -namespace clangd { -namespace json { - -void Expr::copyFrom(const Expr &M) { - Type = M.Type; - switch (Type) { - case T_Null: - case T_Boolean: - case T_Number: - memcpy(Union.buffer, M.Union.buffer, sizeof(Union.buffer)); - break; - case T_StringRef: - create(M.as()); - break; - case T_String: - create(M.as()); - break; - case T_Object: - create(M.as()); - break; - case T_Array: - create(M.as()); - break; - } -} - -void Expr::moveFrom(const Expr &&M) { - Type = M.Type; - switch (Type) { - case T_Null: - case T_Boolean: - case T_Number: - memcpy(Union.buffer, M.Union.buffer, sizeof(Union.buffer)); - break; - case T_StringRef: - create(M.as()); - break; - case T_String: - create(std::move(M.as())); - M.Type = T_Null; - break; - case T_Object: - create(std::move(M.as())); - M.Type = T_Null; - break; - case T_Array: - create(std::move(M.as())); - M.Type = T_Null; - break; - } -} - -void Expr::destroy() { - switch (Type) { - case T_Null: - case T_Boolean: - case T_Number: - break; - case T_StringRef: - as().~StringRef(); - break; - case T_String: - as().~basic_string(); - break; - case T_Object: - as().~ObjectExpr(); - break; - case T_Array: - as().~ArrayExpr(); - break; - } -} - -namespace { -// Simple recursive-descent JSON parser. -class Parser { -public: - Parser(StringRef JSON) - : Start(JSON.begin()), P(JSON.begin()), End(JSON.end()) {} - - bool parseExpr(Expr &Out); - - bool assertEnd() { - eatWhitespace(); - if (P == End) - return true; - return parseError("Text after end of document"); - } - - Error takeError() { - assert(Err); - return std::move(*Err); - } - -private: - void eatWhitespace() { - while (P != End && (*P == ' ' || *P == '\r' || *P == '\n' || *P == '\t')) - ++P; - } - - // On invalid syntax, parseX() functions return false and set Err. - bool parseNumber(char First, double &Out); - bool parseString(std::string &Out); - bool parseUnicode(std::string &Out); - bool parseError(const char *Msg); // always returns false - - char next() { return P == End ? 0 : *P++; } - char peek() { return P == End ? 0 : *P; } - static bool isNumber(char C) { - return C == '0' || C == '1' || C == '2' || C == '3' || C == '4' || - C == '5' || C == '6' || C == '7' || C == '8' || C == '9' || - C == 'e' || C == 'E' || C == '+' || C == '-' || C == '.'; - } - static void encodeUtf8(uint32_t Rune, std::string &Out); - - Optional Err; - const char *Start, *P, *End; -}; - -bool Parser::parseExpr(Expr &Out) { - eatWhitespace(); - if (P == End) - return parseError("Unexpected EOF"); - switch (char C = next()) { - // Bare null/true/false are easy - first char identifies them. - case 'n': - Out = nullptr; - return (next() == 'u' && next() == 'l' && next() == 'l') || - parseError("Invalid bareword"); - case 't': - Out = true; - return (next() == 'r' && next() == 'u' && next() == 'e') || - parseError("Invalid bareword"); - case 'f': - Out = false; - return (next() == 'a' && next() == 'l' && next() == 's' && next() == 'e') || - parseError("Invalid bareword"); - case '"': { - std::string S; - if (parseString(S)) { - Out = std::move(S); - return true; - } - return false; - } - case '[': { - Out = json::ary{}; - json::ary &A = *Out.asArray(); - eatWhitespace(); - if (peek() == ']') { - ++P; - return true; - } - for (;;) { - A.emplace_back(nullptr); - if (!parseExpr(A.back())) - return false; - eatWhitespace(); - switch (next()) { - case ',': - eatWhitespace(); - continue; - case ']': - return true; - default: - return parseError("Expected , or ] after array element"); - } - } - } - case '{': { - Out = json::obj{}; - json::obj &O = *Out.asObject(); - eatWhitespace(); - if (peek() == '}') { - ++P; - return true; - } - for (;;) { - if (next() != '"') - return parseError("Expected object key"); - std::string K; - if (!parseString(K)) - return false; - eatWhitespace(); - if (next() != ':') - return parseError("Expected : after object key"); - eatWhitespace(); - if (!parseExpr(O[std::move(K)])) - return false; - eatWhitespace(); - switch (next()) { - case ',': - eatWhitespace(); - continue; - case '}': - return true; - default: - return parseError("Expected , or } after object property"); - } - } - } - default: - if (isNumber(C)) { - double Num; - if (parseNumber(C, Num)) { - Out = Num; - return true; - } else { - return false; - } - } - return parseError("Expected JSON value"); - } -} - -bool Parser::parseNumber(char First, double &Out) { - SmallString<24> S; - S.push_back(First); - while (isNumber(peek())) - S.push_back(next()); - char *End; - Out = std::strtod(S.c_str(), &End); - return End == S.end() || parseError("Invalid number"); -} - -bool Parser::parseString(std::string &Out) { - // leading quote was already consumed. - for (char C = next(); C != '"'; C = next()) { - if (LLVM_UNLIKELY(P == End)) - return parseError("Unterminated string"); - if (LLVM_UNLIKELY((C & 0x1f) == C)) - return parseError("Control character in string"); - if (LLVM_LIKELY(C != '\\')) { - Out.push_back(C); - continue; - } - // Handle escape sequence. - switch (C = next()) { - case '"': - case '\\': - case '/': - Out.push_back(C); - break; - case 'b': - Out.push_back('\b'); - break; - case 'f': - Out.push_back('\f'); - break; - case 'n': - Out.push_back('\n'); - break; - case 'r': - Out.push_back('\r'); - break; - case 't': - Out.push_back('\t'); - break; - case 'u': - if (!parseUnicode(Out)) - return false; - break; - default: - return parseError("Invalid escape sequence"); - } - } - return true; -} - -void Parser::encodeUtf8(uint32_t Rune, std::string &Out) { - if (Rune <= 0x7F) { - Out.push_back(Rune & 0x7F); - } else if (Rune <= 0x7FF) { - uint8_t FirstByte = 0xC0 | ((Rune & 0x7C0) >> 6); - uint8_t SecondByte = 0x80 | (Rune & 0x3F); - Out.push_back(FirstByte); - Out.push_back(SecondByte); - } else if (Rune <= 0xFFFF) { - uint8_t FirstByte = 0xE0 | ((Rune & 0xF000) >> 12); - uint8_t SecondByte = 0x80 | ((Rune & 0xFC0) >> 6); - uint8_t ThirdByte = 0x80 | (Rune & 0x3F); - Out.push_back(FirstByte); - Out.push_back(SecondByte); - Out.push_back(ThirdByte); - } else if (Rune <= 0x10FFFF) { - uint8_t FirstByte = 0xF0 | ((Rune & 0x1F0000) >> 18); - uint8_t SecondByte = 0x80 | ((Rune & 0x3F000) >> 12); - uint8_t ThirdByte = 0x80 | ((Rune & 0xFC0) >> 6); - uint8_t FourthByte = 0x80 | (Rune & 0x3F); - Out.push_back(FirstByte); - Out.push_back(SecondByte); - Out.push_back(ThirdByte); - Out.push_back(FourthByte); - } else { - llvm_unreachable("Invalid codepoint"); - } -} - -// Parse a \uNNNN escape sequence, the \u have already been consumed. -// May parse multiple escapes in the presence of surrogate pairs. -bool Parser::parseUnicode(std::string &Out) { - // Note that invalid unicode is not a JSON error. It gets replaced by U+FFFD. - auto Invalid = [&] { Out.append(/* UTF-8 */ {'\xef', '\xbf', '\xbd'}); }; - auto Parse4Hex = [this](uint16_t &Out) { - Out = 0; - char Bytes[] = {next(), next(), next(), next()}; - for (unsigned char C : Bytes) { - if (!std::isxdigit(C)) - return parseError("Invalid \\u escape sequence"); - Out <<= 4; - Out |= (C > '9') ? (C & ~0x20) - 'A' + 10 : (C - '0'); - } - return true; - }; - uint16_t First; - if (!Parse4Hex(First)) - return false; - - // We loop to allow proper surrogate-pair error handling. - while (true) { - if (LLVM_LIKELY(First < 0xD800 || First >= 0xE000)) { // BMP. - encodeUtf8(First, Out); - return true; - } - - if (First >= 0xDC00) { - Invalid(); // Lone trailing surrogate. - return true; - } - - // We have a leading surrogate, and need a trailing one. - // Don't advance P: a lone surrogate is valid JSON (but invalid unicode) - if (P + 2 > End || *P != '\\' || *(P + 1) != 'u') { - Invalid(); // Lone leading not followed by \u... - return true; - } - P += 2; - uint16_t Second; - if (!Parse4Hex(Second)) - return false; - if (Second < 0xDC00 || Second >= 0xE000) { - Invalid(); // Leading surrogate not followed by trailing. - First = Second; // Second escape still needs to be processed. - continue; - } - - // Valid surrogate pair. - encodeUtf8(0x10000 | ((First - 0xD800) << 10) | (Second - 0xDC00), Out); - return true; - } -} - -bool Parser::parseError(const char *Msg) { - int Line = 1; - const char *StartOfLine = Start; - for (const char *X = Start; X < P; ++X) { - if (*X == 0x0A) { - ++Line; - StartOfLine = X + 1; - } - } - Err.emplace( - llvm::make_unique(Msg, Line, P - StartOfLine, P - Start)); - return false; -} -} // namespace - -Expected parse(StringRef JSON) { - Parser P(JSON); - json::Expr E = nullptr; - if (P.parseExpr(E)) - if (P.assertEnd()) - return std::move(E); - return P.takeError(); -} -char ParseError::ID = 0; - -} // namespace json -} // namespace clangd -} // namespace clang - -namespace { -void quote(llvm::raw_ostream &OS, llvm::StringRef S) { - OS << '\"'; - for (unsigned char C : S) { - if (C == 0x22 || C == 0x5C) - OS << '\\'; - if (C >= 0x20) { - OS << C; - continue; - } - OS << '\\'; - switch (C) { - // A few characters are common enough to make short escapes worthwhile. - case '\t': - OS << 't'; - break; - case '\n': - OS << 'n'; - break; - case '\r': - OS << 'r'; - break; - default: - OS << 'u'; - llvm::write_hex(OS, C, llvm::HexPrintStyle::Lower, 4); - break; - } - } - OS << '\"'; -} - -enum IndenterAction { - Indent, - Outdent, - Newline, - Space, -}; -} // namespace - -// Prints JSON. The indenter can be used to control formatting. -template -void clang::clangd::json::Expr::print(raw_ostream &OS, - const Indenter &I) const { - switch (Type) { - case T_Null: - OS << "null"; - break; - case T_Boolean: - OS << (as() ? "true" : "false"); - break; - case T_Number: - OS << format("%g", as()); - break; - case T_StringRef: - quote(OS, as()); - break; - case T_String: - quote(OS, as()); - break; - case T_Object: { - bool Comma = false; - OS << '{'; - I(Indent); - for (const auto &P : as()) { - if (Comma) - OS << ','; - Comma = true; - I(Newline); - quote(OS, P.first); - OS << ':'; - I(Space); - P.second.print(OS, I); - } - I(Outdent); - if (Comma) - I(Newline); - OS << '}'; - break; - } - case T_Array: { - bool Comma = false; - OS << '['; - I(Indent); - for (const auto &E : as()) { - if (Comma) - OS << ','; - Comma = true; - I(Newline); - E.print(OS, I); - } - I(Outdent); - if (Comma) - I(Newline); - OS << ']'; - break; - } - } -} - -namespace clang { -namespace clangd { -namespace json { -llvm::raw_ostream &operator<<(raw_ostream &OS, const Expr &E) { - E.print(OS, [](IndenterAction A) { /*ignore*/ }); - return OS; -} - -bool operator==(const Expr &L, const Expr &R) { - if (L.kind() != R.kind()) - return false; - switch (L.kind()) { - case Expr::Null: - return *L.asNull() == *R.asNull(); - case Expr::Boolean: - return *L.asBoolean() == *R.asBoolean(); - case Expr::Number: - return *L.asNumber() == *R.asNumber(); - case Expr::String: - return *L.asString() == *R.asString(); - case Expr::Array: - return *L.asArray() == *R.asArray(); - case Expr::Object: - return *L.asObject() == *R.asObject(); - } - llvm_unreachable("Unknown expression kind"); -} -} // namespace json -} // namespace clangd -} // namespace clang - -void llvm::format_provider::format( - const clang::clangd::json::Expr &E, raw_ostream &OS, StringRef Options) { - if (Options.empty()) { - OS << E; - return; - } - unsigned IndentAmount = 0; - if (Options.getAsInteger(/*Radix=*/10, IndentAmount)) - assert(false && "json::Expr format options should be an integer"); - unsigned IndentLevel = 0; - E.print(OS, [&](IndenterAction A) { - switch (A) { - case Newline: - OS << '\n'; - OS.indent(IndentLevel); - break; - case Space: - OS << ' '; - break; - case Indent: - IndentLevel += IndentAmount; - break; - case Outdent: - IndentLevel -= IndentAmount; - break; - }; - }); -} Index: clangd/JSONRPCDispatcher.h =================================================================== --- clangd/JSONRPCDispatcher.h +++ clangd/JSONRPCDispatcher.h @@ -10,13 +10,13 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H -#include "JSONExpr.h" #include "Logger.h" #include "Protocol.h" #include "Trace.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" +#include "llvm/Support/JSON.h" #include #include @@ -34,7 +34,7 @@ : Pretty(Pretty), Outs(Outs), Logs(Logs), InputMirror(InputMirror) {} /// Emit a JSONRPC message. - void writeMessage(const json::Expr &Result); + void writeMessage(const llvm::json::Value &Result); /// Write a line to the logging stream. void log(const Twine &Message) override; @@ -57,20 +57,20 @@ /// Sends a successful reply. /// Current context must derive from JSONRPCDispatcher::Handler. -void reply(json::Expr &&Result); +void reply(llvm::json::Value &&Result); /// Sends an error response to the client, and logs it. /// Current context must derive from JSONRPCDispatcher::Handler. void replyError(ErrorCode code, const llvm::StringRef &Message); /// Sends a request to the client. /// Current context must derive from JSONRPCDispatcher::Handler. -void call(llvm::StringRef Method, json::Expr &&Params); +void call(llvm::StringRef Method, llvm::json::Value &&Params); /// Main JSONRPC entry point. This parses the JSONRPC "header" and calls the /// registered Handler for the method received. 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. @@ -81,7 +81,7 @@ void registerHandler(StringRef Method, Handler H); /// Parses a JSONRPC message and calls the Handler for it. - bool call(const json::Expr &Message, JSONOutput &Out) const; + bool call(const llvm::json::Value &Message, JSONOutput &Out) const; private: llvm::StringMap Handlers; Index: clangd/JSONRPCDispatcher.cpp =================================================================== --- clangd/JSONRPCDispatcher.cpp +++ clangd/JSONRPCDispatcher.cpp @@ -8,29 +8,30 @@ //===----------------------------------------------------------------------===// #include "JSONRPCDispatcher.h" -#include "JSONExpr.h" #include "ProtocolHandlers.h" #include "Trace.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Chrono.h" #include "llvm/Support/Errno.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/SourceMgr.h" #include +using namespace llvm; using namespace clang; using namespace clangd; namespace { -static Key RequestID; +static Key RequestID; static Key RequestOut; // When tracing, we trace a request and attach the repsonse in reply(). // Because the Span isn't available, we find the current request using Context. class RequestSpan { - RequestSpan(json::obj *Args) : Args(Args) {} + RequestSpan(llvm::json::Object *Args) : Args(Args) {} std::mutex Mu; - json::obj *Args; + llvm::json::Object *Args; static Key> RSKey; public: @@ -41,7 +42,7 @@ } // If there's an enclosing request and the tracer is interested, calls \p F - // with a json::obj where request info can be added. + // with a json::Object where request info can be added. template static void attach(Func &&F) { auto *RequestArgs = Context::current().get(RSKey); if (!RequestArgs || !*RequestArgs || !(*RequestArgs)->Args) @@ -53,7 +54,7 @@ Key> RequestSpan::RSKey; } // namespace -void JSONOutput::writeMessage(const json::Expr &Message) { +void JSONOutput::writeMessage(const json::Value &Message) { std::string S; llvm::raw_string_ostream OS(S); if (Pretty) @@ -86,16 +87,16 @@ InputMirror->flush(); } -void clangd::reply(json::Expr &&Result) { +void clangd::reply(json::Value &&Result) { auto ID = Context::current().get(RequestID); if (!ID) { log("Attempted to reply to a notification!"); return; } - RequestSpan::attach([&](json::obj &Args) { Args["Reply"] = Result; }); + RequestSpan::attach([&](json::Object &Args) { Args["Reply"] = Result; }); Context::current() .getExisting(RequestOut) - ->writeMessage(json::obj{ + ->writeMessage(json::Object{ {"jsonrpc", "2.0"}, {"id", *ID}, {"result", std::move(Result)}, @@ -104,32 +105,32 @@ void clangd::replyError(ErrorCode code, const llvm::StringRef &Message) { log("Error " + Twine(static_cast(code)) + ": " + Message); - RequestSpan::attach([&](json::obj &Args) { - Args["Error"] = - json::obj{{"code", static_cast(code)}, {"message", Message.str()}}; + RequestSpan::attach([&](json::Object &Args) { + Args["Error"] = json::Object{{"code", static_cast(code)}, + {"message", Message.str()}}; }); if (auto ID = Context::current().get(RequestID)) { Context::current() .getExisting(RequestOut) - ->writeMessage(json::obj{ + ->writeMessage(json::Object{ {"jsonrpc", "2.0"}, {"id", *ID}, - {"error", - json::obj{{"code", static_cast(code)}, {"message", Message}}}, + {"error", json::Object{{"code", static_cast(code)}, + {"message", Message}}}, }); } } -void clangd::call(StringRef Method, json::Expr &&Params) { +void clangd::call(StringRef Method, json::Value &&Params) { // FIXME: Generate/Increment IDs for every request so that we can get proper // replies once we need to. - RequestSpan::attach([&](json::obj &Args) { - Args["Call"] = json::obj{{"method", Method.str()}, {"params", Params}}; + RequestSpan::attach([&](json::Object &Args) { + Args["Call"] = json::Object{{"method", Method.str()}, {"params", Params}}; }); Context::current() .getExisting(RequestOut) - ->writeMessage(json::obj{ + ->writeMessage(json::Object{ {"jsonrpc", "2.0"}, {"id", 1}, {"method", Method}, @@ -142,13 +143,14 @@ Handlers[Method] = std::move(H); } -bool JSONRPCDispatcher::call(const json::Expr &Message, JSONOutput &Out) const { +bool JSONRPCDispatcher::call(const json::Value &Message, + JSONOutput &Out) const { // Message must be an object with "jsonrpc":"2.0". - auto *Object = Message.asObject(); + auto *Object = Message.getAsObject(); if (!Object || Object->getString("jsonrpc") != Optional("2.0")) return false; // ID may be any JSON value. If absent, this is a notification. - llvm::Optional ID; + llvm::Optional ID; if (auto *I = Object->get("id")) ID = std::move(*I); // Method must be given. @@ -156,7 +158,7 @@ if (!Method) return false; // Params should be given, use null if not. - json::Expr Params = nullptr; + json::Value Params = nullptr; if (auto *P = Object->get("params")) Params = std::move(*P); Index: clangd/Protocol.h =================================================================== --- clangd/Protocol.h +++ clangd/Protocol.h @@ -14,7 +14,7 @@ // when they're needed. // // Each struct has a toJSON and fromJSON function, that converts between -// the struct and a JSON representation. (See JSONExpr.h) +// the struct and a JSON representation. (See JSON.h) // // Some structs also have operator<< serialization. This is for debugging and // tests, and is not generally machine-readable. @@ -24,9 +24,9 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOL_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOL_H -#include "JSONExpr.h" #include "URI.h" #include "llvm/ADT/Optional.h" +#include "llvm/Support/JSON.h" #include #include #include @@ -76,15 +76,15 @@ }; /// Serialize/deserialize \p URIForFile to/from a string URI. -json::Expr toJSON(const URIForFile &U); -bool fromJSON(const json::Expr &, URIForFile &); +llvm::json::Value toJSON(const URIForFile &U); +bool fromJSON(const llvm::json::Value &, URIForFile &); struct TextDocumentIdentifier { /// The text document's URI. URIForFile uri; }; -json::Expr toJSON(const TextDocumentIdentifier &); -bool fromJSON(const json::Expr &, TextDocumentIdentifier &); +llvm::json::Value toJSON(const TextDocumentIdentifier &); +bool fromJSON(const llvm::json::Value &, TextDocumentIdentifier &); struct Position { /// Line position in a document (zero-based). @@ -111,8 +111,8 @@ std::tie(RHS.line, RHS.character); } }; -bool fromJSON(const json::Expr &, Position &); -json::Expr toJSON(const Position &); +bool fromJSON(const llvm::json::Value &, Position &); +llvm::json::Value toJSON(const Position &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Position &); struct Range { @@ -134,8 +134,8 @@ bool contains(Position Pos) const { return start <= Pos && Pos < end; } }; -bool fromJSON(const json::Expr &, Range &); -json::Expr toJSON(const Range &); +bool fromJSON(const llvm::json::Value &, Range &); +llvm::json::Value toJSON(const Range &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Range &); struct Location { @@ -155,13 +155,13 @@ return std::tie(LHS.uri, LHS.range) < std::tie(RHS.uri, RHS.range); } }; -json::Expr toJSON(const Location &); +llvm::json::Value toJSON(const Location &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Location &); struct Metadata { std::vector extraFlags; }; -bool fromJSON(const json::Expr &, Metadata &); +bool fromJSON(const llvm::json::Value &, Metadata &); struct TextEdit { /// The range of the text document to be manipulated. To insert @@ -172,8 +172,8 @@ /// empty string. std::string newText; }; -bool fromJSON(const json::Expr &, TextEdit &); -json::Expr toJSON(const TextEdit &); +bool fromJSON(const llvm::json::Value &, TextEdit &); +llvm::json::Value toJSON(const TextEdit &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TextEdit &); struct TextDocumentItem { @@ -189,17 +189,17 @@ /// The content of the opened text document. std::string text; }; -bool fromJSON(const json::Expr &, TextDocumentItem &); +bool fromJSON(const llvm::json::Value &, TextDocumentItem &); enum class TraceLevel { Off = 0, Messages = 1, Verbose = 2, }; -bool fromJSON(const json::Expr &E, TraceLevel &Out); +bool fromJSON(const llvm::json::Value &E, TraceLevel &Out); struct NoParams {}; -inline bool fromJSON(const json::Expr &, NoParams &) { return true; } +inline bool fromJSON(const llvm::json::Value &, NoParams &) { return true; } using ShutdownParams = NoParams; using ExitParams = NoParams; @@ -227,7 +227,7 @@ // NOTE: not used by clangd at the moment. // std::vector documentationFormat; }; -bool fromJSON(const json::Expr &, CompletionItemClientCapabilities &); +bool fromJSON(const llvm::json::Value &, CompletionItemClientCapabilities &); struct CompletionClientCapabilities { /// Whether completion supports dynamic registration. @@ -241,7 +241,7 @@ /// `textDocument/completion` request. bool contextSupport = false; }; -bool fromJSON(const json::Expr &, CompletionClientCapabilities &); +bool fromJSON(const llvm::json::Value &, CompletionClientCapabilities &); /// A symbol kind. enum class SymbolKind { @@ -277,7 +277,7 @@ constexpr auto SymbolKindMax = static_cast(SymbolKind::TypeParameter); using SymbolKindBitset = std::bitset; -bool fromJSON(const json::Expr &, SymbolKind &); +bool fromJSON(const llvm::json::Value &, SymbolKind &); struct SymbolKindCapabilities { /// The SymbolKinds that the client supports. If not set, the client only @@ -285,8 +285,8 @@ /// value. llvm::Optional> valueSet; }; -bool fromJSON(const json::Expr &, std::vector &); -bool fromJSON(const json::Expr &, SymbolKindCapabilities &); +bool fromJSON(const llvm::json::Value &, std::vector &); +bool fromJSON(const llvm::json::Value &, SymbolKindCapabilities &); SymbolKind adjustKindToCapability(SymbolKind Kind, SymbolKindBitset &supportedSymbolKinds); @@ -294,7 +294,7 @@ /// Capabilities SymbolKind. llvm::Optional symbolKind; }; -bool fromJSON(const json::Expr &, WorkspaceSymbolCapabilities &); +bool fromJSON(const llvm::json::Value &, WorkspaceSymbolCapabilities &); // FIXME: most of the capabilities are missing from this struct. Only the ones // used by clangd are currently there. @@ -302,7 +302,7 @@ /// Capabilities specific to `workspace/symbol`. llvm::Optional symbol; }; -bool fromJSON(const json::Expr &, WorkspaceClientCapabilities &); +bool fromJSON(const llvm::json::Value &, WorkspaceClientCapabilities &); // FIXME: most of the capabilities are missing from this struct. Only the ones // used by clangd are currently there. @@ -310,7 +310,7 @@ /// Capabilities specific to the `textDocument/completion` CompletionClientCapabilities completion; }; -bool fromJSON(const json::Expr &, TextDocumentClientCapabilities &); +bool fromJSON(const llvm::json::Value &, TextDocumentClientCapabilities &); struct ClientCapabilities { // Workspace specific client capabilities. @@ -320,7 +320,7 @@ TextDocumentClientCapabilities textDocument; }; -bool fromJSON(const json::Expr &, ClientCapabilities &); +bool fromJSON(const llvm::json::Value &, ClientCapabilities &); struct InitializeParams { /// The process Id of the parent process that started @@ -349,7 +349,7 @@ /// The initial trace setting. If omitted trace is disabled ('off'). llvm::Optional trace; }; -bool fromJSON(const json::Expr &, InitializeParams &); +bool fromJSON(const llvm::json::Value &, InitializeParams &); struct DidOpenTextDocumentParams { /// The document that was opened. @@ -358,13 +358,13 @@ /// Extension storing per-file metadata, such as compilation flags. llvm::Optional metadata; }; -bool fromJSON(const json::Expr &, DidOpenTextDocumentParams &); +bool fromJSON(const llvm::json::Value &, DidOpenTextDocumentParams &); struct DidCloseTextDocumentParams { /// The document that was closed. TextDocumentIdentifier textDocument; }; -bool fromJSON(const json::Expr &, DidCloseTextDocumentParams &); +bool fromJSON(const llvm::json::Value &, DidCloseTextDocumentParams &); struct TextDocumentContentChangeEvent { /// The range of the document that changed. @@ -376,7 +376,7 @@ /// The new text of the range/document. std::string text; }; -bool fromJSON(const json::Expr &, TextDocumentContentChangeEvent &); +bool fromJSON(const llvm::json::Value &, TextDocumentContentChangeEvent &); struct DidChangeTextDocumentParams { /// The document that did change. The version number points @@ -393,7 +393,7 @@ /// This is a clangd extension. llvm::Optional wantDiagnostics; }; -bool fromJSON(const json::Expr &, DidChangeTextDocumentParams &); +bool fromJSON(const llvm::json::Value &, DidChangeTextDocumentParams &); enum class FileChangeType { /// The file got created. @@ -403,7 +403,7 @@ /// The file got deleted. Deleted = 3 }; -bool fromJSON(const json::Expr &E, FileChangeType &Out); +bool fromJSON(const llvm::json::Value &E, FileChangeType &Out); struct FileEvent { /// The file's URI. @@ -411,27 +411,27 @@ /// The change type. FileChangeType type = FileChangeType::Created; }; -bool fromJSON(const json::Expr &, FileEvent &); +bool fromJSON(const llvm::json::Value &, FileEvent &); struct DidChangeWatchedFilesParams { /// The actual file events. std::vector changes; }; -bool fromJSON(const json::Expr &, DidChangeWatchedFilesParams &); +bool fromJSON(const llvm::json::Value &, DidChangeWatchedFilesParams &); /// Clangd extension to manage a workspace/didChangeConfiguration notification /// since the data received is described as 'any' type in LSP. struct ClangdConfigurationParamsChange { llvm::Optional compilationDatabasePath; }; -bool fromJSON(const json::Expr &, ClangdConfigurationParamsChange &); +bool fromJSON(const llvm::json::Value &, ClangdConfigurationParamsChange &); struct DidChangeConfigurationParams { // We use this predefined struct because it is easier to use // than the protocol specified type of 'any'. ClangdConfigurationParamsChange settings; }; -bool fromJSON(const json::Expr &, DidChangeConfigurationParams &); +bool fromJSON(const llvm::json::Value &, DidChangeConfigurationParams &); struct FormattingOptions { /// Size of a tab in spaces. @@ -440,8 +440,8 @@ /// Prefer spaces over tabs. bool insertSpaces = false; }; -bool fromJSON(const json::Expr &, FormattingOptions &); -json::Expr toJSON(const FormattingOptions &); +bool fromJSON(const llvm::json::Value &, FormattingOptions &); +llvm::json::Value toJSON(const FormattingOptions &); struct DocumentRangeFormattingParams { /// The document to format. @@ -453,7 +453,7 @@ /// The format options FormattingOptions options; }; -bool fromJSON(const json::Expr &, DocumentRangeFormattingParams &); +bool fromJSON(const llvm::json::Value &, DocumentRangeFormattingParams &); struct DocumentOnTypeFormattingParams { /// The document to format. @@ -468,7 +468,7 @@ /// The format options. FormattingOptions options; }; -bool fromJSON(const json::Expr &, DocumentOnTypeFormattingParams &); +bool fromJSON(const llvm::json::Value &, DocumentOnTypeFormattingParams &); struct DocumentFormattingParams { /// The document to format. @@ -477,13 +477,13 @@ /// The format options FormattingOptions options; }; -bool fromJSON(const json::Expr &, DocumentFormattingParams &); +bool fromJSON(const llvm::json::Value &, DocumentFormattingParams &); struct DocumentSymbolParams { // The text document to find symbols in. TextDocumentIdentifier textDocument; }; -bool fromJSON(const json::Expr &, DocumentSymbolParams &); +bool fromJSON(const llvm::json::Value &, DocumentSymbolParams &); struct Diagnostic { /// The range at which the message applies. @@ -516,14 +516,14 @@ return std::tie(LHS.range, LHS.message) < std::tie(RHS.range, RHS.message); } }; -bool fromJSON(const json::Expr &, Diagnostic &); +bool fromJSON(const llvm::json::Value &, Diagnostic &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Diagnostic &); struct CodeActionContext { /// An array of diagnostics. std::vector diagnostics; }; -bool fromJSON(const json::Expr &, CodeActionContext &); +bool fromJSON(const llvm::json::Value &, CodeActionContext &); struct CodeActionParams { /// The document in which the command was invoked. @@ -535,7 +535,7 @@ /// Context carrying additional information. CodeActionContext context; }; -bool fromJSON(const json::Expr &, CodeActionParams &); +bool fromJSON(const llvm::json::Value &, CodeActionParams &); struct WorkspaceEdit { /// Holds changes to existing resources. @@ -544,8 +544,8 @@ /// Note: "documentChanges" is not currently used because currently there is /// no support for versioned edits. }; -bool fromJSON(const json::Expr &, WorkspaceEdit &); -json::Expr toJSON(const WorkspaceEdit &WE); +bool fromJSON(const llvm::json::Value &, WorkspaceEdit &); +llvm::json::Value toJSON(const WorkspaceEdit &WE); /// Exact commands are not specified in the protocol so we define the /// ones supported by Clangd here. The protocol specifies the command arguments @@ -565,13 +565,13 @@ // Arguments llvm::Optional workspaceEdit; }; -bool fromJSON(const json::Expr &, ExecuteCommandParams &); +bool fromJSON(const llvm::json::Value &, ExecuteCommandParams &); struct Command : public ExecuteCommandParams { std::string title; }; -json::Expr toJSON(const Command &C); +llvm::json::Value toJSON(const Command &C); /// Represents information about programming constructs like variables, classes, /// interfaces etc. @@ -588,7 +588,7 @@ /// The name of the symbol containing this symbol. std::string containerName; }; -json::Expr toJSON(const SymbolInformation &); +llvm::json::Value toJSON(const SymbolInformation &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SymbolInformation &); /// The parameters of a Workspace Symbol Request. @@ -596,12 +596,12 @@ /// A non-empty query string std::string query; }; -bool fromJSON(const json::Expr &, WorkspaceSymbolParams &); +bool fromJSON(const llvm::json::Value &, WorkspaceSymbolParams &); struct ApplyWorkspaceEditParams { WorkspaceEdit edit; }; -json::Expr toJSON(const ApplyWorkspaceEditParams &); +llvm::json::Value toJSON(const ApplyWorkspaceEditParams &); struct TextDocumentPositionParams { /// The text document. @@ -610,7 +610,7 @@ /// The position inside the text document. Position position; }; -bool fromJSON(const json::Expr &, TextDocumentPositionParams &); +bool fromJSON(const llvm::json::Value &, TextDocumentPositionParams &); enum class MarkupKind { PlainText, @@ -621,7 +621,7 @@ MarkupKind kind = MarkupKind::PlainText; std::string value; }; -json::Expr toJSON(const MarkupContent &MC); +llvm::json::Value toJSON(const MarkupContent &MC); struct Hover { /// The hover's content @@ -631,7 +631,7 @@ /// that is used to visualize a hover, e.g. by changing the background color. llvm::Optional range; }; -json::Expr toJSON(const Hover &H); +llvm::json::Value toJSON(const Hover &H); /// The kind of a completion entry. enum class CompletionItemKind { @@ -724,7 +724,7 @@ // data?: any - A data entry field that is preserved on a completion item // between a completion and a completion resolve request. }; -json::Expr toJSON(const CompletionItem &); +llvm::json::Value toJSON(const CompletionItem &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const CompletionItem &); bool operator<(const CompletionItem &, const CompletionItem &); @@ -738,7 +738,7 @@ /// The completion items. std::vector items; }; -json::Expr toJSON(const CompletionList &); +llvm::json::Value toJSON(const CompletionList &); /// A single parameter of a particular signature. struct ParameterInformation { @@ -749,7 +749,7 @@ /// The documentation of this parameter. Optional. std::string documentation; }; -json::Expr toJSON(const ParameterInformation &); +llvm::json::Value toJSON(const ParameterInformation &); /// Represents the signature of something callable. struct SignatureInformation { @@ -763,7 +763,7 @@ /// The parameters of this signature. std::vector parameters; }; -json::Expr toJSON(const SignatureInformation &); +llvm::json::Value toJSON(const SignatureInformation &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SignatureInformation &); @@ -779,7 +779,7 @@ /// The active parameter of the active signature. int activeParameter = 0; }; -json::Expr toJSON(const SignatureHelp &); +llvm::json::Value toJSON(const SignatureHelp &); struct RenameParams { /// The document that was opened. @@ -791,7 +791,7 @@ /// The new name of the symbol. std::string newName; }; -bool fromJSON(const json::Expr &, RenameParams &); +bool fromJSON(const llvm::json::Value &, RenameParams &); enum class DocumentHighlightKind { Text = 1, Read = 2, Write = 3 }; @@ -818,7 +818,7 @@ return LHS.kind == RHS.kind && LHS.range == RHS.range; } }; -json::Expr toJSON(const DocumentHighlight &DH); +llvm::json::Value toJSON(const DocumentHighlight &DH); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DocumentHighlight &); } // namespace clangd Index: clangd/Protocol.cpp =================================================================== --- clangd/Protocol.cpp +++ clangd/Protocol.cpp @@ -23,14 +23,15 @@ namespace clang { namespace clangd { +using namespace llvm; URIForFile::URIForFile(std::string AbsPath) { assert(llvm::sys::path::is_absolute(AbsPath) && "the path is relative"); File = std::move(AbsPath); } -bool fromJSON(const json::Expr &E, URIForFile &R) { - if (auto S = E.asString()) { +bool fromJSON(const json::Value &E, URIForFile &R) { + if (auto S = E.getAsString()) { auto U = URI::parse(*S); if (!U) { log("Failed to parse URI " + *S + ": " + llvm::toString(U.takeError())); @@ -51,28 +52,28 @@ return false; } -json::Expr toJSON(const URIForFile &U) { return U.uri(); } +json::Value toJSON(const URIForFile &U) { return U.uri(); } llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const URIForFile &U) { return OS << U.uri(); } -json::Expr toJSON(const TextDocumentIdentifier &R) { - return json::obj{{"uri", R.uri}}; +json::Value toJSON(const TextDocumentIdentifier &R) { + return json::Object{{"uri", R.uri}}; } -bool fromJSON(const json::Expr &Params, TextDocumentIdentifier &R) { +bool fromJSON(const json::Value &Params, TextDocumentIdentifier &R) { json::ObjectMapper O(Params); return O && O.map("uri", R.uri); } -bool fromJSON(const json::Expr &Params, Position &R) { +bool fromJSON(const json::Value &Params, Position &R) { json::ObjectMapper O(Params); return O && O.map("line", R.line) && O.map("character", R.character); } -json::Expr toJSON(const Position &P) { - return json::obj{ +json::Value toJSON(const Position &P) { + return json::Object{ {"line", P.line}, {"character", P.character}, }; @@ -82,13 +83,13 @@ return OS << P.line << ':' << P.character; } -bool fromJSON(const json::Expr &Params, Range &R) { +bool fromJSON(const json::Value &Params, Range &R) { json::ObjectMapper O(Params); return O && O.map("start", R.start) && O.map("end", R.end); } -json::Expr toJSON(const Range &P) { - return json::obj{ +json::Value toJSON(const Range &P) { + return json::Object{ {"start", P.start}, {"end", P.end}, }; @@ -98,8 +99,8 @@ return OS << R.start << '-' << R.end; } -json::Expr toJSON(const Location &P) { - return json::obj{ +json::Value toJSON(const Location &P) { + return json::Object{ {"uri", P.uri}, {"range", P.range}, }; @@ -109,13 +110,13 @@ return OS << L.range << '@' << L.uri; } -bool fromJSON(const json::Expr &Params, TextDocumentItem &R) { +bool fromJSON(const json::Value &Params, TextDocumentItem &R) { json::ObjectMapper O(Params); 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 json::Expr &Params, Metadata &R) { +bool fromJSON(const json::Value &Params, Metadata &R) { json::ObjectMapper O(Params); if (!O) return false; @@ -123,13 +124,13 @@ return true; } -bool fromJSON(const json::Expr &Params, TextEdit &R) { +bool fromJSON(const json::Value &Params, TextEdit &R) { json::ObjectMapper O(Params); return O && O.map("range", R.range) && O.map("newText", R.newText); } -json::Expr toJSON(const TextEdit &P) { - return json::obj{ +json::Value toJSON(const TextEdit &P) { + return json::Object{ {"range", P.range}, {"newText", P.newText}, }; @@ -141,8 +142,8 @@ return OS << '"'; } -bool fromJSON(const json::Expr &E, TraceLevel &Out) { - if (auto S = E.asString()) { +bool fromJSON(const json::Value &E, TraceLevel &Out) { + if (auto S = E.getAsString()) { if (*S == "off") { Out = TraceLevel::Off; return true; @@ -157,7 +158,7 @@ return false; } -bool fromJSON(const json::Expr &Params, CompletionItemClientCapabilities &R) { +bool fromJSON(const json::Value &Params, CompletionItemClientCapabilities &R) { json::ObjectMapper O(Params); if (!O) return false; @@ -166,7 +167,7 @@ return true; } -bool fromJSON(const json::Expr &Params, CompletionClientCapabilities &R) { +bool fromJSON(const json::Value &Params, CompletionClientCapabilities &R) { json::ObjectMapper O(Params); if (!O) return false; @@ -176,8 +177,8 @@ return true; } -bool fromJSON(const json::Expr &E, SymbolKind &Out) { - if (auto T = E.asInteger()) { +bool fromJSON(const json::Value &E, SymbolKind &Out) { + if (auto T = E.getAsInteger()) { if (*T < static_cast(SymbolKind::File) || *T > static_cast(SymbolKind::TypeParameter)) return false; @@ -187,8 +188,8 @@ return false; } -bool fromJSON(const json::Expr &E, std::vector &Out) { - if (auto *A = E.asArray()) { +bool fromJSON(const json::Value &E, std::vector &Out) { + if (auto *A = E.getAsArray()) { Out.clear(); for (size_t I = 0; I < A->size(); ++I) { SymbolKind KindOut; @@ -200,7 +201,7 @@ return false; } -bool fromJSON(const json::Expr &Params, SymbolKindCapabilities &R) { +bool fromJSON(const json::Value &Params, SymbolKindCapabilities &R) { json::ObjectMapper O(Params); return O && O.map("valueSet", R.valueSet); } @@ -223,17 +224,17 @@ } } -bool fromJSON(const json::Expr &Params, WorkspaceSymbolCapabilities &R) { +bool fromJSON(const json::Value &Params, WorkspaceSymbolCapabilities &R) { json::ObjectMapper O(Params); return O && O.map("symbolKind", R.symbolKind); } -bool fromJSON(const json::Expr &Params, WorkspaceClientCapabilities &R) { +bool fromJSON(const json::Value &Params, WorkspaceClientCapabilities &R) { json::ObjectMapper O(Params); return O && O.map("symbol", R.symbol); } -bool fromJSON(const json::Expr &Params, TextDocumentClientCapabilities &R) { +bool fromJSON(const json::Value &Params, TextDocumentClientCapabilities &R) { json::ObjectMapper O(Params); if (!O) return false; @@ -241,7 +242,7 @@ return true; } -bool fromJSON(const json::Expr &Params, ClientCapabilities &R) { +bool fromJSON(const json::Value &Params, ClientCapabilities &R) { json::ObjectMapper O(Params); if (!O) return false; @@ -250,7 +251,7 @@ return true; } -bool fromJSON(const json::Expr &Params, InitializeParams &R) { +bool fromJSON(const json::Value &Params, InitializeParams &R) { json::ObjectMapper O(Params); if (!O) return false; @@ -265,26 +266,26 @@ return true; } -bool fromJSON(const json::Expr &Params, DidOpenTextDocumentParams &R) { +bool fromJSON(const json::Value &Params, DidOpenTextDocumentParams &R) { json::ObjectMapper O(Params); return O && O.map("textDocument", R.textDocument) && O.map("metadata", R.metadata); } -bool fromJSON(const json::Expr &Params, DidCloseTextDocumentParams &R) { +bool fromJSON(const json::Value &Params, DidCloseTextDocumentParams &R) { json::ObjectMapper O(Params); return O && O.map("textDocument", R.textDocument); } -bool fromJSON(const json::Expr &Params, DidChangeTextDocumentParams &R) { +bool fromJSON(const json::Value &Params, DidChangeTextDocumentParams &R) { json::ObjectMapper O(Params); return O && O.map("textDocument", R.textDocument) && O.map("contentChanges", R.contentChanges) && O.map("wantDiagnostics", R.wantDiagnostics); } -bool fromJSON(const json::Expr &E, FileChangeType &Out) { - if (auto T = E.asInteger()) { +bool fromJSON(const json::Value &E, FileChangeType &Out) { + if (auto T = E.getAsInteger()) { if (*T < static_cast(FileChangeType::Created) || *T > static_cast(FileChangeType::Deleted)) return false; @@ -294,60 +295,60 @@ return false; } -bool fromJSON(const json::Expr &Params, FileEvent &R) { +bool fromJSON(const json::Value &Params, FileEvent &R) { json::ObjectMapper O(Params); return O && O.map("uri", R.uri) && O.map("type", R.type); } -bool fromJSON(const json::Expr &Params, DidChangeWatchedFilesParams &R) { +bool fromJSON(const json::Value &Params, DidChangeWatchedFilesParams &R) { json::ObjectMapper O(Params); return O && O.map("changes", R.changes); } -bool fromJSON(const json::Expr &Params, TextDocumentContentChangeEvent &R) { +bool fromJSON(const json::Value &Params, TextDocumentContentChangeEvent &R) { json::ObjectMapper O(Params); return O && O.map("range", R.range) && O.map("rangeLength", R.rangeLength) && O.map("text", R.text); } -bool fromJSON(const json::Expr &Params, FormattingOptions &R) { +bool fromJSON(const json::Value &Params, FormattingOptions &R) { json::ObjectMapper O(Params); return O && O.map("tabSize", R.tabSize) && O.map("insertSpaces", R.insertSpaces); } -json::Expr toJSON(const FormattingOptions &P) { - return json::obj{ +json::Value toJSON(const FormattingOptions &P) { + return json::Object{ {"tabSize", P.tabSize}, {"insertSpaces", P.insertSpaces}, }; } -bool fromJSON(const json::Expr &Params, DocumentRangeFormattingParams &R) { +bool fromJSON(const json::Value &Params, DocumentRangeFormattingParams &R) { json::ObjectMapper O(Params); return O && O.map("textDocument", R.textDocument) && O.map("range", R.range) && O.map("options", R.options); } -bool fromJSON(const json::Expr &Params, DocumentOnTypeFormattingParams &R) { +bool fromJSON(const json::Value &Params, DocumentOnTypeFormattingParams &R) { json::ObjectMapper O(Params); return O && O.map("textDocument", R.textDocument) && O.map("position", R.position) && O.map("ch", R.ch) && O.map("options", R.options); } -bool fromJSON(const json::Expr &Params, DocumentFormattingParams &R) { +bool fromJSON(const json::Value &Params, DocumentFormattingParams &R) { json::ObjectMapper O(Params); return O && O.map("textDocument", R.textDocument) && O.map("options", R.options); } -bool fromJSON(const json::Expr &Params, DocumentSymbolParams &R) { +bool fromJSON(const json::Value &Params, DocumentSymbolParams &R) { json::ObjectMapper O(Params); return O && O.map("textDocument", R.textDocument); } -bool fromJSON(const json::Expr &Params, Diagnostic &R) { +bool fromJSON(const json::Value &Params, Diagnostic &R) { json::ObjectMapper O(Params); if (!O || !O.map("range", R.range) || !O.map("message", R.message)) return false; @@ -355,7 +356,7 @@ return true; } -bool fromJSON(const json::Expr &Params, CodeActionContext &R) { +bool fromJSON(const json::Value &Params, CodeActionContext &R) { json::ObjectMapper O(Params); return O && O.map("diagnostics", R.diagnostics); } @@ -382,25 +383,25 @@ return OS << '(' << D.severity << "): " << D.message << "]"; } -bool fromJSON(const json::Expr &Params, CodeActionParams &R) { +bool fromJSON(const json::Value &Params, CodeActionParams &R) { json::ObjectMapper O(Params); return O && O.map("textDocument", R.textDocument) && O.map("range", R.range) && O.map("context", R.context); } -bool fromJSON(const json::Expr &Params, WorkspaceEdit &R) { +bool fromJSON(const json::Value &Params, WorkspaceEdit &R) { json::ObjectMapper O(Params); return O && O.map("changes", R.changes); } const llvm::StringLiteral ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND = "clangd.applyFix"; -bool fromJSON(const json::Expr &Params, ExecuteCommandParams &R) { +bool fromJSON(const json::Value &Params, ExecuteCommandParams &R) { json::ObjectMapper O(Params); if (!O || !O.map("command", R.command)) return false; - auto Args = Params.asObject()->getArray("arguments"); + auto Args = Params.getAsObject()->getArray("arguments"); if (R.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND) { return Args && Args->size() == 1 && fromJSON(Args->front(), R.workspaceEdit); @@ -408,8 +409,8 @@ return false; // Unrecognized command. } -json::Expr toJSON(const SymbolInformation &P) { - return json::obj{ +json::Value toJSON(const SymbolInformation &P) { + return json::Object{ {"name", P.name}, {"kind", static_cast(P.kind)}, {"location", P.location}, @@ -423,32 +424,32 @@ return O; } -bool fromJSON(const json::Expr &Params, WorkspaceSymbolParams &R) { +bool fromJSON(const json::Value &Params, WorkspaceSymbolParams &R) { json::ObjectMapper O(Params); return O && O.map("query", R.query); } -json::Expr toJSON(const Command &C) { - auto Cmd = json::obj{{"title", C.title}, {"command", C.command}}; +json::Value toJSON(const Command &C) { + auto Cmd = json::Object{{"title", C.title}, {"command", C.command}}; if (C.workspaceEdit) Cmd["arguments"] = {*C.workspaceEdit}; return std::move(Cmd); } -json::Expr toJSON(const WorkspaceEdit &WE) { +json::Value toJSON(const WorkspaceEdit &WE) { if (!WE.changes) - return json::obj{}; - json::obj FileChanges; + return json::Object{}; + json::Object FileChanges; for (auto &Change : *WE.changes) - FileChanges[Change.first] = json::ary(Change.second); - return json::obj{{"changes", std::move(FileChanges)}}; + FileChanges[Change.first] = json::Array(Change.second); + return json::Object{{"changes", std::move(FileChanges)}}; } -json::Expr toJSON(const ApplyWorkspaceEditParams &Params) { - return json::obj{{"edit", Params.edit}}; +json::Value toJSON(const ApplyWorkspaceEditParams &Params) { + return json::Object{{"edit", Params.edit}}; } -bool fromJSON(const json::Expr &Params, TextDocumentPositionParams &R) { +bool fromJSON(const json::Value &Params, TextDocumentPositionParams &R) { json::ObjectMapper O(Params); return O && O.map("textDocument", R.textDocument) && O.map("position", R.position); @@ -464,18 +465,18 @@ llvm_unreachable("Invalid MarkupKind"); } -json::Expr toJSON(const MarkupContent &MC) { +json::Value toJSON(const MarkupContent &MC) { if (MC.value.empty()) return nullptr; - return json::obj{ + return json::Object{ {"kind", toTextKind(MC.kind)}, {"value", MC.value}, }; } -json::Expr toJSON(const Hover &H) { - json::obj Result{{"contents", toJSON(H.contents)}}; +json::Value toJSON(const Hover &H) { + json::Object Result{{"contents", toJSON(H.contents)}}; if (H.range.hasValue()) Result["range"] = toJSON(*H.range); @@ -483,9 +484,9 @@ return std::move(Result); } -json::Expr toJSON(const CompletionItem &CI) { +json::Value toJSON(const CompletionItem &CI) { assert(!CI.label.empty() && "completion item label is required"); - json::obj Result{{"label", CI.label}}; + json::Object Result{{"label", CI.label}}; if (CI.kind != CompletionItemKind::Missing) Result["kind"] = static_cast(CI.kind); if (!CI.detail.empty()) @@ -503,7 +504,7 @@ if (CI.textEdit) Result["textEdit"] = *CI.textEdit; if (!CI.additionalTextEdits.empty()) - Result["additionalTextEdits"] = json::ary(CI.additionalTextEdits); + Result["additionalTextEdits"] = json::Array(CI.additionalTextEdits); return std::move(Result); } @@ -517,26 +518,26 @@ (R.sortText.empty() ? R.label : R.sortText); } -json::Expr toJSON(const CompletionList &L) { - return json::obj{ +json::Value toJSON(const CompletionList &L) { + return json::Object{ {"isIncomplete", L.isIncomplete}, - {"items", json::ary(L.items)}, + {"items", json::Array(L.items)}, }; } -json::Expr toJSON(const ParameterInformation &PI) { +json::Value toJSON(const ParameterInformation &PI) { assert(!PI.label.empty() && "parameter information label is required"); - json::obj Result{{"label", PI.label}}; + json::Object Result{{"label", PI.label}}; if (!PI.documentation.empty()) Result["documentation"] = PI.documentation; return std::move(Result); } -json::Expr toJSON(const SignatureInformation &SI) { +json::Value toJSON(const SignatureInformation &SI) { assert(!SI.label.empty() && "signature information label is required"); - json::obj Result{ + json::Object Result{ {"label", SI.label}, - {"parameters", json::ary(SI.parameters)}, + {"parameters", json::Array(SI.parameters)}, }; if (!SI.documentation.empty()) Result["documentation"] = SI.documentation; @@ -549,26 +550,26 @@ return O; } -json::Expr toJSON(const SignatureHelp &SH) { +json::Value toJSON(const SignatureHelp &SH) { assert(SH.activeSignature >= 0 && "Unexpected negative value for number of active signatures."); assert(SH.activeParameter >= 0 && "Unexpected negative value for active parameter index"); - return json::obj{ + return json::Object{ {"activeSignature", SH.activeSignature}, {"activeParameter", SH.activeParameter}, - {"signatures", json::ary(SH.signatures)}, + {"signatures", json::Array(SH.signatures)}, }; } -bool fromJSON(const json::Expr &Params, RenameParams &R) { +bool fromJSON(const json::Value &Params, RenameParams &R) { json::ObjectMapper O(Params); return O && O.map("textDocument", R.textDocument) && O.map("position", R.position) && O.map("newName", R.newName); } -json::Expr toJSON(const DocumentHighlight &DH) { - return json::obj{ +json::Value toJSON(const DocumentHighlight &DH) { + return json::Object{ {"range", toJSON(DH.range)}, {"kind", static_cast(DH.kind)}, }; @@ -584,12 +585,13 @@ return O; } -bool fromJSON(const json::Expr &Params, DidChangeConfigurationParams &CCP) { +bool fromJSON(const json::Value &Params, DidChangeConfigurationParams &CCP) { json::ObjectMapper O(Params); return O && O.map("settings", CCP.settings); } -bool fromJSON(const json::Expr &Params, ClangdConfigurationParamsChange &CCPC) { +bool fromJSON(const json::Value &Params, + ClangdConfigurationParamsChange &CCPC) { json::ObjectMapper O(Params); return O && O.map("compilationDatabasePath", CCPC.compilationDatabasePath); } Index: clangd/ProtocolHandlers.cpp =================================================================== --- clangd/ProtocolHandlers.cpp +++ clangd/ProtocolHandlers.cpp @@ -15,6 +15,7 @@ using namespace clang; using namespace clang::clangd; +using namespace llvm; namespace { @@ -27,7 +28,7 @@ void operator()(StringRef Method, void (ProtocolCallbacks::*Handler)(Param)) { // Capture pointers by value, as the lambda will outlive this object. auto *Callbacks = this->Callbacks; - Dispatcher.registerHandler(Method, [=](const json::Expr &RawParams) { + Dispatcher.registerHandler(Method, [=](const json::Value &RawParams) { typename std::remove_reference::type P; if (fromJSON(RawParams, P)) { (Callbacks->*Handler)(P); Index: clangd/Trace.h =================================================================== --- clangd/Trace.h +++ clangd/Trace.h @@ -20,8 +20,8 @@ #include "Context.h" #include "Function.h" -#include "JSONExpr.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/raw_ostream.h" namespace clang { @@ -39,7 +39,7 @@ /// Usually implementations will store an object in the returned context /// whose destructor records the end of the event. /// The args are *Args, only complete when the event ends. - virtual Context beginSpan(llvm::StringRef Name, json::obj *Args) = 0; + virtual Context beginSpan(llvm::StringRef Name, llvm::json::Object *Args) = 0; // Called when a Span is destroyed (it may still be active on other threads). // beginSpan() and endSpan() will always form a proper stack on each thread. // The Context returned by beginSpan is active, but Args is not ready. @@ -48,7 +48,7 @@ virtual void endSpan(){}; /// Called for instant events. - virtual void instant(llvm::StringRef Name, json::obj &&Args) = 0; + virtual void instant(llvm::StringRef Name, llvm::json::Object &&Args) = 0; }; /// Sets up a global EventTracer that consumes events produced by Span and @@ -87,7 +87,7 @@ /// Mutable metadata, if this span is interested. /// Prefer to use SPAN_ATTACH rather than accessing this directly. - json::obj *const Args; + llvm::json::Object *const Args; private: WithContext RestoreCtx; Index: clangd/Trace.cpp =================================================================== --- clangd/Trace.cpp +++ clangd/Trace.cpp @@ -36,9 +36,9 @@ // calculations! Out << R"({"displayTimeUnit":"ns","traceEvents":[)" << "\n"; - rawEvent("M", json::obj{ + rawEvent("M", json::Object{ {"name", "process_name"}, - {"args", json::obj{{"name", "clangd"}}}, + {"args", json::Object{{"name", "clangd"}}}, }); } @@ -49,7 +49,7 @@ // We stash a Span object in the context. It will record the start/end, // and this also allows us to look up the parent Span's information. - Context beginSpan(llvm::StringRef Name, json::obj *Args) override { + Context beginSpan(llvm::StringRef Name, json::Object *Args) override { return Context::current().derive( SpanKey, llvm::make_unique(this, Name, Args)); } @@ -62,18 +62,17 @@ Context::current().getExisting(SpanKey)->markEnded(); } - void instant(llvm::StringRef Name, json::obj &&Args) override { + void instant(llvm::StringRef Name, json::Object &&Args) override { captureThreadMetadata(); - jsonEvent("i", json::obj{{"name", Name}, {"args", std::move(Args)}}); + jsonEvent("i", json::Object{{"name", Name}, {"args", std::move(Args)}}); } // Record an event on the current thread. ph, pid, tid, ts are set. // Contents must be a list of the other JSON key/values. - void jsonEvent(StringRef Phase, json::obj &&Contents, - uint64_t TID = get_threadid(), - double Timestamp = 0) { + void jsonEvent(StringRef Phase, json::Object &&Contents, + uint64_t TID = get_threadid(), double Timestamp = 0) { Contents["ts"] = Timestamp ? Timestamp : timestamp(); - Contents["tid"] = TID; + Contents["tid"] = int64_t(TID); std::lock_guard Lock(Mu); rawEvent(Phase, std::move(Contents)); } @@ -81,7 +80,7 @@ private: class JSONSpan { public: - JSONSpan(JSONTracer *Tracer, llvm::StringRef Name, json::obj *Args) + JSONSpan(JSONTracer *Tracer, llvm::StringRef Name, json::Object *Args) : StartTime(Tracer->timestamp()), EndTime(0), Name(Name), TID(get_threadid()), Tracer(Tracer), Args(Args) { // ~JSONSpan() may run in a different thread, so we need to capture now. @@ -102,15 +101,15 @@ auto FlowID = nextID(); Tracer->jsonEvent("s", - json::obj{{"id", FlowID}, - {"name", "Context crosses threads"}, - {"cat", "dummy"}}, + json::Object{{"id", FlowID}, + {"name", "Context crosses threads"}, + {"cat", "dummy"}}, (*Parent)->TID, (*Parent)->StartTime); Tracer->jsonEvent("f", - json::obj{{"id", FlowID}, - {"bp", "e"}, - {"name", "Context crosses threads"}, - {"cat", "dummy"}}, + json::Object{{"id", FlowID}, + {"bp", "e"}, + {"name", "Context crosses threads"}, + {"cat", "dummy"}}, TID); } } @@ -118,9 +117,9 @@ ~JSONSpan() { // Finally, record the event (ending at EndTime, not timestamp())! Tracer->jsonEvent("X", - json::obj{{"name", std::move(Name)}, - {"args", std::move(*Args)}, - {"dur", EndTime - StartTime}}, + json::Object{{"name", std::move(Name)}, + {"args", std::move(*Args)}, + {"dur", EndTime - StartTime}}, TID, StartTime); } @@ -130,8 +129,8 @@ } private: - static uint64_t nextID() { - static std::atomic Next = {0}; + static int64_t nextID() { + static std::atomic Next = {0}; return Next++; } @@ -140,17 +139,17 @@ std::string Name; uint64_t TID; JSONTracer *Tracer; - json::obj *Args; + json::Object *Args; }; static Key> SpanKey; // Record an event. ph and pid are set. // Contents must be a list of the other JSON key/values. - void rawEvent(StringRef Phase, json::obj &&Event) /*REQUIRES(Mu)*/ { + void rawEvent(StringRef Phase, json::Object &&Event) /*REQUIRES(Mu)*/ { // PID 0 represents the clangd process. Event["pid"] = 0; Event["ph"] = Phase; - Out << Sep << formatv(JSONFormat, json::Expr(std::move(Event))); + Out << Sep << formatv(JSONFormat, json::Value(std::move(Event))); Sep = ",\n"; } @@ -162,10 +161,10 @@ SmallString<32> Name; get_thread_name(Name); if (!Name.empty()) { - rawEvent("M", json::obj{ - {"tid", TID}, + rawEvent("M", json::Object{ + {"tid", int64_t(TID)}, {"name", "thread_name"}, - {"args", json::obj{{"name", Name}}}, + {"args", json::Object{{"name", Name}}}, }); } } @@ -204,14 +203,14 @@ void log(const Twine &Message) { if (!T) return; - T->instant("Log", json::obj{{"Message", Message.str()}}); + T->instant("Log", json::Object{{"Message", Message.str()}}); } // Returned context owns Args. -static Context makeSpanContext(llvm::Twine Name, json::obj *Args) { +static Context makeSpanContext(llvm::Twine Name, json::Object *Args) { if (!T) return Context::current().clone(); - WithContextValue WithArgs{std::unique_ptr(Args)}; + WithContextValue WithArgs{std::unique_ptr(Args)}; return T->beginSpan(Name.isSingleStringRef() ? Name.getSingleStringRef() : llvm::StringRef(Name.str()), Args); @@ -221,7 +220,7 @@ // The args are owned by the context though. They stick around until the // beginSpan() context is destroyed, when the tracing engine will consume them. Span::Span(llvm::Twine Name) - : Args(T ? new json::obj() : nullptr), + : Args(T ? new json::Object() : nullptr), RestoreCtx(makeSpanContext(Name, Args)) {} Span::~Span() { Index: unittests/clangd/CMakeLists.txt =================================================================== --- unittests/clangd/CMakeLists.txt +++ unittests/clangd/CMakeLists.txt @@ -23,7 +23,6 @@ GlobalCompilationDatabaseTests.cpp HeadersTests.cpp IndexTests.cpp - JSONExprTests.cpp QualityTests.cpp SourceCodeTests.cpp SymbolCollectorTests.cpp Index: unittests/clangd/JSONExprTests.cpp =================================================================== --- unittests/clangd/JSONExprTests.cpp +++ unittests/clangd/JSONExprTests.cpp @@ -1,293 +0,0 @@ -//===-- JSONExprTests.cpp - JSON expression unit tests ----------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "JSONExpr.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -namespace clang { -namespace clangd { -namespace json { - -namespace { - -std::string s(const Expr &E) { return llvm::formatv("{0}", E).str(); } -std::string sp(const Expr &E) { return llvm::formatv("{0:2}", E).str(); } - -TEST(JSONExprTests, Types) { - EXPECT_EQ("true", s(true)); - EXPECT_EQ("null", s(nullptr)); - EXPECT_EQ("2.5", s(2.5)); - EXPECT_EQ(R"("foo")", s("foo")); - EXPECT_EQ("[1,2,3]", s({1, 2, 3})); - EXPECT_EQ(R"({"x":10,"y":20})", s(obj{{"x", 10}, {"y", 20}})); -} - -TEST(JSONExprTests, Constructors) { - // Lots of edge cases around empty and singleton init lists. - EXPECT_EQ("[[[3]]]", s({{{3}}})); - EXPECT_EQ("[[[]]]", s({{{}}})); - EXPECT_EQ("[[{}]]", s({{obj{}}})); - EXPECT_EQ(R"({"A":{"B":{}}})", s(obj{{"A", obj{{"B", obj{}}}}})); - EXPECT_EQ(R"({"A":{"B":{"X":"Y"}}})", - s(obj{{"A", obj{{"B", obj{{"X", "Y"}}}}}})); - EXPECT_EQ("null", s(llvm::Optional())); - EXPECT_EQ("2.5", s(llvm::Optional(2.5))); -} - -TEST(JSONExprTests, StringOwnership) { - char X[] = "Hello"; - Expr Alias = static_cast(X); - X[1] = 'a'; - EXPECT_EQ(R"("Hallo")", s(Alias)); - - std::string Y = "Hello"; - Expr Copy = Y; - Y[1] = 'a'; - EXPECT_EQ(R"("Hello")", s(Copy)); -} - -TEST(JSONExprTests, CanonicalOutput) { - // Objects are sorted (but arrays aren't)! - EXPECT_EQ(R"({"a":1,"b":2,"c":3})", s(obj{{"a", 1}, {"c", 3}, {"b", 2}})); - EXPECT_EQ(R"(["a","c","b"])", s({"a", "c", "b"})); - EXPECT_EQ("3", s(3.0)); -} - -TEST(JSONExprTests, Escaping) { - std::string test = { - 0, // Strings may contain nulls. - '\b', '\f', // Have mnemonics, but we escape numerically. - '\r', '\n', '\t', // Escaped with mnemonics. - 'S', '\"', '\\', // Printable ASCII characters. - '\x7f', // Delete is not escaped. - '\xce', '\x94', // Non-ASCII UTF-8 is not escaped. - }; - - std::string teststring = R"("\u0000\u0008\u000c\r\n\tS\"\\)" - "\x7f\xCE\x94\""; - - EXPECT_EQ(teststring, s(test)); - - EXPECT_EQ(R"({"object keys are\nescaped":true})", - s(obj{{"object keys are\nescaped", true}})); -} - -TEST(JSONExprTests, PrettyPrinting) { - const char str[] = R"({ - "empty_array": [], - "empty_object": {}, - "full_array": [ - 1, - null - ], - "full_object": { - "nested_array": [ - { - "property": "value" - } - ] - } -})"; - - EXPECT_EQ(str, sp(obj{ - {"empty_object", obj{}}, - {"empty_array", {}}, - {"full_array", {1, nullptr}}, - {"full_object", - obj{ - {"nested_array", - {obj{ - {"property", "value"}, - }}}, - }}, - })); -} - -TEST(JSONTest, Parse) { - auto Compare = [](llvm::StringRef S, Expr Expected) { - if (auto E = parse(S)) { - // Compare both string forms and with operator==, in case we have bugs. - EXPECT_EQ(*E, Expected); - EXPECT_EQ(sp(*E), sp(Expected)); - } else { - handleAllErrors(E.takeError(), [S](const llvm::ErrorInfoBase &E) { - FAIL() << "Failed to parse JSON >>> " << S << " <<<: " << E.message(); - }); - } - }; - - Compare(R"(true)", true); - Compare(R"(false)", false); - Compare(R"(null)", nullptr); - - Compare(R"(42)", 42); - Compare(R"(2.5)", 2.5); - Compare(R"(2e50)", 2e50); - Compare(R"(1.2e3456789)", std::numeric_limits::infinity()); - - Compare(R"("foo")", "foo"); - Compare(R"("\"\\\b\f\n\r\t")", "\"\\\b\f\n\r\t"); - Compare(R"("\u0000")", llvm::StringRef("\0", 1)); - Compare("\"\x7f\"", "\x7f"); - Compare(R"("\ud801\udc37")", u8"\U00010437"); // UTF16 surrogate pair escape. - Compare("\"\xE2\x82\xAC\xF0\x9D\x84\x9E\"", u8"\u20ac\U0001d11e"); // UTF8 - Compare( - R"("LoneLeading=\ud801, LoneTrailing=\udc01, LeadingLeadingTrailing=\ud801\ud801\udc37")", - u8"LoneLeading=\ufffd, LoneTrailing=\ufffd, " - u8"LeadingLeadingTrailing=\ufffd\U00010437"); // Invalid unicode. - - Compare(R"({"":0,"":0})", obj{{"", 0}}); - Compare(R"({"obj":{},"arr":[]})", obj{{"obj", obj{}}, {"arr", {}}}); - Compare(R"({"\n":{"\u0000":[[[[]]]]}})", - obj{{"\n", obj{ - {llvm::StringRef("\0", 1), {{{{}}}}}, - }}}); - Compare("\r[\n\t] ", {}); -} - -TEST(JSONTest, ParseErrors) { - auto ExpectErr = [](llvm::StringRef Msg, llvm::StringRef S) { - if (auto E = parse(S)) { - // Compare both string forms and with operator==, in case we have bugs. - FAIL() << "Parsed JSON >>> " << S << " <<< but wanted error: " << Msg; - } else { - handleAllErrors(E.takeError(), [S, Msg](const llvm::ErrorInfoBase &E) { - EXPECT_THAT(E.message(), testing::HasSubstr(Msg)) << S; - }); - } - }; - ExpectErr("Unexpected EOF", ""); - ExpectErr("Unexpected EOF", "["); - ExpectErr("Text after end of document", "[][]"); - ExpectErr("Invalid bareword", "fuzzy"); - ExpectErr("Expected , or ]", "[2?]"); - ExpectErr("Expected object key", "{a:2}"); - ExpectErr("Expected : after object key", R"({"a",2})"); - ExpectErr("Expected , or } after object property", R"({"a":2 "b":3})"); - ExpectErr("Expected JSON value", R"([&%!])"); - ExpectErr("Invalid number", "1e1.0"); - ExpectErr("Unterminated string", R"("abc\"def)"); - ExpectErr("Control character in string", "\"abc\ndef\""); - ExpectErr("Invalid escape sequence", R"("\030")"); - ExpectErr("Invalid \\u escape sequence", R"("\usuck")"); - ExpectErr("[3:3, byte=19]", R"({ - "valid": 1, - invalid: 2 -})"); -} - -TEST(JSONTest, Inspection) { - llvm::Expected Doc = parse(R"( - { - "null": null, - "boolean": false, - "number": 2.78, - "string": "json", - "array": [null, true, 3.14, "hello", [1,2,3], {"time": "arrow"}], - "object": {"fruit": "banana"} - } - )"); - EXPECT_TRUE(!!Doc); - - obj *O = Doc->asObject(); - ASSERT_TRUE(O); - - EXPECT_FALSE(O->getNull("missing")); - EXPECT_FALSE(O->getNull("boolean")); - 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")); - ASSERT_TRUE(O->getObject("object")); - EXPECT_EQ(*O->getObject("object"), (obj{{"fruit", "banana"}})); - - ary *A = O->getArray("array"); - ASSERT_TRUE(A); - 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) { - ASSERT_TRUE(E.asObject()); - EXPECT_EQ(E.asObject()->getString("time"), - llvm::Optional("arrow")); - } else - EXPECT_FALSE(E.asObject()); - } -} - -// Sample struct with typical JSON-mapping rules. -struct CustomStruct { - CustomStruct() : B(false) {} - CustomStruct(std::string S, llvm::Optional I, bool B) - : S(S), I(I), B(B) {} - std::string S; - llvm::Optional I; - bool B; -}; -inline bool operator==(const CustomStruct &L, const CustomStruct &R) { - return L.S == R.S && L.I == R.I && L.B == R.B; -} -inline std::ostream &operator<<(std::ostream &OS, const CustomStruct &S) { - return OS << "(" << S.S << ", " << (S.I ? std::to_string(*S.I) : "None") - << ", " << S.B << ")"; -} -bool fromJSON(const json::Expr &E, CustomStruct &R) { - ObjectMapper O(E); - if (!O || !O.map("str", R.S) || !O.map("int", R.I)) - return false; - O.map("bool", R.B); - return true; -} - -TEST(JSONTest, Deserialize) { - std::map> R; - CustomStruct ExpectedStruct = {"foo", 42, true}; - std::map> Expected; - Expr J = obj{{"foo", ary{ - obj{ - {"str", "foo"}, - {"int", 42}, - {"bool", true}, - {"unknown", "ignored"}, - }, - obj{{"str", "bar"}}, - obj{ - {"str", "baz"}, - {"bool", "string"}, // OK, deserialize ignores. - }, - }}}; - Expected["foo"] = { - CustomStruct("foo", 42, true), - CustomStruct("bar", llvm::None, false), - CustomStruct("baz", llvm::None, false), - }; - ASSERT_TRUE(fromJSON(J, R)); - EXPECT_EQ(R, Expected); - - CustomStruct V; - EXPECT_FALSE(fromJSON(nullptr, V)) << "Not an object " << V; - EXPECT_FALSE(fromJSON(obj{}, V)) << "Missing required field " << V; - EXPECT_FALSE(fromJSON(obj{{"str", 1}}, V)) << "Wrong type " << V; - // Optional must parse as the correct type if present. - EXPECT_FALSE(fromJSON(obj{{"str", 1}, {"int", "string"}}, V)) - << "Wrong type for Optional " << V; -} - -} // namespace -} // namespace json -} // namespace clangd -} // namespace clang