[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:
parent
25ea91a838
commit
ec109029b1
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"}
|
||||
|
|
|
@ -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"}
|
||||
|
|
|
@ -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"}
|
||||
|
|
|
@ -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"}
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue