[clangd] Switch from YAMLParser to JSONExpr

Summary:
 - Converted Protocol.h parse() functions to take JSON::Expr.
   These no longer detect and log unknown fields, as this is not that
   useful and no longer free.
   I haven't changed the error handling too much: fields that were
   treated as optional before are still optional, even when it's wrong.
   Exception: object properties with the wrong type are now ignored.
 - Made JSONRPCDispatcher parse using json::parse
 - The bug where 'method' must come before 'params' in the stream is
 fixed as a side-effect. (And the same bug in executeCommand).
 - Some parser crashers fixed as a side effect.
   e.g. https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=3890
 - The debug stream now prettyprints the input messages with --pretty.
 - Request params are attached to traces when tracing is enabled.
 - Fixed some bugs in tests (errors tolerated by YAMLParser, and
 off-by-ones in Content-Length that our null-termination was masking)
 - Fixed a random double-escape bug in ClangdLSPServer (it was our last
 use of YAMLParser!)

Reviewers: ilya-biryukov

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D40406

llvm-svn: 319159
This commit is contained in:
Sam McCall 2017-11-28 09:37:43 +00:00
parent 25ea91a838
commit ec109029b1
14 changed files with 429 additions and 996 deletions

View File

@ -139,7 +139,7 @@ void ClangdLSPServer::onRename(Ctx C, RenameParams &Params) {
std::string Code = Server.getDocument(File);
std::vector<TextEdit> 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 @@ bool ClangdLSPServer::run(std::istream &In) {
// 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);

View File

@ -181,6 +181,16 @@ public:
return as<double>();
return llvm::None;
}
llvm::Optional<int64_t> asInteger() const {
if (LLVM_LIKELY(Type == T_Number)) {
double D = as<double>();
if (LLVM_LIKELY(std::modf(D, &D) == 0 &&
D >= std::numeric_limits<int64_t>::min() &&
D <= std::numeric_limits<int64_t>::max()))
return D;
}
return llvm::None;
}
llvm::Optional<llvm::StringRef> asString() const {
if (Type == T_String)
return llvm::StringRef(as<std::string>());
@ -324,6 +334,11 @@ public:
return V->asNumber();
return llvm::None;
}
llvm::Optional<int64_t> getInteger(const ObjectKey &K) const {
if (auto *V = get(K))
return V->asInteger();
return llvm::None;
}
llvm::Optional<llvm::StringRef> getString(const ObjectKey &K) const {
if (auto *V = get(K))
return V->asString();
@ -374,6 +389,9 @@ public:
llvm::Optional<double> getNumber(size_t I) const {
return (*this)[I].asNumber();
}
llvm::Optional<int64_t> getInteger(size_t I) const {
return (*this)[I].asInteger();
}
llvm::Optional<llvm::StringRef> getString(size_t I) const {
return (*this)[I].asString();
}

View File

@ -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 <istream>
using namespace clang;
@ -101,88 +100,29 @@ void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) {
Handlers[Method] = std::move(H);
}
static void
callHandler(const llvm::StringMap<JSONRPCDispatcher::Handler> &Handlers,
llvm::yaml::ScalarNode *Method, llvm::Optional<json::Expr> 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<StringRef>("2.0"))
return false;
auto *Object = dyn_cast_or_null<llvm::yaml::MappingNode>(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<json::Expr> ID;
for (auto &NextKeyValue : *Object) {
auto *KeyString =
dyn_cast_or_null<llvm::yaml::ScalarNode>(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<llvm::yaml::ScalarNode>(Value);
if (!Version || Version->getRawValue() != "\"2.0\"")
return false;
} else if (KeyValue == "method") {
Method = dyn_cast<llvm::yaml::ScalarNode>(Value);
} else if (KeyValue == "id") {
// ID may be either a string or a number.
if (auto *IdNode = dyn_cast<llvm::yaml::ScalarNode>(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<llvm::yaml::MappingNode>(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 @@ void clangd::runLanguageServerLoop(std::istream &In, JSONOutput &Out,
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 @@ void clangd::runLanguageServerLoop(std::istream &In, JSONOutput &Out,
}
if (ContentLength > 0) {
std::vector<char> JSON(ContentLength + 1, '\0');
std::vector<char> 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 @@ void clangd::runLanguageServerLoop(std::istream &In, JSONOutput &Out,
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)

View File

@ -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 <iosfwd>
#include <mutex>
@ -30,7 +29,7 @@ class JSONOutput : public Logger {
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 @@ public:
/// 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 @@ private:
class JSONRPCDispatcher {
public:
// A handler responds to requests for a particular method name.
using Handler =
std::function<void(RequestContext, llvm::yaml::MappingNode *)>;
using Handler = std::function<void(RequestContext, const json::Expr &)>;
/// Create a new JSONRPCDispatcher. UnknownHandler is called when an unknown
/// method is received.
@ -96,7 +96,7 @@ public:
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<Handler> Handlers;

File diff suppressed because it is too large Load Diff

View File

@ -23,15 +23,12 @@
#include "JSONExpr.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/YAMLParser.h"
#include <string>
#include <vector>
namespace clang {
namespace clangd {
class Logger;
enum class ErrorCode {
// Defined by JSON RPC.
ParseError = -32700,
@ -54,7 +51,7 @@ struct URI {
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 @@ struct TextDocumentIdentifier {
/// The text document's URI.
URI uri;
static llvm::Optional<TextDocumentIdentifier>
parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
static llvm::Optional<TextDocumentIdentifier> parse(const json::Expr &Params);
};
struct Position {
@ -94,8 +90,7 @@ struct Position {
std::tie(RHS.line, RHS.character);
}
static llvm::Optional<Position> parse(llvm::yaml::MappingNode *Params,
clangd::Logger &Logger);
static llvm::Optional<Position> parse(const json::Expr &Params);
static json::Expr unparse(const Position &P);
};
@ -113,8 +108,7 @@ struct Range {
return std::tie(LHS.start, LHS.end) < std::tie(RHS.start, RHS.end);
}
static llvm::Optional<Range> parse(llvm::yaml::MappingNode *Params,
clangd::Logger &Logger);
static llvm::Optional<Range> parse(const json::Expr &Params);
static json::Expr unparse(const Range &P);
};
@ -141,8 +135,7 @@ struct Location {
struct Metadata {
std::vector<std::string> extraFlags;
static llvm::Optional<Metadata> parse(llvm::yaml::MappingNode *Params,
clangd::Logger &Logger);
static llvm::Optional<Metadata> parse(const json::Expr &Params);
};
struct TextEdit {
@ -154,8 +147,7 @@ struct TextEdit {
/// empty string.
std::string newText;
static llvm::Optional<TextEdit> parse(llvm::yaml::MappingNode *Params,
clangd::Logger &Logger);
static llvm::Optional<TextEdit> parse(const json::Expr &Params);
static json::Expr unparse(const TextEdit &P);
};
@ -172,8 +164,7 @@ struct TextDocumentItem {
/// The content of the opened text document.
std::string text;
static llvm::Optional<TextDocumentItem> parse(llvm::yaml::MappingNode *Params,
clangd::Logger &Logger);
static llvm::Optional<TextDocumentItem> parse(const json::Expr &Params);
};
enum class TraceLevel {
@ -183,8 +174,7 @@ enum class TraceLevel {
};
struct NoParams {
static llvm::Optional<NoParams> parse(llvm::yaml::MappingNode *Params,
Logger &Logger) {
static llvm::Optional<NoParams> parse(const json::Expr &Params) {
return NoParams{};
}
};
@ -218,8 +208,7 @@ struct InitializeParams {
/// The initial trace setting. If omitted trace is disabled ('off').
llvm::Optional<TraceLevel> trace;
static llvm::Optional<InitializeParams> parse(llvm::yaml::MappingNode *Params,
clangd::Logger &Logger);
static llvm::Optional<InitializeParams> parse(const json::Expr &Params);
};
struct DidOpenTextDocumentParams {
@ -230,7 +219,7 @@ struct DidOpenTextDocumentParams {
llvm::Optional<Metadata> metadata;
static llvm::Optional<DidOpenTextDocumentParams>
parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
parse(const json::Expr &Params);
};
struct DidCloseTextDocumentParams {
@ -238,7 +227,7 @@ struct DidCloseTextDocumentParams {
TextDocumentIdentifier textDocument;
static llvm::Optional<DidCloseTextDocumentParams>
parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
parse(const json::Expr &Params);
};
struct TextDocumentContentChangeEvent {
@ -246,7 +235,7 @@ struct TextDocumentContentChangeEvent {
std::string text;
static llvm::Optional<TextDocumentContentChangeEvent>
parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
parse(const json::Expr &Params);
};
struct DidChangeTextDocumentParams {
@ -259,7 +248,7 @@ struct DidChangeTextDocumentParams {
std::vector<TextDocumentContentChangeEvent> contentChanges;
static llvm::Optional<DidChangeTextDocumentParams>
parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
parse(const json::Expr &Params);
};
enum class FileChangeType {
@ -277,8 +266,7 @@ struct FileEvent {
/// The change type.
FileChangeType type;
static llvm::Optional<FileEvent> parse(llvm::yaml::MappingNode *Params,
clangd::Logger &Logger);
static llvm::Optional<FileEvent> parse(const json::Expr &Params);
};
struct DidChangeWatchedFilesParams {
@ -286,7 +274,7 @@ struct DidChangeWatchedFilesParams {
std::vector<FileEvent> changes;
static llvm::Optional<DidChangeWatchedFilesParams>
parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
parse(const json::Expr &Params);
};
struct FormattingOptions {
@ -296,8 +284,7 @@ struct FormattingOptions {
/// Prefer spaces over tabs.
bool insertSpaces;
static llvm::Optional<FormattingOptions>
parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
static llvm::Optional<FormattingOptions> parse(const json::Expr &Params);
static json::Expr unparse(const FormattingOptions &P);
};
@ -312,7 +299,7 @@ struct DocumentRangeFormattingParams {
FormattingOptions options;
static llvm::Optional<DocumentRangeFormattingParams>
parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
parse(const json::Expr &Params);
};
struct DocumentOnTypeFormattingParams {
@ -329,7 +316,7 @@ struct DocumentOnTypeFormattingParams {
FormattingOptions options;
static llvm::Optional<DocumentOnTypeFormattingParams>
parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
parse(const json::Expr &Params);
};
struct DocumentFormattingParams {
@ -340,7 +327,7 @@ struct DocumentFormattingParams {
FormattingOptions options;
static llvm::Optional<DocumentFormattingParams>
parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
parse(const json::Expr &Params);
};
struct Diagnostic {
@ -372,16 +359,14 @@ struct Diagnostic {
std::tie(RHS.range, RHS.severity, RHS.message);
}
static llvm::Optional<Diagnostic> parse(llvm::yaml::MappingNode *Params,
clangd::Logger &Logger);
static llvm::Optional<Diagnostic> parse(const json::Expr &Params);
};
struct CodeActionContext {
/// An array of diagnostics.
std::vector<Diagnostic> diagnostics;
static llvm::Optional<CodeActionContext>
parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
static llvm::Optional<CodeActionContext> parse(const json::Expr &Params);
};
struct CodeActionParams {
@ -394,8 +379,7 @@ struct CodeActionParams {
/// Context carrying additional information.
CodeActionContext context;
static llvm::Optional<CodeActionParams> parse(llvm::yaml::MappingNode *Params,
clangd::Logger &Logger);
static llvm::Optional<CodeActionParams> parse(const json::Expr &Params);
};
struct WorkspaceEdit {
@ -405,8 +389,7 @@ struct WorkspaceEdit {
/// Note: "documentChanges" is not currently used because currently there is
/// no support for versioned edits.
static llvm::Optional<WorkspaceEdit> parse(llvm::yaml::MappingNode *Params,
clangd::Logger &Logger);
static llvm::Optional<WorkspaceEdit> parse(const json::Expr &Params);
static json::Expr unparse(const WorkspaceEdit &WE);
};
@ -429,8 +412,7 @@ struct ExecuteCommandParams {
llvm::Optional<WorkspaceEdit> workspaceEdit;
static llvm::Optional<ExecuteCommandParams>
parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
static llvm::Optional<ExecuteCommandParams> parse(const json::Expr &Params);
};
struct ApplyWorkspaceEditParams {
@ -446,7 +428,7 @@ struct TextDocumentPositionParams {
Position position;
static llvm::Optional<TextDocumentPositionParams>
parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
parse(const json::Expr &Params);
};
/// The kind of a completion entry.
@ -611,8 +593,7 @@ struct RenameParams {
/// The new name of the symbol.
std::string newName;
static llvm::Optional<RenameParams> parse(llvm::yaml::MappingNode *Params,
clangd::Logger &Logger);
static llvm::Optional<RenameParams> parse(const json::Expr &Params);
};
} // namespace clangd

View File

@ -21,7 +21,7 @@ namespace {
// 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 <typename Param>
void operator()(StringRef Method,
@ -30,10 +30,10 @@ struct HandlerRegisterer {
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<Param>::type::parse(RawParams, *Out);
return std::decay<Param>::type::parse(RawParams);
}()) {
(Callbacks->*Handler)(std::move(C), *P);
} else {

View File

@ -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 <mutex>
namespace clang {

View File

@ -28,7 +28,7 @@ Content-Length: 146
# 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 @@ Content-Length: 44
{"jsonrpc":"2.0","id":3,"method":"shutdown"}
Content-Length: 33
{"jsonrpc":"2.0":"method":"exit"}
{"jsonrpc":"2.0","method":"exit"}

View File

@ -101,7 +101,7 @@ Content-Length: 149
# 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 @@ Content-Length: 148
# 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 @@ Content-Length: 148
# 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 @@ Content-Length: 148
# 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 @@ Content-Length: 148
# 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 @@ Content-Length: 148
# 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 @@ Content-Length: 149
# 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 <typename MyTemplateParam>\nvoid foo() {\n MyTemplateParam a;\n}\nint main() {\n return 0;\n}\n"}]}}
@ -262,7 +262,7 @@ Content-Length: 149
# 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 @@ Content-Length: 48
{"jsonrpc":"2.0","id":10000,"method":"shutdown"}
Content-Length: 33
{"jsonrpc":"2.0":"method":"exit"}
{"jsonrpc":"2.0","method":"exit"}

View File

@ -47,4 +47,4 @@ Content-Length: 44
{"jsonrpc":"2.0","id":5,"method":"shutdown"}
Content-Length: 33
{"jsonrpc":"2.0":"method":"exit"}
{"jsonrpc":"2.0","method":"exit"}

View File

@ -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"}

View File

@ -79,7 +79,7 @@ Content-Length: 10
{"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

View File

@ -205,6 +205,7 @@ TEST(JSONTest, Inspection) {
EXPECT_TRUE(O->getNull("null"));
EXPECT_EQ(O->getNumber("number"), llvm::Optional<double>(2.78));
EXPECT_FALSE(O->getInteger("number"));
EXPECT_EQ(O->getString("string"), llvm::Optional<llvm::StringRef>("json"));
ASSERT_FALSE(O->getObject("missing"));
ASSERT_FALSE(O->getObject("array"));
@ -216,6 +217,7 @@ TEST(JSONTest, Inspection) {
EXPECT_EQ(A->getBoolean(1), llvm::Optional<bool>(true));
ASSERT_TRUE(A->getArray(4));
EXPECT_EQ(*A->getArray(4), (ary{1, 2, 3}));
EXPECT_EQ(A->getArray(4)->getInteger(1), llvm::Optional<int64_t>(2));
int I = 0;
for (Expr &E : *A) {
if (I++ == 5) {