Creating branches/google/testing and tags/google/testing/2017-11-14 from r317716

git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/branches/google/testing@318248 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/clang-tidy/google/CMakeLists.txt b/clang-tidy/google/CMakeLists.txt
index ff48b63..9a91915 100644
--- a/clang-tidy/google/CMakeLists.txt
+++ b/clang-tidy/google/CMakeLists.txt
@@ -6,6 +6,7 @@
   ExplicitConstructorCheck.cpp
   ExplicitMakePairCheck.cpp
   GlobalNamesInHeadersCheck.cpp
+  GlobalVariableDeclarationCheck.cpp
   GoogleTidyModule.cpp
   IntegerTypesCheck.cpp
   NonConstReferences.cpp
diff --git a/clang-tidy/google/GlobalVariableDeclarationCheck.cpp b/clang-tidy/google/GlobalVariableDeclarationCheck.cpp
new file mode 100644
index 0000000..a278be1
--- /dev/null
+++ b/clang-tidy/google/GlobalVariableDeclarationCheck.cpp
@@ -0,0 +1,90 @@
+//===--- GlobalVariableDeclarationCheck.cpp - clang-tidy-------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GlobalVariableDeclarationCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <string>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace objc {
+
+namespace {
+
+FixItHint generateFixItHint(const VarDecl *Decl, bool IsConst) {
+  char FC = Decl->getName()[0];
+  if (!llvm::isAlpha(FC) || Decl->getName().size() == 1) {
+    // No fix available if first character is not alphabetical character, or it
+    // is a single-character variable, since it is difficult to determine the 
+    // proper fix in this case. Users should create a proper variable name by
+    // their own.
+    return FixItHint();
+  }
+  char SC = Decl->getName()[1];
+  if ((FC == 'k' || FC == 'g') && !llvm::isAlpha(SC)) {
+    // No fix available if the prefix is correct but the second character is not
+    // alphabetical, since it is difficult to determine the proper fix in this
+    // case.
+    return FixItHint();
+  }
+  auto NewName = (IsConst ? "k" : "g") +
+                 llvm::StringRef(std::string(1, FC)).upper() +
+                 Decl->getName().substr(1).str();
+  return FixItHint::CreateReplacement(
+      CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())),
+      llvm::StringRef(NewName));
+}
+}  // namespace
+
+void GlobalVariableDeclarationCheck::registerMatchers(MatchFinder *Finder) {
+  // The relevant Style Guide rule only applies to Objective-C.
+  if (!getLangOpts().ObjC1 && !getLangOpts().ObjC2) {
+    return;
+  }
+  // need to add two matchers since we need to bind different ids to distinguish
+  // constants and variables. Since bind() can only be called on node matchers,
+  // we cannot make it in one matcher.
+  Finder->addMatcher(
+      varDecl(hasGlobalStorage(), unless(hasType(isConstQualified())),
+              unless(matchesName("::g[A-Z]")))
+          .bind("global_var"),
+      this);
+  Finder->addMatcher(varDecl(hasGlobalStorage(), hasType(isConstQualified()),
+                             unless(matchesName("::k[A-Z]")))
+                         .bind("global_const"),
+                     this);
+}
+
+void GlobalVariableDeclarationCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  if (const auto *Decl = Result.Nodes.getNodeAs<VarDecl>("global_var")) {
+    diag(Decl->getLocation(),
+         "non-const global variable '%0' must have a name which starts with "
+         "'g[A-Z]'")
+        << Decl->getName() << generateFixItHint(Decl, false);
+  }
+  if (const auto *Decl = Result.Nodes.getNodeAs<VarDecl>("global_const")) {
+    diag(Decl->getLocation(),
+         "const global variable '%0' must have a name which starts with "
+         "'k[A-Z]'")
+        << Decl->getName() << generateFixItHint(Decl, true);
+  }
+}
+
+}  // namespace objc
+}  // namespace google
+}  // namespace tidy
+}  // namespace clang
diff --git a/clang-tidy/google/GlobalVariableDeclarationCheck.h b/clang-tidy/google/GlobalVariableDeclarationCheck.h
new file mode 100644
index 0000000..ed0352b
--- /dev/null
+++ b/clang-tidy/google/GlobalVariableDeclarationCheck.h
@@ -0,0 +1,39 @@
+//===--- GlobalVariableDeclarationCheck.h - clang-tidy-----------*- 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_CLANG_TIDY_GOOGLE_OBJC_GLOBAL_VARIABLE_DECLARATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_OBJC_GLOBAL_VARIABLE_DECLARATION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace objc {
+
+/// The check for Objective-C global variables and constants naming convention.
+/// The declaration should follow the patterns of 'k[A-Z].*' (constants) or
+/// 'g[A-Z].*' (variables).
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/google-objc-global-variable-declaration.html
+class GlobalVariableDeclarationCheck : public ClangTidyCheck {
+ public:
+  GlobalVariableDeclarationCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+}  // namespace objc
+}  // namespace google
+}  // namespace tidy
+}  // namespace clang
+
+#endif  // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_OBJC_GLOBAL_VARIABLE_DECLARATION_H
diff --git a/clang-tidy/google/GoogleTidyModule.cpp b/clang-tidy/google/GoogleTidyModule.cpp
index 1328463..45d7de5 100644
--- a/clang-tidy/google/GoogleTidyModule.cpp
+++ b/clang-tidy/google/GoogleTidyModule.cpp
@@ -19,6 +19,7 @@
 #include "ExplicitConstructorCheck.h"
 #include "ExplicitMakePairCheck.h"
 #include "GlobalNamesInHeadersCheck.h"
+#include "GlobalVariableDeclarationCheck.h"
 #include "IntegerTypesCheck.h"
 #include "NonConstReferences.h"
 #include "OverloadedUnaryAndCheck.h"
@@ -34,7 +35,7 @@
 namespace google {
 
 class GoogleModule : public ClangTidyModule {
-public:
+ public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
     CheckFactories.registerCheck<build::ExplicitMakePairCheck>(
         "google-build-explicit-make-pair");
@@ -46,6 +47,10 @@
         "google-default-arguments");
     CheckFactories.registerCheck<ExplicitConstructorCheck>(
         "google-explicit-constructor");
+    CheckFactories.registerCheck<readability::GlobalNamesInHeadersCheck>(
+        "google-global-names-in-headers");
+    CheckFactories.registerCheck<objc::GlobalVariableDeclarationCheck>(
+        "google-objc-global-variable-declaration");
     CheckFactories.registerCheck<runtime::IntegerTypesCheck>(
         "google-runtime-int");
     CheckFactories.registerCheck<runtime::OverloadedUnaryAndCheck>(
@@ -61,8 +66,6 @@
     CheckFactories
         .registerCheck<clang::tidy::readability::BracesAroundStatementsCheck>(
             "google-readability-braces-around-statements");
-    CheckFactories.registerCheck<readability::GlobalNamesInHeadersCheck>(
-        "google-global-names-in-headers");
     CheckFactories.registerCheck<clang::tidy::readability::FunctionSizeCheck>(
         "google-readability-function-size");
     CheckFactories
@@ -89,11 +92,11 @@
 static ClangTidyModuleRegistry::Add<GoogleModule> X("google-module",
                                                     "Adds Google lint checks.");
 
-} // namespace google
+}  // namespace google
 
 // This anchor is used to force the linker to link in the generated object file
 // and thus register the GoogleModule.
 volatile int GoogleModuleAnchorSource = 0;
 
-} // namespace tidy
-} // namespace clang
+}  // namespace tidy
+}  // namespace clang
diff --git a/clang-tidy/misc/RedundantExpressionCheck.cpp b/clang-tidy/misc/RedundantExpressionCheck.cpp
index 901b54c..265cb38 100644
--- a/clang-tidy/misc/RedundantExpressionCheck.cpp
+++ b/clang-tidy/misc/RedundantExpressionCheck.cpp
@@ -23,7 +23,6 @@
 #include <algorithm>
 #include <cassert>
 #include <cstdint>
-#include <set>
 #include <string>
 #include <vector>
 
@@ -38,8 +37,8 @@
 using llvm::APSInt;
 } // namespace
 
-static const char KnownBannedMacroNames[] =
-    "EAGAIN;EWOULDBLOCK;SIGCLD;SIGCHLD;";
+static const llvm::StringSet<> KnownBannedMacroNames = {"EAGAIN", "EWOULDBLOCK",
+                                                        "SIGCLD", "SIGCHLD"};
 
 static bool incrementWithoutOverflow(const APSInt &Value, APSInt &Result) {
   Result = Value;
@@ -99,7 +98,6 @@
   case Stmt::StringLiteralClass:
     return cast<StringLiteral>(Left)->getBytes() ==
            cast<StringLiteral>(Right)->getBytes();
-
   case Stmt::DependentScopeDeclRefExprClass:
     if (cast<DependentScopeDeclRefExpr>(Left)->getDeclName() !=
         cast<DependentScopeDeclRefExpr>(Right)->getDeclName())
@@ -113,16 +111,14 @@
   case Stmt::MemberExprClass:
     return cast<MemberExpr>(Left)->getMemberDecl() ==
            cast<MemberExpr>(Right)->getMemberDecl();
-
+  case Stmt::CXXFunctionalCastExprClass:
   case Stmt::CStyleCastExprClass:
-    return cast<CStyleCastExpr>(Left)->getTypeAsWritten() ==
-           cast<CStyleCastExpr>(Right)->getTypeAsWritten();
-
+    return cast<ExplicitCastExpr>(Left)->getTypeAsWritten() ==
+           cast<ExplicitCastExpr>(Right)->getTypeAsWritten();
   case Stmt::CallExprClass:
   case Stmt::ImplicitCastExprClass:
   case Stmt::ArraySubscriptExprClass:
     return true;
-
   case Stmt::UnaryOperatorClass:
     if (cast<UnaryOperator>(Left)->isIncrementDecrementOp())
       return false;
@@ -282,7 +278,8 @@
   }
 }
 
-static void canonicalNegateExpr(BinaryOperatorKind &Opcode, APSInt &Value) {
+static void transformSubToCanonicalAddExpr(BinaryOperatorKind &Opcode,
+                                           APSInt &Value) {
   if (Opcode == BO_Sub) {
     Opcode = BO_Add;
     Value = -Value;
@@ -295,32 +292,77 @@
   return Node.isIntegerConstantExpr(Finder->getASTContext());
 }
 
-// Returns a matcher for integer constant expression.
+AST_MATCHER(BinaryOperator, operandsAreEquivalent) {
+  return areEquivalentExpr(Node.getLHS(), Node.getRHS());
+}
+
+AST_MATCHER(ConditionalOperator, expressionsAreEquivalent) {
+  return areEquivalentExpr(Node.getTrueExpr(), Node.getFalseExpr());
+}
+
+AST_MATCHER(CallExpr, parametersAreEquivalent) {
+  return Node.getNumArgs() == 2 &&
+         areEquivalentExpr(Node.getArg(0), Node.getArg(1));
+}
+
+AST_MATCHER(BinaryOperator, binaryOperatorIsInMacro) {
+  return Node.getOperatorLoc().isMacroID();
+}
+
+AST_MATCHER(ConditionalOperator, conditionalOperatorIsInMacro) {
+  return Node.getQuestionLoc().isMacroID() || Node.getColonLoc().isMacroID();
+}
+
+AST_MATCHER(Expr, isMacro) { return Node.getExprLoc().isMacroID(); }
+
+AST_MATCHER_P(Expr, expandedByMacro, llvm::StringSet<>, Names) {
+  const SourceManager &SM = Finder->getASTContext().getSourceManager();
+  const LangOptions &LO = Finder->getASTContext().getLangOpts();
+  SourceLocation Loc = Node.getExprLoc();
+  while (Loc.isMacroID()) {
+    StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM, LO);
+    if (Names.count(MacroName))
+      return true;
+    Loc = SM.getImmediateMacroCallerLoc(Loc);
+  }
+  return false;
+}
+
+// Returns a matcher for integer constant expressions.
 static ast_matchers::internal::Matcher<Expr>
 matchIntegerConstantExpr(StringRef Id) {
   std::string CstId = (Id + "-const").str();
   return expr(isIntegerConstantExpr()).bind(CstId);
 }
 
-// Retrieve the integer value matched by 'matchIntegerConstantExpr' with name
-// 'Id' and store it into 'Value'.
+// Retrieves the integer expression matched by 'matchIntegerConstantExpr' with
+// name 'Id' and stores it into 'ConstExpr', the value of the expression is
+// stored into `Value`.
 static bool retrieveIntegerConstantExpr(const MatchFinder::MatchResult &Result,
-                                        StringRef Id, APSInt &Value) {
+                                        StringRef Id, APSInt &Value,
+                                        const Expr *&ConstExpr) {
   std::string CstId = (Id + "-const").str();
-  const auto *CstExpr = Result.Nodes.getNodeAs<Expr>(CstId);
-  return CstExpr && CstExpr->isIntegerConstantExpr(Value, *Result.Context);
+  ConstExpr = Result.Nodes.getNodeAs<Expr>(CstId);
+  return ConstExpr && ConstExpr->isIntegerConstantExpr(Value, *Result.Context);
 }
 
-// Returns a matcher for a symbolic expression (any expression except ingeter
-// constant expression).
+// Overloaded `retrieveIntegerConstantExpr` for compatibility.
+static bool retrieveIntegerConstantExpr(const MatchFinder::MatchResult &Result,
+                                        StringRef Id, APSInt &Value) {
+  const Expr *ConstExpr = nullptr;
+  return retrieveIntegerConstantExpr(Result, Id, Value, ConstExpr);
+}
+
+// Returns a matcher for symbolic expressions (matches every expression except
+// ingeter constant expressions).
 static ast_matchers::internal::Matcher<Expr> matchSymbolicExpr(StringRef Id) {
   std::string SymId = (Id + "-sym").str();
   return ignoringParenImpCasts(
       expr(unless(isIntegerConstantExpr())).bind(SymId));
 }
 
-// Retrieve the expression matched by 'matchSymbolicExpr' with name 'Id' and
-// store it into 'SymExpr'.
+// Retrieves the expression matched by 'matchSymbolicExpr' with name 'Id' and
+// stores it into 'SymExpr'.
 static bool retrieveSymbolicExpr(const MatchFinder::MatchResult &Result,
                                  StringRef Id, const Expr *&SymExpr) {
   std::string SymId = (Id + "-sym").str();
@@ -348,7 +390,7 @@
   return ignoringParenImpCasts(BinOpCstExpr);
 }
 
-// Retrieve sub-expressions matched by 'matchBinOpIntegerConstantExpr' with
+// Retrieves sub-expressions matched by 'matchBinOpIntegerConstantExpr' with
 // name 'Id'.
 static bool
 retrieveBinOpIntegerConstantExpr(const MatchFinder::MatchResult &Result,
@@ -362,7 +404,7 @@
   return false;
 }
 
-// Matches relational expression: 'Expr <op> k' (i.e. x < 2, x != 3, 12 <= x).
+// Matches relational expressions: 'Expr <op> k' (i.e. x < 2, x != 3, 12 <= x).
 static ast_matchers::internal::Matcher<Expr>
 matchRelationalIntegerConstantExpr(StringRef Id) {
   std::string CastId = (Id + "-cast").str();
@@ -388,6 +430,7 @@
                     hasUnaryOperand(anyOf(CastExpr, RelationalExpr)))
           .bind(NegateId);
 
+  // Do not bind to double negation.
   const auto NegateNegateRelationalExpr =
       unaryOperator(hasOperatorName("!"),
                     hasUnaryOperand(unaryOperator(
@@ -398,13 +441,12 @@
                NegateNegateRelationalExpr);
 }
 
-// Retrieve sub-expressions matched by 'matchRelationalIntegerConstantExpr' with
+// Retrieves sub-expressions matched by 'matchRelationalIntegerConstantExpr' with
 // name 'Id'.
-static bool
-retrieveRelationalIntegerConstantExpr(const MatchFinder::MatchResult &Result,
-                                      StringRef Id, const Expr *&OperandExpr,
-                                      BinaryOperatorKind &Opcode,
-                                      const Expr *&Symbol, APSInt &Value) {
+static bool retrieveRelationalIntegerConstantExpr(
+    const MatchFinder::MatchResult &Result, StringRef Id,
+    const Expr *&OperandExpr, BinaryOperatorKind &Opcode, const Expr *&Symbol,
+    APSInt &Value, const Expr *&ConstExpr) {
   std::string CastId = (Id + "-cast").str();
   std::string SwapId = (Id + "-swap").str();
   std::string NegateId = (Id + "-negate").str();
@@ -413,8 +455,10 @@
     // Operand received with explicit comparator.
     Opcode = Bin->getOpcode();
     OperandExpr = Bin;
-    if (!retrieveIntegerConstantExpr(Result, Id, Value))
+
+    if (!retrieveIntegerConstantExpr(Result, Id, Value, ConstExpr))
       return false;
+
   } else if (const auto *Cast = Result.Nodes.getNodeAs<CastExpr>(CastId)) {
     // Operand received with implicit comparator (cast).
     Opcode = BO_NE;
@@ -431,56 +475,96 @@
     Opcode = BinaryOperator::reverseComparisonOp(Opcode);
   if (Result.Nodes.getNodeAs<Expr>(NegateId))
     Opcode = BinaryOperator::negateComparisonOp(Opcode);
+  return true;
+}
+
+// Checks for expressions like (X == 4) && (Y != 9)
+static bool areSidesBinaryConstExpressions(const BinaryOperator *&BinOp, const ASTContext *AstCtx) {
+  const auto *LhsBinOp = dyn_cast<BinaryOperator>(BinOp->getLHS());
+  const auto *RhsBinOp = dyn_cast<BinaryOperator>(BinOp->getRHS());
+
+  if (!LhsBinOp || !RhsBinOp)
+    return false;
+
+  if ((LhsBinOp->getLHS()->isIntegerConstantExpr(*AstCtx) ||
+       LhsBinOp->getRHS()->isIntegerConstantExpr(*AstCtx)) &&
+      (RhsBinOp->getLHS()->isIntegerConstantExpr(*AstCtx) ||
+       RhsBinOp->getRHS()->isIntegerConstantExpr(*AstCtx)))
+    return true;
+  return false;
+}
+
+// Retrieves integer constant subexpressions from binary operator expressions
+// that have two equivalent sides
+// E.g.: from (X == 5) && (X == 5) retrieves 5 and 5.
+static bool retrieveConstExprFromBothSides(const BinaryOperator *&BinOp,
+                                           BinaryOperatorKind &MainOpcode,
+                                           BinaryOperatorKind &SideOpcode,
+                                           const Expr *&LhsConst,
+                                           const Expr *&RhsConst,
+                                           const ASTContext *AstCtx) {
+  assert(areSidesBinaryConstExpressions(BinOp, AstCtx) &&
+         "Both sides of binary operator must be constant expressions!");
+
+  MainOpcode = BinOp->getOpcode();
+
+  const auto *BinOpLhs = cast<BinaryOperator>(BinOp->getLHS());
+  const auto *BinOpRhs = cast<BinaryOperator>(BinOp->getRHS());
+
+  LhsConst = BinOpLhs->getLHS()->isIntegerConstantExpr(*AstCtx)
+                 ? BinOpLhs->getLHS()
+                 : BinOpLhs->getRHS();
+  RhsConst = BinOpRhs->getLHS()->isIntegerConstantExpr(*AstCtx)
+                 ? BinOpRhs->getLHS()
+                 : BinOpRhs->getRHS();
+
+  if (!LhsConst || !RhsConst)
+    return false;
+
+  assert(BinOpLhs->getOpcode() == BinOpRhs->getOpcode() &&
+         "Sides of the binary operator must be equivalent expressions!");
+
+  SideOpcode = BinOpLhs->getOpcode();
 
   return true;
 }
 
-AST_MATCHER(BinaryOperator, operandsAreEquivalent) {
-  return areEquivalentExpr(Node.getLHS(), Node.getRHS());
+static bool areExprsFromDifferentMacros(const Expr *LhsExpr,
+                                        const Expr *RhsExpr,
+                                        const ASTContext *AstCtx) {
+  if (!LhsExpr || !RhsExpr)
+    return false;
+
+  SourceLocation LhsLoc = LhsExpr->getExprLoc();
+  SourceLocation RhsLoc = RhsExpr->getExprLoc();
+
+  if (!LhsLoc.isMacroID() || !RhsLoc.isMacroID())
+    return false;
+
+  const SourceManager &SM = AstCtx->getSourceManager();
+  const LangOptions &LO = AstCtx->getLangOpts();
+
+  return !(Lexer::getImmediateMacroName(LhsLoc, SM, LO) ==
+          Lexer::getImmediateMacroName(RhsLoc, SM, LO));
 }
 
-AST_MATCHER(ConditionalOperator, expressionsAreEquivalent) {
-  return areEquivalentExpr(Node.getTrueExpr(), Node.getFalseExpr());
-}
+static bool areExprsMacroAndNonMacro(const Expr *&LhsExpr, const Expr *&RhsExpr) {
+  if (!LhsExpr || !RhsExpr)
+    return false;
 
-AST_MATCHER(CallExpr, parametersAreEquivalent) {
-  return Node.getNumArgs() == 2 &&
-         areEquivalentExpr(Node.getArg(0), Node.getArg(1));
-}
+  SourceLocation LhsLoc = LhsExpr->getExprLoc();
+  SourceLocation RhsLoc = RhsExpr->getExprLoc();
 
-AST_MATCHER(BinaryOperator, binaryOperatorIsInMacro) {
-  return Node.getOperatorLoc().isMacroID();
-}
-
-AST_MATCHER(ConditionalOperator, conditionalOperatorIsInMacro) {
-  return Node.getQuestionLoc().isMacroID() || Node.getColonLoc().isMacroID();
-}
-
-AST_MATCHER(Expr, isMacro) { return Node.getExprLoc().isMacroID(); }
-
-AST_MATCHER_P(Expr, expandedByMacro, std::set<std::string>, Names) {
-  const SourceManager &SM = Finder->getASTContext().getSourceManager();
-  const LangOptions &LO = Finder->getASTContext().getLangOpts();
-  SourceLocation Loc = Node.getExprLoc();
-  while (Loc.isMacroID()) {
-    std::string MacroName = Lexer::getImmediateMacroName(Loc, SM, LO);
-    if (Names.find(MacroName) != Names.end())
-      return true;
-    Loc = SM.getImmediateMacroCallerLoc(Loc);
-  }
-  return false;
+  return LhsLoc.isMacroID() != RhsLoc.isMacroID();
 }
 
 void RedundantExpressionCheck::registerMatchers(MatchFinder *Finder) {
   const auto AnyLiteralExpr = ignoringParenImpCasts(
       anyOf(cxxBoolLiteral(), characterLiteral(), integerLiteral()));
 
-  std::vector<std::string> MacroNames =
-      utils::options::parseStringList(KnownBannedMacroNames);
-  std::set<std::string> Names(MacroNames.begin(), MacroNames.end());
+  const auto BannedIntegerLiteral = integerLiteral(expandedByMacro(KnownBannedMacroNames));
 
-  const auto BannedIntegerLiteral = integerLiteral(expandedByMacro(Names));
-
+  // Binary with equivalent operands, like (X != 2 && X != 2).
   Finder->addMatcher(
       binaryOperator(anyOf(hasOperatorName("-"), hasOperatorName("/"),
                            hasOperatorName("%"), hasOperatorName("|"),
@@ -499,15 +583,16 @@
           .bind("binary"),
       this);
 
+  // Conditional (trenary) operator with equivalent operands, like (Y ? X : X).
   Finder->addMatcher(
       conditionalOperator(expressionsAreEquivalent(),
                           // Filter noisy false positives.
                           unless(conditionalOperatorIsInMacro()),
-                          unless(hasTrueExpression(AnyLiteralExpr)),
                           unless(isInTemplateInstantiation()))
           .bind("cond"),
       this);
 
+  // Overloaded operators with equivalent operands.
   Finder->addMatcher(
       cxxOperatorCallExpr(
           anyOf(
@@ -613,8 +698,8 @@
         !areEquivalentExpr(LhsSymbol, RhsSymbol))
       return;
 
-    canonicalNegateExpr(LhsOpcode, LhsValue);
-    canonicalNegateExpr(RhsOpcode, RhsValue);
+    transformSubToCanonicalAddExpr(LhsOpcode, LhsValue);
+    transformSubToCanonicalAddExpr(RhsOpcode, RhsValue);
 
     // Check expressions: x + 1 == x + 2  or  x + 1 != x + 2.
     if (LhsOpcode == BO_Add && RhsOpcode == BO_Add) {
@@ -674,20 +759,23 @@
   if (const auto *ComparisonOperator = Result.Nodes.getNodeAs<BinaryOperator>(
           "comparisons-of-symbol-and-const")) {
     // Matched expressions are: (x <op> k1) <REL> (x <op> k2).
+    // E.g.: (X < 2) && (X > 4)
     BinaryOperatorKind Opcode = ComparisonOperator->getOpcode();
 
     const Expr *LhsExpr = nullptr, *RhsExpr = nullptr;
-    APSInt LhsValue, RhsValue;
     const Expr *LhsSymbol = nullptr, *RhsSymbol = nullptr;
+    const Expr *LhsConst = nullptr, *RhsConst = nullptr;
     BinaryOperatorKind LhsOpcode, RhsOpcode;
+    APSInt LhsValue, RhsValue;
+
     if (!retrieveRelationalIntegerConstantExpr(
-            Result, "lhs", LhsExpr, LhsOpcode, LhsSymbol, LhsValue) ||
+            Result, "lhs", LhsExpr, LhsOpcode, LhsSymbol, LhsValue, LhsConst) ||
         !retrieveRelationalIntegerConstantExpr(
-            Result, "rhs", RhsExpr, RhsOpcode, RhsSymbol, RhsValue) ||
+            Result, "rhs", RhsExpr, RhsOpcode, RhsSymbol, RhsValue, RhsConst) ||
         !areEquivalentExpr(LhsSymbol, RhsSymbol))
       return;
 
-    // Bring to a canonical form: smallest constant must be on the left side.
+    // Bring expr to a canonical form: smallest constant must be on the left.
     if (APSInt::compareValues(LhsValue, RhsValue) > 0) {
       std::swap(LhsExpr, RhsExpr);
       std::swap(LhsValue, RhsValue);
@@ -695,10 +783,15 @@
       std::swap(LhsOpcode, RhsOpcode);
     }
 
+    // Constants come from two different macros, or one of them is a macro.
+    if (areExprsFromDifferentMacros(LhsConst, RhsConst, Result.Context) ||
+        areExprsMacroAndNonMacro(LhsConst, RhsConst))
+      return;
+
     if ((Opcode == BO_LAnd || Opcode == BO_LOr) &&
         areEquivalentRanges(LhsOpcode, LhsValue, RhsOpcode, RhsValue)) {
       diag(ComparisonOperator->getOperatorLoc(),
-           "equivalent expression on both side of logical operator");
+           "equivalent expression on both sides of logical operator");
       return;
     }
 
@@ -727,16 +820,62 @@
 }
 
 void RedundantExpressionCheck::check(const MatchFinder::MatchResult &Result) {
-  if (const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("binary"))
-    diag(BinOp->getOperatorLoc(), "both side of operator are equivalent");
-  if (const auto *CondOp = Result.Nodes.getNodeAs<ConditionalOperator>("cond"))
-    diag(CondOp->getColonLoc(), "'true' and 'false' expression are equivalent");
-  if (const auto *Call = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("call"))
-    diag(Call->getOperatorLoc(),
-         "both side of overloaded operator are equivalent");
+  if (const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("binary")) {
 
+    // If the expression's constants are macros, check whether they are
+    // intentional.
+    if (areSidesBinaryConstExpressions(BinOp, Result.Context)) {
+      const Expr *LhsConst = nullptr, *RhsConst = nullptr;
+      BinaryOperatorKind MainOpcode, SideOpcode;
+
+      if(!retrieveConstExprFromBothSides(BinOp, MainOpcode, SideOpcode, LhsConst,
+                                     RhsConst, Result.Context))
+          return;
+
+      if (areExprsFromDifferentMacros(LhsConst, RhsConst, Result.Context) ||
+          areExprsMacroAndNonMacro(LhsConst, RhsConst))
+        return;
+    }
+
+    diag(BinOp->getOperatorLoc(), "both sides of operator are equivalent");
+  }
+
+  if (const auto *CondOp =
+          Result.Nodes.getNodeAs<ConditionalOperator>("cond")) {
+    const Expr *TrueExpr = CondOp->getTrueExpr();
+    const Expr *FalseExpr = CondOp->getFalseExpr();
+
+    if (areExprsFromDifferentMacros(TrueExpr, FalseExpr, Result.Context) ||
+        areExprsMacroAndNonMacro(TrueExpr, FalseExpr))
+      return;
+    diag(CondOp->getColonLoc(),
+         "'true' and 'false' expressions are equivalent");
+  }
+
+  if (const auto *Call = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("call")) {
+    diag(Call->getOperatorLoc(),
+         "both sides of overloaded operator are equivalent");
+  }
+
+  // Check for the following bound expressions:
+  // - "binop-const-compare-to-sym",
+  // - "binop-const-compare-to-binop-const",
+  // Produced message:
+  // -> "logical expression is always false/true"
   checkArithmeticExpr(Result);
+
+  // Check for the following bound expression:
+  // - "binop-const-compare-to-const",
+  // Produced message:
+  // -> "logical expression is always false/true"
   checkBitwiseExpr(Result);
+
+  // Check for te following bound expression:
+  // - "comparisons-of-symbol-and-const",
+  // Produced messages:
+  // -> "equivalent expression on both sides of logical operator",
+  // -> "logical expression is always false/true"
+  // -> "expression is redundant"
   checkRelationalExpr(Result);
 }
 
diff --git a/clang-tidy/misc/RedundantExpressionCheck.h b/clang-tidy/misc/RedundantExpressionCheck.h
index 59d2c8f..c0f8bf5 100644
--- a/clang-tidy/misc/RedundantExpressionCheck.h
+++ b/clang-tidy/misc/RedundantExpressionCheck.h
@@ -16,7 +16,8 @@
 namespace tidy {
 namespace misc {
 
-/// Detect useless or suspicious redundant expressions.
+/// The checker detects expressions that are redundant, because they contain
+/// ineffective, useless parts.
 ///
 /// For the user-facing documentation see:
 /// http://clang.llvm.org/extra/clang-tidy/checks/misc-redundant-expression.html
diff --git a/clang-tidy/tool/run-clang-tidy.py b/clang-tidy/tool/run-clang-tidy.py
index 7792c5a..15b21d7 100755
--- a/clang-tidy/tool/run-clang-tidy.py
+++ b/clang-tidy/tool/run-clang-tidy.py
@@ -68,6 +68,12 @@
   return os.path.realpath(result)
 
 
+def make_absolute(f, directory):
+  if os.path.isabs(f):
+    return f
+  return os.path.normpath(os.path.join(directory, f))
+
+
 def get_tidy_invocation(f, clang_tidy_binary, checks, tmpdir, build_path,
                         header_filter, extra_arg, extra_arg_before, quiet):
   """Gets a command line for clang-tidy."""
@@ -223,7 +229,8 @@
 
   # Load the database and extract all files.
   database = json.load(open(os.path.join(build_path, db_path)))
-  files = [entry['file'] for entry in database]
+  files = [make_absolute(entry['file'], entry['directory'])
+           for entry in database]
 
   max_task = args.j
   if max_task == 0:
diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt
index 1825d11..a81da61 100644
--- a/clangd/CMakeLists.txt
+++ b/clangd/CMakeLists.txt
@@ -10,6 +10,7 @@
   DraftStore.cpp
   GlobalCompilationDatabase.cpp
   JSONRPCDispatcher.cpp
+  JSONExpr.cpp
   Logger.cpp
   Protocol.cpp
   ProtocolHandlers.cpp
diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp
index 1689a5f..aa73e73 100644
--- a/clangd/ClangdLSPServer.cpp
+++ b/clangd/ClangdLSPServer.cpp
@@ -10,45 +10,58 @@
 #include "ClangdLSPServer.h"
 #include "JSONRPCDispatcher.h"
 
+#include "llvm/Support/FormatVariadic.h"
+
 using namespace clang::clangd;
 using namespace clang;
 
 namespace {
 
-std::string
+std::vector<TextEdit>
 replacementsToEdits(StringRef Code,
                     const std::vector<tooling::Replacement> &Replacements) {
   // Turn the replacements into the format specified by the Language Server
   // Protocol. Fuse them into one big JSON array.
-  std::string Edits;
+  std::vector<TextEdit> Edits;
   for (auto &R : Replacements) {
     Range ReplacementRange = {
         offsetToPosition(Code, R.getOffset()),
         offsetToPosition(Code, R.getOffset() + R.getLength())};
-    TextEdit TE = {ReplacementRange, R.getReplacementText()};
-    Edits += TextEdit::unparse(TE);
-    Edits += ',';
+    Edits.push_back({ReplacementRange, R.getReplacementText()});
   }
-  if (!Edits.empty())
-    Edits.pop_back();
-
   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
-        }})");
+  C.reply(json::obj{
+      {{"capabilities",
+        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 +71,7 @@
 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; }
@@ -74,7 +87,8 @@
 void ClangdLSPServer::onDocumentDidChange(Ctx C,
                                           DidChangeTextDocumentParams &Params) {
   if (Params.contentChanges.size() != 1)
-    return C.replyError(-32602, "can only apply one change at a time");
+    return C.replyError(ErrorCode::InvalidParams,
+                        "can only apply one change at a time");
   // We only support full syncing right now.
   Server.addDocument(Params.textDocument.uri.file,
                      Params.contentChanges[0].text);
@@ -84,6 +98,35 @@
   Server.onFileEvent(Params);
 }
 
+void ClangdLSPServer::onCommand(Ctx C, ExecuteCommandParams &Params) {
+  if (Params.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND &&
+      Params.workspaceEdit) {
+    // The flow for "apply-fix" :
+    // 1. We publish a diagnostic, including fixits
+    // 2. The user clicks on the diagnostic, the editor asks us for code actions
+    // 3. We send code actions, with the fixit embedded as context
+    // 4. The user selects the fixit, the editor asks us to apply it
+    // 5. We unwrap the changes and send them back to the editor
+    // 6. The editor applies the changes (applyEdit), and sends us a reply (but
+    // we ignore it)
+
+    ApplyWorkspaceEditParams ApplyEdit;
+    ApplyEdit.edit = *Params.workspaceEdit;
+    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.
+    C.call("workspace/applyEdit", ApplyWorkspaceEditParams::unparse(ApplyEdit));
+  } else {
+    // We should not get here because ExecuteCommandParams would not have
+    // parsed in the first place and this handler should not be called. But if
+    // more commands are added, this will be here has a safe guard.
+    C.replyError(
+        ErrorCode::InvalidParams,
+        llvm::formatv("Unsupported command \"{0}\".", Params.command).str());
+  }
+}
+
 void ClangdLSPServer::onDocumentDidClose(Ctx C,
                                          DidCloseTextDocumentParams &Params) {
   Server.removeDocument(Params.textDocument.uri.file);
@@ -93,49 +136,45 @@
     Ctx C, DocumentOnTypeFormattingParams &Params) {
   auto File = Params.textDocument.uri.file;
   std::string Code = Server.getDocument(File);
-  std::string Edits =
-      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 =
-      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 = 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);
-    std::string Edits = replacementsToEdits(Code, Fixes);
-
-    if (!Edits.empty())
-      Commands +=
-          R"({"title":"Apply FixIt ')" + llvm::yaml::escape(D.message) +
-          R"('", "command": "clangd.applyFix", "arguments": [")" +
-          llvm::yaml::escape(Params.textDocument.uri.uri) +
-          R"(", [)" + Edits +
-          R"(]]},)";
+    auto Edits = replacementsToEdits(Code, Fixes);
+    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) {
@@ -147,15 +186,7 @@
                           // 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,
@@ -164,8 +195,9 @@
       Params.textDocument.uri.file,
       Position{Params.position.line, Params.position.character});
   if (!SignatureHelp)
-    return C.replyError(-32602, llvm::toString(SignatureHelp.takeError()));
-  C.reply(SignatureHelp::unparse(SignatureHelp->Value));
+    return C.replyError(ErrorCode::InvalidParams,
+                        llvm::toString(SignatureHelp.takeError()));
+  C.reply(SignatureHelp->Value);
 }
 
 void ClangdLSPServer::onGoToDefinition(Ctx C,
@@ -174,23 +206,16 @@
       Params.textDocument.uri.file,
       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 + "]");
+    return C.replyError(ErrorCode::InvalidParams,
+                        llvm::toString(Items.takeError()));
+  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,
@@ -209,7 +234,7 @@
   // Set up JSONRPCDispatcher.
   JSONRPCDispatcher Dispatcher(
       [](RequestContext Ctx, llvm::yaml::MappingNode *Params) {
-        Ctx.replyError(-32601, "method not found");
+        Ctx.replyError(ErrorCode::MethodNotFound, "method not found");
       });
   registerCallbackHandlers(Dispatcher, Out, /*Callbacks=*/*this);
 
@@ -240,17 +265,16 @@
 
 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(),
@@ -265,10 +289,13 @@
   }
 
   // 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)},
+       }},
+  });
 }
diff --git a/clangd/ClangdLSPServer.h b/clangd/ClangdLSPServer.h
index 261ff61..22f73cb 100644
--- a/clangd/ClangdLSPServer.h
+++ b/clangd/ClangdLSPServer.h
@@ -69,6 +69,7 @@
   void onGoToDefinition(Ctx C, TextDocumentPositionParams &Params) override;
   void onSwitchSourceHeader(Ctx C, TextDocumentIdentifier &Params) override;
   void onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) override;
+  void onCommand(Ctx C, ExecuteCommandParams &Params) override;
 
   std::vector<clang::tooling::Replacement>
   getFixIts(StringRef File, const clangd::Diagnostic &D);
diff --git a/clangd/ClangdUnit.cpp b/clangd/ClangdUnit.cpp
index 82ea54d..aeeb0e7 100644
--- a/clangd/ClangdUnit.cpp
+++ b/clangd/ClangdUnit.cpp
@@ -389,6 +389,7 @@
       assert(CCS && "Expected the CodeCompletionString to be non-null");
       Items.push_back(ProcessCodeCompleteResult(Result, *CCS));
     }
+    std::sort(Items.begin(), Items.end());
   }
 
   GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
@@ -965,10 +966,15 @@
     End.character = SourceMgr.getSpellingColumnNumber(LocEnd) - 1;
     Range R = {Begin, End};
     Location L;
-    L.uri = URI::fromFile(
-        SourceMgr.getFilename(SourceMgr.getSpellingLoc(LocStart)));
-    L.range = R;
-    DeclarationLocations.push_back(L);
+    if (const FileEntry *F =
+            SourceMgr.getFileEntryForID(SourceMgr.getFileID(LocStart))) {
+      StringRef FilePath = F->tryGetRealPathName();
+      if (FilePath.empty())
+        FilePath = F->getName();
+      L.uri = URI::fromFile(FilePath);
+      L.range = R;
+      DeclarationLocations.push_back(L);
+    }
   }
 
   void finish() override {
diff --git a/clangd/JSONExpr.cpp b/clangd/JSONExpr.cpp
new file mode 100644
index 0000000..968892c
--- /dev/null
+++ b/clangd/JSONExpr.cpp
@@ -0,0 +1,221 @@
+#include "JSONExpr.h"
+
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+namespace clang {
+namespace clangd {
+namespace json {
+
+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;
+  }
+  }
+}
+
+namespace clang {
+namespace clangd {
+namespace json {
+llvm::raw_ostream &operator<<(raw_ostream &OS, const Expr &E) {
+  E.print(OS, [](IndenterAction A) { /*ignore*/ });
+  return OS;
+}
+} // namespace json
+} // namespace clangd
+} // namespace clang
+
+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;
+    };
+  });
+}
diff --git a/clangd/JSONExpr.h b/clangd/JSONExpr.h
new file mode 100644
index 0000000..b9cf808
--- /dev/null
+++ b/clangd/JSONExpr.h
@@ -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) : ObjectKey(static_cast<const ObjectKey &&>(C)) {}
+    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
diff --git a/clangd/JSONRPCDispatcher.cpp b/clangd/JSONRPCDispatcher.cpp
index 121ddb9..557fadb 100644
--- a/clangd/JSONRPCDispatcher.cpp
+++ b/clangd/JSONRPCDispatcher.cpp
@@ -8,9 +8,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "JSONRPCDispatcher.h"
+#include "JSONExpr.h"
 #include "ProtocolHandlers.h"
 #include "Trace.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/YAMLParser.h"
 #include <istream>
@@ -18,17 +20,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,25 +54,40 @@
   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"("}})");
+void RequestContext::replyError(ErrorCode code, const llvm::StringRef &Message) {
+  Out.log("Error " + Twine(static_cast<int>(code)) + ": " + Message + "\n");
+  if (ID) {
+    Out.writeMessage(json::obj{
+        {"jsonrpc", "2.0"},
+        {"id", *ID},
+        {"error", json::obj{{"code", static_cast<int>(code)}, {"message", Message}}},
+    });
   }
 }
 
+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(json::obj{
+      {"jsonrpc", "2.0"},
+      {"id", 1},
+      {"method", Method},
+      {"params", std::move(Params)},
+  });
+}
+
 void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) {
   assert(!Handlers.count(Method) && "Handler already registered!");
   Handlers[Method] = std::move(H);
@@ -73,7 +95,7 @@
 
 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;
@@ -81,7 +103,7 @@
   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 {
@@ -99,7 +121,7 @@
   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());
@@ -120,7 +142,19 @@
     } 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;
+          // FIXME: this is locale-sensitive.
+          if (llvm::to_float(V, D))
+            ID.emplace(D);
+        }
+      }
     } else if (KeyValue == "params") {
       if (!Method)
         return false;
@@ -129,7 +163,7 @@
       // 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;
@@ -140,7 +174,7 @@
   // leftovers.
   if (!Method)
     return false;
-  callHandler(Handlers, Method, Id, nullptr, UnknownHandler, Out);
+  callHandler(Handlers, Method, std::move(ID), nullptr, UnknownHandler, Out);
 
   return true;
 }
diff --git a/clangd/JSONRPCDispatcher.h b/clangd/JSONRPCDispatcher.h
index 9071e42..afc787a 100644
--- a/clangd/JSONRPCDispatcher.h
+++ b/clangd/JSONRPCDispatcher.h
@@ -10,7 +10,9 @@
 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H
 
+#include "JSONExpr.h"
 #include "Logger.h"
+#include "Protocol.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringMap.h"
@@ -26,11 +28,11 @@
 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 +47,7 @@
   llvm::raw_ostream &Outs;
   llvm::raw_ostream &Logs;
   llvm::raw_ostream *InputMirror;
+  bool Pretty;
 
   std::mutex StreamMutex;
 };
@@ -52,16 +55,19 @@
 /// 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);
+  void replyError(ErrorCode code, const llvm::StringRef &Message);
+  /// Sends a request to the client.
+  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
diff --git a/clangd/Protocol.cpp b/clangd/Protocol.cpp
index 509e596..8fa23bf 100644
--- a/clangd/Protocol.cpp
+++ b/clangd/Protocol.cpp
@@ -63,7 +63,7 @@
   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 @@
   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 @@
   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,12 +277,11 @@
   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;
+json::Expr TextEdit::unparse(const TextEdit &P) {
+  return json::obj{
+      {"range", P.range},
+      {"newText", P.newText},
+  };
 }
 
 namespace {
@@ -598,11 +595,11 @@
   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>
@@ -846,6 +843,143 @@
   return Result;
 }
 
+llvm::Optional<std::map<std::string, std::vector<TextEdit>>>
+parseWorkspaceEditChange(llvm::yaml::MappingNode *Params,
+                         clangd::Logger &Logger) {
+  std::map<std::string, std::vector<TextEdit>> Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    if (Result.count(KeyValue)) {
+      logIgnoredField(KeyValue, Logger);
+      continue;
+    }
+
+    auto *Value =
+        dyn_cast_or_null<llvm::yaml::SequenceNode>(NextKeyValue.getValue());
+    if (!Value)
+      return llvm::None;
+    for (auto &Item : *Value) {
+      auto *ItemValue = dyn_cast_or_null<llvm::yaml::MappingNode>(&Item);
+      if (!ItemValue)
+        return llvm::None;
+      auto Parsed = TextEdit::parse(ItemValue, Logger);
+      if (!Parsed)
+        return llvm::None;
+
+      Result[KeyValue].push_back(*Parsed);
+    }
+  }
+
+  return Result;
+}
+
+llvm::Optional<WorkspaceEdit>
+WorkspaceEdit::parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger) {
+  WorkspaceEdit Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "changes") {
+      auto *Value =
+          dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+      if (!Value)
+        return llvm::None;
+      auto Parsed = parseWorkspaceEditChange(Value, Logger);
+      if (!Parsed)
+        return llvm::None;
+      Result.changes = std::move(*Parsed);
+    } else {
+      logIgnoredField(KeyValue, Logger);
+    }
+  }
+  return Result;
+}
+
+const std::string ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND =
+    "clangd.applyFix";
+
+llvm::Optional<ExecuteCommandParams>
+ExecuteCommandParams::parse(llvm::yaml::MappingNode *Params,
+                            clangd::Logger &Logger) {
+  ExecuteCommandParams Result;
+  // Depending on which "command" we parse, we will use this function to parse
+  // the command "arguments".
+  std::function<bool(llvm::yaml::MappingNode * Params)> ArgParser = nullptr;
+
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+
+    // Note that "commands" has to be parsed before "arguments" for this to
+    // work properly.
+    if (KeyValue == "command") {
+      auto *ScalarValue =
+          dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+      if (!ScalarValue)
+        return llvm::None;
+      llvm::SmallString<10> Storage;
+      Result.command = ScalarValue->getValue(Storage);
+      if (Result.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND) {
+        ArgParser = [&Result, &Logger](llvm::yaml::MappingNode *Params) {
+          auto WE = WorkspaceEdit::parse(Params, Logger);
+          if (WE)
+            Result.workspaceEdit = WE;
+          return WE.hasValue();
+        };
+      } else {
+        return llvm::None;
+      }
+    } else if (KeyValue == "arguments") {
+      auto *Value = NextKeyValue.getValue();
+      auto *Seq = dyn_cast<llvm::yaml::SequenceNode>(Value);
+      if (!Seq)
+        return llvm::None;
+      for (auto &Item : *Seq) {
+        auto *ItemValue = dyn_cast_or_null<llvm::yaml::MappingNode>(&Item);
+        if (!ItemValue || !ArgParser)
+          return llvm::None;
+        if (!ArgParser(ItemValue))
+          return llvm::None;
+      }
+    } else {
+      logIgnoredField(KeyValue, Logger);
+    }
+  }
+  if (Result.command.empty())
+    return llvm::None;
+
+  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)}};
+}
+
+json::Expr
+ApplyWorkspaceEditParams::unparse(const ApplyWorkspaceEditParams &Params) {
+  return json::obj{{"edit", Params.edit}};
+}
+
 llvm::Optional<TextDocumentPositionParams>
 TextDocumentPositionParams::parse(llvm::yaml::MappingNode *Params,
                                   clangd::Logger &Logger) {
@@ -880,96 +1014,62 @@
   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);
+bool clangd::operator<(const CompletionItem &L, const CompletionItem &R) {
+  return (L.sortText.empty() ? L.label : L.sortText) <
+         (R.sortText.empty() ? R.label : R.sortText);
+}
+
+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)},
+  };
 }
diff --git a/clangd/Protocol.h b/clangd/Protocol.h
index 421fa02..a53f72f 100644
--- a/clangd/Protocol.h
+++ b/clangd/Protocol.h
@@ -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>
@@ -31,6 +32,21 @@
 
 class Logger;
 
+enum class ErrorCode {
+  // Defined by JSON RPC.
+  ParseError = -32700,
+  InvalidRequest = -32600,
+  MethodNotFound = -32601,
+  InvalidParams = -32602,
+  InternalError = -32603,
+
+  ServerNotInitialized = -32002,
+  UnknownErrorCode = -32001,
+
+  // Defined by the protocol.
+  RequestCancelled = -32800,
+};
+
 struct URI {
   std::string uri;
   std::string file;
@@ -39,7 +55,7 @@
   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 +96,7 @@
 
   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 +115,7 @@
 
   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 +135,7 @@
     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,7 +156,7 @@
 
   static llvm::Optional<TextEdit> parse(llvm::yaml::MappingNode *Params,
                                         clangd::Logger &Logger);
-  static std::string unparse(const TextEdit &P);
+  static json::Expr unparse(const TextEdit &P);
 };
 
 struct TextDocumentItem {
@@ -282,7 +298,7 @@
 
   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 {
@@ -382,6 +398,46 @@
                                                 clangd::Logger &Logger);
 };
 
+struct WorkspaceEdit {
+  /// Holds changes to existing resources.
+  llvm::Optional<std::map<std::string, std::vector<TextEdit>>> changes;
+
+  /// Note: "documentChanges" is not currently used because currently there is
+  /// no support for versioned edits.
+
+  static llvm::Optional<WorkspaceEdit> parse(llvm::yaml::MappingNode *Params,
+                                             clangd::Logger &Logger);
+  static json::Expr unparse(const WorkspaceEdit &WE);
+};
+
+/// Exact commands are not specified in the protocol so we define the
+/// ones supported by Clangd here. The protocol specifies the command arguments
+/// to be "any[]" but to make this safer and more manageable, each command we
+/// handle maps to a certain llvm::Optional of some struct to contain its
+/// arguments. Different commands could reuse the same llvm::Optional as
+/// arguments but a command that needs different arguments would simply add a
+/// new llvm::Optional and not use any other ones. In practice this means only
+/// one argument type will be parsed and set.
+struct ExecuteCommandParams {
+  // Command to apply fix-its. Uses WorkspaceEdit as argument.
+  const static std::string CLANGD_APPLY_FIX_COMMAND;
+
+  /// The command identifier, e.g. CLANGD_APPLY_FIX_COMMAND
+  std::string command;
+
+  // Arguments
+
+  llvm::Optional<WorkspaceEdit> workspaceEdit;
+
+  static llvm::Optional<ExecuteCommandParams>
+  parse(llvm::yaml::MappingNode *Params, clangd::Logger &Logger);
+};
+
+struct ApplyWorkspaceEditParams {
+  WorkspaceEdit edit;
+  static json::Expr unparse(const ApplyWorkspaceEditParams &Params);
+};
+
 struct TextDocumentPositionParams {
   /// The text document.
   TextDocumentIdentifier textDocument;
@@ -486,9 +542,11 @@
   //
   // 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);
 };
 
+bool operator<(const CompletionItem &, const CompletionItem &);
+
 /// A single parameter of a particular signature.
 struct ParameterInformation {
 
@@ -498,7 +556,7 @@
   /// 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.
@@ -513,7 +571,7 @@
   /// 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.
@@ -528,7 +586,7 @@
   /// The active parameter of the active signature.
   int activeParameter = 0;
 
-  static std::string unparse(const SignatureHelp &);
+  static json::Expr unparse(const SignatureHelp &);
 };
 
 } // namespace clangd
diff --git a/clangd/ProtocolHandlers.cpp b/clangd/ProtocolHandlers.cpp
index 507fc42..4ca6ee0 100644
--- a/clangd/ProtocolHandlers.cpp
+++ b/clangd/ProtocolHandlers.cpp
@@ -72,4 +72,5 @@
   Register("textDocument/switchSourceHeader",
            &ProtocolCallbacks::onSwitchSourceHeader);
   Register("workspace/didChangeWatchedFiles", &ProtocolCallbacks::onFileEvent);
+  Register("workspace/executeCommand", &ProtocolCallbacks::onCommand);
 }
diff --git a/clangd/ProtocolHandlers.h b/clangd/ProtocolHandlers.h
index bf307c8..f82bd29 100644
--- a/clangd/ProtocolHandlers.h
+++ b/clangd/ProtocolHandlers.h
@@ -52,6 +52,7 @@
   virtual void onGoToDefinition(Ctx C, TextDocumentPositionParams &Params) = 0;
   virtual void onSwitchSourceHeader(Ctx C, TextDocumentIdentifier &Params) = 0;
   virtual void onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) = 0;
+  virtual void onCommand(Ctx C, ExecuteCommandParams &Params) = 0;
 };
 
 void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,
diff --git a/clangd/clients/clangd-vscode/src/extension.ts b/clangd/clients/clangd-vscode/src/extension.ts
index f89ddc9..47c13a1 100644
--- a/clangd/clients/clangd-vscode/src/extension.ts
+++ b/clangd/clients/clangd-vscode/src/extension.ts
@@ -40,27 +40,7 @@
     };
 
     const clangdClient = new vscodelc.LanguageClient('Clang Language Server', serverOptions, clientOptions);
-
-    function applyTextEdits(uri: string, edits: vscodelc.TextEdit[]) {
-        let textEditor = vscode.window.activeTextEditor;
-
-        // FIXME: vscode expects that uri will be percent encoded
-        if (textEditor && textEditor.document.uri.toString(true) === uri) {
-            textEditor.edit(mutator => {
-                for (const edit of edits) {
-                    mutator.replace(clangdClient.protocol2CodeConverter.asRange(edit.range), edit.newText);
-                }
-            }).then((success) => {
-                if (!success) {
-                    vscode.window.showErrorMessage('Failed to apply fixes to the document.');
-                }
-            });
-        }
-    }
-
     console.log('Clang Language Server is now active!');
 
     const disposable = clangdClient.start();
-
-    context.subscriptions.push(disposable, vscode.commands.registerCommand('clangd.applyFix', applyTextEdits));
 }
diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp
index 2808d05..dc8969a 100644
--- a/clangd/tool/ClangdMain.cpp
+++ b/clangd/tool/ClangdMain.cpp
@@ -41,6 +41,10 @@
         "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 @@
   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.
diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst
index b36217a..eed1801 100644
--- a/docs/ReleaseNotes.rst
+++ b/docs/ReleaseNotes.rst
@@ -57,13 +57,21 @@
 Improvements to clang-tidy
 --------------------------
 
+- New `google-objc-global-variable-declaration
+  <http://clang.llvm.org/extra/clang-tidy/checks/google-global-variable-declaration.html>`_ check
+
+  Add new check for Objective-C code to ensure global 
+  variables follow the naming convention of 'k[A-Z].*' (for constants) 
+  or 'g[A-Z].*' (for variables).
+
 - New module `objc` for Objective-C style checks.
 
 - New `objc-forbidden-subclassing
   <http://clang.llvm.org/extra/clang-tidy/checks/objc-forbidden-subclassing.html>`_ check
 
   Ensures Objective-C classes do not subclass any classes which are
-  not intended to be subclassed.
+  not intended to be subclassed. Includes a list of classes from Foundation
+  and UIKit which are documented as not supporting subclassing.
 
 - Renamed checks to use correct term "implicit conversion" instead of "implicit
   cast" and modified messages and option names accordingly:
diff --git a/docs/clang-tidy/checks/google-objc-global-variable-declaration.rst b/docs/clang-tidy/checks/google-objc-global-variable-declaration.rst
new file mode 100644
index 0000000..ae2b1ee
--- /dev/null
+++ b/docs/clang-tidy/checks/google-objc-global-variable-declaration.rst
@@ -0,0 +1,42 @@
+.. title:: clang-tidy - google-objc-global-variable-declaration
+
+google-objc-global-variable-declaration
+=======================================
+
+Finds global variable declarations in Objective-C files that are not follow the pattern
+of variable names in Google's Objective-C Style Guide.
+
+The corresponding style guide rule:
+http://google.github.io/styleguide/objcguide.html#variable-names
+
+All the global variables should follow the pattern of `g[A-Z].*` (variables) or
+`k[A-Z].*` (constants). The check will suggest a variable name that follows the pattern
+if it can be inferred from the original name.
+
+For code:
+
+.. code-block:: objc
+  static NSString* myString = @"hello";
+
+The fix will be:
+
+.. code-block:: objc
+  static NSString* gMyString = @"hello";
+
+Another example of constant:
+
+.. code-block:: objc
+  static NSString* const myConstString = @"hello";
+
+The fix will be:
+
+.. code-block:: objc
+  static NSString* const kMyConstString = @"hello";
+
+However for code that prefixed with non-alphabetical characters like:
+
+.. code-block:: objc
+  static NSString* __anotherString = @"world";
+
+The check will give a warning message but will not be able to suggest a fix. The user
+need to fix it on his own.
diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst
index 38ff9cb..1b74c00 100644
--- a/docs/clang-tidy/checks/list.rst
+++ b/docs/clang-tidy/checks/list.rst
@@ -60,6 +60,7 @@
    google-default-arguments
    google-explicit-constructor
    google-global-names-in-headers
+   google-objc-global-variable-declaration
    google-readability-braces-around-statements (redirects to readability-braces-around-statements) <google-readability-braces-around-statements>
    google-readability-casting
    google-readability-function-size (redirects to readability-function-size) <google-readability-function-size>
diff --git a/docs/clang-tidy/checks/misc-redundant-expression.rst b/docs/clang-tidy/checks/misc-redundant-expression.rst
index ddef9af..83c29bd 100644
--- a/docs/clang-tidy/checks/misc-redundant-expression.rst
+++ b/docs/clang-tidy/checks/misc-redundant-expression.rst
@@ -9,13 +9,13 @@
 
 - redundant,
 
-- always be ``true``,
+- always ``true``,
 
-- always be ``false``,
+- always ``false``,
 
-- always be a constant (zero or one).
+- always a constant (zero or one).
 
-Example:
+Examples:
 
 .. code-block:: c++
 
diff --git a/docs/clang-tidy/checks/modernize-replace-random-shuffle.rst b/docs/clang-tidy/checks/modernize-replace-random-shuffle.rst
index 353f35a..50674d4 100644
--- a/docs/clang-tidy/checks/modernize-replace-random-shuffle.rst
+++ b/docs/clang-tidy/checks/modernize-replace-random-shuffle.rst
@@ -26,3 +26,16 @@
 The second example will also receive a warning that ``randomFunc`` is no longer supported in the same way as before so if the user wants the same functionality, the user will need to change the implementation of the ``randomFunc``.
 
 One thing to be aware of here is that ``std::random_device`` is quite expensive to initialize. So if you are using the code in a performance critical place, you probably want to initialize it elsewhere. 
+Another thing is that the seeding quality of the suggested fix is quite poor: ``std::mt19937`` has an internal state of 624 32-bit integers, but is only seeded with a single integer. So if you require
+higher quality randomness, you should consider seeding better, for example:
+
+.. code-block:: c++
+
+  std::shuffle(v.begin(), v.end(), []() {
+    std::mt19937::result_type seeds[std::mt19937::state_size];
+    std::random_device device;
+    std::uniform_int_distribution<typename std::mt19937::result_type> dist;
+    std::generate(std::begin(seeds), std::end(seeds), [&] { return dist(device); });
+    std::seed_seq seq(std::begin(seeds), std::end(seeds));
+    return std::mt19937(seq);
+  }());
diff --git a/test/clang-tidy/google-objc-global-variable-declaration.m b/test/clang-tidy/google-objc-global-variable-declaration.m
new file mode 100644
index 0000000..cbe5017
--- /dev/null
+++ b/test/clang-tidy/google-objc-global-variable-declaration.m
@@ -0,0 +1,41 @@
+// RUN: %check_clang_tidy %s google-objc-global-variable-declaration %t
+
+@class NSString;
+static NSString* const myConstString = @"hello";
+// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: const global variable 'myConstString' must have a name which starts with 'k[A-Z]' [google-objc-global-variable-declaration]
+// CHECK-FIXES: static NSString* const kMyConstString = @"hello";
+
+static NSString* MyString = @"hi";
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: non-const global variable 'MyString' must have a name which starts with 'g[A-Z]' [google-objc-global-variable-declaration]
+// CHECK-FIXES: static NSString* gMyString = @"hi";
+
+NSString* globalString = @"test";
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: non-const global variable 'globalString' must have a name which starts with 'g[A-Z]' [google-objc-global-variable-declaration]
+// CHECK-FIXES: NSString* gGlobalString = @"test";
+
+static NSString* a = @"too simple";
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: non-const global variable 'a' must have a name which starts with 'g[A-Z]' [google-objc-global-variable-declaration]
+// CHECK-FIXES: static NSString* a = @"too simple";
+
+static NSString* noDef;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: non-const global variable 'noDef' must have a name which starts with 'g[A-Z]' [google-objc-global-variable-declaration]
+// CHECK-FIXES: static NSString* gNoDef;
+
+static NSString* const _notAlpha = @"NotBeginWithAlpha";
+// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: const global variable '_notAlpha' must have a name which starts with 'k[A-Z]' [google-objc-global-variable-declaration]
+// CHECK-FIXES: static NSString* const _notAlpha = @"NotBeginWithAlpha";
+
+static NSString* const k_Alpha = @"SecondNotAlpha";
+// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: const global variable 'k_Alpha' must have a name which starts with 'k[A-Z]' [google-objc-global-variable-declaration]
+// CHECK-FIXES: static NSString* const k_Alpha = @"SecondNotAlpha";
+
+static NSString* const kGood = @"hello";
+// CHECK-MESSAGES-NOT: :[[@LINE-1]]:24: warning: const global variable 'kGood' must have a name which starts with 'k[A-Z]' [google-objc-global-variable-declaration]
+static NSString* gMyIntGood = 0;
+// CHECK-MESSAGES-NOT: :[[@LINE-1]]:18: warning: non-const global variable 'gMyIntGood' must have a name which starts with 'g[A-Z]' [google-objc-global-variable-declaration]
+@implementation Foo
+- (void)f {
+    int x = 0;
+// CHECK-MESSAGES-NOT: :[[@LINE-1]]:9: warning: non-const global variable 'gMyIntGood' must have a name which starts with 'g[A-Z]' [google-objc-global-variable-declaration]
+}
+@end
diff --git a/test/clang-tidy/misc-redundant-expression.cpp b/test/clang-tidy/misc-redundant-expression.cpp
index e865ba0..5192f1f 100644
--- a/test/clang-tidy/misc-redundant-expression.cpp
+++ b/test/clang-tidy/misc-redundant-expression.cpp
@@ -15,69 +15,71 @@
 extern int bar(int x);
 extern int bat(int x, int y);
 
-int Test(int X, int Y) {
+int TestSimpleEquivalent(int X, int Y) {
   if (X - X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent [misc-redundant-expression]
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent [misc-redundant-expression]
   if (X / X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent
   if (X % X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent
 
   if (X & X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent
   if (X | X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent
   if (X ^ X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent
 
   if (X < X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent
   if (X <= X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent
   if (X > X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent
   if (X >= X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent
 
   if (X && X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent
   if (X || X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent
 
   if (X != (((X)))) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent
 
   if (X + 1 == X + 1) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent
   if (X + 1 != X + 1) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent
   if (X + 1 <= X + 1) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent
   if (X + 1 >= X + 1) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent
 
   if ((X != 1 || Y != 1) && (X != 1 || Y != 1)) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: both sides of operator are equivalent
   if (P.a[X - P.x] != P.a[X - P.x]) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: both sides of operator are equivalent
 
   if ((int)X < (int)X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both sides of operator are equivalent
+  if (int(X) < int(X)) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both sides of operator are equivalent
 
   if ( + "dummy" == + "dummy") return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: both sides of operator are equivalent
   if (L"abc" == L"abc") return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both sides of operator are equivalent
 
   if (foo(0) - 2 < foo(0) - 2) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: both sides of operator are equivalent
   if (foo(bar(0)) < (foo(bar((0))))) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: both sides of operator are equivalent
 
   if (P1.x < P2.x && P1.x < P2.x) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: both sides of operator are equivalent
   if (P2.a[P1.x + 2] < P2.x && P2.a[(P1.x) + (2)] < (P2.x)) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: both sides of operator are equivalent
 
   return 0;
 }
@@ -102,23 +104,36 @@
   return 0;
 }
 
+#define COND_OP_MACRO 9
+#define COND_OP_OTHER_MACRO 9
 int TestConditional(int x, int y) {
   int k = 0;
   k += (y < 0) ? x : x;
-  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 'true' and 'false' expression are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 'true' and 'false' expressions are equivalent
   k += (y < 0) ? x + 1 : x + 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: 'true' and 'false' expression are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: 'true' and 'false' expressions are equivalent
+  k += (y < 0) ? COND_OP_MACRO : COND_OP_MACRO;
+  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: 'true' and 'false' expressions are equivalent
+
+  // Do not match for conditional operators with a macro and a const.
+  k += (y < 0) ? COND_OP_MACRO : 9;
+  // Do not match for conditional operators with expressions from different macros.
+  k += (y < 0) ? COND_OP_MACRO : COND_OP_OTHER_MACRO;
   return k;
 }
+#undef COND_OP_MACRO
+#undef COND_OP_OTHER_MACRO
 
 struct MyStruct {
   int x;
 } Q;
+
 bool operator==(const MyStruct& lhs, const MyStruct& rhs) { return lhs.x == rhs.x; }
-bool TestOperator(const MyStruct& S) {
+
+bool TestOperator(MyStruct& S) {
   if (S == Q) return false;
-  return S == S;
-  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: both side of overloaded operator are equivalent
+  if (S == S) return true;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of overloaded operator are equivalent
 }
 
 #define LT(x, y) (void)((x) < (y))
@@ -132,6 +147,7 @@
   LT(X+1, X + 1);
   COND(X < Y, X, X);
   EQUALS(Q, Q);
+  return 0;
 }
 
 int TestFalsePositive(int* A, int X, float F) {
@@ -149,7 +165,8 @@
 #define NOT_EAGAIN 3
   if (EAGAIN == 0 | EAGAIN == 0) return 0;
   if (NOT_EAGAIN == 0 | NOT_EAGAIN == 0) return 0;
-  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: both sides of operator are equivalent
+  return 0;
 }
 
 struct MyClass {
@@ -159,7 +176,7 @@
 void TemplateCheck() {
   static_assert(T::Value == U::Value, "should be identical");
   static_assert(T::Value == T::Value, "should be identical");
-  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: both side of overloaded operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: both sides of overloaded operator are equivalent
 }
 void TestTemplate() { TemplateCheck<MyClass, MyClass>(); }
 
@@ -218,6 +235,7 @@
   // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
   if (X + 1 == X - (~0U)) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+
   if (X + 1 == X - (~0ULL)) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
 
@@ -230,7 +248,8 @@
   return 0;
 }
 
-int TestBitwise(int X) {
+int TestBitwise(int X, int Y) {
+
   if ((X & 0xFF) == 0xF00) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
   if ((X & 0xFF) != 0xF00) return 1;
@@ -262,7 +281,14 @@
   return 0;
 }
 
-int TestRelational(int X, int Y) {
+int TestLogical(int X, int Y){
+#define CONFIG 0
+  if (CONFIG && X) return 1; // OK, consts from macros are considered intentional
+#undef CONFIG
+#define CONFIG 1
+  if (CONFIG || X) return 1;
+#undef CONFIG
+
   if (X == 10 && X != 10) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
   if (X == 10 && (X != 10)) return 1;
@@ -289,14 +315,25 @@
   // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always false
 
   if (X && !!X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: equivalent expression on both side of logical operator
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: equivalent expression on both sides of logical operator
   if (X != 0 && X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both side of logical operator
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both sides of logical operator
   if (X != 0 && !!X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both side of logical operator
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both sides of logical operator
   if (X == 0 && !X) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both side of logical operator
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both sides of logical operator
 
+  // Should not match.
+  if (X == 10 && Y == 10) return 1;
+  if (X != 10 && X != 12) return 1;
+  if (X == 10 || X == 12) return 1;
+  if (!X && !Y) return 1;
+  if (!X && Y) return 1;
+  if (!X && Y == 0) return 1;
+  if (X == 10 && Y != 10) return 1;
+}
+
+int TestRelational(int X, int Y) {
   if (X == 10 && X > 10) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
   if (X == 10 && X < 10) return 1;
@@ -323,18 +360,22 @@
   // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always true
   if (X != 7 || X != 14) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always true
+  if (X == 7 || X != 5) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+  if (X != 7 || X == 7) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always true
 
   if (X < 7 && X < 6) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
   if (X < 7 && X < 7) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent
   if (X < 7 && X < 8) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant
 
   if (X < 7 && X <= 5) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
   if (X < 7 && X <= 6) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: equivalent expression on both side of logical operator
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: equivalent expression on both sides of logical operator
   if (X < 7 && X <= 7) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant
   if (X < 7 && X <= 8) return 1;
@@ -345,14 +386,21 @@
   if (X <= 7 && X < 7) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
   if (X <= 7 && X < 8) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both side of logical operator
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both sides of logical operator
+
+  if (X >= 7 && X > 6) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both sides of logical operator
+  if (X >= 7 && X > 7) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+  if (X >= 7 && X > 8) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
 
   if (X <= 7 && X <= 5) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
   if (X <= 7 && X <= 6) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
   if (X <= 7 && X <= 7) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both sides of operator are equivalent
   if (X <= 7 && X <= 8) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: expression is redundant
 
@@ -379,14 +427,18 @@
   if (X < 7 || X < 6) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant
   if (X < 7 || X < 7) return 1;
-  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent
   if (X < 7 || X < 8) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
 
+  if (X > 7 || X > 6) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+  if (X > 7 || X > 7) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent
+  if (X > 7 || X > 8) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant
+
   // Should not match.
-  if (X == 10 && Y == 10) return 1;
-  if (X != 10 && X != 12) return 1;
-  if (X == 10 || X == 12) return 1;
   if (X < 10 || X > 12) return 1;
   if (X > 10 && X < 12) return 1;
   if (X < 10 || X >= 12) return 1;
@@ -401,12 +453,56 @@
   if (X > 10 && X != 11) return 1;
   if (X >= 10 && X <= 10) return 1;
   if (X <= 10 && X >= 10) return 1;
-  if (!X && !Y) return 1;
-  if (!X && Y) return 1;
-  if (!X && Y == 0) return 1;
-  if (X == 10 && Y != 10) return 1;
   if (X < 0 || X > 0) return 1;
+}
 
+int TestRelationalMacros(int X){
+#define SOME_MACRO 3
+#define SOME_MACRO_SAME_VALUE 3
+#define SOME_OTHER_MACRO 9
+  // Do not match for redundant relational macro expressions that can be
+  // considered intentional, and for some particular values, non redundant.
+
+  // Test cases for expressions with the same macro on both sides.
+  if (X < SOME_MACRO && X > SOME_MACRO) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: logical expression is always false
+  if (X < SOME_MACRO && X == SOME_MACRO) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: logical expression is always false
+  if (X < SOME_MACRO || X >= SOME_MACRO) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: logical expression is always true
+  if (X <= SOME_MACRO || X > SOME_MACRO) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: logical expression is always true
+  if (X != SOME_MACRO && X > SOME_MACRO) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+  if (X != SOME_MACRO && X < SOME_MACRO) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+
+  // Test cases for two different macros.
+  if (X < SOME_MACRO && X > SOME_OTHER_MACRO) return 1;
+  if (X != SOME_MACRO && X >= SOME_OTHER_MACRO) return 1;
+  if (X != SOME_MACRO && X != SOME_OTHER_MACRO) return 1;
+  if (X == SOME_MACRO || X == SOME_MACRO_SAME_VALUE) return 1;
+  if (X == SOME_MACRO || X <= SOME_MACRO_SAME_VALUE) return 1;
+  if (X == SOME_MACRO || X > SOME_MACRO_SAME_VALUE) return 1;
+  if (X < SOME_MACRO && X <= SOME_OTHER_MACRO) return 1;
+  if (X == SOME_MACRO && X > SOME_OTHER_MACRO) return 1;
+  if (X == SOME_MACRO && X != SOME_OTHER_MACRO) return 1;
+  if (X == SOME_MACRO && X != SOME_MACRO_SAME_VALUE) return 1;
+  if (X == SOME_MACRO_SAME_VALUE && X == SOME_MACRO ) return 1;
+
+  // Test cases for a macro and a const.
+  if (X < SOME_MACRO && X > 9) return 1;
+  if (X != SOME_MACRO && X >= 9) return 1;
+  if (X != SOME_MACRO && X != 9) return 1;
+  if (X == SOME_MACRO || X == 3) return 1;
+  if (X == SOME_MACRO || X <= 3) return 1;
+  if (X < SOME_MACRO && X <= 9) return 1;
+  if (X == SOME_MACRO && X != 9) return 1;
+  if (X == SOME_MACRO && X == 9) return 1;
+
+#undef SOME_OTHER_MACRO
+#undef SOME_MACRO_SAME_VALUE
+#undef SOME_MACRO
   return 0;
 }
 
@@ -419,7 +515,7 @@
 }
 
 enum Color { Red, Yellow, Green };
-int TestRelatiopnalWithEnum(enum Color C) {
+int TestRelationalWithEnum(enum Color C) {
   if (C == Red && C == Yellow) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: logical expression is always false
   if (C == Red && C != Red) return 1;
@@ -437,7 +533,7 @@
 template<class T>
 int TestRelationalTemplated(int X) {
   // This test causes a corner case with |isIntegerConstantExpr| where the type
-  // is dependant. There is an assert failing when evaluating
+  // is dependent. There is an assert failing when evaluating
   // sizeof(<incomplet-type>).
   if (sizeof(T) == 4 || sizeof(T) == 8) return 1;
 
@@ -450,10 +546,13 @@
 int TestWithSignedUnsigned(int X) {
   if (X + 1 == X + 1ULL) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+
   if ((X & 0xFFU) == 0xF00) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: logical expression is always false
+
   if ((X & 0xFF) == 0xF00U) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+
   if ((X & 0xFFU) == 0xF00U) return 1;
   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: logical expression is always false
 
@@ -476,6 +575,7 @@
   if (X <= X + 0xFFFFFFFFU) return 1;
   if (X <= X + 0x7FFFFFFF) return 1;
   if (X <= X + 0x80000000) return 1;
+
   if (X <= 0xFFFFFFFFU && X > 0) return 1;
   if (X <= 0xFFFFFFFFU && X > 0U) return 1;
 
diff --git a/test/clangd/authority-less-uri.test b/test/clangd/authority-less-uri.test
index 494f691..d33a1c2 100644
--- a/test/clangd/authority-less-uri.test
+++ b/test/clangd/authority-less-uri.test
@@ -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 @@
 {"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"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","insertText":"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":{"textDocument":{"uri":"file:///main.cpp"},"uri":"file:///main.cpp","position":{"line":3,"character":5}}}

 # Test params parsing in the presence of a 1.x-compatible client (inlined "uri")

 #

-# CHECK: {"jsonrpc":"2.0","id":2,"result":[

-# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"000035a","filterText":"a","insertText":"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"}

diff --git a/test/clangd/completion-items-kinds.test b/test/clangd/completion-items-kinds.test
index 8ad7cfa..83d2c7b 100644
--- a/test/clangd/completion-items-kinds.test
+++ b/test/clangd/completion-items-kinds.test
@@ -11,27 +11,26 @@
 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"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_cast","filterText":"static_cast","insertText":"static_cast<${1:type}>(${2:expression})","insertTextFormat":2}

+# CHECK-DAG: {"filterText":"static_cast","insertText":"static_cast<${1:type}>(${2:expression})","insertTextFormat":2,"kind":15,"label":"static_cast<type>(expression)","sortText":"000040static_cast"}

 #

 # Struct

-# CHECK-DAG: {"label":"Struct","kind":7,"sortText":"000050Struct","filterText":"Struct","insertText":"Struct","insertTextFormat":1}

+# CHECK-DAG: {"filterText":"Struct","insertText":"Struct","insertTextFormat":1,"kind":7,"label":"Struct","sortText":"000050Struct"}

 #

 # Macro

-# CHECK-DAG: {"label":"MACRO","kind":1,"sortText":"000070MACRO","filterText":"MACRO","insertText":"MACRO","insertTextFormat":1}

+# CHECK-DAG: {"filterText":"MACRO","insertText":"MACRO","insertTextFormat":1,"kind":1,"label":"MACRO","sortText":"000070MACRO"}

 #

 # Variable

-# CHECK-DAG: {"label":"variable","kind":6,"detail":"int","sortText":"000012variable","filterText":"variable","insertText":"variable","insertTextFormat":1}

+# CHECK-DAG: {"detail":"int","filterText":"variable","insertText":"variable","insertTextFormat":1,"kind":6,"label":"variable","sortText":"000012variable"}

 #

 # Function

-# CHECK-DAG: {"label":"function()","kind":3,"detail":"int","sortText":"000012function","filterText":"function","insertText":"function()","insertTextFormat":1}

+# CHECK-DAG: {"detail":"int","filterText":"function","insertText":"function()","insertTextFormat":1,"kind":3,"label":"function()","sortText":"000012function"}

 #

-#

-# CHECK: ]}

+# CHECK-SAME: ]}

 

 {"jsonrpc":"2.0","id":3,"method":"shutdown","params":null}

diff --git a/test/clangd/completion-priorities.test b/test/clangd/completion-priorities.test
index 35ecd61..45501dc 100644
--- a/test/clangd/completion-priorities.test
+++ b/test/clangd/completion-priorities.test
@@ -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.

 #

 

@@ -13,28 +13,74 @@
 Content-Length: 151

 

 {"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":12,"character":8}}}

-# 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,

+# CHECK-NEXT:  "jsonrpc": "2.0",

+# CHECK-NEXT:  "result": [

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "void",

+# CHECK-NEXT:      "filterText": "priv",

+# CHECK-NEXT:      "insertText": "priv",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "priv()",

+# CHECK-NEXT:      "sortText": "000034priv"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "void",

+# CHECK-NEXT:      "filterText": "prot",

+# CHECK-NEXT:      "insertText": "prot",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "prot()",

+# CHECK-NEXT:      "sortText": "000034prot"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "void",

+# CHECK-NEXT:      "filterText": "pub",

+# CHECK-NEXT:      "insertText": "pub",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "pub()",

+# CHECK-NEXT:      "sortText": "000034pub"

+# CHECK-NEXT:    },

 Content-Length: 151

 

 {"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"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,

+# CHECK-NEXT:  "jsonrpc": "2.0",

+# CHECK-NEXT:  "result": [

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "void",

+# CHECK-NEXT:      "filterText": "pub",

+# CHECK-NEXT:      "insertText": "pub",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "pub()",

+# CHECK-NEXT:      "sortText": "000034pub"

+# CHECK-NEXT:    },

+# priv() and prot() are at the end of the list

+# CHECK-NEXT:    {

+#      CHECK:      "detail": "void",

+#      CHECK:      "filterText": "priv",

+# CHECK-NEXT:      "insertText": "priv",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "priv()",

+# CHECK-NEXT:      "sortText": "200034priv"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "void",

+# CHECK-NEXT:      "filterText": "prot",

+# CHECK-NEXT:      "insertText": "prot",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "prot()",

+# CHECK-NEXT:      "sortText": "200034prot"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

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

diff --git a/test/clangd/completion-qualifiers.test b/test/clangd/completion-qualifiers.test
index 97cc002..982d7d9 100644
--- a/test/clangd/completion-qualifiers.test
+++ b/test/clangd/completion-qualifiers.test
@@ -1,4 +1,4 @@
-# RUN: clangd -run-synchronously < %s | FileCheck %s

+# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace %s

 Content-Length: 125

 

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

@@ -8,15 +8,42 @@
 Content-Length: 151

 

 {"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"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","insertText":"foo","insertTextFormat":1}

-# CHECK-DAG: {"label":"bar() const","kind":2,"detail":"int","sortText":"000037bar","filterText":"bar","insertText":"bar","insertTextFormat":1}

-# CHECK-DAG: {"label":"Foo::foo() const","kind":2,"detail":"int","sortText":"000037foo","filterText":"foo","insertText":"foo","insertTextFormat":1}

-# CHECK: ]}

+#      CHECK:  "id": 2,

+# CHECK-NEXT:  "jsonrpc": "2.0",

+# CHECK-NEXT:  "result": [

+# Eligible const functions are at the top of the list.

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int",

+# CHECK-NEXT:      "filterText": "bar",

+# CHECK-NEXT:      "insertText": "bar",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "bar() const",

+# CHECK-NEXT:      "sortText": "000037bar"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int",

+# CHECK-NEXT:      "filterText": "foo",

+# CHECK-NEXT:      "insertText": "foo",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "Foo::foo() const",

+# CHECK-NEXT:      "sortText": "000037foo"

+# CHECK-NEXT:    },

+# Ineligible non-const function is at the bottom of the list.

+# CHECK-NEXT:    {

+#      CHECK:      "detail": "int",

+#      CHECK:      "filterText": "foo",

+# CHECK-NEXT:      "insertText": "foo",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "foo() const",

+# CHECK-NEXT:      "sortText": "200035foo"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

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

diff --git a/test/clangd/completion-snippet.test b/test/clangd/completion-snippet.test
index fdf797d..ec1ac35 100644
--- a/test/clangd/completion-snippet.test
+++ b/test/clangd/completion-snippet.test
@@ -1,4 +1,4 @@
-# RUN: clangd -run-synchronously -enable-snippets < %s | FileCheck %s

+# RUN: clangd -pretty -run-synchronously -enable-snippets < %s | FileCheck -strict-whitespace %s

 # It is absolutely vital that this file has CRLF line endings.

 #

 Content-Length: 125

@@ -12,30 +12,73 @@
 Content-Length: 148

 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}

-# 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","insertText":"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":"ccc","insertText":"ccc","insertTextFormat":1}

-# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake &","sortText":"000079operator=","filterText":"operator=","insertText":"operator=(${1:const fake &})","insertTextFormat":2}

-# CHECK-DAG: {"label":"~fake()","kind":4,"detail":"void","sortText":"000079~fake","filterText":"~fake","insertText":"~fake()","insertTextFormat":1}

-# CHECK-DAG: {"label":"f(int i, const float f) const","kind":2,"detail":"int","sortText":"000035f","filterText":"f","insertText":"f(${1:int i}, ${2:const float f})","insertTextFormat":2}

-# CHECK: ]}

-Content-Length: 148

-

-{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"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","insertText":"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":"ccc","insertText":"ccc","insertTextFormat":1}

-# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake &","sortText":"000079operator=","filterText":"operator=","insertText":"operator=(${1:const fake &})","insertTextFormat":2}

-# CHECK-DAG: {"label":"~fake()","kind":4,"detail":"void","sortText":"000079~fake","filterText":"~fake","insertText":"~fake()","insertTextFormat":1}

-# CHECK-DAG: {"label":"f(int i, const float f) const","kind":2,"detail":"int","sortText":"000035f","filterText":"f","insertText":"f(${1:int i}, ${2:const float f})","insertTextFormat":2}

-# CHECK: ]}

+#      CHECK:  "id": 1,

+# CHECK-NEXT:  "jsonrpc": "2.0",

+# CHECK-NEXT:  "result": [

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int",

+# CHECK-NEXT:      "filterText": "a",

+# CHECK-NEXT:      "insertText": "a",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 5,

+# CHECK-NEXT:      "label": "a",

+# CHECK-NEXT:      "sortText": "000035a"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int",

+# CHECK-NEXT:      "filterText": "bb",

+# CHECK-NEXT:      "insertText": "bb",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 5,

+# CHECK-NEXT:      "label": "bb",

+# CHECK-NEXT:      "sortText": "000035bb"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int",

+# CHECK-NEXT:      "filterText": "ccc",

+# CHECK-NEXT:      "insertText": "ccc",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 5,

+# CHECK-NEXT:      "label": "ccc",

+# CHECK-NEXT:      "sortText": "000035ccc"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int",

+# CHECK-NEXT:      "filterText": "f",

+# CHECK-NEXT:      "insertText": "f(${1:int i}, ${2:const float f})",

+# CHECK-NEXT:      "insertTextFormat": 2,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "f(int i, const float f) const",

+# CHECK-NEXT:      "sortText": "000035f"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "filterText": "fake",

+# CHECK-NEXT:      "insertText": "fake::",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 7,

+# CHECK-NEXT:      "label": "fake::",

+# CHECK-NEXT:      "sortText": "000075fake"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "fake &",

+# CHECK-NEXT:      "filterText": "operator=",

+# CHECK-NEXT:      "insertText": "operator=(${1:const fake &})",

+# CHECK-NEXT:      "insertTextFormat": 2,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "operator=(const fake &)",

+# CHECK-NEXT:      "sortText": "000079operator="

+# CHECK-NEXT:    },

+# FIXME: Why do some buildbots show an extra operator==(fake&&) here?

+#      CHECK:    {

+#      CHECK:      "detail": "void",

+# CHECK-NEXT:      "filterText": "~fake",

+# CHECK-NEXT:      "insertText": "~fake()",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 4,

+# CHECK-NEXT:      "label": "~fake()",

+# CHECK-NEXT:      "sortText": "000079~fake"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 # Update the source file and check for completions again.

 Content-Length: 226

 

@@ -44,15 +87,21 @@
 Content-Length: 148

 

 {"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"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()","insertTextFormat":1}

-# CHECK: ]}

+#      CHECK:  "id": 3,

+# CHECK-NEXT:  "jsonrpc": "2.0",

+# CHECK-NEXT:  "result": [

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int (*)(int, int)",

+# CHECK-NEXT:      "filterText": "func",

+# CHECK-NEXT:      "insertText": "func()",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "func()",

+# CHECK-NEXT:      "sortText": "000034func"

+# CHECK-NEXT:    },

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

diff --git a/test/clangd/completion.test b/test/clangd/completion.test
index bc2b530..7ff0652 100644
--- a/test/clangd/completion.test
+++ b/test/clangd/completion.test
@@ -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,30 +12,142 @@
 Content-Length: 148

 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}

-# 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","insertText":"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":"ccc","insertText":"ccc","insertTextFormat":1}

-# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake &","sortText":"000079operator=","filterText":"operator=","insertText":"operator=","insertTextFormat":1}

-# CHECK-DAG: {"label":"~fake()","kind":4,"detail":"void","sortText":"000079~fake","filterText":"~fake","insertText":"~fake","insertTextFormat":1}

-# CHECK-DAG: {"label":"f(int i, const float f) const","kind":2,"detail":"int","sortText":"000035f","filterText":"f","insertText":"f","insertTextFormat":1}

-# CHECK: ]}

+#      CHECK:  "id": 1

+# CHECK-NEXT:  "jsonrpc": "2.0",

+# CHECK-NEXT:  "result": [

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int",

+# CHECK-NEXT:      "filterText": "a",

+# CHECK-NEXT:      "insertText": "a",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 5,

+# CHECK-NEXT:      "label": "a",

+# CHECK-NEXT:      "sortText": "000035a"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int",

+# CHECK-NEXT:      "filterText": "bb",

+# CHECK-NEXT:      "insertText": "bb",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 5,

+# CHECK-NEXT:      "label": "bb",

+# CHECK-NEXT:      "sortText": "000035bb"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int",

+# CHECK-NEXT:      "filterText": "ccc",

+# CHECK-NEXT:      "insertText": "ccc",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 5,

+# CHECK-NEXT:      "label": "ccc",

+# CHECK-NEXT:      "sortText": "000035ccc"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int",

+# CHECK-NEXT:      "filterText": "f",

+# CHECK-NEXT:      "insertText": "f",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "f(int i, const float f) const",

+# CHECK-NEXT:      "sortText": "000035f"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "filterText": "fake",

+# CHECK-NEXT:      "insertText": "fake",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 7,

+# CHECK-NEXT:      "label": "fake::",

+# CHECK-NEXT:      "sortText": "000075fake"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "fake &",

+# CHECK-NEXT:      "filterText": "operator=",

+# CHECK-NEXT:      "insertText": "operator=",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "operator=(const fake &)",

+# CHECK-NEXT:      "sortText": "000079operator="

+# CHECK-NEXT:    },

+# FIXME: Why do some buildbots show an extra operator==(fake&&) here?

+#      CHECK:    {

+#      CHECK:      "detail": "void",

+# CHECK-NEXT:      "filterText": "~fake",

+# CHECK-NEXT:      "insertText": "~fake",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 4,

+# CHECK-NEXT:      "label": "~fake()",

+# CHECK-NEXT:      "sortText": "000079~fake"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 148

 

 {"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"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","insertText":"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":"ccc","insertText":"ccc","insertTextFormat":1}

-# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake &","sortText":"000079operator=","filterText":"operator=","insertText":"operator=","insertTextFormat":1}

-# CHECK-DAG: {"label":"~fake()","kind":4,"detail":"void","sortText":"000079~fake","filterText":"~fake","insertText":"~fake","insertTextFormat":1}

-# CHECK-DAG: {"label":"f(int i, const float f) const","kind":2,"detail":"int","sortText":"000035f","filterText":"f","insertText":"f","insertTextFormat":1}

-# CHECK: ]}

+#      CHECK:  "id": 2

+# CHECK-NEXT:  "jsonrpc": "2.0",

+# CHECK-NEXT:  "result": [

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int",

+# CHECK-NEXT:      "filterText": "a",

+# CHECK-NEXT:      "insertText": "a",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 5,

+# CHECK-NEXT:      "label": "a",

+# CHECK-NEXT:      "sortText": "000035a"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int",

+# CHECK-NEXT:      "filterText": "bb",

+# CHECK-NEXT:      "insertText": "bb",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 5,

+# CHECK-NEXT:      "label": "bb",

+# CHECK-NEXT:      "sortText": "000035bb"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int",

+# CHECK-NEXT:      "filterText": "ccc",

+# CHECK-NEXT:      "insertText": "ccc",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 5,

+# CHECK-NEXT:      "label": "ccc",

+# CHECK-NEXT:      "sortText": "000035ccc"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int",

+# CHECK-NEXT:      "filterText": "f",

+# CHECK-NEXT:      "insertText": "f",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "f(int i, const float f) const",

+# CHECK-NEXT:      "sortText": "000035f"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "filterText": "fake",

+# CHECK-NEXT:      "insertText": "fake",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 7,

+# CHECK-NEXT:      "label": "fake::",

+# CHECK-NEXT:      "sortText": "000075fake"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "fake &",

+# CHECK-NEXT:      "filterText": "operator=",

+# CHECK-NEXT:      "insertText": "operator=",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "operator=(const fake &)",

+# CHECK-NEXT:      "sortText": "000079operator="

+# CHECK-NEXT:    },

+#      CHECK:    {

+#      CHECK:      "detail": "void",

+# CHECK-NEXT:      "filterText": "~fake",

+# CHECK-NEXT:      "insertText": "~fake",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 4,

+# CHECK-NEXT:      "label": "~fake()",

+# CHECK-NEXT:      "sortText": "000079~fake"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 # Update the source file and check for completions again.

 Content-Length: 226

 

@@ -44,15 +156,48 @@
 Content-Length: 148

 

 {"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"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","insertTextFormat":1}

-# CHECK: ]}

+#      CHECK:    "id": 3,

+# CHECK-NEXT:  "jsonrpc": "2.0",

+# CHECK-NEXT:  "result": [

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "int (*)(int, int)",

+# CHECK-NEXT:      "filterText": "func",

+# CHECK-NEXT:      "insertText": "func",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "func()",

+# CHECK-NEXT:      "sortText": "000034func"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "filterText": "fancy",

+# CHECK-NEXT:      "insertText": "fancy",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 7,

+# CHECK-NEXT:      "label": "fancy::",

+# CHECK-NEXT:      "sortText": "000075fancy"

+# CHECK-NEXT:    },

+# CHECK-NEXT:    {

+# CHECK-NEXT:      "detail": "fancy &",

+# CHECK-NEXT:      "filterText": "operator=",

+# CHECK-NEXT:      "insertText": "operator=",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 2,

+# CHECK-NEXT:      "label": "operator=(const fancy &)",

+# CHECK-NEXT:      "sortText": "000079operator="

+# CHECK-NEXT:    },

+#      CHECK:    {

+#      CHECK:      "detail": "void",

+# CHECK-NEXT:      "filterText": "~fancy",

+# CHECK-NEXT:      "insertText": "~fancy",

+# CHECK-NEXT:      "insertTextFormat": 1,

+# CHECK-NEXT:      "kind": 4,

+# CHECK-NEXT:      "label": "~fancy()",

+# CHECK-NEXT:      "sortText": "000079~fancy"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

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

diff --git a/test/clangd/definitions.test b/test/clangd/definitions.test
index c52e5de..a50f058 100644
--- a/test/clangd/definitions.test
+++ b/test/clangd/definitions.test
@@ -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 @@
 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":0}}}

 # Go to local variable

-# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 148

 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":1}}}

 # Go to local variable, end of token

-# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 214

 

 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":2},"contentChanges":[{"text":"struct Foo {\nint x;\n};\nint main() {\n  Foo bar = { x : 1 };\n}\n"}]}}

@@ -29,8 +59,23 @@
 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":14}}}

 # Go to field, GNU old-style field designator 

-# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 215

 

 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":3},"contentChanges":[{"text":"struct Foo {\nint x;\n};\nint main() {\n  Foo baz = { .x = 2 };\n}\n"}]}}

@@ -39,8 +84,23 @@
 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":15}}}

 # Go to field, field designator 

-# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 187

 

 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":4},"contentChanges":[{"text":"int main() {\n   main();\n   return 0;\n}"}]}}

@@ -49,8 +109,23 @@
 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":3}}}

 # Go to function declaration, function call 

-# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 208

 

 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"struct Foo {\n};\nint main() {\n   Foo bar;\n   return 0;\n}\n"}]}}

@@ -59,8 +134,23 @@
 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":3}}}

 # Go to struct declaration, new struct instance 

-# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 231

 

 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"namespace n1 {\nstruct Foo {\n};\n}\nint main() {\n   n1::Foo bar;\n   return 0;\n}\n"}]}}

@@ -69,8 +159,23 @@
 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":4}}}

 # Go to struct declaration, new struct instance, qualified name 

-# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 215

 

 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":6},"contentChanges":[{"text":"struct Foo {\n  int x;\n};\nint main() {\n   Foo bar;\n   bar.x;\n}\n"}]}}

@@ -79,8 +184,23 @@
 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}}

 # Go to field declaration, field reference 

-# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 220

 

 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n  void x();\n};\nint main() {\n   Foo bar;\n   bar.x();\n}\n"}]}}

@@ -89,8 +209,23 @@
 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}}

 # Go to method declaration, method call 

-# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 240

 

 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n};\ntypedef Foo TypedefFoo;\nint main() {\n   TypedefFoo bar;\n   return 0;\n}\n"}]}}

@@ -99,8 +234,23 @@
 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":10}}}

 # Go to typedef 

-# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 254

 

 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"template <typename MyTemplateParam>\nvoid foo() {\n MyTemplateParam a;\n}\nint main() {\n   return 0;\n}\n"}]}}

@@ -109,8 +259,9 @@
 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":13}}}

 # Go to template type parameter. Fails until clangIndex is modified to handle those.

-# no-CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 10}, "end": {"line": 0, "character": 34}}}]}

-

+#      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","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 @@
 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":6,"character":4}}}

 # Go to namespace, static method call 

-# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 265

 

 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace ns {\nstruct Foo {\n  int field;\n  Foo(int param) : field(param) {}\n};\n}\nint main() {\n   return 0;\n}\n"}]}}

@@ -128,9 +294,24 @@
 Content-Length: 149

 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":21}}}

-# Go to field, member initializer 

-# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 204

 

 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define MY_MACRO 0\nint main() {\n  return MY_MACRO;\n}\n"}]}}

@@ -139,8 +320,23 @@
 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"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", "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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 217

 

 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define FOO 1\nint a = FOO;\n#define FOO 2\nint b = FOO;\n#undef FOO\n"}]}}

@@ -149,29 +345,77 @@
 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":8}}}

 # Go to macro, re-defined later

-# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///{{([A-Za-z]:/)?}}main.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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 148

 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":8}}}

 # Go to macro, undefined later

-# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 148

 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":7}}}

 # Go to macro, being undefined

-# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "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:///{{([A-Z]:/)?}}main.cpp"

+# CHECK-NEXT:    }

+# CHECK-NEXT:  ]

 Content-Length: 156

 

 {"jsonrpc":"2.0","id":2,"method":"textDocument/definition","params":{"textDocument":{"uri":"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"}

diff --git a/test/clangd/diagnostics-preamble.test b/test/clangd/diagnostics-preamble.test
index c4adbe3..befb809 100644
--- a/test/clangd/diagnostics-preamble.test
+++ b/test/clangd/diagnostics-preamble.test
@@ -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: 206

 

 {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"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":"file:///main.cpp","diagnostics":[]}}

-

+#      CHECK:  "method": "textDocument/publishDiagnostics",

+# CHECK-NEXT:  "params": {

+# CHECK-NEXT:    "diagnostics": [],

+# CHECK-NEXT:    "uri": "file:///{{([A-Z]:/)?}}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"}

diff --git a/test/clangd/diagnostics.test b/test/clangd/diagnostics.test
index 41838c4..99c222d 100644
--- a/test/clangd/diagnostics.test
+++ b/test/clangd/diagnostics.test
@@ -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: 152

 

 {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///foo.c","languageId":"c","version":1,"text":"void main() {}"}}}

-#

-# CHECK: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///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"

+# 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"}

diff --git a/test/clangd/did-change-watch-files.test b/test/clangd/did-change-watch-files.test
index d29184d..717a6dd 100644
--- a/test/clangd/did-change-watch-files.test
+++ b/test/clangd/did-change-watch-files.test
@@ -5,18 +5,7 @@
 Content-Length: 143

 

 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootUri":"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","type":1},{"uri":"file:///path/to/file2.cpp","type":2},{"uri":"file:///path/to/file3.cpp","type":3}]}}

diff --git a/test/clangd/execute-command.test b/test/clangd/execute-command.test
new file mode 100644
index 0000000..833690d
--- /dev/null
+++ b/test/clangd/execute-command.test
@@ -0,0 +1,112 @@
+# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace %s

+# It is absolutely vital that this file has CRLF line endings.

+#

+Content-Length: 125

+

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

+#

+Content-Length: 180

+

+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///foo.c","languageId":"c","version":1,"text":"int main(int i, char **a) { if (i = 2) {}}"}}}

+#      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"

+# CHECK-NEXT:  }

+Content-Length: 72

+

+{"jsonrpc":"2.0","id":3,"method":"workspace/executeCommand","params":{}}

+# No command name

+Content-Length: 85

+

+{"jsonrpc":"2.0","id":4,"method":"workspace/executeCommand","params":{"command": {}}}

+# Invalid, non-scalar command name

+Content-Length: 345

+

+{"jsonrpc":"2.0","id":5,"method":"workspace/executeCommand","params":{"command":"clangd.applyFix","custom":"foo", "arguments":[{"changes":{"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":")"}]}}]}}

+Content-Length: 117

+

+{"jsonrpc":"2.0","id":6,"method":"workspace/executeCommand","params":{"command":"clangd.applyFix","arguments":"foo"}}

+# Arguments not a sequence.

+Content-Length: 93

+

+{"jsonrpc":"2.0","id":7,"method":"workspace/executeCommand","params":{"command":"mycommand"}}

+# Unknown command.

+Content-Length: 132

+

+{"jsonrpc":"2.0","id":8,"method":"workspace/executeCommand","params":{"command":"clangd.applyFix","custom":"foo", "arguments":[""]}}

+# ApplyFix argument not a mapping node.

+Content-Length: 345

+

+{"jsonrpc":"2.0","id":9,"method":"workspace/executeCommand","params":{"command":"clangd.applyFix","arguments":[{"custom":"foo", "changes":{"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":")"}]}}]}}

+# Custom field in WorkspaceEdit

+Content-Length: 132

+

+{"jsonrpc":"2.0","id":10,"method":"workspace/executeCommand","params":{"command":"clangd.applyFix","arguments":[{"changes":"foo"}]}}

+# changes in WorkspaceEdit with no mapping node

+Content-Length: 346

+

+{"jsonrpc":"2.0","id":11,"method":"workspace/executeCommand","params":{"command":"clangd.applyFix","arguments":[{"changes":{"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":")"}], "custom":"foo"}}]}}

+# Custom field in WorkspaceEditChange

+Content-Length: 150

+

+{"jsonrpc":"2.0","id":12,"method":"workspace/executeCommand","params":{"command":"clangd.applyFix","arguments":[{"changes":{"file:///foo.c":"bar"}}]}}

+# No sequence node for TextEdits

+Content-Length: 149

+

+{"jsonrpc":"2.0","id":13,"method":"workspace/executeCommand","params":{"command":"clangd.applyFix","arguments":[{"changes":{"file:///foo.c":[""]}}]}}

+# No mapping node for TextEdit

+Content-Length: 265

+

+{"jsonrpc":"2.0","id":14,"method":"workspace/executeCommand","params":{"command":"clangd.applyFix","arguments":[{"changes":{"file:///foo.c":[{"range":{"start":{"line":0,"character":32},"end":{"line":0,"character":32}},"newText":"("},{"range":"","newText":")"}]}}]}}

+# TextEdit not decoded

+Content-Length: 345

+

+{"jsonrpc":"2.0","id":9,"method":"workspace/executeCommand","params":{"arguments":[{"custom":"foo", "changes":{"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":")"}]}}],"command":"clangd.applyFix"}}

+# Command name after arguments

+Content-Length: 44

+

+{"jsonrpc":"2.0","id":3,"method":"shutdown"}

diff --git a/test/clangd/extra-flags.test b/test/clangd/extra-flags.test
index fe70379..8defbef 100644
--- a/test/clangd/extra-flags.test
+++ b/test/clangd/extra-flags.test
@@ -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: 205

 

 {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"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":"file:///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"

+# CHECK-NEXT:  }

 Content-Length: 175

 

 {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///foo.c","version":2},"contentChanges":[{"text":"int main() { int i; return i; }"}]}}

-# CHECK: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///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"

+# 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"}

diff --git a/test/clangd/fixits.test b/test/clangd/fixits.test
index ab5b71e..2a16c8c 100644
--- a/test/clangd/fixits.test
+++ b/test/clangd/fixits.test
@@ -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,25 +8,242 @@
 Content-Length: 180

 

 {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"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":"file:///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"

+# CHECK-NEXT:  }

 Content-Length: 746

 

- {"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"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": ["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": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character": 35}}, "newText": "=="}]]}]

-#

+{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"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:  "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": [

+# 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": [

+# 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":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"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":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"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": ["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": ["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": [

+# 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": [

+# 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":4,"method":"workspace/executeCommand","params":{"command":"clangd.applyFix","arguments":[{"changes":{"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":")"}]}}]}}

+#      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": [

+# 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"}

diff --git a/test/clangd/formatting.test b/test/clangd/formatting.test
index 09068fc..87181e2 100644
--- a/test/clangd/formatting.test
+++ b/test/clangd/formatting.test
@@ -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,"rootPath":"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":{"uri":"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":{"textDocument":{"uri":"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","version":5},"contentChanges":[{"text":"int foo ( int x ) {\n  x = x + 1;\n  return x;\n    }"}]}}

@@ -33,14 +74,68 @@
 Content-Length: 233

 

 {"jsonrpc":"2.0","id":2,"method":"textDocument/rangeFormatting","params":{"textDocument":{"uri":"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":{"textDocument":{"uri":"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","version":9},"contentChanges":[{"text":"int foo(int x) {\n  x = x + 1;\n  return x;\n}"}]}}

@@ -49,8 +144,9 @@
 Content-Length: 153

 

 {"jsonrpc":"2.0","id":4,"method":"textDocument/formatting","params":{"textDocument":{"uri":"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","version":5},"contentChanges":[{"text":"int foo ( int x ) {\n  x = x + 1;\n  return x;\n}"}]}}

@@ -59,13 +155,53 @@
 Content-Length: 204

 

 {"jsonrpc":"2.0","id":5,"method":"textDocument/onTypeFormatting","params":{"textDocument":{"uri":"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"}

diff --git a/test/clangd/initialize-params-invalid.test b/test/clangd/initialize-params-invalid.test
index 77520d6..9f9c69d 100644
--- a/test/clangd/initialize-params-invalid.test
+++ b/test/clangd/initialize-params-invalid.test
@@ -1,26 +1,47 @@
-# 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":"","rootUri":"file:///path/to/workspace","capabilities":{},"trace":"off"}}

-# CHECK: Content-Length: 535

-# 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: }}}

-#

+#      CHECK:  "id": 0,

+# CHECK-NEXT:  "jsonrpc": "2.0",

+# CHECK-NEXT:  "result": {

+# CHECK-NEXT:    "capabilities": {

+# 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:    }

+# 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"}

diff --git a/test/clangd/initialize-params.test b/test/clangd/initialize-params.test
index 5756232..60fc05e 100644
--- a/test/clangd/initialize-params.test
+++ b/test/clangd/initialize-params.test
@@ -1,26 +1,50 @@
-# 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,"rootUri":"file:///path/to/workspace","capabilities":{},"trace":"off"}}

-# CHECK: Content-Length: 535

-# 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: }}}

-#

+#      CHECK:  "id": 0,

+# CHECK-NEXT:  "jsonrpc": "2.0",

+# CHECK-NEXT:  "result": {

+# CHECK-NEXT:    "capabilities": {

+# 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:    }

+# 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"}

diff --git a/test/clangd/input-mirror.test b/test/clangd/input-mirror.test
index db54bcf..5b07cb5 100644
--- a/test/clangd/input-mirror.test
+++ b/test/clangd/input-mirror.test
@@ -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: 44

 

 {"jsonrpc":"2.0","id":3,"method":"shutdown"}

-# CHECK: {"jsonrpc":"2.0","id":3,"result":null}

 Content-Length: 33

 

 {"jsonrpc":"2.0":"method":"exit"}

diff --git a/test/clangd/protocol.test b/test/clangd/protocol.test
index 61fa90c..7837410 100644
--- a/test/clangd/protocol.test
+++ b/test/clangd/protocol.test
@@ -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 @@
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"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":{"uri":"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 @@
 {"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"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","insertText":"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 @@
 {"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"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","insertText":"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 @@
 {"jsonrpc":"2.0","id":5,"method":"textDocument/completion","params":{"textDocument":{"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","insertText":"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":{"textDocument":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}}

diff --git a/test/clangd/signature-help.test b/test/clangd/signature-help.test
index c28a309..d19422b 100644
--- a/test/clangd/signature-help.test
+++ b/test/clangd/signature-help.test
@@ -15,12 +15,12 @@
 Content-Length: 151

 

 {"jsonrpc":"2.0","id":1,"method":"textDocument/signatureHelp","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":8,"character":9}}}

-# CHECK: {"jsonrpc":"2.0","id":1,"result":{"activeSignature":0,"activeParameter":0,"signatures":[

+# CHECK: {"id":1,"jsonrpc":"2.0","result":{"activeParameter":0,"activeSignature":0,"signatures":[

 # 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: 151

 

 {"jsonrpc":"2.0","id":2,"method":"textDocument/signatureHelp","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":8,"character":9}}}

-# CHECK: {"jsonrpc":"2.0","id":2,"result":{"activeSignature":0,"activeParameter":0,"signatures":[

+# CHECK: {"id":2,"jsonrpc":"2.0","result":{"activeParameter":0,"activeSignature":0,"signatures":[

 # 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":{"textDocument":{"uri":"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"}

diff --git a/test/clangd/unsupported-method.test b/test/clangd/unsupported-method.test
index cccbb5c..0ce22bb 100644
--- a/test/clangd/unsupported-method.test
+++ b/test/clangd/unsupported-method.test
@@ -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: 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"}

diff --git a/tool-template/CMakeLists.txt b/tool-template/CMakeLists.txt
index 8223e4c..125523a 100644
--- a/tool-template/CMakeLists.txt
+++ b/tool-template/CMakeLists.txt
@@ -12,4 +12,5 @@
   clangBasic
   clangFrontend
   clangTooling
+  clangToolingRefactor
   )
diff --git a/tool-template/ToolTemplate.cpp b/tool-template/ToolTemplate.cpp
index f97575e..5345d77 100644
--- a/tool-template/ToolTemplate.cpp
+++ b/tool-template/ToolTemplate.cpp
@@ -40,7 +40,9 @@
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Lex/Lexer.h"
 #include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Execution.h"
 #include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/AtomicChange.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -54,19 +56,31 @@
 namespace {
 class ToolTemplateCallback : public MatchFinder::MatchCallback {
 public:
-  ToolTemplateCallback(std::map<std::string, Replacements> *Replace)
-      : Replace(Replace) {}
+  ToolTemplateCallback(ExecutionContext &Context) : Context(Context) {}
 
   void run(const MatchFinder::MatchResult &Result) override {
     // TODO: This routine will get called for each thing that the matchers
     // find.
     // At this point, you can examine the match, and do whatever you want,
     // including replacing the matched text with other text
-    (void)Replace; // This to prevent an "unused member variable" warning;
+    auto *D = Result.Nodes.getNodeAs<NamedDecl>("decl");
+    assert(D);
+    // Use AtomicChange to get a key.
+    if (D->getLocStart().isValid()) {
+      AtomicChange Change(*Result.SourceManager, D->getLocStart());
+      Context.reportResult(Change.getKey(), D->getQualifiedNameAsString());
+    }
+  }
+
+  void onStartOfTranslationUnit() override {
+    Context.reportResult("START", "Start of TU.");
+  }
+  void onEndOfTranslationUnit() override {
+    Context.reportResult("END", "End of TU.");
   }
 
 private:
-  std::map<std::string, Replacements> *Replace;
+  ExecutionContext &Context;
 };
 } // end anonymous namespace
 
@@ -76,15 +90,33 @@
 
 int main(int argc, const char **argv) {
   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
-  CommonOptionsParser OptionsParser(argc, argv, ToolTemplateCategory);
-  RefactoringTool Tool(OptionsParser.getCompilations(),
-                       OptionsParser.getSourcePathList());
+
+  auto Executor = clang::tooling::createExecutorFromCommandLineArgs(
+      argc, argv, ToolTemplateCategory);
+
+  if (!Executor) {
+    llvm::errs() << llvm::toString(Executor.takeError()) << "\n";
+    return 1;
+  }
+
   ast_matchers::MatchFinder Finder;
-  ToolTemplateCallback Callback(&Tool.getReplacements());
+  ToolTemplateCallback Callback(*Executor->get()->getExecutionContext());
 
   // TODO: Put your matchers here.
   // Use Finder.addMatcher(...) to define the patterns in the AST that you
   // want to match against. You are not limited to just one matcher!
+  //
+  // This is a sample matcher:
+  Finder.addMatcher(
+      namedDecl(cxxRecordDecl(), isExpansionInMainFile()).bind("decl"),
+      &Callback);
 
-  return Tool.run(newFrontendActionFactory(&Finder).get());
+  auto Err = Executor->get()->execute(newFrontendActionFactory(&Finder));
+  if (Err) {
+    llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+  }
+  Executor->get()->getToolResults()->forEachResult(
+      [](llvm::StringRef key, llvm::StringRef value) {
+        llvm::errs() << "----" << key.str() << "\n" << value.str() << "\n";
+      });
 }
diff --git a/unittests/clangd/CMakeLists.txt b/unittests/clangd/CMakeLists.txt
index f45bc71..5be935c 100644
--- a/unittests/clangd/CMakeLists.txt
+++ b/unittests/clangd/CMakeLists.txt
@@ -10,6 +10,7 @@
 
 add_extra_unittest(ClangdTests
   ClangdTests.cpp
+  JSONExprTests.cpp
   TraceTests.cpp
   )
 
diff --git a/unittests/clangd/JSONExprTests.cpp b/unittests/clangd/JSONExprTests.cpp
new file mode 100644
index 0000000..a95b4b0
--- /dev/null
+++ b/unittests/clangd/JSONExprTests.cpp
@@ -0,0 +1,116 @@
+//===-- 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.
+  };
+
+  std::string teststring = R"("\u0000\u0008\u000c\r\n\tS\"\\)"
+                           "\x7f\xCE\x94\"";
+
+  EXPECT_EQ(teststring, s(test));
+
+  EXPECT_EQ(R"({"object keys are\nescaped":true})",
+            s(obj{{"object keys are\nescaped", true}}));
+}
+
+TEST(JSONExprTests, PrettyPrinting) {
+  const char str[] = R"({
+  "empty_array": [],
+  "empty_object": {},
+  "full_array": [
+    1,
+    null
+  ],
+  "full_object": {
+    "nested_array": [
+      {
+        "property": "value"
+      }
+    ]
+  }
+})";
+
+  EXPECT_EQ(
+      str,
+      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