Hi, On Mon, Oct 9, 2017 at 9:58 AM, Ilya Biryukov via cfe-commits <cfe-commits@lists.llvm.org> wrote: > Author: ibiryukov > Date: Mon Oct 9 09:58:16 2017 > New Revision: 315214 > > URL: http://llvm.org/viewvc/llvm-project?rev=315214&view=rev > Log: > [clangd] Added a command-line arg to mirror clangd input into a file. > > Summary: The arg is useful for debugging and creating test cases. > > Reviewers: bkramer, krasimir > > Reviewed By: bkramer > > Subscribers: klimek, cfe-commits > > Differential Revision: https://reviews.llvm.org/D37970 > > Added: > clang-tools-extra/trunk/test/clangd/input-mirror.test > Modified: > clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp > clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h > clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp > > Modified: clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp > URL: > http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp?rev=315214&r1=315213&r2=315214&view=diff > ============================================================================== > --- clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp (original) > +++ clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp Mon Oct 9 09:58:16 > 2017 > @@ -37,6 +37,14 @@ void JSONOutput::log(const Twine &Messag > Logs.flush(); > } > > +void JSONOutput::mirrorInput(const Twine &Message) { > + if (!InputMirror) > + return; > + > + *InputMirror << Message; > + InputMirror->flush(); > +} > + > void Handler::handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) { > Output.log("Method ignored.\n"); > // Return that this method is unsupported. > @@ -147,6 +155,14 @@ void clangd::runLanguageServerLoop(std:: > continue; > } > > + Out.mirrorInput(Line); > + // Mirror '\n' that gets consumed by std::getline, but is not included > in > + // the resulting Line. > + // Note that '\r' is part of Line, so we don't need to mirror it > + // separately. > + if (!In.eof()) > + Out.mirrorInput("\n"); > + > llvm::StringRef LineRef(Line); > > // We allow YAML-style comments in headers. Technically this isn't part > @@ -163,9 +179,8 @@ void clangd::runLanguageServerLoop(std:: > if (LineRef.consume_front("Content-Length: ")) { > if (ContentLength != 0) { > Out.log("Warning: Duplicate Content-Length header received. " > - "The previous value for this message (" > - + std::to_string(ContentLength) > - + ") was ignored.\n"); > + "The previous value for this message (" + > + std::to_string(ContentLength) + ") was ignored.\n"); > } > > llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength); > @@ -185,15 +200,13 @@ void clangd::runLanguageServerLoop(std:: > // parser. > std::vector<char> JSON(ContentLength + 1, '\0'); > In.read(JSON.data(), ContentLength); > + Out.mirrorInput(StringRef(JSON.data(), In.gcount())); > > // If the stream is aborted before we read ContentLength bytes, In > // will have eofbit and failbit set. > if (!In) { > - Out.log("Input was aborted. Read only " > - + std::to_string(In.gcount()) > - + " bytes of expected " > - + std::to_string(ContentLength) > - + ".\n"); > + Out.log("Input was aborted. Read only " + > std::to_string(In.gcount()) + > + " bytes of expected " + std::to_string(ContentLength) + > ".\n"); > break; > } > > @@ -209,8 +222,8 @@ void clangd::runLanguageServerLoop(std:: > if (IsDone) > break; > } else { > - Out.log( "Warning: Missing Content-Length header, or message has zero " > - "length.\n" ); > + Out.log("Warning: Missing Content-Length header, or message has zero " > + "length.\n"); > } > } > } > > Modified: clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h > URL: > http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h?rev=315214&r1=315213&r2=315214&view=diff > ============================================================================== > --- clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h (original) > +++ clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h Mon Oct 9 09:58:16 > 2017 > @@ -24,8 +24,9 @@ namespace clangd { > /// them. > class JSONOutput : public Logger { > public: > - JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs) > - : Outs(Outs), Logs(Logs) {} > + JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs, > + llvm::raw_ostream *InputMirror = nullptr) > + : Outs(Outs), Logs(Logs), InputMirror(InputMirror) {} > > /// Emit a JSONRPC message. > void writeMessage(const Twine &Message); > @@ -33,9 +34,15 @@ public: > /// Write to the logging stream. > void log(const Twine &Message) override; > > + /// Mirror \p Message into InputMirror stream. Does nothing if InputMirror > is > + /// null. > + /// Unlike other methods of JSONOutput, mirrorInput is not thread-safe. > + void mirrorInput(const Twine &Message); > + > private: > llvm::raw_ostream &Outs; > llvm::raw_ostream &Logs; > + llvm::raw_ostream *InputMirror; > > std::mutex StreamMutex; > }; > > Modified: clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp > URL: > http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp?rev=315214&r1=315213&r2=315214&view=diff > ============================================================================== > --- clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp (original) > +++ clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp Mon Oct 9 09:58:16 > 2017 > @@ -9,10 +9,12 @@ > > #include "ClangdLSPServer.h" > #include "JSONRPCDispatcher.h" > +#include "Path.h" > #include "llvm/Support/CommandLine.h" > #include "llvm/Support/FileSystem.h" > #include "llvm/Support/Path.h" > #include "llvm/Support/Program.h" > +#include "llvm/Support/raw_ostream.h" > #include <iostream> > #include <memory> > #include <string> > @@ -43,11 +45,17 @@ static llvm::cl::opt<bool> RunSynchronou > llvm::cl::desc("Parse on main thread. If set, -j is ignored"), > llvm::cl::init(false), llvm::cl::Hidden); > > -static llvm::cl::opt<std::string> > +static llvm::cl::opt<Path> > ResourceDir("resource-dir", > llvm::cl::desc("Directory for system clang headers"), > llvm::cl::init(""), llvm::cl::Hidden); > > +static llvm::cl::opt<Path> InputMirrorFile( > + "input-mirror-file", > + llvm::cl::desc( > + "Mirror all LSP input to the specified file. Useful for debugging."), > + llvm::cl::init(""), llvm::cl::Hidden); > + > int main(int argc, char *argv[]) { > llvm::cl::ParseCommandLineOptions(argc, argv, "clangd"); > > @@ -63,9 +71,21 @@ int main(int argc, char *argv[]) { > WorkerThreadsCount = 0; > > /// Validate command line arguments. > + llvm::Optional<llvm::raw_fd_ostream> InputMirrorStream; > + if (!InputMirrorFile.empty()) { > + std::error_code EC; > + InputMirrorStream.emplace(InputMirrorFile, /*ref*/ EC, > llvm::sys::fs::F_RW); > + if (EC) { > + InputMirrorStream.reset(); > + llvm::errs() << "Error while opening an input mirror file: " > + << EC.message(); > + } > + } > + > llvm::raw_ostream &Outs = llvm::outs(); > llvm::raw_ostream &Logs = llvm::errs(); > - JSONOutput Out(Outs, Logs); > + JSONOutput Out(Outs, Logs, > + InputMirrorStream ? InputMirrorStream.getPointer() : > nullptr); > > // If --compile-commands-dir arg was invoked, check value and override > default > // path. > > Added: clang-tools-extra/trunk/test/clangd/input-mirror.test > URL: > http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/input-mirror.test?rev=315214&view=auto > ============================================================================== > --- clang-tools-extra/trunk/test/clangd/input-mirror.test (added) > +++ clang-tools-extra/trunk/test/clangd/input-mirror.test Mon Oct 9 09:58:16 > 2017 > @@ -0,0 +1,154 @@ > +# RUN: clangd -run-synchronously -input-mirror-file %t < %s > +# Note that we have to use '-Z' as -input-mirror-file does not have a > newline at the end of file.
"-Z" isn't supported in some versions of diff, I tested "-b" and it seems to do the job here, can you change that? Thanks, > +# RUN: diff -Z %t %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: 172 > + > +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"int > main() {\nint a;\na;\n}\n"}}} > + > +Content-Length: 148 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":0}}} > +# Go to local variable > + > +Content-Length: 148 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":1}}} > +# Go to local variable, end of token > + > +Content-Length: 214 > + > +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":2},"contentChanges":[{"text":"struct > Foo {\nint x;\n};\nint main() {\n Foo bar = { x : 1 };\n}\n"}]}} > + > +Content-Length: 149 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":14}}} > +# Go to field, GNU old-style field designator > + > +Content-Length: 215 > + > +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":3},"contentChanges":[{"text":"struct > Foo {\nint x;\n};\nint main() {\n Foo baz = { .x = 2 };\n}\n"}]}} > + > +Content-Length: 149 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":15}}} > +# Go to field, field designator > + > +Content-Length: 187 > + > +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":4},"contentChanges":[{"text":"int > main() {\n main();\n return 0;\n}"}]}} > + > +Content-Length: 148 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":3}}} > +# Go to function declaration, function call > + > +Content-Length: 208 > + > +{"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"}]}} > + > +Content-Length: 148 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":3}}} > +# Go to struct declaration, new struct instance > + > +Content-Length: 231 > + > +{"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"}]}} > + > +Content-Length: 148 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":4}}} > +# Go to struct declaration, new struct instance, qualified name > + > +Content-Length: 215 > + > +{"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"}]}} > + > +Content-Length: 148 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}} > +# Go to field declaration, field reference > + > +Content-Length: 220 > + > +{"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"}]}} > + > +Content-Length: 148 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}} > +# Go to method declaration, method call > + > +Content-Length: 240 > + > +{"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"}]}} > + > +Content-Length: 149 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":10}}} > +# Go to typedef > + > +Content-Length: 254 > + > +{"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"}]}} > + > +Content-Length: 149 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":13}}} > +# Go to template type parameter. Fails until clangIndex is modified to > handle those. > +# no-CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", > "range": {"start": {"line": 0, "character": 10}, "end": {"line": 0, > "character": 34}}}]} > + > +Content-Length: 256 > + > +{"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"}]}} > + > +Content-Length: 148 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":6,"character":4}}} > +# Go to namespace, static method call > + > +Content-Length: 265 > + > +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace > ns {\nstruct Foo {\n int field;\n Foo(int param) : field(param) > {}\n};\n}\nint main() {\n return 0;\n}\n"}]}} > + > +Content-Length: 149 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":21}}} > +# Go to field, member initializer > + > +Content-Length: 204 > + > +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define > MY_MACRO 0\nint main() {\n return MY_MACRO;\n}\n"}]}} > + > +Content-Length: 148 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":9}}} > +# Go to macro. > + > +Content-Length: 217 > + > +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define > FOO 1\nint a = FOO;\n#define FOO 2\nint b = FOO;\n#undef FOO\n"}]}} > + > +Content-Length: 148 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":8}}} > +# Go to macro, re-defined later > + > +Content-Length: 148 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":8}}} > +# Go to macro, undefined later > + > +Content-Length: 148 > + > +{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":7}}} > +# Go to macro, being undefined > + > +Content-Length: 44 > + > +{"jsonrpc":"2.0","id":3,"method":"shutdown"} > > > _______________________________________________ > cfe-commits mailing list > cfe-commits@lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits -- Bruno Cardoso Lopes http://www.brunocardoso.cc _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits