[clang-tools-extra] r317780 - [clangd] Add rename support.
Haojian Wu via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 9 03:30:05 PST 2017
Author: hokein
Date: Thu Nov 9 03:30:04 2017
New Revision: 317780
URL: http://llvm.org/viewvc/llvm-project?rev=317780&view=rev
Log:
[clangd] Add rename support.
Summary:
Make clangd handle "textDocument/rename" request. The rename
functionality comes from the "local-rename" sub-tool of clang-refactor.
Currently clangd only supports local rename (only symbol occurrences in
the main file will be renamed).
Reviewers: sammccall, ilya-biryukov
Reviewed By: sammccall
Subscribers: cfe-commits, ioeric, arphaman, mgorny
Differential Revision: https://reviews.llvm.org/D39676
Added:
clang-tools-extra/trunk/test/clangd/rename.test
Modified:
clang-tools-extra/trunk/clangd/CMakeLists.txt
clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
clang-tools-extra/trunk/clangd/ClangdLSPServer.h
clang-tools-extra/trunk/clangd/ClangdServer.cpp
clang-tools-extra/trunk/clangd/ClangdServer.h
clang-tools-extra/trunk/clangd/ClangdUnit.cpp
clang-tools-extra/trunk/clangd/ClangdUnit.h
clang-tools-extra/trunk/clangd/Protocol.cpp
clang-tools-extra/trunk/clangd/Protocol.h
clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp
clang-tools-extra/trunk/clangd/ProtocolHandlers.h
clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test
clang-tools-extra/trunk/test/clangd/initialize-params.test
Modified: clang-tools-extra/trunk/clangd/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CMakeLists.txt?rev=317780&r1=317779&r2=317780&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clangd/CMakeLists.txt Thu Nov 9 03:30:04 2017
@@ -27,6 +27,7 @@ add_clang_library(clangDaemon
clangSerialization
clangTooling
clangToolingCore
+ clangToolingRefactor
${LLVM_PTHREAD_LIB}
)
Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=317780&r1=317779&r2=317780&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Thu Nov 9 03:30:04 2017
@@ -57,6 +57,7 @@ void ClangdLSPServer::onInitialize(Ctx C
{"triggerCharacters", {"(", ","}},
}},
{"definitionProvider", true},
+ {"renameProvider", true},
{"executeCommandProvider",
json::obj{
{"commands", {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}},
@@ -127,6 +128,22 @@ void ClangdLSPServer::onCommand(Ctx C, E
}
}
+void ClangdLSPServer::onRename(Ctx C, RenameParams &Params) {
+ auto File = Params.textDocument.uri.file;
+ auto Replacements = Server.rename(File, Params.position, Params.newName);
+ if (!Replacements) {
+ C.replyError(
+ ErrorCode::InternalError,
+ llvm::toString(Replacements.takeError()));
+ return;
+ }
+ 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}};
+ C.reply(WorkspaceEdit::unparse(WE));
+}
+
void ClangdLSPServer::onDocumentDidClose(Ctx C,
DidCloseTextDocumentParams &Params) {
Server.removeDocument(Params.textDocument.uri.file);
Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.h?rev=317780&r1=317779&r2=317780&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.h Thu Nov 9 03:30:04 2017
@@ -70,6 +70,7 @@ private:
void onSwitchSourceHeader(Ctx C, TextDocumentIdentifier &Params) override;
void onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) override;
void onCommand(Ctx C, ExecuteCommandParams &Params) override;
+ void onRename(Ctx C, RenameParams &Parames) override;
std::vector<clang::tooling::Replacement>
getFixIts(StringRef File, const clangd::Diagnostic &D);
Modified: clang-tools-extra/trunk/clangd/ClangdServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=317780&r1=317779&r2=317780&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdServer.cpp Thu Nov 9 03:30:04 2017
@@ -9,6 +9,8 @@
#include "ClangdServer.h"
#include "clang/Format/Format.h"
+#include "clang/Tooling/Refactoring/RefactoringResultConsumer.h"
+#include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Tooling/CompilationDatabase.h"
@@ -51,6 +53,28 @@ std::string getStandardResourceDir() {
return CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy);
}
+class RefactoringResultCollector final
+ : public tooling::RefactoringResultConsumer {
+public:
+ void handleError(llvm::Error Err) override {
+ assert(!Result.hasValue());
+ // FIXME: figure out a way to return better message for DiagnosticError.
+ // clangd uses llvm::toString to convert the Err to string, however, for
+ // DiagnosticError, only "clang diagnostic" will be generated.
+ Result = std::move(Err);
+ }
+
+ // Using the handle(SymbolOccurrences) from parent class.
+ using tooling::RefactoringResultConsumer::handle;
+
+ void handle(tooling::AtomicChanges SourceReplacements) override {
+ assert(!Result.hasValue());
+ Result = std::move(SourceReplacements);
+ }
+
+ Optional<Expected<tooling::AtomicChanges>> Result;
+};
+
} // namespace
size_t clangd::positionToOffset(StringRef Code, Position P) {
@@ -333,6 +357,54 @@ std::vector<tooling::Replacement> Clangd
return formatCode(Code, File, {tooling::Range(PreviousLBracePos, Len)});
}
+Expected<std::vector<tooling::Replacement>>
+ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName) {
+ std::string Code = getDocument(File);
+ std::shared_ptr<CppFile> Resources = Units.getFile(File);
+ RefactoringResultCollector ResultCollector;
+ Resources->getAST().get()->runUnderLock([&](ParsedAST *AST) {
+ const SourceManager &SourceMgr = AST->getASTContext().getSourceManager();
+ const FileEntry *FE =
+ SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
+ if (!FE)
+ return;
+ SourceLocation SourceLocationBeg =
+ clangd::getBeginningOfIdentifier(*AST, Pos, FE);
+ tooling::RefactoringRuleContext Context(
+ AST->getASTContext().getSourceManager());
+ Context.setASTContext(AST->getASTContext());
+ auto Rename = clang::tooling::RenameOccurrences::initiate(
+ Context, SourceRange(SourceLocationBeg), NewName.str());
+ if (!Rename) {
+ ResultCollector.Result = Rename.takeError();
+ return;
+ }
+ Rename->invoke(ResultCollector, Context);
+ });
+ assert(ResultCollector.Result.hasValue());
+ if (!ResultCollector.Result.getValue())
+ return ResultCollector.Result->takeError();
+
+ std::vector<tooling::Replacement> Replacements;
+ for (const tooling::AtomicChange &Change : ResultCollector.Result->get()) {
+ tooling::Replacements ChangeReps = Change.getReplacements();
+ for (const auto &Rep : ChangeReps) {
+ // FIXME: Right now we only support renaming the main file, so we drop
+ // replacements not for the main file. In the future, we might consider to
+ // support:
+ // * rename in any included header
+ // * rename only in the "main" header
+ // * provide an error if there are symbols we won't rename (e.g.
+ // std::vector)
+ // * rename globally in project
+ // * rename in open files
+ if (Rep.getFilePath() == File)
+ Replacements.push_back(Rep);
+ }
+ }
+ return Replacements;
+}
+
std::string ClangdServer::getDocument(PathRef File) {
auto draft = DraftMgr.getDraft(File);
assert(draft.Draft && "File is not tracked, cannot get contents");
Modified: clang-tools-extra/trunk/clangd/ClangdServer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.h?rev=317780&r1=317779&r2=317780&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdServer.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdServer.h Thu Nov 9 03:30:04 2017
@@ -290,6 +290,10 @@ public:
std::vector<tooling::Replacement> formatFile(PathRef File);
/// Run formatting after a character was typed at \p Pos in \p File.
std::vector<tooling::Replacement> formatOnType(PathRef File, Position Pos);
+ /// Rename all occurrences of the symbol at the \p Pos in \p File to
+ /// \p NewName.
+ Expected<std::vector<tooling::Replacement>> rename(PathRef File, Position Pos,
+ llvm::StringRef NewName);
/// Gets current document contents for \p File. \p File must point to a
/// currently tracked file.
Modified: clang-tools-extra/trunk/clangd/ClangdUnit.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdUnit.cpp?rev=317780&r1=317779&r2=317780&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdUnit.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdUnit.cpp Thu Nov 9 03:30:04 2017
@@ -1007,44 +1007,6 @@ private:
}
};
-SourceLocation getBeginningOfIdentifier(ParsedAST &Unit, const Position &Pos,
- const FileEntry *FE) {
- // The language server protocol uses zero-based line and column numbers.
- // Clang uses one-based numbers.
-
- const ASTContext &AST = Unit.getASTContext();
- const SourceManager &SourceMgr = AST.getSourceManager();
-
- SourceLocation InputLocation =
- getMacroArgExpandedLocation(SourceMgr, FE, Pos);
- if (Pos.character == 0) {
- return InputLocation;
- }
-
- // This handle cases where the position is in the middle of a token or right
- // after the end of a token. In theory we could just use GetBeginningOfToken
- // to find the start of the token at the input position, but this doesn't
- // work when right after the end, i.e. foo|.
- // So try to go back by one and see if we're still inside the an identifier
- // token. If so, Take the beginning of this token.
- // (It should be the same identifier because you can't have two adjacent
- // identifiers without another token in between.)
- SourceLocation PeekBeforeLocation = getMacroArgExpandedLocation(
- SourceMgr, FE, Position{Pos.line, Pos.character - 1});
- Token Result;
- if (Lexer::getRawToken(PeekBeforeLocation, Result, SourceMgr,
- AST.getLangOpts(), false)) {
- // getRawToken failed, just use InputLocation.
- return InputLocation;
- }
-
- if (Result.is(tok::raw_identifier)) {
- return Lexer::GetBeginningOfToken(PeekBeforeLocation, SourceMgr,
- AST.getLangOpts());
- }
-
- return InputLocation;
-}
} // namespace
std::vector<Location> clangd::findDefinitions(ParsedAST &AST, Position Pos,
@@ -1436,3 +1398,43 @@ CppFile::RebuildGuard::~RebuildGuard() {
Lock.unlock();
File.RebuildCond.notify_all();
}
+
+SourceLocation clangd::getBeginningOfIdentifier(ParsedAST &Unit,
+ const Position &Pos,
+ const FileEntry *FE) {
+ // The language server protocol uses zero-based line and column numbers.
+ // Clang uses one-based numbers.
+
+ const ASTContext &AST = Unit.getASTContext();
+ const SourceManager &SourceMgr = AST.getSourceManager();
+
+ SourceLocation InputLocation =
+ getMacroArgExpandedLocation(SourceMgr, FE, Pos);
+ if (Pos.character == 0) {
+ return InputLocation;
+ }
+
+ // This handle cases where the position is in the middle of a token or right
+ // after the end of a token. In theory we could just use GetBeginningOfToken
+ // to find the start of the token at the input position, but this doesn't
+ // work when right after the end, i.e. foo|.
+ // So try to go back by one and see if we're still inside the an identifier
+ // token. If so, Take the beginning of this token.
+ // (It should be the same identifier because you can't have two adjacent
+ // identifiers without another token in between.)
+ SourceLocation PeekBeforeLocation = getMacroArgExpandedLocation(
+ SourceMgr, FE, Position{Pos.line, Pos.character - 1});
+ Token Result;
+ if (Lexer::getRawToken(PeekBeforeLocation, Result, SourceMgr,
+ AST.getLangOpts(), false)) {
+ // getRawToken failed, just use InputLocation.
+ return InputLocation;
+ }
+
+ if (Result.is(tok::raw_identifier)) {
+ return Lexer::GetBeginningOfToken(PeekBeforeLocation, SourceMgr,
+ AST.getLangOpts());
+ }
+
+ return InputLocation;
+}
Modified: clang-tools-extra/trunk/clangd/ClangdUnit.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdUnit.h?rev=317780&r1=317779&r2=317780&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdUnit.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdUnit.h Thu Nov 9 03:30:04 2017
@@ -304,6 +304,10 @@ SignatureHelp signatureHelp(PathRef File
std::shared_ptr<PCHContainerOperations> PCHs,
clangd::Logger &Logger);
+/// Get the beginning SourceLocation at a specified \p Pos.
+SourceLocation getBeginningOfIdentifier(ParsedAST &Unit, const Position &Pos,
+ const FileEntry *FE);
+
/// Get definition of symbol at a specified \p Pos.
std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos,
clangd::Logger &Logger);
Modified: clang-tools-extra/trunk/clangd/Protocol.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.cpp?rev=317780&r1=317779&r2=317780&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.cpp (original)
+++ clang-tools-extra/trunk/clangd/Protocol.cpp Thu Nov 9 03:30:04 2017
@@ -1073,3 +1073,51 @@ json::Expr SignatureHelp::unparse(const
{"signatures", json::ary(SH.signatures)},
};
}
+
+llvm::Optional<RenameParams>
+RenameParams::parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger) {
+ RenameParams Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+
+ if (KeyValue == "textDocument") {
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ continue;
+ auto *Map = dyn_cast<llvm::yaml::MappingNode>(Value);
+ if (!Map)
+ return llvm::None;
+ auto Parsed = TextDocumentIdentifier::parse(Map, Logger);
+ if (!Parsed)
+ return llvm::None;
+ Result.textDocument = std::move(*Parsed);
+ } else if (KeyValue == "position") {
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ continue;
+ auto Parsed = Position::parse(Value, Logger);
+ if (!Parsed)
+ return llvm::None;
+ Result.position = std::move(*Parsed);
+ } else if (KeyValue == "newName") {
+ auto *Value = NextKeyValue.getValue();
+ if (!Value)
+ continue;
+ auto *Node = dyn_cast<llvm::yaml::ScalarNode>(Value);
+ if (!Node)
+ return llvm::None;
+ llvm::SmallString<10> Storage;
+ Result.newName = Node->getValue(Storage);
+ } else {
+ logIgnoredField(KeyValue, Logger);
+ }
+ }
+ return Result;
+}
Modified: clang-tools-extra/trunk/clangd/Protocol.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=317780&r1=317779&r2=317780&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.h (original)
+++ clang-tools-extra/trunk/clangd/Protocol.h Thu Nov 9 03:30:04 2017
@@ -589,6 +589,20 @@ struct SignatureHelp {
static json::Expr unparse(const SignatureHelp &);
};
+struct RenameParams {
+ /// The document that was opened.
+ TextDocumentIdentifier textDocument;
+
+ /// The position at which this request was sent.
+ Position position;
+
+ /// The new name of the symbol.
+ std::string newName;
+
+ static llvm::Optional<RenameParams> parse(llvm::yaml::MappingNode *Params,
+ clangd::Logger &Logger);
+};
+
} // namespace clangd
} // namespace clang
Modified: clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp?rev=317780&r1=317779&r2=317780&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp (original)
+++ clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp Thu Nov 9 03:30:04 2017
@@ -71,6 +71,7 @@ void clangd::registerCallbackHandlers(JS
Register("textDocument/definition", &ProtocolCallbacks::onGoToDefinition);
Register("textDocument/switchSourceHeader",
&ProtocolCallbacks::onSwitchSourceHeader);
+ Register("textDocument/rename", &ProtocolCallbacks::onRename);
Register("workspace/didChangeWatchedFiles", &ProtocolCallbacks::onFileEvent);
Register("workspace/executeCommand", &ProtocolCallbacks::onCommand);
}
Modified: clang-tools-extra/trunk/clangd/ProtocolHandlers.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.h?rev=317780&r1=317779&r2=317780&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ProtocolHandlers.h (original)
+++ clang-tools-extra/trunk/clangd/ProtocolHandlers.h Thu Nov 9 03:30:04 2017
@@ -53,6 +53,7 @@ public:
virtual void onSwitchSourceHeader(Ctx C, TextDocumentIdentifier &Params) = 0;
virtual void onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) = 0;
virtual void onCommand(Ctx C, ExecuteCommandParams &Params) = 0;
+ virtual void onRename(Ctx C, RenameParams &Parames) = 0;
};
void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,
Modified: clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test?rev=317780&r1=317779&r2=317780&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test (original)
+++ clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test Thu Nov 9 03:30:04 2017
@@ -30,6 +30,7 @@ Content-Length: 142
# CHECK-NEXT: "clangd.applyFix"
# CHECK-NEXT: ]
# CHECK-NEXT: },
+# CHECK-NEXT: "renameProvider": true,
# CHECK-NEXT: "signatureHelpProvider": {
# CHECK-NEXT: "triggerCharacters": [
# CHECK-NEXT: "(",
Modified: clang-tools-extra/trunk/test/clangd/initialize-params.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/initialize-params.test?rev=317780&r1=317779&r2=317780&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clangd/initialize-params.test (original)
+++ clang-tools-extra/trunk/test/clangd/initialize-params.test Thu Nov 9 03:30:04 2017
@@ -30,6 +30,7 @@ Content-Length: 143
# CHECK-NEXT: "clangd.applyFix"
# CHECK-NEXT: ]
# CHECK-NEXT: },
+# CHECK-NEXT: "renameProvider": true,
# CHECK-NEXT: "signatureHelpProvider": {
# CHECK-NEXT: "triggerCharacters": [
# CHECK-NEXT: "(",
Added: clang-tools-extra/trunk/test/clangd/rename.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/rename.test?rev=317780&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clangd/rename.test (added)
+++ clang-tools-extra/trunk/test/clangd/rename.test Thu Nov 9 03:30:04 2017
@@ -0,0 +1,50 @@
+# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace %s
+# It is absolutely vital that this file has CRLF line endings.
+#
+Content-Length: 125
+
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
+
+Content-Length: 150
+
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///foo.cpp","languageId":"cpp","version":1,"text":"int foo;"}}}
+
+Content-Length: 159
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/rename","params":{"textDocument":{"uri":"file:///foo.cpp"},"position":{"line":0,"character":5},"newName":"bar"}}
+# CHECK: "id": 1,
+# CHECK-NEXT: "jsonrpc": "2.0",
+# CHECK-NEXT: "result": {
+# CHECK-NEXT: "changes": {
+# CHECK-NEXT: "file:///foo.cpp": [
+# CHECK-NEXT: {
+# CHECK-NEXT: "newText": "bar",
+# CHECK-NEXT: "range": {
+# CHECK-NEXT: "end": {
+# CHECK-NEXT: "character": 7
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: },
+# CHECK-NEXT: "start": {
+# CHECK-NEXT: "character": 4
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+Content-Length: 159
+
+{"jsonrpc":"2.0","id":2,"method":"textDocument/rename","params":{"textDocument":{"uri":"file:///foo.cpp"},"position":{"line":0,"character":2},"newName":"bar"}}
+# CHECK: "error": {
+# CHECK-NEXT: "code": -32603,
+# CHECK-NEXT: "message": "clang diagnostic"
+# CHECK-NEXT: },
+# CHECK-NEXT: "id": 2,
+# CHECK-NEXT: "jsonrpc": "2.0"
+Content-Length: 44
+
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+Content-Length: 33
+
+{"jsonrpc":"2.0":"method":"exit"}
More information about the cfe-commits
mailing list