[clang-tools-extra] r317486 - Adds a json::Expr type to represent intermediate JSON expressions.

Yung, Douglas via cfe-commits cfe-commits at lists.llvm.org
Wed Nov 8 19:26:25 PST 2017


Hi Sam, first I want to thank you for fixing up the test so that the build bot is green again, that is very much appreciated!

Secondly, I want to say that this issue does not seem to affect everything on Windows. I’m not familiar with how JSON related stuff is supposed to work exactly, but even with the short example I provided before, not all of the URIs when run on Windows included the “C:/” part.

To expand on this, here again is the original repro case that I sent:

# Begin failure.test
Content-Length: 125

{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}

Content-Length: 217

{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp<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<file:///\\main.cpp>"},"position":{"line":1,"character":8}}}
# End failure.test

I have attached the full output of running “clangd.exe –pretty –run-synchronously < failure.test” as result.txt, but I want to specifically call out the output produced by the second and third calls. The second one produces the following uri value:

"uri": “file:///main.cpp<file:///\\main.cpp>”

While the third one instead produces this uri value:

"uri": “file:///C:/main.cpp<file:///C:\main.cpp>”

Both of these were produced by the same run on my Windows box, so it seems something else might be triggering the reinterpretation of the uri.

Douglas Yung


From: Ilya Biryukov [mailto:ibiryukov at google.com]
Sent: Wednesday, November 08, 2017 8:08
To: Marc-André Laperle
Cc: Sam McCall; cfe-commits; Yung, Douglas
Subject: Re: [clang-tools-extra] r317486 - Adds a json::Expr type to represent intermediate JSON expressions.

Clang remembers the last name used to access the file in FileManager and we get the filename from FileManager later, so it's not that surprising that it all broke apart.
Sam said that using file:///c:/main.cpp<file:///c:\main.cpp> crashes clang on Linux for some reason, we'll probably have to resort to regexes as an immediate solution and figure out how to fix the crash later.

On Wed, Nov 8, 2017 at 8:47 PM, Marc-André Laperle <marc-andre.laperle at ericsson.com<mailto:marc-andre.laperle at ericsson.com>> wrote:

+1 for file://c:/main.cpp<file:///c:\main.cpp>

(A bit surprised we only see this problem now though?)

Marc-André

________________________________
From: Ilya Biryukov <ibiryukov at google.com<mailto:ibiryukov at google.com>>
Sent: Wednesday, November 8, 2017 8:29 AM
To: Sam McCall
Cc: Marc-André Laperle; cfe-commits; Yung, Douglas
Subject: Re: [clang-tools-extra] r317486 - Adds a json::Expr type to represent intermediate JSON expressions.

Yeah and `file://c:/main.cpp` looks like the best option to me.

On Wed, Nov 8, 2017 at 6:26 PM, Sam McCall <sam.mccall at gmail.com<mailto:sam.mccall at gmail.com>> wrote:
Oops, +list.

r317686 makes just this test fuzzy, to unbreak the buildbot.

On Wed, Nov 8, 2017 at 1:15 PM, Sam McCall <sam.mccall at gmail.com<mailto:sam.mccall at gmail.com>> wrote:
On Wed, Nov 8, 2017 at 12:55 PM, Ilya Biryukov <ibiryukov at google.com<mailto:ibiryukov at google.com>> wrote:
What's the exact problem with "file://" URIs on Windows? How to properly convert the Windows-style absolute paths into those? (i.e. "C:\Users\Admin\MyProject\Main.cpp"?)
If so, there is an overview in Wikipedia (https://en.wikipedia.org/wiki/File_URI_scheme#Windows_2). In short, they should look this way: file:///c/Users/Admin/MyProject/Main.cpp<file:///\\c\Users\Admin\MyProject\Main.cpp>..
nit: the c needs to keep its colon: file:///c:/Users/<file:///c:\Users\>...

Yeah, when I thought about this some more, it's mostly that the tests assume we can pretend there's a file /main.cpp = file:///main.cpp<file:///\\main.cpp>.
On windows, canonicalizing that filename results in C:\main.cpp = file:///C:/main.cpp<file:///C:\main.cpp>, which is not what our tests expect.

Options I see:
 - make every test that checks a filename fuzzy (this seems doomed to failure)
 - find a URI that works with both unix and windows paths (file:///C:/main.cpp<file:///C:\main.cpp>?)
 - avoid canonicalizing filenames in ways visible to tests
 - ???

On Wed, Nov 8, 2017 at 4:37 PM, Sam McCall <sam.mccall at gmail.com<mailto:sam.mccall at gmail.com>> wrote:
I think all the JSON-related windows breakages are now fixed, and the remaining definitions.test breakage was caused by r317585.
(Currently trying to get a windows machine to verify this)

I have to confess I don't know much about how file:// URIs should work on windows. Marc-Andre - any thoughts, or maybe we should revert until we can work this one out?

On Wed, Nov 8, 2017 at 3:39 AM, Yung, Douglas <douglas.yung at sony.com<mailto:douglas.yung at sony.com>> wrote:
Hi Sam,

The test definitions.test is currently failing on the PS4 Windows bot because the URI returned is of the format "file:///C:/main.cpp<file:///C:\main.cpp>" instead of the expected "file:///main.cpp<file:///\\main.cpp>" when run on a Windows machine. Can you please fix this ASAP so that we can get the bot green again?

Here you can see the failure on the build bot:
http://lab.llvm.org:8011/builders/llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast/builds/13343

You can reproduce the failure by feeding the following file to clangd on Windows:

# Begin failure.test
Content-Length: 125

{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}

Content-Length: 217

{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp<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<file:///\\main.cpp>"},"position":{"line":1,"character":8}}}
# End failure.test


This is cut down from test\clangd\definitions.test. The failure is on line 337 where it is looking for this:

# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"

However, clangd is generating:

"uri": "file:///C:/main.cpp<file:///C:\main.cpp>"

If you could fix this as soon as possible it would be greatly appreciated since it is causing the PS4 Windows bot to be red.

Thanks!

Douglas Yung

> -----Original Message-----
> From: cfe-commits [mailto:cfe-commits-bounces at lists.llvm.org<mailto:cfe-commits-bounces at lists.llvm.org>] On Behalf Of Sam
> McCall via cfe-commits
> Sent: Monday, November 06, 2017 7:41
> To: cfe-commits at lists.llvm.org<mailto:cfe-commits at lists.llvm.org>
> Subject: [clang-tools-extra] r317486 - Adds a json::Expr type to represent
> intermediate JSON expressions.
>
> Author: sammccall
> Date: Mon Nov  6 07:40:30 2017
> New Revision: 317486
>
> URL: http://llvm.org/viewvc/llvm-project?rev=317486&view=rev
> Log:
> Adds a json::Expr type to represent intermediate JSON expressions.
>
> Summary:
> This form can be created with a nice clang-format-friendly literal syntax,
> and gets escaping right. It knows how to call unparse() on our Protocol types.
> All the places where we pass around JSON internally now use this type.
>
> Object properties are sorted (stored as std::map) and so serialization is
> canonicalized, with optional prettyprinting (triggered by a -pretty flag).
> This makes the lit tests much nicer to read and somewhat nicer to debug.
> (Unfortunately the completion tests use CHECK-DAG, which only has
> line-granularity, so pretty-printing is disabled there. In future we
> could make completion ordering deterministic, or switch to unittests).
>
> Compared to the current approach, it has some efficiencies like avoiding
> copies
> of string literals used as object keys, but is probably slower overall.
> I think the code/test quality benefits are worth it.
>
> This patch doesn't attempt to do anything about JSON *parsing*.
> It takes direction from the proposal in this doc[1], but is limited in scope
> and visibility, for now.
> I am of half a mind just to use Expr as the target of a parser, and maybe do a
> little string deduplication, but not bother with clever memory allocation.
> That would be simple, and fast enough for clangd...
> [1]
> https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM
> 4/edit
>
> +cc d0k so he can tell me not to use std::map.
>
> Reviewers: ioeric, malaperle
>
> Subscribers: bkramer, ilya-biryukov, mgorny, klimek
>
> Differential Revision: https://reviews.llvm.org/D39435
>
> Added:
>     clang-tools-extra/trunk/clangd/JSONExpr.cpp
>     clang-tools-extra/trunk/clangd/JSONExpr.h
>     clang-tools-extra/trunk/unittests/clangd/JSONExprTests.cpp
> Modified:
>     clang-tools-extra/trunk/clangd/CMakeLists.txt
>     clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
>     clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp
>     clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h
>     clang-tools-extra/trunk/clangd/Protocol.cpp
>     clang-tools-extra/trunk/clangd/Protocol.h
>     clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp
>     clang-tools-extra/trunk/test/clangd/authority-less-uri.test
>     clang-tools-extra/trunk/test/clangd/completion-items-kinds.test
>     clang-tools-extra/trunk/test/clangd/completion-priorities.test
>     clang-tools-extra/trunk/test/clangd/completion-qualifiers.test
>     clang-tools-extra/trunk/test/clangd/completion-snippet.test
>     clang-tools-extra/trunk/test/clangd/completion.test
>     clang-tools-extra/trunk/test/clangd/definitions.test
>     clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test
>     clang-tools-extra/trunk/test/clangd/diagnostics.test
>     clang-tools-extra/trunk/test/clangd/did-change-watch-files.test
>     clang-tools-extra/trunk/test/clangd/execute-command.test
>     clang-tools-extra/trunk/test/clangd/extra-flags.test
>     clang-tools-extra/trunk/test/clangd/fixits.test
>     clang-tools-extra/trunk/test/clangd/formatting.test
>     clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test
>     clang-tools-extra/trunk/test/clangd/initialize-params.test
>     clang-tools-extra/trunk/test/clangd/input-mirror.test
>     clang-tools-extra/trunk/test/clangd/protocol.test
>     clang-tools-extra/trunk/test/clangd/signature-help.test
>     clang-tools-extra/trunk/test/clangd/unsupported-method.test
>     clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt
>
> Modified: clang-tools-extra/trunk/clangd/CMakeLists.txt
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/clangd/CMakeLists.txt?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/clangd/CMakeLists.txt (original)
> +++ clang-tools-extra/trunk/clangd/CMakeLists.txt Mon Nov  6 07:40:30 2017
> @@ -10,6 +10,7 @@ add_clang_library(clangDaemon
>    DraftStore.cpp
>    GlobalCompilationDatabase.cpp
>    JSONRPCDispatcher.cpp
> +  JSONExpr.cpp
>    Logger.cpp
>    Protocol.cpp
>    ProtocolHandlers.cpp
>
> Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/clangd/ClangdLSPServer.cpp?rev=317486&r1=317485&r2=317486&view=dif
> f
> ==============================================================================
> --- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original)
> +++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Mon Nov  6 07:40:30
> 2017
> @@ -20,35 +20,46 @@ namespace {
>  std::vector<TextEdit>
>  replacementsToEdits(StringRef Code,
>                      const std::vector<tooling::Replacement> &Replacements) {
> -  std::vector<TextEdit> Edits;
>    // Turn the replacements into the format specified by the Language Server
> -  // Protocol.
> +  // Protocol. Fuse them into one big JSON array.
> +  std::vector<TextEdit> Edits;
>    for (auto &R : Replacements) {
>      Range ReplacementRange = {
>          offsetToPosition(Code, R.getOffset()),
>          offsetToPosition(Code, R.getOffset() + R.getLength())};
>      Edits.push_back({ReplacementRange, R.getReplacementText()});
>    }
> -
>    return Edits;
>  }
>
>  } // namespace
>
>  void ClangdLSPServer::onInitialize(Ctx C, InitializeParams &Params) {
> -  C.reply(
> -      R"({"capabilities":{
> -          "textDocumentSync": 1,
> -          "documentFormattingProvider": true,
> -          "documentRangeFormattingProvider": true,
> -          "documentOnTypeFormattingProvider":
> {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
> -          "codeActionProvider": true,
> -          "completionProvider": {"resolveProvider": false,
> "triggerCharacters": [".",">",":"]},
> -          "signatureHelpProvider": {"triggerCharacters": ["(",","]},
> -          "definitionProvider": true,
> -          "executeCommandProvider": {"commands": [")" +
> -      ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND + R"("]}
> -        }})");
> +  C.reply(json::obj{
> +      {"textDocumentSync", 1},
> +      {"documentFormattingProvider", true},
> +      {"documentRangeFormattingProvider", true},
> +      {"documentOnTypeFormattingProvider",
> +       json::obj{
> +           {"firstTriggerCharacter", "}"},
> +           {"moreTriggerCharacter", {}},
> +       }},
> +      {"codeActionProvider", true},
> +      {"completionProvider",
> +       json::obj{
> +           {"resolveProvider", false},
> +           {"triggerCharacters", {".", ">", ":"}},
> +       }},
> +      {"signatureHelpProvider",
> +       json::obj{
> +           {"triggerCharacters", {"(", ","}},
> +       }},
> +      {"definitionProvider", true},
> +      {"executeCommandProvider",
> +       json::obj{
> +           {"commands", {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}},
> +       }},
> +  });
>    if (Params.rootUri && !Params.rootUri->file.empty())
>      Server.setRootPath(Params.rootUri->file);
>    else if (Params.rootPath && !Params.rootPath->empty())
> @@ -58,7 +69,7 @@ void ClangdLSPServer::onInitialize(Ctx C
>  void ClangdLSPServer::onShutdown(Ctx C, ShutdownParams &Params) {
>    // Do essentially nothing, just say we're ready to exit.
>    ShutdownRequestReceived = true;
> -  C.reply("null");
> +  C.reply(nullptr);
>  }
>
>  void ClangdLSPServer::onExit(Ctx C, ExitParams &Params) { IsDone = true; }
> @@ -98,7 +109,7 @@ void ClangdLSPServer::onCommand(Ctx C, E
>
>      ApplyWorkspaceEditParams ApplyEdit;
>      ApplyEdit.edit = *Params.workspaceEdit;
> -    C.reply("\"Fix applied.\"");
> +    C.reply("Fix applied.");
>      // We don't need the response so id == 1 is OK.
>      // Ideally, we would wait for the response and if there is no error, we
>      // would reply success/failure to the original RPC.
> @@ -121,51 +132,45 @@ void ClangdLSPServer::onDocumentOnTypeFo
>      Ctx C, DocumentOnTypeFormattingParams &Params) {
>    auto File = Params.textDocument.uri.file;
>    std::string Code = Server.getDocument(File);
> -  std::string Edits = TextEdit::unparse(
> -      replacementsToEdits(Code, Server.formatOnType(File, Params.position)));
> -  C.reply(Edits);
> +  C.reply(json::ary(
> +      replacementsToEdits(Code, Server.formatOnType(File,
> Params.position))));
>  }
>
>  void ClangdLSPServer::onDocumentRangeFormatting(
>      Ctx C, DocumentRangeFormattingParams &Params) {
>    auto File = Params.textDocument.uri.file;
>    std::string Code = Server.getDocument(File);
> -  std::string Edits = TextEdit::unparse(
> -      replacementsToEdits(Code, Server.formatRange(File, Params.range)));
> -  C.reply(Edits);
> +  C.reply(json::ary(
> +      replacementsToEdits(Code, Server.formatRange(File, Params.range))));
>  }
>
>  void ClangdLSPServer::onDocumentFormatting(Ctx C,
>                                             DocumentFormattingParams &Params)
> {
>    auto File = Params.textDocument.uri.file;
>    std::string Code = Server.getDocument(File);
> -  std::string Edits =
> -      TextEdit::unparse(replacementsToEdits(Code, Server.formatFile(File)));
> -  C.reply(Edits);
> +  C.reply(json::ary(replacementsToEdits(Code, Server.formatFile(File))));
>  }
>
>  void ClangdLSPServer::onCodeAction(Ctx C, CodeActionParams &Params) {
>    // We provide a code action for each diagnostic at the requested location
>    // which has FixIts available.
>    std::string Code = Server.getDocument(Params.textDocument.uri.file);
> -  std::string Commands;
> +  json::ary Commands;
>    for (Diagnostic &D : Params.context.diagnostics) {
>      std::vector<clang::tooling::Replacement> Fixes =
>          getFixIts(Params.textDocument.uri.file, D);
>      auto Edits = replacementsToEdits(Code, Fixes);
> -    WorkspaceEdit WE;
> -    WE.changes = {{llvm::yaml::escape(Params.textDocument.uri.uri), Edits}};
> -
> -    if (!Edits.empty())
> -      Commands +=
> -          R"({"title":"Apply FixIt ')" + llvm::yaml::escape(D.message) +
> -          R"('", "command": ")" +
> -          ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND +
> -          R"(", "arguments": [)" + WorkspaceEdit::unparse(WE) + R"(]},)";
> +    if (!Edits.empty()) {
> +      WorkspaceEdit WE;
> +      WE.changes = {{Params.textDocument.uri.uri, std::move(Edits)}};
> +      Commands.push_back(json::obj{
> +          {"title", llvm::formatv("Apply FixIt {0}", D.message)},
> +          {"command", ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND},
> +          {"arguments", {WE}},
> +      });
> +    }
>    }
> -  if (!Commands.empty())
> -    Commands.pop_back();
> -  C.reply("[" + Commands + "]");
> +  C.reply(std::move(Commands));
>  }
>
>  void ClangdLSPServer::onCompletion(Ctx C, TextDocumentPositionParams &Params)
> {
> @@ -177,15 +182,7 @@ void ClangdLSPServer::onCompletion(Ctx C
>                            // had an API that would allow to attach callbacks
> to
>                            // futures returned by ClangdServer.
>                     .Value;
> -
> -  std::string Completions;
> -  for (const auto &Item : Items) {
> -    Completions += CompletionItem::unparse(Item);
> -    Completions += ",";
> -  }
> -  if (!Completions.empty())
> -    Completions.pop_back();
> -  C.reply("[" + Completions + "]");
> +  C.reply(json::ary(Items));
>  }
>
>  void ClangdLSPServer::onSignatureHelp(Ctx C,
> @@ -195,7 +192,7 @@ void ClangdLSPServer::onSignatureHelp(Ct
>        Position{Params.position.line, Params.position.character});
>    if (!SignatureHelp)
>      return C.replyError(-32602, llvm::toString(SignatureHelp.takeError()));
> -  C.reply(SignatureHelp::unparse(SignatureHelp->Value));
> +  C.reply(SignatureHelp->Value);
>  }
>
>  void ClangdLSPServer::onGoToDefinition(Ctx C,
> @@ -205,22 +202,14 @@ void ClangdLSPServer::onGoToDefinition(C
>        Position{Params.position.line, Params.position.character});
>    if (!Items)
>      return C.replyError(-32602, llvm::toString(Items.takeError()));
> -
> -  std::string Locations;
> -  for (const auto &Item : Items->Value) {
> -    Locations += Location::unparse(Item);
> -    Locations += ",";
> -  }
> -  if (!Locations.empty())
> -    Locations.pop_back();
> -  C.reply("[" + Locations + "]");
> +  C.reply(json::ary(Items->Value));
>  }
>
>  void ClangdLSPServer::onSwitchSourceHeader(Ctx C,
>                                             TextDocumentIdentifier &Params) {
>    llvm::Optional<Path> Result = Server.switchSourceHeader(Params.uri.file);
>    std::string ResultUri;
> -  C.reply(Result ? URI::unparse(URI::fromFile(*Result)) : R"("")");
> +  C.reply(Result ? URI::fromFile(*Result).uri : "");
>  }
>
>  ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
> @@ -270,17 +259,16 @@ ClangdLSPServer::getFixIts(StringRef Fil
>
>  void ClangdLSPServer::onDiagnosticsReady(
>      PathRef File, Tagged<std::vector<DiagWithFixIts>> Diagnostics) {
> -  std::string DiagnosticsJSON;
> +  json::ary DiagnosticsJSON;
>
>    DiagnosticToReplacementMap LocalFixIts; // Temporary storage
>    for (auto &DiagWithFixes : Diagnostics.Value) {
>      auto Diag = DiagWithFixes.Diag;
> -    DiagnosticsJSON +=
> -        R"({"range":)" + Range::unparse(Diag.range) +
> -        R"(,"severity":)" + std::to_string(Diag.severity) +
> -        R"(,"message":")" + llvm::yaml::escape(Diag.message) +
> -        R"("},)";
> -
> +    DiagnosticsJSON.push_back(json::obj{
> +        {"range", Diag.range},
> +        {"severity", Diag.severity},
> +        {"message", Diag.message},
> +    });
>      // We convert to Replacements to become independent of the SourceManager.
>      auto &FixItsForDiagnostic = LocalFixIts[Diag];
>      std::copy(DiagWithFixes.FixIts.begin(), DiagWithFixes.FixIts.end(),
> @@ -295,10 +283,13 @@ void ClangdLSPServer::onDiagnosticsReady
>    }
>
>    // Publish diagnostics.
> -  if (!DiagnosticsJSON.empty())
> -    DiagnosticsJSON.pop_back(); // Drop trailing comma.
> -  Out.writeMessage(
> -
> R"({"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri"
> :")" +
> -      URI::fromFile(File).uri + R"(","diagnostics":[)" + DiagnosticsJSON +
> -      R"(]}})");
> +  Out.writeMessage(json::obj{
> +      {"jsonrpc", "2.0"},
> +      {"method", "textDocument/publishDiagnostics"},
> +      {"params",
> +       json::obj{
> +           {"uri", URI::fromFile(File)},
> +           {"diagnostics", std::move(DiagnosticsJSON)},
> +       }},
> +  });
>  }
>
> Added: clang-tools-extra/trunk/clangd/JSONExpr.cpp
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/clangd/JSONExpr.cpp?rev=317486&view=auto
> ==============================================================================
> --- clang-tools-extra/trunk/clangd/JSONExpr.cpp (added)
> +++ clang-tools-extra/trunk/clangd/JSONExpr.cpp Mon Nov  6 07:40:30 2017
> @@ -0,0 +1,216 @@
> +#include "JSONExpr.h"
> +
> +#include "llvm/Support/Format.h"
> +
> +namespace clang {
> +namespace clangd {
> +namespace json {
> +using namespace llvm;
> +
> +void Expr::copyFrom(const Expr &M) {
> +  Type = M.Type;
> +  switch (Type) {
> +  case T_Null:
> +  case T_Boolean:
> +  case T_Number:
> +    memcpy(Union.buffer, M.Union.buffer, sizeof(Union.buffer));
> +    break;
> +  case T_StringRef:
> +    create<StringRef>(M.as<StringRef>());
> +    break;
> +  case T_String:
> +    create<std::string>(M.as<std::string>());
> +    break;
> +  case T_Object:
> +    create<Object>(M.as<Object>());
> +    break;
> +  case T_Array:
> +    create<Array>(M.as<Array>());
> +    break;
> +  }
> +}
> +
> +void Expr::moveFrom(const Expr &&M) {
> +  Type = M.Type;
> +  switch (Type) {
> +  case T_Null:
> +  case T_Boolean:
> +  case T_Number:
> +    memcpy(Union.buffer, M.Union.buffer, sizeof(Union.buffer));
> +    break;
> +  case T_StringRef:
> +    create<StringRef>(M.as<StringRef>());
> +    break;
> +  case T_String:
> +    create<std::string>(std::move(M.as<std::string>()));
> +    M.Type = T_Null;
> +    break;
> +  case T_Object:
> +    create<Object>(std::move(M.as<Object>()));
> +    M.Type = T_Null;
> +    break;
> +  case T_Array:
> +    create<Array>(std::move(M.as<Array>()));
> +    M.Type = T_Null;
> +    break;
> +  }
> +}
> +
> +void Expr::destroy() {
> +  switch (Type) {
> +  case T_Null:
> +  case T_Boolean:
> +  case T_Number:
> +    break;
> +  case T_StringRef:
> +    as<StringRef>().~StringRef();
> +    break;
> +  case T_String:
> +    as<std::string>().~basic_string();
> +    break;
> +  case T_Object:
> +    as<Object>().~Object();
> +    break;
> +  case T_Array:
> +    as<Array>().~Array();
> +    break;
> +  }
> +}
> +
> +} // namespace json
> +} // namespace clangd
> +} // namespace clang
> +
> +namespace {
> +void quote(llvm::raw_ostream &OS, llvm::StringRef S) {
> +  OS << '\"';
> +  for (unsigned char C : S) {
> +    if (C == 0x22 || C == 0x5C)
> +      OS << '\\';
> +    if (C >= 0x20) {
> +      OS << C;
> +      continue;
> +    }
> +    OS << '\\';
> +    switch (C) {
> +    // A few characters are common enough to make short escapes worthwhile.
> +    case '\t':
> +      OS << 't';
> +      break;
> +    case '\n':
> +      OS << 'n';
> +      break;
> +    case '\r':
> +      OS << 'r';
> +      break;
> +    default:
> +      OS << 'u';
> +      llvm::write_hex(OS, C, llvm::HexPrintStyle::Lower, 4);
> +      break;
> +    }
> +  }
> +  OS << '\"';
> +}
> +
> +enum IndenterAction {
> +  Indent,
> +  Outdent,
> +  Newline,
> +  Space,
> +};
> +} // namespace
> +
> +// Prints JSON. The indenter can be used to control formatting.
> +template <typename Indenter>
> +void clang::clangd::json::Expr::print(raw_ostream &OS,
> +                                      const Indenter &I) const {
> +  switch (Type) {
> +  case T_Null:
> +    OS << "null";
> +    break;
> +  case T_Boolean:
> +    OS << (as<bool>() ? "true" : "false");
> +    break;
> +  case T_Number:
> +    OS << format("%g", as<double>());
> +    break;
> +  case T_StringRef:
> +    quote(OS, as<StringRef>());
> +    break;
> +  case T_String:
> +    quote(OS, as<std::string>());
> +    break;
> +  case T_Object: {
> +    bool Comma = false;
> +    OS << '{';
> +    I(Indent);
> +    for (const auto &P : as<Expr::Object>()) {
> +      if (Comma)
> +        OS << ',';
> +      Comma = true;
> +      I(Newline);
> +      quote(OS, P.first);
> +      OS << ':';
> +      I(Space);
> +      P.second.print(OS, I);
> +    }
> +    I(Outdent);
> +    if (Comma)
> +      I(Newline);
> +    OS << '}';
> +    break;
> +  }
> +  case T_Array: {
> +    bool Comma = false;
> +    OS << '[';
> +    I(Indent);
> +    for (const auto &E : as<Expr::Array>()) {
> +      if (Comma)
> +        OS << ',';
> +      Comma = true;
> +      I(Newline);
> +      E.print(OS, I);
> +    }
> +    I(Outdent);
> +    if (Comma)
> +      I(Newline);
> +    OS << ']';
> +    break;
> +  }
> +  }
> +}
> +
> +llvm::raw_ostream &clang::clangd::json::operator<<(raw_ostream &OS,
> +                                                   const Expr &E) {
> +  E.print(OS, [](IndenterAction A) { /*ignore*/ });
> +  return OS;
> +}
> +
> +void llvm::format_provider<clang::clangd::json::Expr>::format(
> +    const clang::clangd::json::Expr &E, raw_ostream &OS, StringRef Options) {
> +  if (Options.empty()) {
> +    OS << E;
> +    return;
> +  }
> +  unsigned IndentAmount = 0;
> +  if (Options.getAsInteger(/*Radix=*/10, IndentAmount))
> +    assert(false && "json::Expr format options should be an integer");
> +  unsigned IndentLevel = 0;
> +  E.print(OS, [&](IndenterAction A) {
> +    switch (A) {
> +    case Newline:
> +      OS << '\n';
> +      OS.indent(IndentLevel);
> +      break;
> +    case Space:
> +      OS << ' ';
> +      break;
> +    case Indent:
> +      IndentLevel += IndentAmount;
> +      break;
> +    case Outdent:
> +      IndentLevel -= IndentAmount;
> +      break;
> +    };
> +  });
> +}
>
> Added: clang-tools-extra/trunk/clangd/JSONExpr.h
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/clangd/JSONExpr.h?rev=317486&view=auto
> ==============================================================================
> --- clang-tools-extra/trunk/clangd/JSONExpr.h (added)
> +++ clang-tools-extra/trunk/clangd/JSONExpr.h Mon Nov  6 07:40:30 2017
> @@ -0,0 +1,247 @@
> +//===--- JSONExpr.h - composable JSON expressions ---------------*- C++ -*-
> ===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===---------------------------------------------------------------------
> ===//
> +
> +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSON_H
> +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSON_H
> +
> +#include <map>
> +
> +#include "llvm/ADT/SmallVector.h"
> +#include "llvm/ADT/StringRef.h"
> +#include "llvm/Support/FormatVariadic.h"
> +#include "llvm/Support/raw_ostream.h"
> +
> +namespace clang {
> +namespace clangd {
> +namespace json {
> +
> +// An Expr is an opaque temporary JSON structure used to compose documents.
> +// They can be copied, but should generally be moved.
> +//
> +// You can implicitly construct literals from:
> +//   - strings: std::string, SmallString, formatv, StringRef, char*
> +//              (char*, and StringRef are references, not copies!)
> +//   - numbers
> +//   - booleans
> +//   - null: nullptr
> +//   - arrays: {"foo", 42.0, false}
> +//   - serializable things: any T with a T::unparse(const T&) -> Expr
> +//
> +// They can also be constructed from object/array helpers:
> +//   - json::obj is a type like map<StringExpr, Expr>
> +//   - json::ary is a type like vector<Expr>
> +// These can be list-initialized, or used to build up collections in a loop.
> +// json::ary(Collection) converts all items in a collection to Exprs.
> +//
> +// Exprs can be serialized to JSON:
> +//   1) raw_ostream << Expr                    // Basic formatting.
> +//   2) raw_ostream << formatv("{0}", Expr)    // Basic formatting.
> +//   3) raw_ostream << formatv("{0:2}", Expr)  // Pretty-print with indent 2.
> +class Expr {
> +public:
> +  class Object;
> +  class ObjectKey;
> +  class Array;
> +
> +  // It would be nice to have Expr() be null. But that would make {} null
> too...
> +  Expr(const Expr &M) { copyFrom(M); }
> +  Expr(Expr &&M) { moveFrom(std::move(M)); }
> +  // "cheating" move-constructor for moving from initializer_list.
> +  Expr(const Expr &&M) { moveFrom(std::move(M)); }
> +  Expr(std::initializer_list<Expr> Elements) : Expr(Array(Elements)) {}
> +  Expr(Array &&Elements) : Type(T_Array) {
> create<Array>(std::move(Elements)); }
> +  Expr(Object &&Properties) : Type(T_Object) {
> +    create<Object>(std::move(Properties));
> +  }
> +  // Strings: types with value semantics.
> +  Expr(std::string &&V) : Type(T_String) { create<std::string>(std::move(V));
> }
> +  Expr(const std::string &V) : Type(T_String) { create<std::string>(V); }
> +  Expr(const llvm::SmallVectorImpl<char> &V) : Type(T_String) {
> +    create<std::string>(V.begin(), V.end());
> +  }
> +  Expr(const llvm::formatv_object_base &V) : Expr(V.str()){};
> +  // Strings: types with reference semantics.
> +  Expr(llvm::StringRef V) : Type(T_StringRef) { create<llvm::StringRef>(V); }
> +  Expr(const char *V) : Type(T_StringRef) { create<llvm::StringRef>(V); }
> +  Expr(std::nullptr_t) : Type(T_Null) {}
> +  // Prevent implicit conversions to boolean.
> +  template <typename T, typename = typename std::enable_if<
> +                            std::is_same<T, bool>::value>::type>
> +  Expr(T B) : Type(T_Boolean) {
> +    create<bool>(B);
> +  }
> +  // Numbers: arithmetic types that are not boolean.
> +  template <
> +      typename T,
> +      typename = typename std::enable_if<std::is_arithmetic<T>::value>::type,
> +      typename = typename std::enable_if<std::integral_constant<
> +          bool, !std::is_same<T, bool>::value>::value>::type>
> +  Expr(T D) : Type(T_Number) {
> +    create<double>(D);
> +  }
> +  // Types with a static T::unparse function returning an Expr.
> +  // FIXME: should this be a free unparse() function found by ADL?
> +  template <typename T,
> +            typename = typename std::enable_if<std::is_same<
> +                Expr, decltype(T::unparse(*(const T *)nullptr))>::value>>
> +  Expr(const T &V) : Expr(T::unparse(V)) {}
> +
> +  Expr &operator=(const Expr &M) {
> +    destroy();
> +    copyFrom(M);
> +    return *this;
> +  }
> +  Expr &operator=(Expr &&M) {
> +    destroy();
> +    moveFrom(std::move(M));
> +    return *this;
> +  }
> +  ~Expr() { destroy(); }
> +
> +  friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Expr &);
> +
> +private:
> +  void destroy();
> +  void copyFrom(const Expr &M);
> +  // We allow moving from *const* Exprs, by marking all members as mutable!
> +  // This hack is needed to support initializer-list syntax efficiently.
> +  // (std::initializer_list<T> is a container of const T).
> +  void moveFrom(const Expr &&M);
> +
> +  template <typename T, typename... U> void create(U &&... V) {
> +    new (&as<T>()) T(std::forward<U>(V)...);
> +  }
> +  template <typename T> T &as() const {
> +    return *reinterpret_cast<T *>(Union.buffer);
> +  }
> +
> +  template <typename Indenter>
> +  void print(llvm::raw_ostream &, const Indenter &) const;
> +  friend struct llvm::format_provider<clang::clangd::json::Expr>;
> +
> +  enum ExprType : char {
> +    T_Null,
> +    T_Boolean,
> +    T_Number,
> +    T_StringRef,
> +    T_String,
> +    T_Object,
> +    T_Array,
> +  };
> +  mutable ExprType Type;
> +
> +public:
> +  // ObjectKey is a used to capture keys in Expr::Objects. It's like Expr
> but:
> +  //   - only strings are allowed
> +  //   - it's copyable (for std::map)
> +  //   - we're slightly more eager to copy, to allow efficient key compares
> +  //   - it's optimized for the string literal case (Owned == nullptr)
> +  class ObjectKey {
> +  public:
> +    ObjectKey(const char *S) : Data(S) {}
> +    ObjectKey(llvm::StringRef S) : Data(S) {}
> +    ObjectKey(std::string &&V)
> +        : Owned(new std::string(std::move(V))), Data(*Owned) {}
> +    ObjectKey(const std::string &V) : Owned(new std::string(V)), Data(*Owned)
> {}
> +    ObjectKey(const llvm::SmallVectorImpl<char> &V)
> +        : ObjectKey(std::string(V.begin(), V.end())) {}
> +    ObjectKey(const llvm::formatv_object_base &V) : ObjectKey(V.str()) {}
> +
> +    ObjectKey(const ObjectKey &C) { *this = C; }
> +    ObjectKey(ObjectKey &&C) = default;
> +    ObjectKey &operator=(const ObjectKey &C) {
> +      if (C.Owned) {
> +        Owned.reset(new std::string(*C.Owned));
> +        Data = *Owned;
> +      } else {
> +        Data = C.Data;
> +      }
> +      return *this;
> +    }
> +    ObjectKey &operator=(ObjectKey &&) = default;
> +
> +    operator llvm::StringRef() const { return Data; }
> +
> +    friend bool operator<(const ObjectKey &L, const ObjectKey &R) {
> +      return L.Data < R.Data;
> +    }
> +
> +    // "cheating" move-constructor for moving from initializer_list.
> +    ObjectKey(const ObjectKey &&V) {
> +      Owned = std::move(V.Owned);
> +      Data = V.Data;
> +    }
> +
> +  private:
> +    mutable std::unique_ptr<std::string> Owned; // mutable for cheating.
> +    llvm::StringRef Data;
> +  };
> +
> +  class Object : public std::map<ObjectKey, Expr> {
> +  public:
> +    explicit Object() {}
> +    // Use a custom struct for list-init, because pair forces extra copies.
> +    struct KV;
> +    explicit Object(std::initializer_list<KV> Properties);
> +
> +    // Allow [] as if Expr was default-constructible as null.
> +    Expr &operator[](const ObjectKey &K) {
> +      return emplace(K, Expr(nullptr)).first->second;
> +    }
> +    Expr &operator[](ObjectKey &&K) {
> +      return emplace(std::move(K), Expr(nullptr)).first->second;
> +    }
> +  };
> +
> +  class Array : public std::vector<Expr> {
> +  public:
> +    explicit Array() {}
> +    explicit Array(std::initializer_list<Expr> Elements) {
> +      reserve(Elements.size());
> +      for (const Expr &V : Elements)
> +        emplace_back(std::move(V));
> +    };
> +    template <typename Collection> explicit Array(const Collection &C) {
> +      for (const auto &V : C)
> +        emplace_back(V);
> +    }
> +  };
> +
> +private:
> +  mutable llvm::AlignedCharArrayUnion<bool, double, llvm::StringRef,
> +                                      std::string, Array, Object>
> +      Union;
> +};
> +
> +struct Expr::Object::KV {
> +  ObjectKey K;
> +  Expr V;
> +};
> +
> +inline Expr::Object::Object(std::initializer_list<KV> Properties) {
> +  for (const auto &P : Properties)
> +    emplace(std::move(P.K), std::move(P.V));
> +}
> +
> +// Give Expr::{Object,Array} more convenient names for literal use.
> +using obj = Expr::Object;
> +using ary = Expr::Array;
> +
> +} // namespace json
> +} // namespace clangd
> +} // namespace clang
> +
> +namespace llvm {
> +template <> struct format_provider<clang::clangd::json::Expr> {
> +  static void format(const clang::clangd::json::Expr &, raw_ostream &,
> +                     StringRef);
> +};
> +} // namespace llvm
> +
> +#endif
>
> Modified: clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/clangd/JSONRPCDispatcher.cpp?rev=317486&r1=317485&r2=317486&view=d
> iff
> ==============================================================================
> --- clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp (original)
> +++ clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp Mon Nov  6 07:40:30
> 2017
> @@ -8,6 +8,7 @@
>  //===----------------------------------------------------------------------
> ===//
>
>  #include "JSONRPCDispatcher.h"
> +#include "JSONExpr.h"
>  #include "ProtocolHandlers.h"
>  #include "Trace.h"
>  #include "llvm/ADT/SmallString.h"
> @@ -18,17 +19,22 @@
>  using namespace clang;
>  using namespace clangd;
>
> -void JSONOutput::writeMessage(const Twine &Message) {
> -  llvm::SmallString<128> Storage;
> -  StringRef M = Message.toStringRef(Storage);
> +void JSONOutput::writeMessage(const json::Expr &Message) {
> +  std::string S;
> +  llvm::raw_string_ostream OS(S);
> +  if (Pretty)
> +    OS << llvm::formatv("{0:2}", Message);
> +  else
> +    OS << Message;
> +  OS.flush();
>
>    std::lock_guard<std::mutex> Guard(StreamMutex);
>    // Log without headers.
> -  Logs << "--> " << M << '\n';
> +  Logs << "--> " << S << '\n';
>    Logs.flush();
>
>    // Emit message with header.
> -  Outs << "Content-Length: " << M.size() << "\r\n\r\n" << M;
> +  Outs << "Content-Length: " << S.size() << "\r\n\r\n" << S;
>    Outs.flush();
>  }
>
> @@ -47,30 +53,38 @@ void JSONOutput::mirrorInput(const Twine
>    InputMirror->flush();
>  }
>
> -void RequestContext::reply(const llvm::Twine &Result) {
> -  if (ID.empty()) {
> +void RequestContext::reply(json::Expr &&Result) {
> +  if (!ID) {
>      Out.log("Attempted to reply to a notification!\n");
>      return;
>    }
> -  Out.writeMessage(llvm::Twine(R"({"jsonrpc":"2.0","id":)") + ID +
> -                   R"(,"result":)" + Result + "}");
> +  Out.writeMessage(json::obj{
> +      {"jsonrpc", "2.0"},
> +      {"id", *ID},
> +      {"result", std::move(Result)},
> +  });
>  }
>
>  void RequestContext::replyError(int code, const llvm::StringRef &Message) {
>    Out.log("Error " + llvm::Twine(code) + ": " + Message + "\n");
> -  if (!ID.empty()) {
> -    Out.writeMessage(llvm::Twine(R"({"jsonrpc":"2.0","id":)") + ID +
> -                     R"(,"error":{"code":)" + llvm::Twine(code) +
> -                     R"(,"message":")" + llvm::yaml::escape(Message) +
> -                     R"("}})");
> +  if (ID) {
> +    Out.writeMessage(json::obj{
> +        {"jsonrpc", "2.0"},
> +        {"id", *ID},
> +        {"error", json::obj{{"code", code}, {"message", Message}}},
> +    });
>    }
>  }
>
> -void RequestContext::call(StringRef Method, StringRef Params) {
> +void RequestContext::call(StringRef Method, json::Expr &&Params) {
>    // FIXME: Generate/Increment IDs for every request so that we can get
> proper
>    // replies once we need to.
> -  Out.writeMessage(llvm::Twine(R"({"jsonrpc":"2.0","id":1,"method":")" +
> -                               Method + R"(","params":)" + Params + R"(})"));
> +  Out.writeMessage(json::obj{
> +      {"jsonrpc", "2.0"},
> +      {"id", 1},
> +      {"method", Method},
> +      {"params", std::move(Params)},
> +  });
>  }
>
>  void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) {
> @@ -80,7 +94,7 @@ void JSONRPCDispatcher::registerHandler(
>
>  static void
>  callHandler(const llvm::StringMap<JSONRPCDispatcher::Handler> &Handlers,
> -            llvm::yaml::ScalarNode *Method, llvm::yaml::ScalarNode *Id,
> +            llvm::yaml::ScalarNode *Method, llvm::Optional<json::Expr> ID,
>              llvm::yaml::MappingNode *Params,
>              const JSONRPCDispatcher::Handler &UnknownHandler, JSONOutput
> &Out) {
>    llvm::SmallString<64> MethodStorage;
> @@ -88,7 +102,7 @@ callHandler(const llvm::StringMap<JSONRP
>    auto I = Handlers.find(MethodStr);
>    auto &Handler = I != Handlers.end() ? I->second : UnknownHandler;
>    trace::Span Tracer(MethodStr);
> -  Handler(RequestContext(Out, Id ? Id->getRawValue() : ""), Params);
> +  Handler(RequestContext(Out, std::move(ID)), Params);
>  }
>
>  bool JSONRPCDispatcher::call(StringRef Content, JSONOutput &Out) const {
> @@ -106,7 +120,7 @@ bool JSONRPCDispatcher::call(StringRef C
>    llvm::yaml::ScalarNode *Version = nullptr;
>    llvm::yaml::ScalarNode *Method = nullptr;
>    llvm::yaml::MappingNode *Params = nullptr;
> -  llvm::yaml::ScalarNode *Id = nullptr;
> +  llvm::Optional<json::Expr> ID;
>    for (auto &NextKeyValue : *Object) {
>      auto *KeyString =
>          dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
> @@ -127,7 +141,18 @@ bool JSONRPCDispatcher::call(StringRef C
>      } else if (KeyValue == "method") {
>        Method = dyn_cast<llvm::yaml::ScalarNode>(Value);
>      } else if (KeyValue == "id") {
> -      Id = dyn_cast<llvm::yaml::ScalarNode>(Value);
> +      // 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;
> +          if (!V.getAsDouble(D))
> +            ID.emplace(D);
> +        }
> +      }
>      } else if (KeyValue == "params") {
>        if (!Method)
>          return false;
> @@ -136,7 +161,7 @@ bool JSONRPCDispatcher::call(StringRef C
>        // 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, Id, Params, UnknownHandler, Out);
> +      callHandler(Handlers, Method, std::move(ID), Params, UnknownHandler,
> Out);
>        return true;
>      } else {
>        return false;
> @@ -147,7 +172,7 @@ bool JSONRPCDispatcher::call(StringRef C
>    // leftovers.
>    if (!Method)
>      return false;
> -  callHandler(Handlers, Method, Id, nullptr, UnknownHandler, Out);
> +  callHandler(Handlers, Method, std::move(ID), nullptr, UnknownHandler, Out);
>
>    return true;
>  }
>
> Modified: clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/clangd/JSONRPCDispatcher.h?rev=317486&r1=317485&r2=317486&view=dif
> f
> ==============================================================================
> --- clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h (original)
> +++ clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h Mon Nov  6 07:40:30
> 2017
> @@ -10,6 +10,7 @@
>  #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H
>  #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H
>
> +#include "JSONExpr.h"
>  #include "Logger.h"
>  #include "clang/Basic/LLVM.h"
>  #include "llvm/ADT/SmallString.h"
> @@ -26,11 +27,11 @@ namespace clangd {
>  class JSONOutput : public Logger {
>  public:
>    JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs,
> -             llvm::raw_ostream *InputMirror = nullptr)
> -      : Outs(Outs), Logs(Logs), InputMirror(InputMirror) {}
> +             llvm::raw_ostream *InputMirror = nullptr, bool Pretty = false)
> +      : Outs(Outs), Logs(Logs), InputMirror(InputMirror), Pretty(Pretty) {}
>
>    /// Emit a JSONRPC message.
> -  void writeMessage(const Twine &Message);
> +  void writeMessage(const json::Expr &Result);
>
>    /// Write to the logging stream.
>    /// No newline is implicitly added. (TODO: we should fix this!)
> @@ -45,6 +46,7 @@ private:
>    llvm::raw_ostream &Outs;
>    llvm::raw_ostream &Logs;
>    llvm::raw_ostream *InputMirror;
> +  bool Pretty;
>
>    std::mutex StreamMutex;
>  };
> @@ -52,18 +54,19 @@ private:
>  /// Context object passed to handlers to allow replies.
>  class RequestContext {
>  public:
> -  RequestContext(JSONOutput &Out, StringRef ID) : Out(Out), ID(ID) {}
> +  RequestContext(JSONOutput &Out, llvm::Optional<json::Expr> ID)
> +      : Out(Out), ID(std::move(ID)) {}
>
> -  /// Sends a successful reply. Result should be well-formed JSON.
> -  void reply(const Twine &Result);
> +  /// Sends a successful reply.
> +  void reply(json::Expr &&Result);
>    /// Sends an error response to the client, and logs it.
>    void replyError(int code, const llvm::StringRef &Message);
>    /// Sends a request to the client.
> -  void call(llvm::StringRef Method, StringRef Params);
> +  void call(llvm::StringRef Method, json::Expr &&Params);
>
>  private:
>    JSONOutput &Out;
> -  llvm::SmallString<64> ID; // Valid JSON, or empty for a notification.
> +  llvm::Optional<json::Expr> ID;
>  };
>
>  /// Main JSONRPC entry point. This parses the JSONRPC "header" and calls the
>
> Modified: clang-tools-extra/trunk/clangd/Protocol.cpp
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/clangd/Protocol.cpp?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/clangd/Protocol.cpp (original)
> +++ clang-tools-extra/trunk/clangd/Protocol.cpp Mon Nov  6 07:40:30 2017
> @@ -63,7 +63,7 @@ URI URI::parse(llvm::yaml::ScalarNode *P
>    return URI::fromUri(Param->getValue(Storage));
>  }
>
> -std::string URI::unparse(const URI &U) { return "\"" + U.uri + "\""; }
> +json::Expr URI::unparse(const URI &U) { return U.uri; }
>
>  llvm::Optional<TextDocumentIdentifier>
>  TextDocumentIdentifier::parse(llvm::yaml::MappingNode *Params,
> @@ -125,11 +125,11 @@ llvm::Optional<Position> Position::parse
>    return Result;
>  }
>
> -std::string Position::unparse(const Position &P) {
> -  std::string Result;
> -  llvm::raw_string_ostream(Result)
> -      << llvm::format(R"({"line": %d, "character": %d})", P.line,
> P.character);
> -  return Result;
> +json::Expr Position::unparse(const Position &P) {
> +  return json::obj{
> +      {"line", P.line},
> +      {"character", P.character},
> +  };
>  }
>
>  llvm::Optional<Range> Range::parse(llvm::yaml::MappingNode *Params,
> @@ -165,20 +165,18 @@ llvm::Optional<Range> Range::parse(llvm:
>    return Result;
>  }
>
> -std::string Range::unparse(const Range &P) {
> -  std::string Result;
> -  llvm::raw_string_ostream(Result) << llvm::format(
> -      R"({"start": %s, "end": %s})", Position::unparse(P.start).c_str(),
> -      Position::unparse(P.end).c_str());
> -  return Result;
> +json::Expr Range::unparse(const Range &P) {
> +  return json::obj{
> +      {"start", P.start},
> +      {"end", P.end},
> +  };
>  }
>
> -std::string Location::unparse(const Location &P) {
> -  std::string Result;
> -  llvm::raw_string_ostream(Result) << llvm::format(
> -      R"({"uri": %s, "range": %s})", URI::unparse(P.uri).c_str(),
> -      Range::unparse(P.range).c_str());
> -  return Result;
> +json::Expr Location::unparse(const Location &P) {
> +  return json::obj{
> +      {"uri", P.uri},
> +      {"range", P.range},
> +  };
>  }
>
>  llvm::Optional<TextDocumentItem>
> @@ -279,25 +277,11 @@ llvm::Optional<TextEdit> TextEdit::parse
>    return Result;
>  }
>
> -std::string TextEdit::unparse(const TextEdit &P) {
> -  std::string Result;
> -  llvm::raw_string_ostream(Result) << llvm::format(
> -      R"({"range": %s, "newText": "%s"})", Range::unparse(P.range).c_str(),
> -      llvm::yaml::escape(P.newText).c_str());
> -  return Result;
> -}
> -
> -std::string TextEdit::unparse(const std::vector<TextEdit> &TextEdits) {
> -  // Fuse all edits into one big JSON array.
> -  std::string Edits;
> -  for (auto &TE : TextEdits) {
> -    Edits += TextEdit::unparse(TE);
> -    Edits += ',';
> -  }
> -  if (!Edits.empty())
> -    Edits.pop_back();
> -
> -  return "[" + Edits + "]";
> +json::Expr TextEdit::unparse(const TextEdit &P) {
> +  return json::obj{
> +      {"range", P.range},
> +      {"newText", P.newText},
> +  };
>  }
>
>  namespace {
> @@ -611,11 +595,11 @@ FormattingOptions::parse(llvm::yaml::Map
>    return Result;
>  }
>
> -std::string FormattingOptions::unparse(const FormattingOptions &P) {
> -  std::string Result;
> -  llvm::raw_string_ostream(Result) << llvm::format(
> -      R"({"tabSize": %d, "insertSpaces": %d})", P.tabSize, P.insertSpaces);
> -  return Result;
> +json::Expr FormattingOptions::unparse(const FormattingOptions &P) {
> +  return json::obj{
> +      {"tabSize", P.tabSize},
> +      {"insertSpaces", P.insertSpaces},
> +  };
>  }
>
>  llvm::Optional<DocumentRangeFormattingParams>
> @@ -982,28 +966,18 @@ ExecuteCommandParams::parse(llvm::yaml::
>    return Result;
>  }
>
> -std::string WorkspaceEdit::unparse(const WorkspaceEdit &WE) {
> -  std::string Changes;
> -  for (auto &Change : *WE.changes) {
> -    Changes += llvm::formatv(R"("{0}": {1})", Change.first,
> -                             TextEdit::unparse(Change.second));
> -    Changes += ',';
> -  }
> -  if (!Changes.empty())
> -    Changes.pop_back();
> -
> -  std::string Result;
> -  llvm::raw_string_ostream(Result)
> -      << llvm::format(R"({"changes": {%s}})", Changes.c_str());
> -  return Result;
> +json::Expr WorkspaceEdit::unparse(const WorkspaceEdit &WE) {
> +  if (!WE.changes)
> +    return json::obj{};
> +  json::obj FileChanges;
> +  for (auto &Change : *WE.changes)
> +    FileChanges[Change.first] = json::ary(Change.second);
> +  return json::obj{{"changes", std::move(FileChanges)}};
>  }
>
> -std::string
> +json::Expr
>  ApplyWorkspaceEditParams::unparse(const ApplyWorkspaceEditParams &Params) {
> -  std::string Result;
> -  llvm::raw_string_ostream(Result) << llvm::format(
> -      R"({"edit": %s})", WorkspaceEdit::unparse(Params.edit).c_str());
> -  return Result;
> +  return json::obj{{"edit", Params.edit}};
>  }
>
>  llvm::Optional<TextDocumentPositionParams>
> @@ -1040,96 +1014,57 @@ TextDocumentPositionParams::parse(llvm::
>    return Result;
>  }
>
> -std::string CompletionItem::unparse(const CompletionItem &CI) {
> -  std::string Result = "{";
> -  llvm::raw_string_ostream Os(Result);
> +json::Expr CompletionItem::unparse(const CompletionItem &CI) {
>    assert(!CI.label.empty() && "completion item label is required");
> -  Os << R"("label":")" << llvm::yaml::escape(CI.label) << R"(",)";
> +  json::obj Result{{"label", CI.label}};
>    if (CI.kind != CompletionItemKind::Missing)
> -    Os << R"("kind":)" << static_cast<int>(CI.kind) << R"(,)";
> +    Result["kind"] = static_cast<int>(CI.kind);
>    if (!CI.detail.empty())
> -    Os << R"("detail":")" << llvm::yaml::escape(CI.detail) << R"(",)";
> +    Result["detail"] = CI.detail;
>    if (!CI.documentation.empty())
> -    Os << R"("documentation":")" << llvm::yaml::escape(CI.documentation)
> -       << R"(",)";
> +    Result["documentation"] = CI.documentation;
>    if (!CI.sortText.empty())
> -    Os << R"("sortText":")" << llvm::yaml::escape(CI.sortText) << R"(",)";
> +    Result["sortText"] = CI.sortText;
>    if (!CI.filterText.empty())
> -    Os << R"("filterText":")" << llvm::yaml::escape(CI.filterText) <<
> R"(",)";
> +    Result["filterText"] = CI.filterText;
>    if (!CI.insertText.empty())
> -    Os << R"("insertText":")" << llvm::yaml::escape(CI.insertText) <<
> R"(",)";
> -  if (CI.insertTextFormat != InsertTextFormat::Missing) {
> -    Os << R"("insertTextFormat":)" << static_cast<int>(CI.insertTextFormat)
> -       << R"(,)";
> -  }
> +    Result["insertText"] = CI.insertText;
> +  if (CI.insertTextFormat != InsertTextFormat::Missing)
> +    Result["insertTextFormat"] = static_cast<int>(CI.insertTextFormat);
>    if (CI.textEdit)
> -    Os << R"("textEdit":)" << TextEdit::unparse(*CI.textEdit) << ',';
> -  if (!CI.additionalTextEdits.empty()) {
> -    Os << R"("additionalTextEdits":[)";
> -    for (const auto &Edit : CI.additionalTextEdits)
> -      Os << TextEdit::unparse(Edit) << ",";
> -    Os.flush();
> -    // The list additionalTextEdits is guaranteed nonempty at this point.
> -    // Replace the trailing comma with right brace.
> -    Result.back() = ']';
> -  }
> -  Os.flush();
> -  // Label is required, so Result is guaranteed to have a trailing comma.
> -  Result.back() = '}';
> -  return Result;
> +    Result["textEdit"] = *CI.textEdit;
> +  if (!CI.additionalTextEdits.empty())
> +    Result["additionalTextEdits"] = json::ary(CI.additionalTextEdits);
> +  return std::move(Result);
>  }
>
> -std::string ParameterInformation::unparse(const ParameterInformation &PI) {
> -  std::string Result = "{";
> -  llvm::raw_string_ostream Os(Result);
> +json::Expr ParameterInformation::unparse(const ParameterInformation &PI) {
>    assert(!PI.label.empty() && "parameter information label is required");
> -  Os << R"("label":")" << llvm::yaml::escape(PI.label) << '\"';
> +  json::obj Result{{"label", PI.label}};
>    if (!PI.documentation.empty())
> -    Os << R"(,"documentation":")" << llvm::yaml::escape(PI.documentation)
> -       << '\"';
> -  Os << '}';
> -  Os.flush();
> -  return Result;
> +    Result["documentation"] = PI.documentation;
> +  return std::move(Result);
>  }
>
> -std::string SignatureInformation::unparse(const SignatureInformation &SI) {
> -  std::string Result = "{";
> -  llvm::raw_string_ostream Os(Result);
> +json::Expr SignatureInformation::unparse(const SignatureInformation &SI) {
>    assert(!SI.label.empty() && "signature information label is required");
> -  Os << R"("label":")" << llvm::yaml::escape(SI.label) << '\"';
> +  json::obj Result{
> +      {"label", SI.label},
> +      {"parameters", json::ary(SI.parameters)},
> +  };
>    if (!SI.documentation.empty())
> -    Os << R"(,"documentation":")" << llvm::yaml::escape(SI.documentation)
> -       << '\"';
> -  Os << R"(,"parameters":[)";
> -  for (const auto &Parameter : SI.parameters) {
> -    Os << ParameterInformation::unparse(Parameter) << ',';
> -  }
> -  Os.flush();
> -  if (SI.parameters.empty())
> -    Result.push_back(']');
> -  else
> -    Result.back() = ']'; // Replace the last `,` with an `]`.
> -  Result.push_back('}');
> -  return Result;
> +    Result["documentation"] = SI.documentation;
> +  return std::move(Result);
>  }
>
> -std::string SignatureHelp::unparse(const SignatureHelp &SH) {
> -  std::string Result = "{";
> -  llvm::raw_string_ostream Os(Result);
> +json::Expr SignatureHelp::unparse(const SignatureHelp &SH) {
>    assert(SH.activeSignature >= 0 &&
>           "Unexpected negative value for number of active signatures.");
>    assert(SH.activeParameter >= 0 &&
>           "Unexpected negative value for active parameter index");
> -  Os << R"("activeSignature":)" << SH.activeSignature
> -     << R"(,"activeParameter":)" << SH.activeParameter <<
> R"(,"signatures":[)";
> -  for (const auto &Signature : SH.signatures) {
> -    Os << SignatureInformation::unparse(Signature) << ',';
> -  }
> -  Os.flush();
> -  if (SH.signatures.empty())
> -    Result.push_back(']');
> -  else
> -    Result.back() = ']'; // Replace the last `,` with an `]`.
> -  Result.push_back('}');
> -  return Result;
> +  return json::obj{
> +      {"activeSignature", SH.activeSignature},
> +      {"activeParameter", SH.activeParameter},
> +      {"signatures", json::ary(SH.signatures)},
> +  };
>  }
>
> Modified: clang-tools-extra/trunk/clangd/Protocol.h
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/clangd/Protocol.h?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/clangd/Protocol.h (original)
> +++ clang-tools-extra/trunk/clangd/Protocol.h Mon Nov  6 07:40:30 2017
> @@ -21,6 +21,7 @@
>  #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOL_H
>  #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOL_H
>
> +#include "JSONExpr.h"
>  #include "llvm/ADT/Optional.h"
>  #include "llvm/Support/YAMLParser.h"
>  #include <string>
> @@ -39,7 +40,7 @@ struct URI {
>    static URI fromFile(llvm::StringRef file);
>
>    static URI parse(llvm::yaml::ScalarNode *Param);
> -  static std::string unparse(const URI &U);
> +  static json::Expr unparse(const URI &U);
>
>    friend bool operator==(const URI &LHS, const URI &RHS) {
>      return LHS.uri == RHS.uri;
> @@ -80,7 +81,7 @@ struct Position {
>
>    static llvm::Optional<Position> parse(llvm::yaml::MappingNode *Params,
>                                          clangd::Logger &Logger);
> -  static std::string unparse(const Position &P);
> +  static json::Expr unparse(const Position &P);
>  };
>
>  struct Range {
> @@ -99,7 +100,7 @@ struct Range {
>
>    static llvm::Optional<Range> parse(llvm::yaml::MappingNode *Params,
>                                       clangd::Logger &Logger);
> -  static std::string unparse(const Range &P);
> +  static json::Expr unparse(const Range &P);
>  };
>
>  struct Location {
> @@ -119,7 +120,7 @@ struct Location {
>      return std::tie(LHS.uri, LHS.range) < std::tie(RHS.uri, RHS.range);
>    }
>
> -  static std::string unparse(const Location &P);
> +  static json::Expr unparse(const Location &P);
>  };
>
>  struct Metadata {
> @@ -140,8 +141,7 @@ struct TextEdit {
>
>    static llvm::Optional<TextEdit> parse(llvm::yaml::MappingNode *Params,
>                                          clangd::Logger &Logger);
> -  static std::string unparse(const TextEdit &P);
> -  static std::string unparse(const std::vector<TextEdit> &TextEdits);
> +  static json::Expr unparse(const TextEdit &P);
>  };
>
>  struct TextDocumentItem {
> @@ -283,7 +283,7 @@ struct FormattingOptions {
>
>    static llvm::Optional<FormattingOptions>
>    parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
> -  static std::string unparse(const FormattingOptions &P);
> +  static json::Expr unparse(const FormattingOptions &P);
>  };
>
>  struct DocumentRangeFormattingParams {
> @@ -392,7 +392,7 @@ struct WorkspaceEdit {
>
>    static llvm::Optional<WorkspaceEdit> parse(llvm::yaml::MappingNode *Params,
>                                               clangd::Logger &Logger);
> -  static std::string unparse(const WorkspaceEdit &WE);
> +  static json::Expr unparse(const WorkspaceEdit &WE);
>  };
>
>  /// Exact commands are not specified in the protocol so we define the
> @@ -420,7 +420,7 @@ struct ExecuteCommandParams {
>
>  struct ApplyWorkspaceEditParams {
>    WorkspaceEdit edit;
> -  static std::string unparse(const ApplyWorkspaceEditParams &Params);
> +  static json::Expr unparse(const ApplyWorkspaceEditParams &Params);
>  };
>
>  struct TextDocumentPositionParams {
> @@ -527,7 +527,7 @@ struct CompletionItem {
>    //
>    // data?: any - A data entry field that is preserved on a completion item
>    //              between a completion and a completion resolve request.
> -  static std::string unparse(const CompletionItem &P);
> +  static json::Expr unparse(const CompletionItem &P);
>  };
>
>  /// A single parameter of a particular signature.
> @@ -539,7 +539,7 @@ struct ParameterInformation {
>    /// The documentation of this parameter. Optional.
>    std::string documentation;
>
> -  static std::string unparse(const ParameterInformation &);
> +  static json::Expr unparse(const ParameterInformation &);
>  };
>
>  /// Represents the signature of something callable.
> @@ -554,7 +554,7 @@ struct SignatureInformation {
>    /// The parameters of this signature.
>    std::vector<ParameterInformation> parameters;
>
> -  static std::string unparse(const SignatureInformation &);
> +  static json::Expr unparse(const SignatureInformation &);
>  };
>
>  /// Represents the signature of a callable.
> @@ -569,7 +569,7 @@ struct SignatureHelp {
>    /// The active parameter of the active signature.
>    int activeParameter = 0;
>
> -  static std::string unparse(const SignatureHelp &);
> +  static json::Expr unparse(const SignatureHelp &);
>  };
>
>  } // namespace clangd
>
> 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=317486&r1=317485&r2=317486&view=dif
> f
> ==============================================================================
> --- clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp (original)
> +++ clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp Mon Nov  6 07:40:30
> 2017
> @@ -41,6 +41,10 @@ static llvm::cl::opt<bool> EnableSnippet
>          "Present snippet completions instead of plaintext completions"),
>      llvm::cl::init(false));
>
> +static llvm::cl::opt<bool>
> +    PrettyPrint("pretty", llvm::cl::desc("Pretty-print JSON output"),
> +                llvm::cl::init(false));
> +
>  static llvm::cl::opt<bool> RunSynchronously(
>      "run-synchronously",
>      llvm::cl::desc("Parse on main thread. If set, -j is ignored"),
> @@ -104,7 +108,8 @@ int main(int argc, char *argv[]) {
>    llvm::raw_ostream &Outs = llvm::outs();
>    llvm::raw_ostream &Logs = llvm::errs();
>    JSONOutput Out(Outs, Logs,
> -                 InputMirrorStream ? InputMirrorStream.getPointer() :
> nullptr);
> +                 InputMirrorStream ? InputMirrorStream.getPointer() :
> nullptr,
> +                 PrettyPrint);
>
>    // If --compile-commands-dir arg was invoked, check value and override
> default
>    // path.
>
> Modified: clang-tools-extra/trunk/test/clangd/authority-less-uri.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/authority-less-
> uri.test?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/authority-less-uri.test (original)
> +++ clang-tools-extra/trunk/test/clangd/authority-less-uri.test Mon Nov  6
> 07:40:30 2017
> @@ -1,4 +1,4 @@
> -# RUN: clangd -run-synchronously < %s | FileCheck %s
> +# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace
> %s
>  # It is absolutely vital that this file has CRLF line endings.
>  #
>  # Test authority-less URI
> @@ -15,22 +15,34 @@ Content-Length: 146
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocu
> ment":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}}
>  # Test authority-less URI
>  #
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[
> -# CHECK-DAG:
> {"label":"a","kind":5,"detail":"int","sortText":"000035a","filterText":"a","in
> sertText":"a","insertTextFormat":1}
> -# CHECK: ]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +#      CHECK:      "filterText": "fake",
> +# CHECK-NEXT:      "insertText": "fake",
> +# CHECK-NEXT:      "insertTextFormat": 1,
> +# CHECK-NEXT:      "kind": 7,
> +# CHECK-NEXT:      "label": "fake::",
> +# CHECK-NEXT:      "sortText": "000075fake"
> +#      CHECK:  ]
>  Content-Length: 172
>
>
> {"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"uri":"file:///main.cpp<file:///\\main.cpp>","position":{"line":3
> ,"character":5}}}
>  # Test params parsing in the presence of a 1.x-compatible client (inlined
> "uri")
>  #
> -# CHECK: {"jsonrpc":"2.0","id":2,"result":[
> -# CHECK-DAG:
> {"label":"a","kind":5,"detail":"int","sortText":"000035a","filterText":"a","in
> sertText":"a","insertTextFormat":1}
> -# CHECK: ]}
> +#      CHECK:  "id": 2,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +#      CHECK:      "filterText": "fake",
> +# CHECK-NEXT:      "insertText": "fake",
> +# CHECK-NEXT:      "insertTextFormat": 1,
> +# CHECK-NEXT:      "kind": 7,
> +# CHECK-NEXT:      "label": "fake::",
> +# CHECK-NEXT:      "sortText": "000075fake"
> +#      CHECK:  ]
>  Content-Length: 44
>
>  {"jsonrpc":"2.0","id":3,"method":"shutdown"}
> -# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> Modified: clang-tools-extra/trunk/test/clangd/completion-items-kinds.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/completion-items-
> kinds.test?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/completion-items-kinds.test (original)
> +++ clang-tools-extra/trunk/test/clangd/completion-items-kinds.test Mon Nov  6
> 07:40:30 2017
> @@ -11,27 +11,26 @@ Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":4,"character":7}}}
>  Content-Length: 58
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[
> +# CHECK: {"id":1,"jsonrpc":"2.0","result":[
>  #
>  # Keyword
> -# CHECK-DAG:
> {"label":"int","kind":14,"sortText":"000050int","filterText":"int","insertText
> ":"int","insertTextFormat":1}
> +# CHECK-DAG:
> {"filterText":"int","insertText":"int","insertTextFormat":1,"kind":14,"label":
> "int","sortText":"000050int"}
>  #
>  # Code pattern
> -# CHECK-DAG:
> {"label":"static_cast<type>(expression)","kind":15,"sortText":"000040static_ca
> st","filterText":"static_cast","insertText":"static_cast<${1:type}>(${2:expres
> sion})","insertTextFormat":2}
> +# CHECK-DAG:
> {"filterText":"static_cast","insertText":"static_cast<${1:type}>(${2:expressio
> n})","insertTextFormat":2,"kind":15,"label":"static_cast<type>(expression)","s
> ortText":"000040static_cast"}
>  #
>  # Struct
> -# CHECK-DAG:
> {"label":"Struct","kind":7,"sortText":"000050Struct","filterText":"Struct","in
> sertText":"Struct","insertTextFormat":1}
> +# CHECK-DAG:
> {"filterText":"Struct","insertText":"Struct","insertTextFormat":1,"kind":7,"la
> bel":"Struct","sortText":"000050Struct"}
>  #
>  # Macro
> -# CHECK-DAG:
> {"label":"MACRO","kind":1,"sortText":"000070MACRO","filterText":"MACRO","inser
> tText":"MACRO","insertTextFormat":1}
> +# CHECK-DAG:
> {"filterText":"MACRO","insertText":"MACRO","insertTextFormat":1,"kind":1,"labe
> l":"MACRO","sortText":"000070MACRO"}
>  #
>  # Variable
> -# CHECK-DAG:
> {"label":"variable","kind":6,"detail":"int","sortText":"000012variable","filte
> rText":"variable","insertText":"variable","insertTextFormat":1}
> +# CHECK-DAG:
> {"detail":"int","filterText":"variable","insertText":"variable","insertTextFor
> mat":1,"kind":6,"label":"variable","sortText":"000012variable"}
>  #
>  # Function
> -# CHECK-DAG:
> {"label":"function()","kind":3,"detail":"int","sortText":"000012function","fil
> terText":"function","insertText":"function()","insertTextFormat":1}
> +# CHECK-DAG:
> {"detail":"int","filterText":"function","insertText":"function()","insertTextF
> ormat":1,"kind":3,"label":"function()","sortText":"000012function"}
>  #
> -#
> -# CHECK: ]}
> +# CHECK-SAME: ]}
>
>  {"jsonrpc":"2.0","id":3,"method":"shutdown","params":null}
>
> Modified: clang-tools-extra/trunk/test/clangd/completion-priorities.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/completion-
> priorities.test?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/completion-priorities.test (original)
> +++ clang-tools-extra/trunk/test/clangd/completion-priorities.test Mon Nov  6
> 07:40:30 2017
> @@ -16,25 +16,24 @@ Content-Length: 151
>  # The order of results returned by codeComplete seems to be
>  # nondeterministic, so we check regardless of order.
>  #
> -# CHECK: {"jsonrpc":"2.0","id":2,"result":[
> -# CHECK-DAG:
> {"label":"pub()","kind":2,"detail":"void","sortText":"000034pub","filterText":
> "pub","insertText":"pub","insertTextFormat":1}
> -# CHECK-DAG:
> {"label":"prot()","kind":2,"detail":"void","sortText":"000034prot","filterText
> ":"prot","insertText":"prot","insertTextFormat":1}
> -# CHECK-DAG:
> {"label":"priv()","kind":2,"detail":"void","sortText":"000034priv","filterText
> ":"priv","insertText":"priv","insertTextFormat":1}
> -# CHECK: ]}
> +# CHECK: {"id":2,"jsonrpc":"2.0","result":[
> +# CHECK-DAG:
> {"detail":"void","filterText":"pub","insertText":"pub","insertTextFormat":1,"k
> ind":2,"label":"pub()","sortText":"000034pub"}
> +# CHECK-DAG:
> {"detail":"void","filterText":"prot","insertText":"prot","insertTextFormat":1,
> "kind":2,"label":"prot()","sortText":"000034prot"}
> +# CHECK-DAG:
> {"detail":"void","filterText":"priv","insertText":"priv","insertTextFormat":1,
> "kind":2,"label":"priv()","sortText":"000034priv"}
> +# CHECK-SAME: ]}
>
>  Content-Length: 151
>
>
> {"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":17,"character":4}}}
> -# CHECK: {"jsonrpc":"2.0","id":3,"result":[
> -# CHECK-DAG:
> {"label":"pub()","kind":2,"detail":"void","sortText":"000034pub","filterText":
> "pub","insertText":"pub","insertTextFormat":1}
> -# CHECK-DAG:
> {"label":"prot()","kind":2,"detail":"void","sortText":"200034prot","filterText
> ":"prot","insertText":"prot","insertTextFormat":1}
> -# CHECK-DAG:
> {"label":"priv()","kind":2,"detail":"void","sortText":"200034priv","filterText
> ":"priv","insertText":"priv","insertTextFormat":1}
> -# CHECK: ]}
> +# CHECK: {"id":3,"jsonrpc":"2.0","result":[
> +# CHECK-DAG:
> {"detail":"void","filterText":"pub","insertText":"pub","insertTextFormat":1,"k
> ind":2,"label":"pub()","sortText":"000034pub"}
> +# CHECK-DAG:
> {"detail":"void","filterText":"prot","insertText":"prot","insertTextFormat":1,
> "kind":2,"label":"prot()","sortText":"200034prot"}
> +# CHECK-DAG:
> {"detail":"void","filterText":"priv","insertText":"priv","insertTextFormat":1,
> "kind":2,"label":"priv()","sortText":"200034priv"}
> +# CHECK-SAME: ]}
>
>  Content-Length: 58
>
>  {"jsonrpc":"2.0","id":4,"method":"shutdown","params":null}
> -# CHECK: {"jsonrpc":"2.0","id":4,"result":null}
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> Modified: clang-tools-extra/trunk/test/clangd/completion-qualifiers.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/completion-
> qualifiers.test?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/completion-qualifiers.test (original)
> +++ clang-tools-extra/trunk/test/clangd/completion-qualifiers.test Mon Nov  6
> 07:40:30 2017
> @@ -8,15 +8,14 @@ Content-Length: 297
>  Content-Length: 151
>
>
> {"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":11,"character":8}}}
> -# CHECK: {"jsonrpc":"2.0","id":2,"result":[
> -# CHECK-DAG: {"label":"foo()
> const","kind":2,"detail":"int","sortText":"200035foo","filterText":"foo","inse
> rtText":"foo","insertTextFormat":1}
> -# CHECK-DAG: {"label":"bar()
> const","kind":2,"detail":"int","sortText":"000037bar","filterText":"bar","inse
> rtText":"bar","insertTextFormat":1}
> -# CHECK-DAG: {"label":"Foo::foo()
> const","kind":2,"detail":"int","sortText":"000037foo","filterText":"foo","inse
> rtText":"foo","insertTextFormat":1}
> -# CHECK: ]}
> +# CHECK: {"id":2,"jsonrpc":"2.0","result":[
> +# CHECK-DAG:
> {"detail":"int","filterText":"foo","insertText":"foo","insertTextFormat":1,"ki
> nd":2,"label":"foo() const","sortText":"200035foo"}
> +# CHECK-DAG:
> {"detail":"int","filterText":"bar","insertText":"bar","insertTextFormat":1,"ki
> nd":2,"label":"bar() const","sortText":"000037bar"}
> +# CHECK-DAG:
> {"detail":"int","filterText":"foo","insertText":"foo","insertTextFormat":1,"ki
> nd":2,"label":"Foo::foo() const","sortText":"000037foo"}
> +# CHECK-SAME: ]}
>  Content-Length: 44
>
>  {"jsonrpc":"2.0","id":4,"method":"shutdown"}
> -# CHECK: {"jsonrpc":"2.0","id":4,"result":null}
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> Modified: clang-tools-extra/trunk/test/clangd/completion-snippet.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/completion-
> snippet.test?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/completion-snippet.test (original)
> +++ clang-tools-extra/trunk/test/clangd/completion-snippet.test Mon Nov  6
> 07:40:30 2017
> @@ -15,27 +15,27 @@ Content-Length: 148
>  # The order of results returned by codeComplete seems to be
>  # nondeterministic, so we check regardless of order.
>  #
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[
> -# CHECK-DAG:
> {"label":"a","kind":5,"detail":"int","sortText":"000035a","filterText":"a","in
> sertText":"a","insertTextFormat":1}
> -# CHECK-DAG:
> {"label":"bb","kind":5,"detail":"int","sortText":"000035bb","filterText":"bb",
> "insertText":"bb","insertTextFormat":1}
> -# CHECK-DAG:
> {"label":"ccc","kind":5,"detail":"int","sortText":"000035ccc","filterText":"cc
> c","insertText":"ccc","insertTextFormat":1}
> -# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake
> &","sortText":"000079operator=","filterText":"operator=","insertText":"operato
> r=(${1:const fake &})","insertTextFormat":2}
> -# CHECK-DAG:
> {"label":"~fake()","kind":4,"detail":"void","sortText":"000079~fake","filterTe
> xt":"~fake","insertText":"~fake()","insertTextFormat":1}
> -# CHECK-DAG: {"label":"f(int i, const float f)
> const","kind":2,"detail":"int","sortText":"000035f","filterText":"f","insertTe
> xt":"f(${1:int i}, ${2:const float f})","insertTextFormat":2}
> -# CHECK: ]}
> +# CHECK: {"id":1,"jsonrpc":"2.0","result":[
> +# CHECK-DAG:
> {"detail":"int","filterText":"a","insertText":"a","insertTextFormat":1,"kind":
> 5,"label":"a","sortText":"000035a"}
> +# CHECK-DAG:
> {"detail":"int","filterText":"bb","insertText":"bb","insertTextFormat":1,"kind
> ":5,"label":"bb","sortText":"000035bb"}
> +# CHECK-DAG:
> {"detail":"int","filterText":"ccc","insertText":"ccc","insertTextFormat":1,"ki
> nd":5,"label":"ccc","sortText":"000035ccc"}
> +# CHECK-DAG: {"detail":"fake
> &","filterText":"operator=","insertText":"operator=(${1:const fake
> &})","insertTextFormat":2,"kind":2,"label":"operator=(const fake
> &)","sortText":"000079operator="}
> +# CHECK-DAG:
> {"detail":"void","filterText":"~fake","insertText":"~fake()","insertTextFormat
> ":1,"kind":4,"label":"~fake()","sortText":"000079~fake"}
> +# CHECK-DAG: {"detail":"int","filterText":"f","insertText":"f(${1:int i},
> ${2:const float f})","insertTextFormat":2,"kind":2,"label":"f(int i, const
> float f) const","sortText":"000035f"}
> +# CHECK-SAME: ]}
>  Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":3,"character":5}}}
>  # Repeat the completion request, expect the same results.
>  #
> -# CHECK: {"jsonrpc":"2.0","id":2,"result":[
> -# CHECK-DAG:
> {"label":"a","kind":5,"detail":"int","sortText":"000035a","filterText":"a","in
> sertText":"a","insertTextFormat":1}
> -# CHECK-DAG:
> {"label":"bb","kind":5,"detail":"int","sortText":"000035bb","filterText":"bb",
> "insertText":"bb","insertTextFormat":1}
> -# CHECK-DAG:
> {"label":"ccc","kind":5,"detail":"int","sortText":"000035ccc","filterText":"cc
> c","insertText":"ccc","insertTextFormat":1}
> -# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake
> &","sortText":"000079operator=","filterText":"operator=","insertText":"operato
> r=(${1:const fake &})","insertTextFormat":2}
> -# CHECK-DAG:
> {"label":"~fake()","kind":4,"detail":"void","sortText":"000079~fake","filterTe
> xt":"~fake","insertText":"~fake()","insertTextFormat":1}
> -# CHECK-DAG: {"label":"f(int i, const float f)
> const","kind":2,"detail":"int","sortText":"000035f","filterText":"f","insertTe
> xt":"f(${1:int i}, ${2:const float f})","insertTextFormat":2}
> -# CHECK: ]}
> +# CHECK: {"id":2,"jsonrpc":"2.0","result":[
> +# CHECK-DAG:
> {"detail":"int","filterText":"a","insertText":"a","insertTextFormat":1,"kind":
> 5,"label":"a","sortText":"000035a"}
> +# CHECK-DAG:
> {"detail":"int","filterText":"bb","insertText":"bb","insertTextFormat":1,"kind
> ":5,"label":"bb","sortText":"000035bb"}
> +# CHECK-DAG:
> {"detail":"int","filterText":"ccc","insertText":"ccc","insertTextFormat":1,"ki
> nd":5,"label":"ccc","sortText":"000035ccc"}
> +# CHECK-DAG: {"detail":"fake
> &","filterText":"operator=","insertText":"operator=(${1:const fake
> &})","insertTextFormat":2,"kind":2,"label":"operator=(const fake
> &)","sortText":"000079operator="}
> +# CHECK-DAG:
> {"detail":"void","filterText":"~fake","insertText":"~fake()","insertTextFormat
> ":1,"kind":4,"label":"~fake()","sortText":"000079~fake"}
> +# CHECK-DAG: {"detail":"int","filterText":"f","insertText":"f(${1:int i},
> ${2:const float f})","insertTextFormat":2,"kind":2,"label":"f(int i, const
> float f) const","sortText":"000035f"}
> +# CHECK-SAME: ]}
>  # Update the source file and check for completions again.
>  Content-Length: 226
>
> @@ -44,15 +44,12 @@ Content-Length: 226
>  Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":3,"character":5}}}
> -# Repeat the completion request, expect the same results.
> -#
> -# CHECK: {"jsonrpc":"2.0","id":3,"result":[
> -# CHECK-DAG: {"label":"func()","kind":2,"detail":"int (*)(int,
> int)","sortText":"000034func","filterText":"func","insertText":"func()","inser
> tTextFormat":1}
> -# CHECK: ]}
> +# CHECK: {"id":3,"jsonrpc":"2.0","result":[
> +# CHECK-DAG: {"detail":"int (*)(int,
> int)","filterText":"func","insertText":"func()","insertTextFormat":1,"kind":2,
> "label":"func()","sortText":"000034func"}
> +# CHECK-SAME: ]}
>  Content-Length: 44
>
>  {"jsonrpc":"2.0","id":4,"method":"shutdown"}
> -# CHECK: {"jsonrpc":"2.0","id":4,"result":null}
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> Modified: clang-tools-extra/trunk/test/clangd/completion.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/completion.test?rev=317486&r1=317485&r2=317486&view=di
> ff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/completion.test (original)
> +++ clang-tools-extra/trunk/test/clangd/completion.test Mon Nov  6 07:40:30
> 2017
> @@ -15,27 +15,27 @@ Content-Length: 148
>  # The order of results returned by codeComplete seems to be
>  # nondeterministic, so we check regardless of order.
>  #
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[
> -# CHECK-DAG:
> {"label":"a","kind":5,"detail":"int","sortText":"000035a","filterText":"a","in
> sertText":"a","insertTextFormat":1}
> -# CHECK-DAG:
> {"label":"bb","kind":5,"detail":"int","sortText":"000035bb","filterText":"bb",
> "insertText":"bb","insertTextFormat":1}
> -# CHECK-DAG:
> {"label":"ccc","kind":5,"detail":"int","sortText":"000035ccc","filterText":"cc
> c","insertText":"ccc","insertTextFormat":1}
> -# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake
> &","sortText":"000079operator=","filterText":"operator=","insertText":"operato
> r=","insertTextFormat":1}
> -# CHECK-DAG:
> {"label":"~fake()","kind":4,"detail":"void","sortText":"000079~fake","filterTe
> xt":"~fake","insertText":"~fake","insertTextFormat":1}
> -# CHECK-DAG: {"label":"f(int i, const float f)
> const","kind":2,"detail":"int","sortText":"000035f","filterText":"f","insertTe
> xt":"f","insertTextFormat":1}
> -# CHECK: ]}
> +# CHECK: {"id":1,"jsonrpc":"2.0","result":[
> +# CHECK-DAG:
> {"detail":"int","filterText":"a","insertText":"a","insertTextFormat":1,"kind":
> 5,"label":"a","sortText":"000035a"}
> +# CHECK-DAG:
> {"detail":"int","filterText":"bb","insertText":"bb","insertTextFormat":1,"kind
> ":5,"label":"bb","sortText":"000035bb"}
> +# CHECK-DAG:
> {"detail":"int","filterText":"ccc","insertText":"ccc","insertTextFormat":1,"ki
> nd":5,"label":"ccc","sortText":"000035ccc"}
> +# CHECK-DAG: {"detail":"fake
> &","filterText":"operator=","insertText":"operator=","insertTextFormat":1,"kin
> d":2,"label":"operator=(const fake &)","sortText":"000079operator="}
> +# CHECK-DAG:
> {"detail":"void","filterText":"~fake","insertText":"~fake","insertTextFormat":
> 1,"kind":4,"label":"~fake()","sortText":"000079~fake"}
> +# CHECK-DAG:
> {"detail":"int","filterText":"f","insertText":"f","insertTextFormat":1,"kind":
> 2,"label":"f(int i, const float f) const","sortText":"000035f"}
> +# CHECK-SAME: ]}
>  Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":3,"character":5}}}
>  # Repeat the completion request, expect the same results.
>  #
> -# CHECK: {"jsonrpc":"2.0","id":2,"result":[
> -# CHECK-DAG:
> {"label":"a","kind":5,"detail":"int","sortText":"000035a","filterText":"a","in
> sertText":"a","insertTextFormat":1}
> -# CHECK-DAG:
> {"label":"bb","kind":5,"detail":"int","sortText":"000035bb","filterText":"bb",
> "insertText":"bb","insertTextFormat":1}
> -# CHECK-DAG:
> {"label":"ccc","kind":5,"detail":"int","sortText":"000035ccc","filterText":"cc
> c","insertText":"ccc","insertTextFormat":1}
> -# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake
> &","sortText":"000079operator=","filterText":"operator=","insertText":"operato
> r=","insertTextFormat":1}
> -# CHECK-DAG:
> {"label":"~fake()","kind":4,"detail":"void","sortText":"000079~fake","filterTe
> xt":"~fake","insertText":"~fake","insertTextFormat":1}
> -# CHECK-DAG: {"label":"f(int i, const float f)
> const","kind":2,"detail":"int","sortText":"000035f","filterText":"f","insertTe
> xt":"f","insertTextFormat":1}
> -# CHECK: ]}
> +# CHECK: {"id":2,"jsonrpc":"2.0","result":[
> +# CHECK-DAG:
> {"detail":"int","filterText":"a","insertText":"a","insertTextFormat":1,"kind":
> 5,"label":"a","sortText":"000035a"}
> +# CHECK-DAG:
> {"detail":"int","filterText":"bb","insertText":"bb","insertTextFormat":1,"kind
> ":5,"label":"bb","sortText":"000035bb"}
> +# CHECK-DAG:
> {"detail":"int","filterText":"ccc","insertText":"ccc","insertTextFormat":1,"ki
> nd":5,"label":"ccc","sortText":"000035ccc"}
> +# CHECK-DAG: {"detail":"fake
> &","filterText":"operator=","insertText":"operator=","insertTextFormat":1,"kin
> d":2,"label":"operator=(const fake &)","sortText":"000079operator="}
> +# CHECK-DAG:
> {"detail":"void","filterText":"~fake","insertText":"~fake","insertTextFormat":
> 1,"kind":4,"label":"~fake()","sortText":"000079~fake"}
> +# CHECK-DAG:
> {"detail":"int","filterText":"f","insertText":"f","insertTextFormat":1,"kind":
> 2,"label":"f(int i, const float f) const","sortText":"000035f"}
> +# CHECK-SAME: ]}
>  # Update the source file and check for completions again.
>  Content-Length: 226
>
> @@ -46,13 +46,12 @@ Content-Length: 148
>
> {"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":3,"character":5}}}
>  # Repeat the completion request, expect the same results.
>  #
> -# CHECK: {"jsonrpc":"2.0","id":3,"result":[
> -# CHECK-DAG: {"label":"func()","kind":2,"detail":"int (*)(int,
> int)","sortText":"000034func","filterText":"func","insertText":"func","insertT
> extFormat":1}
> -# CHECK: ]}
> +# CHECK: {"id":3,"jsonrpc":"2.0","result":[
> +# CHECK-DAG: {"detail":"int (*)(int,
> int)","filterText":"func","insertText":"func","insertTextFormat":1,"kind":2,"l
> abel":"func()","sortText":"000034func"}
> +# CHECK-SAME: ]}
>  Content-Length: 44
>
>  {"jsonrpc":"2.0","id":4,"method":"shutdown"}
> -# CHECK: {"jsonrpc":"2.0","id":4,"result":null}
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> Modified: clang-tools-extra/trunk/test/clangd/definitions.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/definitions.test?rev=317486&r1=317485&r2=317486&view=d
> iff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/definitions.test (original)
> +++ clang-tools-extra/trunk/test/clangd/definitions.test Mon Nov  6 07:40:30
> 2017
> @@ -1,4 +1,4 @@
> -# RUN: clangd -run-synchronously < %s | FileCheck %s
> +# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace
> %s
>  # It is absolutely vital that this file has CRLF line endings.
>  #
>  Content-Length: 125
> @@ -13,14 +13,44 @@ Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":2,"character":0}}}
>  # Go to local variable
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp<file:///\\main.cpp>",
> "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1,
> "character": 5}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 5,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 0,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":2,"character":1}}}
>  # Go to local variable, end of token
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp<file:///\\main.cpp>",
> "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1,
> "character": 5}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 5,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 0,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 214
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///main.cpp<file:///\\main.cpp>","version":2},"contentChanges":[{"text":"struct Foo
> {\nint x;\n};\nint main() {\n  Foo bar = { x : 1 };\n}\n"}]}}
> @@ -29,8 +59,23 @@ Content-Length: 149
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":4,"character":14}}}
>  # Go to field, GNU old-style field designator
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp<file:///\\main.cpp>",
> "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1,
> "character": 5}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 5,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 0,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 215
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///main.cpp<file:///\\main.cpp>","version":3},"contentChanges":[{"text":"struct Foo
> {\nint x;\n};\nint main() {\n  Foo baz = { .x = 2 };\n}\n"}]}}
> @@ -39,8 +84,23 @@ Content-Length: 149
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":4,"character":15}}}
>  # Go to field, field designator
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp<file:///\\main.cpp>",
> "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1,
> "character": 5}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 5,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 0,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 187
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///main.cpp<file:///\\main.cpp>","version":4},"contentChanges":[{"text":"int main() {\n
> main();\n   return 0;\n}"}]}}
> @@ -49,8 +109,23 @@ Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":1,"character":3}}}
>  # Go to function declaration, function call
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp<file:///\\main.cpp>",
> "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3,
> "character": 1}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 1,
> +# CHECK-NEXT:          "line": 3
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 0,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 208
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///main.cpp<file:///\\main.cpp>","version":5},"contentChanges":[{"text":"struct Foo
> {\n};\nint main() {\n   Foo bar;\n   return 0;\n}\n"}]}}
> @@ -59,8 +134,23 @@ Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":3,"character":3}}}
>  # Go to struct declaration, new struct instance
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp<file:///\\main.cpp>",
> "range": {"start": {"line": 0, "character": 0}, "end": {"line": 1,
> "character": 1}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 1,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 0,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 231
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///main.cpp<file:///\\main.cpp>","version":5},"contentChanges":[{"text":"namespace n1
> {\nstruct Foo {\n};\n}\nint main() {\n   n1::Foo bar;\n   return 0;\n}\n"}]}}
> @@ -69,8 +159,23 @@ Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":5,"character":4}}}
>  # Go to struct declaration, new struct instance, qualified name
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp<file:///\\main.cpp>",
> "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3,
> "character": 1}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 1,
> +# CHECK-NEXT:          "line": 3
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 0,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 215
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///main.cpp<file:///\\main.cpp>","version":6},"contentChanges":[{"text":"struct Foo {\n
> int x;\n};\nint main() {\n   Foo bar;\n   bar.x;\n}\n"}]}}
> @@ -79,8 +184,23 @@ Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":5,"character":7}}}
>  # Go to field declaration, field reference
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp<file:///\\main.cpp>",
> "range": {"start": {"line": 1, "character": 2}, "end": {"line": 1,
> "character": 7}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 7,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 2,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 220
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///main.cpp<file:///\\main.cpp>","version":7},"contentChanges":[{"text":"struct Foo {\n
> void x();\n};\nint main() {\n   Foo bar;\n   bar.x();\n}\n"}]}}
> @@ -89,8 +209,23 @@ Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":5,"character":7}}}
>  # Go to method declaration, method call
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp<file:///\\main.cpp>",
> "range": {"start": {"line": 1, "character": 2}, "end": {"line": 1,
> "character": 10}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 10,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 2,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 240
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///main.cpp<file:///\\main.cpp>","version":7},"contentChanges":[{"text":"struct Foo
> {\n};\ntypedef Foo TypedefFoo;\nint main() {\n   TypedefFoo bar;\n   return
> 0;\n}\n"}]}}
> @@ -99,8 +234,23 @@ Content-Length: 149
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":4,"character":10}}}
>  # Go to typedef
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp<file:///\\main.cpp>",
> "range": {"start": {"line": 2, "character": 0}, "end": {"line": 2,
> "character": 22}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 22,
> +# CHECK-NEXT:          "line": 2
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 0,
> +# CHECK-NEXT:          "line": 2
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 254
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///main.cpp<file:///\\main.cpp>","version":7},"contentChanges":[{"text":"template
> <typename MyTemplateParam>\nvoid foo() {\n MyTemplateParam a;\n}\nint main()
> {\n   return 0;\n}\n"}]}}
> @@ -109,8 +259,9 @@ Content-Length: 149
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<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<file:///\\main.cpp>",
> "range": {"start": {"line": 0, "character": 10}, "end": {"line": 0,
> "character": 34}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": []
>  Content-Length: 256
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///main.cpp<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"}]}}
> @@ -119,8 +270,23 @@ Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":6,"character":4}}}
>  # Go to namespace, static method call
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp<file:///\\main.cpp>",
> "range": {"start": {"line": 0, "character": 0}, "end": {"line": 4,
> "character": 1}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 1,
> +# CHECK-NEXT:          "line": 4
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 0,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 265
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///main.cpp<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"}]}}
> @@ -128,9 +294,24 @@ Content-Length: 265
>  Content-Length: 149
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":3,"character":21}}}
> -# Go to field, member initializer
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp<file:///\\main.cpp>",
> "range": {"start": {"line": 2, "character": 2}, "end": {"line": 2,
> "character": 11}}}]}
> -
> +# Go to field, member initializer
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 11,
> +# CHECK-NEXT:          "line": 2
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 2,
> +# CHECK-NEXT:          "line": 2
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 204
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///main.cpp<file:///\\main.cpp>","version":7},"contentChanges":[{"text":"#define
> MY_MACRO 0\nint main() {\n  return MY_MACRO;\n}\n"}]}}
> @@ -139,8 +320,23 @@ Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":2,"character":9}}}
>  # Go to macro.
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///{{([A-Za-
> z]:/)?}}main.cpp<file:///\\%7b%7b([A-Za-%0b%3e%20z]:\)?%7d%7dmain.cpp>", "range": {"start": {"line": 0, "character": 8}, "end":
> {"line": 0, "character": 18}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 18,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 8,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 217
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///main.cpp<file:///\\main.cpp>","version":7},"contentChanges":[{"text":"#define FOO
> 1\nint a = FOO;\n#define FOO 2\nint b = FOO;\n#undef FOO\n"}]}}
> @@ -149,29 +345,77 @@ Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":1,"character":8}}}
>  # Go to macro, re-defined later
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///{{([A-Za-
> z]:/)?}}main.cpp<file:///\\%7b%7b([A-Za-%0b%3e%20z]:\)?%7d%7dmain.cpp>", "range": {"start": {"line": 0, "character": 8}, "end":
> {"line": 0, "character": 13}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 13,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 8,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":3,"character":8}}}
>  # Go to macro, undefined later
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp<file:///\\main.cpp>",
> "range": {"start": {"line": 2, "character": 8}, "end": {"line": 2,
> "character": 13}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 13,
> +# CHECK-NEXT:          "line": 2
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 8,
> +# CHECK-NEXT:          "line": 2
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 148
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":4,"character":7}}}
>  # Go to macro, being undefined
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp<file:///\\main.cpp>",
> "range": {"start": {"line": 2, "character": 8}, "end": {"line": 2,
> "character": 13}}}]}
> -
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 13,
> +# CHECK-NEXT:          "line": 2
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 8,
> +# CHECK-NEXT:          "line": 2
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 156
>
>
> {"jsonrpc":"2.0","id":2,"method":"textDocument/definition","params":{"textDocu
> ment":{"uri":"file:///doesnotexist.cpp<file:///\\doesnotexist.cpp>"},"position":{"line":4,"character":7}}}
> -# CHECK: {"jsonrpc":"2.0","id":2,"error":{"code":-
> 32602,"message":"findDefinitions called on non-added file"}}
> -
> +#      CHECK:  "error": {
> +# CHECK-NEXT:    "code": -32602,
> +# CHECK-NEXT:    "message": "findDefinitions called on non-added file"
> +# CHECK-NEXT:  },
> +# CHECK-NEXT:  "id": 2,
> +# CHECK-NEXT:  "jsonrpc": "2.0"
>  Content-Length: 48
>
>  {"jsonrpc":"2.0","id":10000,"method":"shutdown"}
> -# CHECK: {"jsonrpc":"2.0","id":10000,"result":null}
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> Modified: clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/diagnostics-
> preamble.test?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test (original)
> +++ clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test Mon Nov  6
> 07:40:30 2017
> @@ -1,4 +1,4 @@
> -# RUN: clangd -run-synchronously < %s | FileCheck %s
> +# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace
> %s
>  # It is absolutely vital that this file has CRLF line endings.
>  #
>  Content-Length: 125
> @@ -8,12 +8,14 @@ Content-Length: 125
>  Content-Length: 206
>
>
> {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"ur
> i":"file:///main.cpp<file:///\\main.cpp>","languageId":"cpp","version":1,"text":"#ifndef
> FOO\n#define FOO\nint a;\n#else\nint a = b;#endif\n\n\n"}}}
> -# CHECK:
> {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"f
> ile:///main.cpp","diagnostics":[]}}
> -
> +#      CHECK:  "method": "textDocument/publishDiagnostics",
> +# CHECK-NEXT:  "params": {
> +# CHECK-NEXT:    "diagnostics": [],
> +# CHECK-NEXT:    "uri": "file:///main.cpp<file:///\\main.cpp>"
> +# CHECK-NEXT:  }
>  Content-Length: 58
>
>  {"jsonrpc":"2.0","id":2,"method":"shutdown","params":null}
> -# CHECK: {"jsonrpc":"2.0","id":2,"result":null}
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> Modified: clang-tools-extra/trunk/test/clangd/diagnostics.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/diagnostics.test?rev=317486&r1=317485&r2=317486&view=d
> iff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/diagnostics.test (original)
> +++ clang-tools-extra/trunk/test/clangd/diagnostics.test Mon Nov  6 07:40:30
> 2017
> @@ -1,4 +1,4 @@
> -# RUN: clangd -run-synchronously < %s | FileCheck %s
> +# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace
> %s
>  # It is absolutely vital that this file has CRLF line endings.
>  #
>  Content-Length: 125
> @@ -8,14 +8,43 @@ Content-Length: 125
>  Content-Length: 152
>
>
> {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"ur
> i":"file:///foo.c<file:///\\foo.c>","languageId":"c","version":1,"text":"void main() {}"}}}
> -#
> -# CHECK:
> {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"f
> ile:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 1},
> "end": {"line": 0, "character": 1}},"severity":2,"message":"return type of
> 'main' is not 'int'"},{"range":{"start": {"line": 0, "character": 1}, "end":
> {"line": 0, "character": 1}},"severity":3,"message":"change return type to
> 'int'"}]}}
> -#
> -#
> +#      CHECK:  "method": "textDocument/publishDiagnostics",
> +# CHECK-NEXT:  "params": {
> +# CHECK-NEXT:    "diagnostics": [
> +# CHECK-NEXT:      {
> +# CHECK-NEXT:        "message": "return type of 'main' is not 'int'",
> +# CHECK-NEXT:        "range": {
> +# CHECK-NEXT:          "end": {
> +# CHECK-NEXT:            "character": 1,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          },
> +# CHECK-NEXT:          "start": {
> +# CHECK-NEXT:            "character": 1,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "severity": 2
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      {
> +# CHECK-NEXT:        "message": "change return type to 'int'",
> +# CHECK-NEXT:        "range": {
> +# CHECK-NEXT:          "end": {
> +# CHECK-NEXT:            "character": 1,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          },
> +# CHECK-NEXT:          "start": {
> +# CHECK-NEXT:            "character": 1,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "severity": 3
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    ],
> +# CHECK-NEXT:    "uri": "file:///foo.c<file:///\\foo.c>"
> +# CHECK-NEXT:  }
>  Content-Length: 44
>
>  {"jsonrpc":"2.0","id":5,"method":"shutdown"}
> -# CHECK: {"jsonrpc":"2.0","id":5,"result":null}
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> Modified: clang-tools-extra/trunk/test/clangd/did-change-watch-files.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/did-change-watch-
> files.test?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/did-change-watch-files.test (original)
> +++ clang-tools-extra/trunk/test/clangd/did-change-watch-files.test Mon Nov  6
> 07:40:30 2017
> @@ -5,18 +5,7 @@
>  Content-Length: 143
>
>
> {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootU
> ri":"file:///path/to/workspace<file:///\\path\to\workspace>","capabilities":{},"trace":"off"}}
> -# CHECK: Content-Length: 466
> -# CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{
> -# CHECK:   "textDocumentSync": 1,
> -# CHECK:   "documentFormattingProvider": true,
> -# CHECK:   "documentRangeFormattingProvider": true,
> -# CHECK:   "documentOnTypeFormattingProvider":
> {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
> -# CHECK:   "codeActionProvider": true,
> -# CHECK:   "completionProvider": {"resolveProvider": false,
> "triggerCharacters": [".",">",":"]},
> -# CHECK:   "definitionProvider": true
> -# CHECK: }}}
> -#
> -#Normal case
> +# Normal case.
>  Content-Length: 217
>
>
> {"jsonrpc":"2.0","method":"workspace/didChangeWatchedFiles","params":{"changes
> ":[{"uri":"file:///path/to/file.cpp<file:///\\path\to\file.cpp>","type":1},{"uri":"file:///path/to/file2.c
> pp<file:///\\path\to\file2.c%0b%3e%20pp>","type":2},{"uri":"file:///path/to/file3.cpp<file:///\\path\to\file3.cpp>","type":3}]}}
>
> Modified: clang-tools-extra/trunk/test/clangd/execute-command.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/execute-
> command.test?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/execute-command.test (original)
> +++ clang-tools-extra/trunk/test/clangd/execute-command.test Mon Nov  6
> 07:40:30 2017
> @@ -1,4 +1,4 @@
> -# RUN: clangd -run-synchronously < %s | FileCheck %s
> +# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace
> %s
>  # It is absolutely vital that this file has CRLF line endings.
>  #
>  Content-Length: 125
> @@ -8,9 +8,54 @@ Content-Length: 125
>  Content-Length: 180
>
>
> {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"ur
> i":"file:///foo.c<file:///\\foo.c>","languageId":"c","version":1,"text":"int main(int i, char
> **a) { if (i = 2) {}}"}}}
> -#
> -# CHECK:
> {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"f
> ile:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 35},
> "end": {"line": 0, "character": 35}},"severity":2,"message":"using the result
> of an assignment as a condition without parentheses"},{"range":{"start":
> {"line": 0, "character": 35}, "end": {"line": 0, "character":
> 35}},"severity":3,"message":"place parentheses around the assignment to
> silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end":
> {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this
> assignment into an equality comparison"}]}}
> -#
> +#      CHECK:  "method": "textDocument/publishDiagnostics",
> +# CHECK-NEXT:  "params": {
> +# CHECK-NEXT:    "diagnostics": [
> +# CHECK-NEXT:      {
> +# CHECK-NEXT:        "message": "using the result of an assignment as a
> condition without parentheses",
> +# CHECK-NEXT:        "range": {
> +# CHECK-NEXT:          "end": {
> +# CHECK-NEXT:            "character": 35,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          },
> +# CHECK-NEXT:          "start": {
> +# CHECK-NEXT:            "character": 35,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "severity": 2
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      {
> +# CHECK-NEXT:        "message": "place parentheses around the assignment to
> silence this warning",
> +# CHECK-NEXT:        "range": {
> +# CHECK-NEXT:          "end": {
> +# CHECK-NEXT:            "character": 35,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          },
> +# CHECK-NEXT:          "start": {
> +# CHECK-NEXT:            "character": 35,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "severity": 3
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      {
> +# CHECK-NEXT:        "message": "use '==' to turn this assignment into an
> equality comparison",
> +# CHECK-NEXT:        "range": {
> +# CHECK-NEXT:          "end": {
> +# CHECK-NEXT:            "character": 35,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          },
> +# CHECK-NEXT:          "start": {
> +# CHECK-NEXT:            "character": 35,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "severity": 3
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    ],
> +# CHECK-NEXT:    "uri": "file:///foo.c<file:///\\foo.c>"
> +# CHECK-NEXT:  }
>  Content-Length: 72
>
>  {"jsonrpc":"2.0","id":3,"method":"workspace/executeCommand","params":{}}
>
> Modified: clang-tools-extra/trunk/test/clangd/extra-flags.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/extra-
> flags.test?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/extra-flags.test (original)
> +++ clang-tools-extra/trunk/test/clangd/extra-flags.test Mon Nov  6 07:40:30
> 2017
> @@ -1,4 +1,4 @@
> -# RUN: clangd -run-synchronously < %s | FileCheck %s
> +# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace
> %s
>  # It is absolutely vital that this file has CRLF line endings.
>  #
>  Content-Length: 125
> @@ -8,17 +8,80 @@ Content-Length: 125
>  Content-Length: 205
>
>
> {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"ur
> i":"file:///foo.c<file:///\\foo.c>","languageId":"c","version":1,"text":"int main() { int i;
> return i; }"},"metadata":{"extraFlags":["-Wall"]}}}
> -# CHECK:
> {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"f
> ile:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 28},
> "end": {"line": 0, "character": 28}},"severity":2,"message":"variable 'i' is
> uninitialized when used here"},{"range":{"start": {"line": 0, "character":
> 19}, "end": {"line": 0, "character": 19}},"severity":3,"message":"initialize
> the variable 'i' to silence this warning"}]}}
> -#
> +#      CHECK:  "method": "textDocument/publishDiagnostics",
> +# CHECK-NEXT:  "params": {
> +# CHECK-NEXT:    "diagnostics": [
> +# CHECK-NEXT:      {
> +# CHECK-NEXT:        "message": "variable 'i' is uninitialized when used
> here",
> +# CHECK-NEXT:        "range": {
> +# CHECK-NEXT:          "end": {
> +# CHECK-NEXT:            "character": 28,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          },
> +# CHECK-NEXT:          "start": {
> +# CHECK-NEXT:            "character": 28,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "severity": 2
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      {
> +# CHECK-NEXT:        "message": "initialize the variable 'i' to silence this
> warning",
> +# CHECK-NEXT:        "range": {
> +# CHECK-NEXT:          "end": {
> +# CHECK-NEXT:            "character": 19,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          },
> +# CHECK-NEXT:          "start": {
> +# CHECK-NEXT:            "character": 19,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "severity": 3
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    ],
> +# CHECK-NEXT:    "uri": "file:///foo.c<file:///\\foo.c>"
> +# CHECK-NEXT:  }
>  Content-Length: 175
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///foo.c<file:///\\foo.c>","version":2},"contentChanges":[{"text":"int main() { int
> i; return i; }"}]}}
> -# CHECK:
> {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"f
> ile:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 28},
> "end": {"line": 0, "character": 28}},"severity":2,"message":"variable 'i' is
> uninitialized when used here"},{"range":{"start": {"line": 0, "character":
> 19}, "end": {"line": 0, "character": 19}},"severity":3,"message":"initialize
> the variable 'i' to silence this warning"}]}}
> -#
> +#      CHECK:  "method": "textDocument/publishDiagnostics",
> +# CHECK-NEXT:  "params": {
> +# CHECK-NEXT:    "diagnostics": [
> +# CHECK-NEXT:      {
> +# CHECK-NEXT:        "message": "variable 'i' is uninitialized when used
> here",
> +# CHECK-NEXT:        "range": {
> +# CHECK-NEXT:          "end": {
> +# CHECK-NEXT:            "character": 28,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          },
> +# CHECK-NEXT:          "start": {
> +# CHECK-NEXT:            "character": 28,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "severity": 2
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      {
> +# CHECK-NEXT:        "message": "initialize the variable 'i' to silence this
> warning",
> +# CHECK-NEXT:        "range": {
> +# CHECK-NEXT:          "end": {
> +# CHECK-NEXT:            "character": 19,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          },
> +# CHECK-NEXT:          "start": {
> +# CHECK-NEXT:            "character": 19,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "severity": 3
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    ],
> +# CHECK-NEXT:    "uri": "file:///foo.c<file:///\\foo.c>"
> +# CHECK-NEXT:  }
>  Content-Length: 44
>
>  {"jsonrpc":"2.0","id":5,"method":"shutdown"}
> -# CHECK: {"jsonrpc":"2.0","id":5,"result":null}
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> Modified: clang-tools-extra/trunk/test/clangd/fixits.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/fixits.test?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/fixits.test (original)
> +++ clang-tools-extra/trunk/test/clangd/fixits.test Mon Nov  6 07:40:30 2017
> @@ -1,4 +1,4 @@
> -# RUN: clangd -run-synchronously < %s | FileCheck %s
> +# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace
> %s
>  # It is absolutely vital that this file has CRLF line endings.
>  #
>  Content-Length: 125
> @@ -8,30 +8,242 @@ Content-Length: 125
>  Content-Length: 180
>
>
> {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"ur
> i":"file:///foo.c<file:///\\foo.c>","languageId":"c","version":1,"text":"int main(int i, char
> **a) { if (i = 2) {}}"}}}
> -#
> -# CHECK:
> {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"f
> ile:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 35},
> "end": {"line": 0, "character": 35}},"severity":2,"message":"using the result
> of an assignment as a condition without parentheses"},{"range":{"start":
> {"line": 0, "character": 35}, "end": {"line": 0, "character":
> 35}},"severity":3,"message":"place parentheses around the assignment to
> silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end":
> {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this
> assignment into an equality comparison"}]}}
> -#
> +#      CHECK:    "method": "textDocument/publishDiagnostics",
> +# CHECK-NEXT:  "params": {
> +# CHECK-NEXT:    "diagnostics": [
> +# CHECK-NEXT:      {
> +# CHECK-NEXT:        "message": "using the result of an assignment as a
> condition without parentheses",
> +# CHECK-NEXT:        "range": {
> +# CHECK-NEXT:          "end": {
> +# CHECK-NEXT:            "character": 35,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          },
> +# CHECK-NEXT:          "start": {
> +# CHECK-NEXT:            "character": 35,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "severity": 2
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      {
> +# CHECK-NEXT:        "message": "place parentheses around the assignment to
> silence this warning",
> +# CHECK-NEXT:        "range": {
> +# CHECK-NEXT:          "end": {
> +# CHECK-NEXT:            "character": 35,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          },
> +# CHECK-NEXT:          "start": {
> +# CHECK-NEXT:            "character": 35,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "severity": 3
> +# CHECK-NEXT:      },
> +# CHECK-NEXT:      {
> +# CHECK-NEXT:        "message": "use '==' to turn this assignment into an
> equality comparison",
> +# CHECK-NEXT:        "range": {
> +# CHECK-NEXT:          "end": {
> +# CHECK-NEXT:            "character": 35,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          },
> +# CHECK-NEXT:          "start": {
> +# CHECK-NEXT:            "character": 35,
> +# CHECK-NEXT:            "line": 0
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "severity": 3
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    ],
> +# CHECK-NEXT:    "uri": "file:///foo.c<file:///\\foo.c>"
> +# CHECK-NEXT:  }
>  Content-Length: 746
>
>
> {"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocu
> ment":{"uri":"file:///foo.c<file:///\\foo.c>"},"range":{"start":{"line":104,"character":13},"en
> d":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start":
> {"line": 0, "character": 35}, "end": {"line": 0, "character":
> 35}},"severity":2,"message":"using the result of an assignment as a condition
> without parentheses"},{"range":{"start": {"line": 0, "character": 35}, "end":
> {"line": 0, "character": 35}},"severity":3,"message":"place parentheses around
> the assignment to silence this warning"},{"range":{"start": {"line": 0,
> "character": 35}, "end": {"line": 0, "character":
> 35}},"severity":3,"message":"use '==' to turn this assignment into an equality
> comparison"}]}}}
> -#
> -# CHECK: {"jsonrpc":"2.0","id":2,"result":[{"title":"Apply FixIt 'place
> parentheses around the assignment to silence this warning'", "command":
> "clangd.applyFix", "arguments": [{"changes": {"file:///foo.c<file:///\\foo.c>": [{"range":
> {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 32}},
> "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end":
> {"line": 0, "character": 37}}, "newText": ")"}]}}]},{"title":"Apply FixIt 'use
> '==' to turn this assignment into an equality comparison'", "command":
> "clangd.applyFix", "arguments": [{"changes": {"file:///foo.c<file:///\\foo.c>": [{"range":
> {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character": 35}},
> "newText": "=="}]}}]}]}
> -#
> +#      CHECK:  "id": 2,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "arguments": [
> +# CHECK-NEXT:        {
> +# CHECK-NEXT:          "changes": {
> +# CHECK-NEXT:            "file:///foo.c<file:///\\foo.c>": [
> +# CHECK-NEXT:              {
> +# CHECK-NEXT:                "newText": "(",
> +# CHECK-NEXT:                "range": {
> +# CHECK-NEXT:                  "end": {
> +# CHECK-NEXT:                    "character": 32,
> +# CHECK-NEXT:                    "line": 0
> +# CHECK-NEXT:                  },
> +# CHECK-NEXT:                  "start": {
> +# CHECK-NEXT:                    "character": 32,
> +# CHECK-NEXT:                    "line": 0
> +# CHECK-NEXT:                  }
> +# CHECK-NEXT:                }
> +# CHECK-NEXT:              },
> +# CHECK-NEXT:              {
> +# CHECK-NEXT:                "newText": ")",
> +# CHECK-NEXT:                "range": {
> +# CHECK-NEXT:                  "end": {
> +# CHECK-NEXT:                    "character": 37,
> +# CHECK-NEXT:                    "line": 0
> +# CHECK-NEXT:                  },
> +# CHECK-NEXT:                  "start": {
> +# CHECK-NEXT:                    "character": 37,
> +# CHECK-NEXT:                    "line": 0
> +# CHECK-NEXT:                  }
> +# CHECK-NEXT:                }
> +# CHECK-NEXT:              }
> +# CHECK-NEXT:            ]
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      ],
> +# CHECK-NEXT:      "command": "clangd.applyFix",
> +# CHECK-NEXT:      "title": "Apply FixIt place parentheses around the
> assignment to silence this warning"
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "arguments": [
> +# CHECK-NEXT:        {
> +# CHECK-NEXT:          "changes": {
> +# CHECK-NEXT:            "file:///foo.c<file:///\\foo.c>": [
> +# CHECK-NEXT:              {
> +# CHECK-NEXT:                "newText": "==",
> +# CHECK-NEXT:                "range": {
> +# CHECK-NEXT:                  "end": {
> +# CHECK-NEXT:                    "character": 35,
> +# CHECK-NEXT:                    "line": 0
> +# CHECK-NEXT:                  },
> +# CHECK-NEXT:                  "start": {
> +# CHECK-NEXT:                    "character": 34,
> +# CHECK-NEXT:                    "line": 0
> +# CHECK-NEXT:                  }
> +# CHECK-NEXT:                }
> +# CHECK-NEXT:              }
> +# CHECK-NEXT:            ]
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      ],
> +# CHECK-NEXT:      "command": "clangd.applyFix",
> +# CHECK-NEXT:      "title": "Apply FixIt use '==' to turn this assignment
> into an equality comparison"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 771
>
> -
> {"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocu
> ment":{"uri":"file:///foo.c<file:///\\foo.c>"},"range":{"start":{"line":104,"character":13},"en
> d":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start":
> {"line": 0, "character": 35}, "end": {"line": 0, "character":
> 35}},"severity":2,"code":"1","source":"foo","message":"using the result of an
> assignment as a condition without parentheses"},{"range":{"start": {"line": 0,
> "character": 35}, "end": {"line": 0, "character":
> 35}},"severity":3,"message":"place parentheses around the assignment to
> silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end":
> {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this
> assignment into an equality comparison"}]}}}
> +{"jsonrpc":"2.0","id":3,"method":"textDocument/codeAction","params":{"textDoc
> ument":{"uri":"file:///foo.c<file:///\\foo.c>"},"range":{"start":{"line":104,"character":13},"e
> nd":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start":
> {"line": 0, "character": 35}, "end": {"line": 0, "character":
> 35}},"severity":2,"code":"1","source":"foo","message":"using the result of an
> assignment as a condition without parentheses"},{"range":{"start": {"line": 0,
> "character": 35}, "end": {"line": 0, "character":
> 35}},"severity":3,"message":"place parentheses around the assignment to
> silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end":
> {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this
> assignment into an equality comparison"}]}}}
>  # Make sure unused "code" and "source" fields ignored gracefully
> -# CHECK: {"jsonrpc":"2.0","id":2,"result":[{"title":"Apply FixIt 'place
> parentheses around the assignment to silence this warning'", "command":
> "clangd.applyFix", "arguments": [{"changes": {"file:///foo.c<file:///\\foo.c>": [{"range":
> {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 32}},
> "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end":
> {"line": 0, "character": 37}}, "newText": ")"}]}}]},{"title":"Apply FixIt 'use
> '==' to turn this assignment into an equality comparison'", "command":
> "clangd.applyFix", "arguments": [{"changes": {"file:///foo.c<file:///\\foo.c>": [{"range":
> {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character": 35}},
> "newText": "=="}]}}]}]}
> -#
> +#      CHECK:  "id": 3,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "arguments": [
> +# CHECK-NEXT:        {
> +# CHECK-NEXT:          "changes": {
> +# CHECK-NEXT:            "file:///foo.c<file:///\\foo.c>": [
> +# CHECK-NEXT:              {
> +# CHECK-NEXT:                "newText": "(",
> +# CHECK-NEXT:                "range": {
> +# CHECK-NEXT:                  "end": {
> +# CHECK-NEXT:                    "character": 32,
> +# CHECK-NEXT:                    "line": 0
> +# CHECK-NEXT:                  },
> +# CHECK-NEXT:                  "start": {
> +# CHECK-NEXT:                    "character": 32,
> +# CHECK-NEXT:                    "line": 0
> +# CHECK-NEXT:                  }
> +# CHECK-NEXT:                }
> +# CHECK-NEXT:              },
> +# CHECK-NEXT:              {
> +# CHECK-NEXT:                "newText": ")",
> +# CHECK-NEXT:                "range": {
> +# CHECK-NEXT:                  "end": {
> +# CHECK-NEXT:                    "character": 37,
> +# CHECK-NEXT:                    "line": 0
> +# CHECK-NEXT:                  },
> +# CHECK-NEXT:                  "start": {
> +# CHECK-NEXT:                    "character": 37,
> +# CHECK-NEXT:                    "line": 0
> +# CHECK-NEXT:                  }
> +# CHECK-NEXT:                }
> +# CHECK-NEXT:              }
> +# CHECK-NEXT:            ]
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      ],
> +# CHECK-NEXT:      "command": "clangd.applyFix",
> +# CHECK-NEXT:      "title": "Apply FixIt place parentheses around the
> assignment to silence this warning"
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "arguments": [
> +# CHECK-NEXT:        {
> +# CHECK-NEXT:          "changes": {
> +# CHECK-NEXT:            "file:///foo.c<file:///\\foo.c>": [
> +# CHECK-NEXT:              {
> +# CHECK-NEXT:                "newText": "==",
> +# CHECK-NEXT:                "range": {
> +# CHECK-NEXT:                  "end": {
> +# CHECK-NEXT:                    "character": 35,
> +# CHECK-NEXT:                    "line": 0
> +# CHECK-NEXT:                  },
> +# CHECK-NEXT:                  "start": {
> +# CHECK-NEXT:                    "character": 34,
> +# CHECK-NEXT:                    "line": 0
> +# CHECK-NEXT:                  }
> +# CHECK-NEXT:                }
> +# CHECK-NEXT:              }
> +# CHECK-NEXT:            ]
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      ],
> +# CHECK-NEXT:      "command": "clangd.applyFix",
> +# CHECK-NEXT:      "title": "Apply FixIt use '==' to turn this assignment
> into an equality comparison"
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 329
>
> -
> {"jsonrpc":"2.0","id":3,"method":"workspace/executeCommand","params":{"command
> ":"clangd.applyFix","arguments":[{"changes":{"file:///foo.c<file:///\\foo.c>":[{"range":{"start
> ":{"line":0,"character":32},"end":{"line":0,"character":32}},"newText":"("},{"
> range":{"start":{"line":0,"character":37},"end":{"line":0,"character":37}},"ne
> wText":")"}]}}]}}
> -# CHECK: {"jsonrpc":"2.0","id":3,"result":"Fix applied."}
> -# CHECK:
> {"jsonrpc":"2.0","id":1,"method":"workspace/applyEdit","params":{"edit":
> {"changes": {"file:///foo.c<file:///\\foo.c>": [{"range": {"start": {"line": 0, "character":
> 32}, "end": {"line": 0, "character": 32}}, "newText": "("},{"range": {"start":
> {"line": 0, "character": 37}, "end": {"line": 0, "character": 37}}, "newText":
> ")"}]}}}}
> +{"jsonrpc":"2.0","id":4,"method":"workspace/executeCommand","params":{"comman
> d":"clangd.applyFix","arguments":[{"changes":{"file:///foo.c<file:///\\foo.c>":[{"range":{"star
> t":{"line":0,"character":32},"end":{"line":0,"character":32}},"newText":"("},{
> "range":{"start":{"line":0,"character":37},"end":{"line":0,"character":37}},"n
> ewText":")"}]}}]}}
> +#      CHECK:  "id": 4,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": "Fix applied."
> +#
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "method": "workspace/applyEdit",
> +# CHECK-NEXT:  "params": {
> +# CHECK-NEXT:    "edit": {
> +# CHECK-NEXT:      "changes": {
> +# CHECK-NEXT:        "file:///foo.c<file:///\\foo.c>": [
> +# CHECK-NEXT:          {
> +# CHECK-NEXT:            "newText": "(",
> +# CHECK-NEXT:            "range": {
> +# CHECK-NEXT:              "end": {
> +# CHECK-NEXT:                "character": 32,
> +# CHECK-NEXT:                "line": 0
> +# CHECK-NEXT:              },
> +# CHECK-NEXT:              "start": {
> +# CHECK-NEXT:                "character": 32,
> +# CHECK-NEXT:                "line": 0
> +# CHECK-NEXT:              }
> +# CHECK-NEXT:            }
> +# CHECK-NEXT:          },
> +# CHECK-NEXT:          {
> +# CHECK-NEXT:            "newText": ")",
> +# CHECK-NEXT:            "range": {
> +# CHECK-NEXT:              "end": {
> +# CHECK-NEXT:                "character": 37,
> +# CHECK-NEXT:                "line": 0
> +# CHECK-NEXT:              },
> +# CHECK-NEXT:              "start": {
> +# CHECK-NEXT:                "character": 37,
> +# CHECK-NEXT:                "line": 0
> +# CHECK-NEXT:              }
> +# CHECK-NEXT:            }
> +# CHECK-NEXT:          }
> +# CHECK-NEXT:        ]
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  }
>  Content-Length: 44
>
> -{"jsonrpc":"2.0","id":3,"method":"shutdown"}
> -# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
> +{"jsonrpc":"2.0","id":4,"method":"shutdown"}
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> Modified: clang-tools-extra/trunk/test/clangd/formatting.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/formatting.test?rev=317486&r1=317485&r2=317486&view=di
> ff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/formatting.test (original)
> +++ clang-tools-extra/trunk/test/clangd/formatting.test Mon Nov  6 07:40:30
> 2017
> @@ -1,30 +1,71 @@
> -# RUN: clangd < %s | FileCheck %s
> +# 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,"rootP
> ath":"clangd","capabilities":{},"trace":"off"}}
> -# CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{
> -# CHECK:   "textDocumentSync": 1,
> -# CHECK:   "documentFormattingProvider": true,
> -# CHECK:   "documentRangeFormattingProvider": true,
> -# CHECK:   "documentOnTypeFormattingProvider":
> {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
> -# CHECK:   "codeActionProvider": true,
> -# CHECK:   "completionProvider": {"resolveProvider": false,
> "triggerCharacters": [".",">",":"]},
> -# CHECK:   "definitionProvider": true
> -# CHECK: }}}
> -#
>  Content-Length: 193
>
>
> {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"ur
> i":"file:///foo.c<file:///\\foo.c>","languageId":"c","version":1,"text":"int foo ( int x ) {\n
> x = x+1;\n    return x;\n    }"}}}
> -#
> -#
>  Content-Length: 233
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/rangeFormatting","params":{"tex
> tDocument":{"uri":"file:///foo.c<file:///\\foo.c>"},"range":{"start":{"line":1,"character":4},"
> end":{"line":1,"character":12}},"options":{"tabSize":4,"insertSpaces":true}}}
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"range": {"start": {"line": 0,
> "character": 19}, "end": {"line": 1, "character": 4}}, "newText": "\n
> "},{"range": {"start": {"line": 1, "character": 9}, "end": {"line": 1,
> "character": 9}}, "newText": " "},{"range": {"start": {"line": 1, "character":
> 10}, "end": {"line": 1, "character": 10}}, "newText": " "},{"range": {"start":
> {"line": 1, "character": 12}, "end": {"line": 2, "character": 4}}, "newText":
> "\n  "}]}
> -#
> -#
> +#      CHECK:   "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "newText": "\n  ",
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 4,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 19,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "newText": " ",
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 9,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 9,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "newText": " ",
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 10,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 10,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "newText": "\n  ",
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 4,
> +# CHECK-NEXT:          "line": 2
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 12,
> +# CHECK-NEXT:          "line": 1
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 197
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///foo.c<file:///\\foo.c>","version":5},"contentChanges":[{"text":"int foo ( int x )
> {\n  x = x + 1;\n  return x;\n    }"}]}}
> @@ -33,14 +74,68 @@ Content-Length: 197
>  Content-Length: 233
>
>
> {"jsonrpc":"2.0","id":2,"method":"textDocument/rangeFormatting","params":{"tex
> tDocument":{"uri":"file:///foo.c<file:///\\foo.c>"},"range":{"start":{"line":1,"character":2},"
> end":{"line":1,"character":12}},"options":{"tabSize":4,"insertSpaces":true}}}
> -# CHECK: {"jsonrpc":"2.0","id":2,"result":[]}
> -#
> +#      CHECK:  "id": 2,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": []
>  Content-Length: 153
>
>
> {"jsonrpc":"2.0","id":3,"method":"textDocument/formatting","params":{"textDocu
> ment":{"uri":"file:///foo.c<file:///\\foo.c>"},"options":{"tabSize":4,"insertSpaces":true}}}
> -# CHECK: {"jsonrpc":"2.0","id":3,"result":[{"range": {"start": {"line": 0,
> "character": 7}, "end": {"line": 0, "character": 8}}, "newText": ""},{"range":
> {"start": {"line": 0, "character": 9}, "end": {"line": 0, "character": 10}},
> "newText": ""},{"range": {"start": {"line": 0, "character": 15}, "end":
> {"line": 0, "character": 16}}, "newText": ""},{"range": {"start": {"line": 2,
> "character": 11}, "end": {"line": 3, "character": 4}}, "newText": "\n"}]}
> -#
> -#
> +#      CHECK:  "id": 3,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "newText": "",
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 8,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 7,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "newText": "",
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 10,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 9,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "newText": "",
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 16,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 15,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "newText": "\n",
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 4,
> +# CHECK-NEXT:          "line": 3
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 11,
> +# CHECK-NEXT:          "line": 2
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>  Content-Length: 190
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///foo.c<file:///\\foo.c>","version":9},"contentChanges":[{"text":"int foo(int x)
> {\n  x = x + 1;\n  return x;\n}"}]}}
> @@ -49,8 +144,9 @@ Content-Length: 190
>  Content-Length: 153
>
>
> {"jsonrpc":"2.0","id":4,"method":"textDocument/formatting","params":{"textDocu
> ment":{"uri":"file:///foo.c<file:///\\foo.c>"},"options":{"tabSize":4,"insertSpaces":true}}}
> -# CHECK: {"jsonrpc":"2.0","id":4,"result":[]}
> -#
> +#      CHECK:  "id": 4,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": []
>  Content-Length: 193
>
>
> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"
> uri":"file:///foo.c<file:///\\foo.c>","version":5},"contentChanges":[{"text":"int foo ( int x )
> {\n  x = x + 1;\n  return x;\n}"}]}}
> @@ -59,13 +155,53 @@ Content-Length: 193
>  Content-Length: 204
>
>
> {"jsonrpc":"2.0","id":5,"method":"textDocument/onTypeFormatting","params":{"te
> xtDocument":{"uri":"file:///foo.c<file:///\\foo.c>"},"position":{"line":3,"character":1},"ch":"
> }","options":{"tabSize":4,"insertSpaces":true}}}
> -# CHECK: {"jsonrpc":"2.0","id":5,"result":[{"range": {"start": {"line": 0,
> "character": 7}, "end": {"line": 0, "character": 8}}, "newText": ""},{"range":
> {"start": {"line": 0, "character": 9}, "end": {"line": 0, "character": 10}},
> "newText": ""},{"range": {"start": {"line": 0, "character": 15}, "end":
> {"line": 0, "character": 16}}, "newText": ""}]}
> -#
> +#      CHECK:  "id": 5,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "newText": "",
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 8,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 7,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "newText": "",
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 10,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 9,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    {
> +# CHECK-NEXT:      "newText": "",
> +# CHECK-NEXT:      "range": {
> +# CHECK-NEXT:        "end": {
> +# CHECK-NEXT:          "character": 16,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        },
> +# CHECK-NEXT:        "start": {
> +# CHECK-NEXT:          "character": 15,
> +# CHECK-NEXT:          "line": 0
> +# CHECK-NEXT:        }
> +# CHECK-NEXT:      }
> +# CHECK-NEXT:    }
> +# CHECK-NEXT:  ]
>
>  Content-Length: 44
>
>  {"jsonrpc":"2.0","id":6,"method":"shutdown"}
> -# CHECK: {"jsonrpc":"2.0","id":6,"result":null}
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> 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=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test
> (original)
> +++ clang-tools-extra/trunk/test/clangd/initialize-params-invalid.test Mon Nov
> 6 07:40:30 2017
> @@ -1,27 +1,45 @@
> -# RUN: clangd -run-synchronously < %s | FileCheck %s
> +# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace
> %s
>  # It is absolutely vital that this file has CRLF line endings.
>  #
>  # Test with invalid initialize request parameters
>  Content-Length: 142
>
>
> {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":"","rootUr
> i":"file:///path/to/workspace<file:///\\path\to\workspace>","capabilities":{},"trace":"off"}}
> -# CHECK: Content-Length: 606
> -# CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{
> -# CHECK:   "textDocumentSync": 1,
> -# CHECK:   "documentFormattingProvider": true,
> -# CHECK:   "documentRangeFormattingProvider": true,
> -# CHECK:   "documentOnTypeFormattingProvider":
> {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
> -# CHECK:   "codeActionProvider": true,
> -# CHECK:   "completionProvider": {"resolveProvider": false,
> "triggerCharacters": [".",">",":"]},
> -# CHECK:   "signatureHelpProvider": {"triggerCharacters": ["(",","]},
> -# CHECK:   "definitionProvider": true,
> -# CHECK:   "executeCommandProvider": {"commands": ["clangd.applyFix"]}
> -# CHECK: }}}
> -#
> +#      CHECK:  "id": 0,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": {
> +# CHECK-NEXT:    "codeActionProvider": true,
> +# CHECK-NEXT:    "completionProvider": {
> +# CHECK-NEXT:      "resolveProvider": false,
> +# CHECK-NEXT:      "triggerCharacters": [
> +# CHECK-NEXT:        ".",
> +# CHECK-NEXT:        ">",
> +# CHECK-NEXT:        ":"
> +# CHECK-NEXT:      ]
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    "definitionProvider": true,
> +# CHECK-NEXT:    "documentFormattingProvider": true,
> +# CHECK-NEXT:    "documentOnTypeFormattingProvider": {
> +# CHECK-NEXT:      "firstTriggerCharacter": "}",
> +# CHECK-NEXT:      "moreTriggerCharacter": []
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    "documentRangeFormattingProvider": true,
> +# CHECK-NEXT:    "executeCommandProvider": {
> +# CHECK-NEXT:      "commands": [
> +# CHECK-NEXT:        "clangd.applyFix"
> +# CHECK-NEXT:      ]
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    "signatureHelpProvider": {
> +# CHECK-NEXT:      "triggerCharacters": [
> +# CHECK-NEXT:        "(",
> +# CHECK-NEXT:        ","
> +# CHECK-NEXT:      ]
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    "textDocumentSync": 1
> +# CHECK-NEXT:  }
>  Content-Length: 44
>
>  {"jsonrpc":"2.0","id":3,"method":"shutdown"}
> -# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> 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=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/initialize-params.test (original)
> +++ clang-tools-extra/trunk/test/clangd/initialize-params.test Mon Nov  6
> 07:40:30 2017
> @@ -1,27 +1,48 @@
> -# RUN: clangd -run-synchronously < %s | FileCheck %s
> +# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace
> %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,"rootU
> ri":"file:///path/to/workspace<file:///\\path\to\workspace>","capabilities":{},"trace":"off"}}
> -# CHECK: Content-Length: 606
> -# CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{
> -# CHECK:   "textDocumentSync": 1,
> -# CHECK:   "documentFormattingProvider": true,
> -# CHECK:   "documentRangeFormattingProvider": true,
> -# CHECK:   "documentOnTypeFormattingProvider":
> {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
> -# CHECK:   "codeActionProvider": true,
> -# CHECK:   "completionProvider": {"resolveProvider": false,
> "triggerCharacters": [".",">",":"]},
> -# CHECK:   "signatureHelpProvider": {"triggerCharacters": ["(",","]},
> -# CHECK:   "definitionProvider": true,
> -# CHECK:   "executeCommandProvider": {"commands": ["clangd.applyFix"]}
> -# CHECK: }}}
> -#
> +#      CHECK:  "id": 0,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": {
> +# CHECK-NEXT:    "codeActionProvider": true,
> +# CHECK-NEXT:    "completionProvider": {
> +# CHECK-NEXT:      "resolveProvider": false,
> +# CHECK-NEXT:      "triggerCharacters": [
> +# CHECK-NEXT:        ".",
> +# CHECK-NEXT:        ">",
> +# CHECK-NEXT:        ":"
> +# CHECK-NEXT:      ]
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    "definitionProvider": true,
> +# CHECK-NEXT:    "documentFormattingProvider": true,
> +# CHECK-NEXT:    "documentOnTypeFormattingProvider": {
> +# CHECK-NEXT:      "firstTriggerCharacter": "}",
> +# CHECK-NEXT:      "moreTriggerCharacter": []
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    "documentRangeFormattingProvider": true,
> +# CHECK-NEXT:    "executeCommandProvider": {
> +# CHECK-NEXT:      "commands": [
> +# CHECK-NEXT:        "clangd.applyFix"
> +# CHECK-NEXT:      ]
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    "signatureHelpProvider": {
> +# CHECK-NEXT:      "triggerCharacters": [
> +# CHECK-NEXT:        "(",
> +# CHECK-NEXT:        ","
> +# CHECK-NEXT:      ]
> +# CHECK-NEXT:    },
> +# CHECK-NEXT:    "textDocumentSync": 1
> +# CHECK-NEXT:  }
>  Content-Length: 44
>
>  {"jsonrpc":"2.0","id":3,"method":"shutdown"}
> -# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
> +#      CHECK:  "id": 3,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": null
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> Modified: 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=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/input-mirror.test (original)
> +++ clang-tools-extra/trunk/test/clangd/input-mirror.test Mon Nov  6 07:40:30
> 2017
> @@ -1,4 +1,4 @@
> -# RUN: clangd -run-synchronously -input-mirror-file %t < %s
> +# RUN: clangd -pretty -run-synchronously -input-mirror-file %t < %s
>  # Note that we have to use '-b' as -input-mirror-file does not have a newline
> at the end of file.
>  # RUN: diff -b %t %s
>  # It is absolutely vital that this file has CRLF line endings.
> @@ -152,7 +152,6 @@ Content-Length: 148
>  Content-Length: 44
>
>  {"jsonrpc":"2.0","id":3,"method":"shutdown"}
> -# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> Modified: clang-tools-extra/trunk/test/clangd/protocol.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/protocol.test?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/protocol.test (original)
> +++ clang-tools-extra/trunk/test/clangd/protocol.test Mon Nov  6 07:40:30 2017
> @@ -1,5 +1,5 @@
> -# RUN: not clangd -run-synchronously < %s | FileCheck %s
> -# RUN: not clangd -run-synchronously < %s 2>&1 | FileCheck -check-
> prefix=STDERR %s
> +# RUN: not clangd -pretty -run-synchronously < %s | FileCheck -strict-
> whitespace %s
> +# RUN: not clangd -pretty -run-synchronously < %s 2>&1 | FileCheck -check-
> prefix=STDERR %s
>  # vim: fileformat=dos
>  # It is absolutely vital that this file has CRLF line endings.
>  #
> @@ -12,16 +12,9 @@ Content-Type: application/vscode-jsonrpc
>
> {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootP
> ath":"clangd","capabilities":{},"trace":"off"}}
>  # Test message with Content-Type after Content-Length
>  #
> -# CHECK: "jsonrpc":"2.0","id":0,"result":{"capabilities":{
> -# CHECK-DAG: "textDocumentSync": 1,
> -# CHECK-DAG: "documentFormattingProvider": true,
> -# CHECK-DAG: "documentRangeFormattingProvider": true,
> -# CHECK-DAG: "documentOnTypeFormattingProvider":
> {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
> -# CHECK-DAG: "codeActionProvider": true,
> -# CHECK-DAG: "completionProvider": {"resolveProvider": false,
> "triggerCharacters": [".",">",":"]},
> -# CHECK-DAG: "definitionProvider": true
> -# CHECK: }}
> -
> +#      CHECK:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": {
> +#      CHECK:  }
>  Content-Length: 246
>
>
> {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"ur
> i":"file:///main.cpp<file:///\\main.cpp>","languageId":"cpp","version":1,"text":"struct fake { int
> a, bb, ccc; int f(int i, const float f) const; };\nint main() {\n  fake f;\n
> f.\n}\n"}}}
> @@ -36,9 +29,16 @@ Content-Length: 146
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocu
> ment":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}}
>  # Test message with Content-Type before Content-Length
>  #
> -# CHECK: {"jsonrpc":"2.0","id":1,"result":[
> -# CHECK-DAG:
> {"label":"a","kind":5,"detail":"int","sortText":"000035a","filterText":"a","in
> sertText":"a","insertTextFormat":1}
> -# CHECK: ]}
> +#      CHECK:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +#      CHECK:      "filterText": "fake",
> +# CHECK-NEXT:      "insertText": "fake",
> +# CHECK-NEXT:      "insertTextFormat": 1,
> +# CHECK-NEXT:      "kind": 7,
> +# CHECK-NEXT:      "label": "fake::",
> +# CHECK-NEXT:      "sortText": "000075fake"
> +#      CHECK:  ]
>
>  X-Test: Testing
>  Content-Type: application/vscode-jsonrpc; charset-utf-8
> @@ -55,9 +55,16 @@ Content-Length: 146
>
> {"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocu
> ment":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}}
>  # Test message with duplicate Content-Length headers
>  #
> -# CHECK: {"jsonrpc":"2.0","id":3,"result":[
> -# CHECK-DAG:
> {"label":"a","kind":5,"detail":"int","sortText":"000035a","filterText":"a","in
> sertText":"a","insertTextFormat":1}
> -# CHECK: ]}
> +#      CHECK:  "id": 3,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +#      CHECK:      "filterText": "fake",
> +# CHECK-NEXT:      "insertText": "fake",
> +# CHECK-NEXT:      "insertTextFormat": 1,
> +# CHECK-NEXT:      "kind": 7,
> +# CHECK-NEXT:      "label": "fake::",
> +# CHECK-NEXT:      "sortText": "000075fake"
> +#      CHECK:  ]
>  # STDERR: Warning: Duplicate Content-Length header received. The previous
> value for this message (10) was ignored.
>
>  Content-Type: application/vscode-jsonrpc; charset-utf-8
> @@ -74,10 +81,16 @@ Content-Length: 146
>
> {"jsonrpc":"2.0","id":5,"method":"textDocument/completion","params":{"textDocu
> ment":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}}
>  # Test message with Content-Type before Content-Length
>  #
> -# CHECK: {"jsonrpc":"2.0","id":5,"result":[
> -# CHECK-DAG:
> {"label":"a","kind":5,"detail":"int","sortText":"000035a","filterText":"a","in
> sertText":"a","insertTextFormat":1}
> -# CHECK: ]}
> -
> +#      CHECK:  "id": 5,
> +# CHECK-NEXT:  "jsonrpc": "2.0",
> +# CHECK-NEXT:  "result": [
> +#      CHECK:      "filterText": "fake",
> +# CHECK-NEXT:      "insertText": "fake",
> +# CHECK-NEXT:      "insertTextFormat": 1,
> +# CHECK-NEXT:      "kind": 7,
> +# CHECK-NEXT:      "label": "fake::",
> +# CHECK-NEXT:      "sortText": "000075fake"
> +#      CHECK:  ]
>  Content-Length: 1024
>
>
> {"jsonrpc":"2.0","id":5,"method":"textDocument/completion","params":{"textDocu
> ment":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}}
>
> Modified: clang-tools-extra/trunk/test/clangd/signature-help.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/signature-
> help.test?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/signature-help.test (original)
> +++ clang-tools-extra/trunk/test/clangd/signature-help.test Mon Nov  6
> 07:40:30 2017
> @@ -15,12 +15,12 @@ Content-Length: 333
>  Content-Length: 151
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/signatureHelp","params":{"textD
> ocument":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":8,"character":9}}}
> -# CHECK:
> {"jsonrpc":"2.0","id":1,"result":{"activeSignature":0,"activeParameter":0,"sig
> natures":[
> +# CHECK:
> {"id":1,"jsonrpc":"2.0","result":{"activeParameter":0,"activeSignature":0,"sig
> natures":[
>  # CHECK-DAG: {"label":"foo(float x, float y) ->
> void","parameters":[{"label":"float x"},{"label":"float y"}]}
>  # CHECK-DAG: {"label":"foo(float x, int y) ->
> void","parameters":[{"label":"float x"},{"label":"int y"}]}
>  # CHECK-DAG: {"label":"foo(int x, float y) ->
> void","parameters":[{"label":"int x"},{"label":"float y"}]}
>  # CHECK-DAG: {"label":"foo(int x, int y) -> void","parameters":[{"label":"int
> x"},{"label":"int y"}]}
> -# CHECK: ]}
> +# CHECK-SAME: ]}
>
>  # Modify the document
>  Content-Length: 333
> @@ -31,21 +31,20 @@ Content-Length: 333
>  Content-Length: 151
>
>
> {"jsonrpc":"2.0","id":2,"method":"textDocument/signatureHelp","params":{"textD
> ocument":{"uri":"file:///main.cpp<file:///\\main.cpp>"},"position":{"line":8,"character":9}}}
> -# CHECK:
> {"jsonrpc":"2.0","id":2,"result":{"activeSignature":0,"activeParameter":0,"sig
> natures":[
> +# CHECK:
> {"id":2,"jsonrpc":"2.0","result":{"activeParameter":0,"activeSignature":0,"sig
> natures":[
>  # CHECK-DAG: {"label":"bar(int x, int y = 0) ->
> void","parameters":[{"label":"int x"},{"label":"int y = 0"}]}
>  # CHECK-DAG: {"label":"bar(float x = 0, int y = 42) ->
> void","parameters":[{"label":"float x = 0"},{"label":"int y = 42"}]}
> -# CHECK: ]}
> +# CHECK-SAME: ]}
>
>  Content-Length: 159
>
>
> {"jsonrpc":"2.0","id":3,"method":"textDocument/signatureHelp","params":{"textD
> ocument":{"uri":"file:///doesnotexist.cpp<file:///\\doesnotexist.cpp>"},"position":{"line":8,"character":9
> }}}
> -# CHECK: {"jsonrpc":"2.0","id":3,"error":{"code":-
> 32602,"message":"signatureHelp is called for non-added document"}}
> +# CHECK: {"error":{"code":-32602,"message":"signatureHelp is called for non-
> added document"},"id":3,"jsonrpc":"2.0"}
>
>  # Shutdown.
>  Content-Length: 49
>
>  {"jsonrpc":"2.0","id":100000,"method":"shutdown"}
> -# CHECK: {"jsonrpc":"2.0","id":100000,"result":null}
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> Modified: clang-tools-extra/trunk/test/clangd/unsupported-method.test
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/test/clangd/unsupported-
> method.test?rev=317486&r1=317485&r2=317486&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/test/clangd/unsupported-method.test (original)
> +++ clang-tools-extra/trunk/test/clangd/unsupported-method.test Mon Nov  6
> 07:40:30 2017
> @@ -1,4 +1,4 @@
> -# RUN: clangd -run-synchronously < %s | FileCheck %s
> +# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace
> %s
>  # It is absolutely vital that this file has CRLF line endings.
>  #
>  Content-Length: 125
> @@ -12,12 +12,16 @@ Content-Length: 143
>  Content-Length: 92
>
>
> {"jsonrpc":"2.0","id":1,"method":"textDocument/jumpInTheAirLikeYouJustDontCare
> ","params":{}}
> -# CHECK: {"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"method not
> found"}}
> +#      CHECK:  "error": {
> +# CHECK-NEXT:    "code": -32601,
> +# CHECK-NEXT:    "message": "method not found"
> +# CHECK-NEXT:  },
> +# CHECK-NEXT:  "id": 1,
> +# CHECK-NEXT:  "jsonrpc": "2.0"
>
>  Content-Length: 44
>
>  {"jsonrpc":"2.0","id":2,"method":"shutdown"}
> -# CHECK: {"jsonrpc":"2.0","id":2,"result":null}
>  Content-Length: 33
>
>  {"jsonrpc":"2.0":"method":"exit"}
>
> Modified: clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/unittests/clangd/CMakeLists.txt?rev=317486&r1=317485&r2=317486&vie
> w=diff
> ==============================================================================
> --- clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt (original)
> +++ clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt Mon Nov  6
> 07:40:30 2017
> @@ -10,6 +10,7 @@ include_directories(
>
>  add_extra_unittest(ClangdTests
>    ClangdTests.cpp
> +  JSONExprTests.cpp
>    TraceTests.cpp
>    )
>
>
> Added: clang-tools-extra/trunk/unittests/clangd/JSONExprTests.cpp
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-
> extra/trunk/unittests/clangd/JSONExprTests.cpp?rev=317486&view=auto
> ==============================================================================
> --- clang-tools-extra/trunk/unittests/clangd/JSONExprTests.cpp (added)
> +++ clang-tools-extra/trunk/unittests/clangd/JSONExprTests.cpp Mon Nov  6
> 07:40:30 2017
> @@ -0,0 +1,112 @@
> +//===-- JSONExprTests.cpp - JSON expression unit tests ----------*- C++ -*-
> ===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------
> ===//
> +
> +#include "JSONExpr.h"
> +
> +#include "gmock/gmock.h"
> +#include "gtest/gtest.h"
> +
> +namespace clang {
> +namespace clangd {
> +namespace json {
> +namespace {
> +
> +std::string s(const Expr &E) { return llvm::formatv("{0}", E).str(); }
> +std::string sp(const Expr &E) { return llvm::formatv("{0:2}", E).str(); }
> +
> +TEST(JSONExprTests, Types) {
> +  EXPECT_EQ("true", s(true));
> +  EXPECT_EQ("null", s(nullptr));
> +  EXPECT_EQ("2.5", s(2.5));
> +  EXPECT_EQ(R"("foo")", s("foo"));
> +  EXPECT_EQ("[1,2,3]", s({1, 2, 3}));
> +  EXPECT_EQ(R"({"x":10,"y":20})", s(obj{{"x", 10}, {"y", 20}}));
> +}
> +
> +TEST(JSONExprTests, Constructors) {
> +  // Lots of edge cases around empty and singleton init lists.
> +  EXPECT_EQ("[[[3]]]", s({{{3}}}));
> +  EXPECT_EQ("[[[]]]", s({{{}}}));
> +  EXPECT_EQ("[[{}]]", s({{obj{}}}));
> +  EXPECT_EQ(R"({"A":{"B":{}}})", s(obj{{"A", obj{{"B", obj{}}}}}));
> +  EXPECT_EQ(R"({"A":{"B":{"X":"Y"}}})",
> +            s(obj{{"A", obj{{"B", obj{{"X", "Y"}}}}}}));
> +}
> +
> +TEST(JSONExprTests, StringOwnership) {
> +  char X[] = "Hello";
> +  Expr Alias = static_cast<const char *>(X);
> +  X[1] = 'a';
> +  EXPECT_EQ(R"("Hallo")", s(Alias));
> +
> +  std::string Y = "Hello";
> +  Expr Copy = Y;
> +  Y[1] = 'a';
> +  EXPECT_EQ(R"("Hello")", s(Copy));
> +}
> +
> +TEST(JSONExprTests, CanonicalOutput) {
> +  // Objects are sorted (but arrays aren't)!
> +  EXPECT_EQ(R"({"a":1,"b":2,"c":3})", s(obj{{"a", 1}, {"c", 3}, {"b", 2}}));
> +  EXPECT_EQ(R"(["a","c","b"])", s({"a", "c", "b"}));
> +  EXPECT_EQ("3", s(3.0));
> +}
> +
> +TEST(JSONExprTests, Escaping) {
> +  std::string test = {
> +      0,                    // Strings may contain nulls.
> +      '\b',   '\f',         // Have mnemonics, but we escape numerically.
> +      '\r',   '\n',   '\t', // Escaped with mnemonics.
> +      'S',    '\"',   '\\', // Printable ASCII characters.
> +      '\x7f',               // Delete is not escaped.
> +      '\xce', '\x94',       // Non-ASCII UTF-8 is not escaped.
> +  };
> +  EXPECT_EQ(R"("\u0000\u0008\u000c\r\n\tS\"\\)"
> +            u8"\x7fΔ\"",
> +            s(test));
> +
> +  EXPECT_EQ(R"({"object keys are\nescaped":true})",
> +            s(obj{{"object keys are\nescaped", true}}));
> +}
> +
> +TEST(JSONExprTests, PrettyPrinting) {
> +  EXPECT_EQ(
> +      R"({
> +  "empty_array": [],
> +  "empty_object": {},
> +  "full_array": [
> +    1,
> +    null
> +  ],
> +  "full_object": {
> +    "nested_array": [
> +      {
> +        "property": "value"
> +      }
> +    ]
> +  }
> +})",
> +      sp(obj{
> +          {"empty_object", obj{}},
> +          {"empty_array", {}},
> +          {"full_array", {1, nullptr}},
> +          {"full_object",
> +           obj{
> +               {"nested_array",
> +                {obj{
> +                    {"property", "value"},
> +                }}},
> +           }},
> +      }));
> +}
> +
> +} // namespace
> +} // namespace json
> +} // namespace clangd
> +} // namespace clang
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org<mailto:cfe-commits at lists.llvm.org>
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits




--
Regards,
Ilya Biryukov





--
Regards,
Ilya Biryukov



--
Regards,
Ilya Biryukov
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20171109/7f5873bc/attachment-0001.html>
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: result.txt
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20171109/7f5873bc/attachment-0001.txt>


More information about the cfe-commits mailing list