kadircet created this revision.
kadircet added a reviewer: hokein.
Herald added subscribers: arphaman, javed.absar.
Herald added a project: All.
kadircet requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.

We've been running this internally for months now, without any
stability or correctness concerns. It has ~40% speed up on incremental
diagnostics latencies (as preamble can get invalidated through code completion
etc.).


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D153882

Files:
  clang-tools-extra/clangd/ClangdLSPServer.cpp
  clang-tools-extra/clangd/ClangdLSPServer.h
  clang-tools-extra/clangd/ClangdServer.cpp
  clang-tools-extra/clangd/ClangdServer.h
  clang-tools-extra/clangd/Config.h
  clang-tools-extra/clangd/ConfigCompile.cpp
  clang-tools-extra/clangd/ConfigFragment.h
  clang-tools-extra/clangd/ConfigYAML.cpp
  clang-tools-extra/clangd/ParsedAST.cpp
  clang-tools-extra/clangd/ParsedAST.h
  clang-tools-extra/clangd/Preamble.cpp
  clang-tools-extra/clangd/Preamble.h
  clang-tools-extra/clangd/TUScheduler.cpp
  clang-tools-extra/clangd/tool/Check.cpp
  clang-tools-extra/clangd/unittests/ClangdTests.cpp
  clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
  clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
  clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
  clang-tools-extra/clangd/unittests/FeatureModulesTests.cpp
  clang-tools-extra/clangd/unittests/ModulesTests.cpp
  clang-tools-extra/clangd/unittests/ParsedASTTests.cpp
  clang-tools-extra/clangd/unittests/PreambleTests.cpp
  clang-tools-extra/clangd/unittests/SelectionTests.cpp
  clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
  clang-tools-extra/clangd/unittests/TestTU.cpp
  clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp

Index: clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp
+++ clang-tools-extra/clangd/unittests/TypeHierarchyTests.cpp
@@ -402,7 +402,7 @@
 
   // The compiler should produce a diagnostic for hitting the
   // template instantiation depth.
-  ASSERT_TRUE(!AST.getDiagnostics()->empty());
+  ASSERT_FALSE(AST.getDiagnostics().empty());
 
   // Make sure getTypeHierarchy() doesn't get into an infinite recursion.
   // The parent is reported as "S" because "S<0>" is an invalid instantiation.
Index: clang-tools-extra/clangd/unittests/TestTU.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TestTU.cpp
+++ clang-tools-extra/clangd/unittests/TestTU.cpp
@@ -132,8 +132,6 @@
     llvm::errs() << "Failed to build code:\n" << Code;
     std::abort();
   }
-  assert(AST->getDiagnostics() &&
-         "TestTU should always build an AST with a fresh Preamble");
   // Check for error diagnostics and report gtest failures (unless expected).
   // This guards against accidental syntax errors silently subverting tests.
   // error-ok is awfully primitive - using clang -verify would be nicer.
@@ -150,7 +148,7 @@
   }();
   if (!ErrorOk) {
     // We always build AST with a fresh preamble in TestTU.
-    for (const auto &D : *AST->getDiagnostics())
+    for (const auto &D : AST->getDiagnostics())
       if (D.Severity >= DiagnosticsEngine::Error) {
         llvm::errs()
             << "TestTU failed to build (suppress with /*error-ok*/): \n"
Index: clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
+++ clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
@@ -126,7 +126,7 @@
     class CaptureDiags : public ParsingCallbacks {
     public:
       void onMainAST(PathRef File, ParsedAST &AST, PublishFn Publish) override {
-        reportDiagnostics(File, *AST.getDiagnostics(), Publish);
+        reportDiagnostics(File, AST.getDiagnostics(), Publish);
       }
 
       void onFailedAST(PathRef File, llvm::StringRef Version,
@@ -141,9 +141,8 @@
         if (!D)
           return;
         Publish([&]() {
-          const_cast<
-              llvm::unique_function<void(PathRef, std::vector<Diag>)> &> (*D)(
-              File, std::move(Diags));
+          const_cast<llvm::unique_function<void(PathRef, std::vector<Diag>)> &>(
+              *D)(File, Diags);
         });
       }
     };
@@ -230,20 +229,24 @@
     Notification Ready;
     TUScheduler S(CDB, optsForTest(), captureDiags());
     auto Path = testPath("foo.cpp");
-    updateWithDiags(S, Path, "", WantDiagnostics::Yes,
+    // Semicolons here and in the following inputs are significant. They ensure
+    // preamble stays the same across runs. Otherwise we might get multiple
+    // diagnostics callbacks, once with the stale preamble and another with the
+    // fresh preamble.
+    updateWithDiags(S, Path, ";", WantDiagnostics::Yes,
                     [&](std::vector<Diag>) { Ready.wait(); });
-    updateWithDiags(S, Path, "request diags", WantDiagnostics::Yes,
+    updateWithDiags(S, Path, ";request diags", WantDiagnostics::Yes,
                     [&](std::vector<Diag>) { ++CallbackCount; });
-    updateWithDiags(S, Path, "auto (clobbered)", WantDiagnostics::Auto,
+    updateWithDiags(S, Path, ";auto (clobbered)", WantDiagnostics::Auto,
                     [&](std::vector<Diag>) {
                       ADD_FAILURE()
                           << "auto should have been cancelled by auto";
                     });
-    updateWithDiags(S, Path, "request no diags", WantDiagnostics::No,
+    updateWithDiags(S, Path, ";request no diags", WantDiagnostics::No,
                     [&](std::vector<Diag>) {
                       ADD_FAILURE() << "no diags should not be called back";
                     });
-    updateWithDiags(S, Path, "auto (produces)", WantDiagnostics::Auto,
+    updateWithDiags(S, Path, ";auto (produces)", WantDiagnostics::Auto,
                     [&](std::vector<Diag>) { ++CallbackCount; });
     Ready.notify();
 
@@ -833,7 +836,9 @@
   CDB.ExtraClangFlags.push_back("-DSOMETHING");
   ASSERT_TRUE(DoUpdate(SourceContents));
   ASSERT_FALSE(DoUpdate(SourceContents));
-  ASSERT_EQ(S.fileStats().lookup(Source).ASTBuilds, 4u);
+  // This causes 2 AST builds always. We first build an AST with the stale
+  // preamble, and build a second AST once the fresh preamble is ready.
+  ASSERT_EQ(S.fileStats().lookup(Source).ASTBuilds, 5u);
   ASSERT_EQ(S.fileStats().lookup(Source).PreambleBuilds, 3u);
 }
 
@@ -1274,10 +1279,6 @@
         DiagVersions;
   };
 
-  Config Cfg;
-  Cfg.Diagnostics.AllowStalePreamble = true;
-  WithContextValue WithCfg(Config::Key, std::move(Cfg));
-
   DiagCollector Collector;
   Notification UnblockPreamble;
   auto DiagCallbacks = std::make_unique<BlockPreambleThread>(
Index: clang-tools-extra/clangd/unittests/SelectionTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/SelectionTests.cpp
+++ clang-tools-extra/clangd/unittests/SelectionTests.cpp
@@ -690,7 +690,7 @@
   auto TU = TestTU::withCode(Test.code());
   TU.AdditionalFiles["Expand.inc"] = "MACRO\n";
   auto AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(), ::testing::IsEmpty());
+  EXPECT_THAT(AST.getDiagnostics(), ::testing::IsEmpty());
   auto T = makeSelectionTree(Case, AST);
 
   EXPECT_EQ("BreakStmt", T.commonAncestor()->kind());
Index: clang-tools-extra/clangd/unittests/PreambleTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/PreambleTests.cpp
+++ clang-tools-extra/clangd/unittests/PreambleTests.cpp
@@ -595,15 +595,6 @@
   }
 }
 
-TEST(PreamblePatch, DropsDiagnostics) {
-  llvm::StringLiteral Code = "#define FOO\nx;/* error-ok */";
-  // First check that this code generates diagnostics.
-  EXPECT_THAT(*TestTU::withCode(Code).build().getDiagnostics(),
-              testing::Not(testing::IsEmpty()));
-  // Ensure they are dropeed when a patched preamble is used.
-  EXPECT_FALSE(createPatchedAST("", Code)->getDiagnostics());
-}
-
 TEST(PreamblePatch, MacroLoc) {
   llvm::StringLiteral Baseline = "\n#define MACRO 12\nint num = MACRO;";
   llvm::StringLiteral Modified = " \n#define MACRO 12\nint num = MACRO;";
@@ -638,16 +629,12 @@
 }
 
 TEST(PreamblePatch, DiagnosticsFromMainASTAreInRightPlace) {
-  Config Cfg;
-  Cfg.Diagnostics.AllowStalePreamble = true;
-  WithContextValue WithCfg(Config::Key, std::move(Cfg));
-
   {
     Annotations Code("#define FOO");
     // Check with removals from preamble.
     Annotations NewCode("[[x]];/* error-ok */");
     auto AST = createPatchedAST(Code.code(), NewCode.code());
-    EXPECT_THAT(*AST->getDiagnostics(),
+    EXPECT_THAT(AST->getDiagnostics(),
                 ElementsAre(Diag(NewCode.range(), "missing_type_specifier")));
   }
   {
@@ -658,14 +645,13 @@
 #define BAR
 [[x]];/* error-ok */)");
     auto AST = createPatchedAST(Code.code(), NewCode.code());
-    EXPECT_THAT(*AST->getDiagnostics(),
+    EXPECT_THAT(AST->getDiagnostics(),
                 ElementsAre(Diag(NewCode.range(), "missing_type_specifier")));
   }
 }
 
 TEST(PreamblePatch, DiagnosticsToPreamble) {
   Config Cfg;
-  Cfg.Diagnostics.AllowStalePreamble = true;
   Cfg.Diagnostics.UnusedIncludes = Config::IncludesPolicy::Strict;
   Cfg.Diagnostics.MissingIncludes = Config::IncludesPolicy::Strict;
   WithContextValue WithCfg(Config::Key, std::move(Cfg));
@@ -680,7 +666,7 @@
     // Check with removals from preamble.
     Annotations NewCode(R"([[#  include "foo.h"]])");
     auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
-    EXPECT_THAT(*AST->getDiagnostics(),
+    EXPECT_THAT(AST->getDiagnostics(),
                 ElementsAre(Diag(NewCode.range(), "unused-includes")));
   }
   {
@@ -694,7 +680,7 @@
 $foo[[#include "foo.h"]])");
     auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
     EXPECT_THAT(
-        *AST->getDiagnostics(),
+        AST->getDiagnostics(),
         UnorderedElementsAre(Diag(NewCode.range("bar"), "unused-includes"),
                              Diag(NewCode.range("foo"), "unused-includes")));
   }
@@ -709,17 +695,13 @@
 #define $foo2[[FOO]] 2)");
     auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
     EXPECT_THAT(
-        *AST->getDiagnostics(),
+        AST->getDiagnostics(),
         ElementsAre(AllOf(Diag(NewCode.range("foo2"), "-Wmacro-redefined"),
                           withNote(Diag(NewCode.range("foo1"))))));
   }
 }
 
 TEST(PreamblePatch, TranslatesDiagnosticsInPreamble) {
-  Config Cfg;
-  Cfg.Diagnostics.AllowStalePreamble = true;
-  WithContextValue WithCfg(Config::Key, std::move(Cfg));
-
   {
     // Check with additions to preamble.
     Annotations Code("#include [[<foo>]]");
@@ -727,7 +709,7 @@
 #define BAR
 #include [[<foo>]])");
     auto AST = createPatchedAST(Code.code(), NewCode.code());
-    EXPECT_THAT(*AST->getDiagnostics(),
+    EXPECT_THAT(AST->getDiagnostics(),
                 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
   }
   {
@@ -737,7 +719,7 @@
 #include [[<foo>]])");
     Annotations NewCode("#include [[<foo>]]");
     auto AST = createPatchedAST(Code.code(), NewCode.code());
-    EXPECT_THAT(*AST->getDiagnostics(),
+    EXPECT_THAT(AST->getDiagnostics(),
                 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
   }
   {
@@ -745,7 +727,7 @@
     Annotations Code("#include [[<foo>]]");
     Annotations NewCode("#define BAR\n#define BAZ\n");
     auto AST = createPatchedAST(Code.code(), NewCode.code());
-    EXPECT_THAT(*AST->getDiagnostics(), IsEmpty());
+    EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
   }
   {
     // Picks closest line in case of multiple alternatives.
@@ -756,7 +738,7 @@
 #define BAR
 #include <foo>)");
     auto AST = createPatchedAST(Code.code(), NewCode.code());
-    EXPECT_THAT(*AST->getDiagnostics(),
+    EXPECT_THAT(AST->getDiagnostics(),
                 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
   }
   {
@@ -764,7 +746,7 @@
     Annotations Code("#include [[<foo>]]");
     Annotations NewCode(" # include <foo>");
     auto AST = createPatchedAST(Code.code(), NewCode.code());
-    EXPECT_THAT(*AST->getDiagnostics(), IsEmpty());
+    EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
   }
   {
     // Multiple lines.
@@ -775,7 +757,7 @@
     Annotations NewCode(R"(#include [[<fo\
 o>]])");
     auto AST = createPatchedAST(Code.code(), NewCode.code());
-    EXPECT_THAT(*AST->getDiagnostics(),
+    EXPECT_THAT(AST->getDiagnostics(),
                 ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
   }
   {
@@ -788,7 +770,7 @@
     Annotations NewCode(R"(#include <fo\
 x>)");
     auto AST = createPatchedAST(Code.code(), NewCode.code());
-    EXPECT_THAT(*AST->getDiagnostics(), IsEmpty());
+    EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
   }
   {
     // Preserves notes.
@@ -802,7 +784,7 @@
 #define $main[[BAR]] 2)");
     auto AST = createPatchedAST(Code.code(), NewCode.code());
     EXPECT_THAT(
-        *AST->getDiagnostics(),
+        AST->getDiagnostics(),
         ElementsAre(AllOf(Diag(NewCode.range("main"), "-Wmacro-redefined"),
                           withNote(Diag(NewCode.range("note"))))));
   }
@@ -815,7 +797,7 @@
 #define $main[[BAR]] 2)");
     auto AST = createPatchedAST(Code.code(), NewCode.code());
     EXPECT_THAT(
-        *AST->getDiagnostics(),
+        AST->getDiagnostics(),
         ElementsAre(AllOf(Diag(NewCode.range("main"), "-Wmacro-redefined"),
                           Field(&Diag::Notes, IsEmpty()))));
   }
@@ -828,7 +810,7 @@
 #define BAZ 0
 #define BAR 1)");
     auto AST = createPatchedAST(Code.code(), NewCode.code());
-    EXPECT_THAT(*AST->getDiagnostics(), IsEmpty());
+    EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
   }
   {
     Annotations Code(R"(
@@ -841,7 +823,7 @@
     // We shouldn't emit any diagnotiscs (and shouldn't crash).
     Annotations NewCode("");
     auto AST = createPatchedAST(Code.code(), NewCode.code());
-    EXPECT_THAT(*AST->getDiagnostics(), IsEmpty());
+    EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
   }
   {
     Annotations Code(R"(
@@ -858,7 +840,7 @@
     )");
     auto AST = createPatchedAST(Code.code(), NewCode.code());
     EXPECT_THAT(
-        *AST->getDiagnostics(),
+        AST->getDiagnostics(),
         ElementsAre(Diag(NewCode.range(), "pp_unterminated_conditional")));
   }
 }
@@ -868,10 +850,6 @@
 }
 
 TEST(PreamblePatch, MacroAndMarkHandling) {
-  Config Cfg;
-  Cfg.Diagnostics.AllowStalePreamble = true;
-  WithContextValue WithCfg(Config::Key, std::move(Cfg));
-
   {
     Annotations Code(R"cpp(
 #ifndef FOO
Index: clang-tools-extra/clangd/unittests/ParsedASTTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ParsedASTTests.cpp
+++ clang-tools-extra/clangd/unittests/ParsedASTTests.cpp
@@ -385,7 +385,6 @@
   auto PatchedAST = ParsedAST::build(testPath("foo.cpp"), Inputs, std::move(CI),
                                      {}, EmptyPreamble);
   ASSERT_TRUE(PatchedAST);
-  ASSERT_FALSE(PatchedAST->getDiagnostics());
 
   // Ensure source location information is correct, including resolved paths.
   EXPECT_THAT(PatchedAST->getIncludeStructure().MainFileIncludes,
@@ -533,7 +532,7 @@
     ;
   )cpp";
   auto AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(),
+  EXPECT_THAT(AST.getDiagnostics(),
               ElementsAre(diag("recursively when building a preamble")));
   EXPECT_FALSE(mainIsGuarded(AST));
 
@@ -542,7 +541,7 @@
     #include "self.h" // error-ok
   )cpp";
   AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(), ElementsAre(diag("nested too deeply")));
+  EXPECT_THAT(AST.getDiagnostics(), ElementsAre(diag("nested too deeply")));
   EXPECT_FALSE(mainIsGuarded(AST));
 
   TU.Code = R"cpp(
@@ -551,7 +550,7 @@
     ;
   )cpp";
   AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+  EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
   EXPECT_TRUE(mainIsGuarded(AST));
 
   TU.Code = R"cpp(
@@ -560,7 +559,7 @@
     #include "self.h"
   )cpp";
   AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+  EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
   EXPECT_TRUE(mainIsGuarded(AST));
 
   TU.Code = R"cpp(
@@ -569,7 +568,7 @@
     #include "self.h"
   )cpp";
   AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+  EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
   EXPECT_TRUE(mainIsGuarded(AST));
 
   TU.Code = R"cpp(
@@ -580,7 +579,7 @@
     ;
   )cpp";
   AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(),
+  EXPECT_THAT(AST.getDiagnostics(),
               ElementsAre(diag("recursively when building a preamble")));
   EXPECT_TRUE(mainIsGuarded(AST));
 
@@ -592,7 +591,7 @@
     #endif
   )cpp";
   AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+  EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
   EXPECT_TRUE(mainIsGuarded(AST));
 
   // Guarded too late...
@@ -604,7 +603,7 @@
     #endif
   )cpp";
   AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(),
+  EXPECT_THAT(AST.getDiagnostics(),
               ElementsAre(diag("recursively when building a preamble")));
   EXPECT_FALSE(mainIsGuarded(AST));
 
@@ -616,7 +615,7 @@
     #endif
   )cpp";
   AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(),
+  EXPECT_THAT(AST.getDiagnostics(),
               ElementsAre(diag("recursively when building a preamble")));
   EXPECT_FALSE(mainIsGuarded(AST));
 
@@ -628,7 +627,7 @@
     #endif
   )cpp";
   AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+  EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
   EXPECT_FALSE(mainIsGuarded(AST));
 
   TU.Code = R"cpp(
@@ -637,7 +636,7 @@
     ;
   )cpp";
   AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(),
+  EXPECT_THAT(AST.getDiagnostics(),
               ElementsAre(diag("recursively when building a preamble")));
   EXPECT_TRUE(mainIsGuarded(AST));
 
@@ -647,7 +646,7 @@
     #pragma once
   )cpp";
   AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(),
+  EXPECT_THAT(AST.getDiagnostics(),
               ElementsAre(diag("recursively when building a preamble")));
   EXPECT_TRUE(mainIsGuarded(AST));
 }
@@ -682,13 +681,13 @@
   TU.Code = guard(Interface);
   TU.AdditionalFiles = {{"impl.h", Implementation}};
   auto AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+  EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
   EXPECT_TRUE(mainIsGuarded(AST));
   // Slightly harder: the `#pragma once` is part of the preamble, and we
   // need to transfer it to the main file's HeaderFileInfo.
   TU.Code = once(Interface);
   AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+  EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
   EXPECT_TRUE(mainIsGuarded(AST));
 
   // Editing the implementation file, which is not include guarded.
@@ -698,14 +697,14 @@
   AST = TU.build();
   // The diagnostic is unfortunate in this case, but correct per our model.
   // Ultimately the include is skipped and the code is parsed correctly though.
-  EXPECT_THAT(*AST.getDiagnostics(),
+  EXPECT_THAT(AST.getDiagnostics(),
               ElementsAre(diag("in included file: main file cannot be included "
                                "recursively when building a preamble")));
   EXPECT_FALSE(mainIsGuarded(AST));
   // Interface is pragma once guarded, same thing.
   TU.AdditionalFiles = {{"iface.h", once(Interface)}};
   AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(),
+  EXPECT_THAT(AST.getDiagnostics(),
               ElementsAre(diag("in included file: main file cannot be included "
                                "recursively when building a preamble")));
   EXPECT_FALSE(mainIsGuarded(AST));
Index: clang-tools-extra/clangd/unittests/ModulesTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ModulesTests.cpp
+++ clang-tools-extra/clangd/unittests/ModulesTests.cpp
@@ -60,7 +60,7 @@
       header "module.h"
     }
 )modulemap";
-  EXPECT_TRUE(TU.build().getDiagnostics()->empty());
+  EXPECT_TRUE(TU.build().getDiagnostics().empty());
 }
 
 TEST(Modules, Diagnostic) {
Index: clang-tools-extra/clangd/unittests/FeatureModulesTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/FeatureModulesTests.cpp
+++ clang-tools-extra/clangd/unittests/FeatureModulesTests.cpp
@@ -76,13 +76,13 @@
 
   {
     auto AST = TU.build();
-    EXPECT_THAT(*AST.getDiagnostics(), testing::Not(testing::IsEmpty()));
+    EXPECT_THAT(AST.getDiagnostics(), testing::Not(testing::IsEmpty()));
   }
 
   TU.FeatureModules = &FMS;
   {
     auto AST = TU.build();
-    EXPECT_THAT(*AST.getDiagnostics(), testing::IsEmpty());
+    EXPECT_THAT(AST.getDiagnostics(), testing::IsEmpty());
   }
 }
 
@@ -111,13 +111,13 @@
 
   {
     auto AST = TU.build();
-    EXPECT_THAT(*AST.getDiagnostics(), testing::Not(testing::IsEmpty()));
+    EXPECT_THAT(AST.getDiagnostics(), testing::Not(testing::IsEmpty()));
   }
 
   TU.FeatureModules = &FMS;
   {
     auto AST = TU.build();
-    EXPECT_THAT(*AST.getDiagnostics(), testing::IsEmpty());
+    EXPECT_THAT(AST.getDiagnostics(), testing::IsEmpty());
   }
 }
 
Index: clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
+++ clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
@@ -175,7 +175,7 @@
   )cpp");
   auto TU = TestTU::withCode(Test.code());
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ElementsAre(
           // Make sure the whole token is highlighted.
           AllOf(Diag(Test.range("range"),
@@ -224,7 +224,7 @@
   )cpp");
   auto TU = TestTU::withCode(Test.code());
   TU.ExtraArgs = {"-Wswitch"};
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               ElementsAre(Diag(Test.range(),
                                "enumeration value 'X' not handled in switch")));
 }
@@ -232,14 +232,14 @@
 TEST(DiagnosticsTest, FlagsMatter) {
   Annotations Test("[[void]] main() {} // error-ok");
   auto TU = TestTU::withCode(Test.code());
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               ElementsAre(AllOf(Diag(Test.range(), "'main' must return 'int'"),
                                 withFix(Fix(Test.range(), "int",
                                             "change 'void' to 'int'")))));
   // Same code built as C gets different diagnostics.
   TU.Filename = "Plain.c";
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ElementsAre(AllOf(
           Diag(Test.range(), "return type of 'main' is not 'int'"),
           withFix(Fix(Test.range(), "int", "change return type to 'int'")))));
@@ -251,7 +251,7 @@
   )cpp");
 
   auto TU = TestTU::withCode(Test.code());
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               ElementsAre(::testing::AllOf(
                   Diag(Test.range(), "'not-found.h' file not found"),
                   diagSource(Diag::Clang), diagName("pp_file_not_found"))));
@@ -268,7 +268,7 @@
                                        "hicpp-uppercase-literal-suffix");
   // Verify that we filter out the duplicated diagnostic message.
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
           Diag(Test.range(),
                "floating point literal has suffix 'f', which is not uppercase"),
@@ -288,7 +288,7 @@
   // The check doesn't handle template instantiations which ends up emitting
   // duplicated messages, verify that we deduplicate them.
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
           Diag(Test.range(),
                "floating point literal has suffix 'f', which is not uppercase"),
@@ -324,7 +324,7 @@
                                        "misc-no-recursion");
   TU.ExtraArgs.push_back("-Wno-unsequenced");
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ifTidyChecks(UnorderedElementsAre(
           AllOf(Diag(Test.range("deprecated"),
                      "inclusion of deprecated C++ header 'assert.h'; consider "
@@ -367,7 +367,7 @@
   TU.AdditionalFiles["a.h"] = TU.AdditionalFiles["b.h"] = "";
   TU.ClangTidyProvider = addTidyChecks("llvm-include-order");
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ifTidyChecks(Contains(
           AllOf(Diag(Test.range(), "#includes are not sorted properly"),
                 diagSource(Diag::ClangTidy), diagName("llvm-include-order")))));
@@ -385,7 +385,7 @@
   TestTU TU = TestTU::withCode(Main.code());
   TU.HeaderCode = Header.code().str();
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ElementsAre(AllOf(
           Diag(Main.range(), "in template: base specifier must name a class"),
           withNote(Diag(Header.range(), "error occurred here"),
@@ -411,7 +411,7 @@
     }
     }
   )cpp";
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               UnorderedElementsAre(
                   Diag(Main.range(),
                        "in template: "
@@ -437,7 +437,7 @@
     }
   )cpp";
   TU.ParseOpts.PreambleParseForwardingFunctions = true;
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               UnorderedElementsAre(
                   Diag(Main.range(),
                        "in template: "
@@ -465,7 +465,7 @@
   TestTU TU = TestTU::withCode(Main.code());
   TU.ClangTidyProvider = addTidyChecks("modernize-loop-convert");
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
           Diag(Main.range(), "use range-based for loop instead"),
           diagSource(Diag::ClangTidy), diagName("modernize-loop-convert")))));
@@ -481,14 +481,14 @@
   )cpp");
   auto TU = TestTU::withCode(Main.code());
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ElementsAre(Diag(Main.range(), "use of undeclared identifier 'unknown'"),
                   Diag(Main.range("ret"),
                        "void function 'x' should not return a value")));
   Config Cfg;
   Cfg.Diagnostics.Suppress.insert("return-type");
   WithContextValue WithCfg(Config::Key, std::move(Cfg));
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               ElementsAre(Diag(Main.range(),
                                "use of undeclared identifier 'unknown'")));
 }
@@ -505,7 +505,7 @@
   Config Cfg;
   Cfg.Diagnostics.Suppress.insert("init_conversion_failed");
   WithContextValue WithCfg(Config::Key, std::move(Cfg));
-  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
 }
 
 TEST(DiagnosticTest, ClangTidySuppressionComment) {
@@ -532,7 +532,7 @@
   TestTU TU = TestTU::withCode(Main.code());
   TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
           Diag(Main.range(), "result of integer division used in a floating "
                              "point context; possible loss of precision"),
@@ -565,7 +565,7 @@
 
   // Expect to see warning from user macros, but not system macros.
   // This matches clang-tidy --system-headers=0 (the default).
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               ifTidyChecks(
                   UnorderedElementsAre(Diag(Main.range("inline"), BadDivision),
                                        Diag(Main.range("user"), BadDivision))));
@@ -582,7 +582,7 @@
   TU.ClangTidyProvider =
       addTidyChecks("bugprone-integer-division", "bugprone-integer-division");
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
           Diag(Main.range(), "result of integer division used in a floating "
                              "point context; possible loss of precision"),
@@ -615,54 +615,54 @@
             diagSeverity(DiagnosticsEngine::Warning));
 
   // Check the -Wunused warning isn't initially on.
-  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
 
   // We enable warnings based on clang-tidy extra args, if the matching
   // clang-diagnostic- is there.
   TU.ClangTidyProvider =
       addClangArgs({"-Wunused"}, "clang-diagnostic-unused-function");
-  EXPECT_THAT(*TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
+  EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
 
   // clang-diagnostic-* is acceptable
   TU.ClangTidyProvider = addClangArgs({"-Wunused"}, "clang-diagnostic-*");
-  EXPECT_THAT(*TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
+  EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
   // And plain * (may turn on other checks too).
   TU.ClangTidyProvider = addClangArgs({"-Wunused"}, "*");
-  EXPECT_THAT(*TU.build().getDiagnostics(), Contains(UnusedFooWarning));
+  EXPECT_THAT(TU.build().getDiagnostics(), Contains(UnusedFooWarning));
   // And we can explicitly exclude a category too.
   TU.ClangTidyProvider = addClangArgs(
       {"-Wunused"}, "clang-diagnostic-*,-clang-diagnostic-unused-function");
-  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
 
   // Without the exact check specified, the warnings are not enabled.
   TU.ClangTidyProvider = addClangArgs({"-Wunused"}, "clang-diagnostic-unused");
-  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
 
   // We don't respect other args.
   TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Dfoo=bar"},
                                       "clang-diagnostic-unused-function");
-  EXPECT_THAT(*TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning))
+  EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning))
       << "Not unused function 'bar'!";
 
   // -Werror doesn't apply to warnings enabled by clang-tidy extra args.
   TU.ExtraArgs = {"-Werror"};
   TU.ClangTidyProvider =
       addClangArgs({"-Wunused"}, "clang-diagnostic-unused-function");
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
 
   // But clang-tidy extra args won't *downgrade* errors to warnings either.
   TU.ExtraArgs = {"-Wunused", "-Werror"};
   TU.ClangTidyProvider =
       addClangArgs({"-Wunused"}, "clang-diagnostic-unused-function");
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               ElementsAre(diagSeverity(DiagnosticsEngine::Error)));
 
   // FIXME: we're erroneously downgrading the whole group, this should be Error.
   TU.ExtraArgs = {"-Wunused-function", "-Werror"};
   TU.ClangTidyProvider =
       addClangArgs({"-Wunused"}, "clang-diagnostic-unused-label");
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
 
   // This looks silly, but it's the typical result if a warning is enabled by a
@@ -670,26 +670,26 @@
   TU.ExtraArgs = {};
   TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Wno-unused"},
                                       "clang-diagnostic-unused-function");
-  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
 
   // Overriding only works in the proper order.
   TU.ClangTidyProvider =
       addClangArgs({"-Wunused"}, {"clang-diagnostic-unused-function"});
-  EXPECT_THAT(*TU.build().getDiagnostics(), SizeIs(1));
+  EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
 
   // More specific vs less-specific: match clang behavior
   TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Wno-unused-function"},
                                       {"clang-diagnostic-unused-function"});
-  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
   TU.ClangTidyProvider = addClangArgs({"-Wunused-function", "-Wno-unused"},
                                       {"clang-diagnostic-unused-function"});
-  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
 
   // We do allow clang-tidy config to disable warnings from the compile
   // command. It's unclear this is ideal, but it's hard to avoid.
   TU.ExtraArgs = {"-Wunused"};
   TU.ClangTidyProvider = addClangArgs({"-Wno-unused"}, {});
-  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
 }
 
 TEST(DiagnosticTest, LongFixMessages) {
@@ -703,7 +703,7 @@
   )cpp");
   TestTU TU = TestTU::withCode(Source.code());
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ElementsAre(withFix(Fix(
           Source.range(),
           "somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier",
@@ -719,7 +719,7 @@
     }
   )cpp");
   TU.Code = std::string(Source.code());
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               ElementsAre(withFix(
                   Fix(Source.range(), "ident", "change 'ide\\…' to 'ident'"))));
 }
@@ -729,7 +729,7 @@
   TestTU TU = TestTU::withCode(Source.code());
   TU.ExtraArgs = {"-Wnewline-eof"};
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ElementsAre(withFix((Fix(Source.range(), "\n", "insert '\\n'")))));
 }
 
@@ -743,7 +743,7 @@
   TestTU TU = TestTU::withCode(Main.code());
   TU.ClangTidyProvider =
       addTidyChecks("bugprone-integer-division", "bugprone-integer-division");
-  EXPECT_THAT(*TU.build().getDiagnostics(), UnorderedElementsAre());
+  EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
 }
 
 TEST(DiagnosticTest, ClangTidyNoLiteralDataInMacroToken) {
@@ -758,7 +758,7 @@
   )cpp");
   TestTU TU = TestTU::withCode(Main.code());
   TU.ClangTidyProvider = addTidyChecks("bugprone-bad-signal-to-kill-thread");
-  EXPECT_THAT(*TU.build().getDiagnostics(), UnorderedElementsAre()); // no-crash
+  EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre()); // no-crash
 }
 
 TEST(DiagnosticTest, ElseAfterReturnRange) {
@@ -774,7 +774,7 @@
   )cpp");
   TestTU TU = TestTU::withCode(Main.code());
   TU.ClangTidyProvider = addTidyChecks("llvm-else-after-return");
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               ifTidyChecks(ElementsAre(
                   Diag(Main.range(), "do not use 'else' after 'return'"))));
 }
@@ -826,7 +826,7 @@
   ExpectedDFix.Edits.push_back(
       TextEdit{Main.range("MathHeader"), "#include <math.h>\n\n"});
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ifTidyChecks(UnorderedElementsAre(
           AllOf(Diag(Main.range("A"), "'A' should be initialized in a member "
                                       "initializer of the constructor"),
@@ -855,7 +855,7 @@
     #endif
     )cpp");
   EXPECT_THAT(
-      *TestTU::withCode(Test.code()).build().getDiagnostics(),
+      TestTU::withCode(Test.code()).build().getDiagnostics(),
       ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
 }
 
@@ -865,7 +865,7 @@
   )cpp");
   TU.ExtraArgs.push_back("-Xclang");
   TU.ExtraArgs.push_back("-verify");
-  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
 }
 
 TEST(DiagnosticTest, IgnoreBEFilelistOptions) {
@@ -876,7 +876,7 @@
         "-fxray-always-instrument=null", "-fxray-never-instrument=null",
         "-fxray-attr-list=null"}) {
     TU.ExtraArgs.push_back(DisableOption);
-    EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+    EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
     TU.ExtraArgs.pop_back();
   }
 }
@@ -888,7 +888,7 @@
     int symbol;
   )cpp");
   TU.Filename = "foo.h";
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               ElementsAre(diagName("pp_including_mainfile_in_preamble")));
   EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
 }
@@ -901,7 +901,7 @@
     int symbol;
   )cpp");
   TU.Filename = "foo.h";
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               Not(Contains(diagName("pp_including_mainfile_in_preamble"))));
   EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
 }
@@ -918,7 +918,7 @@
   )cpp");
   TU.Filename = "foo.h";
   // FIXME: should be no errors here.
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               ElementsAre(diagName("pp_including_mainfile_in_preamble")));
   EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
 }
@@ -930,7 +930,7 @@
 #pragma clang assume_nonnull end
 )cpp");
   auto AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+  EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
   const auto *X = cast<FunctionDecl>(findDecl(AST, "foo")).getParamDecl(0);
   ASSERT_TRUE(X->getOriginalType()->getNullability() ==
               NullabilityKind::NonNull);
@@ -947,7 +947,7 @@
 )cpp");
   TU.AdditionalFiles = {{"foo.h", std::string(Header.code())}};
   auto AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(),
+  EXPECT_THAT(AST.getDiagnostics(),
               ElementsAre(diagName("pp_eof_in_assume_nonnull")));
   const auto *X = cast<FunctionDecl>(findDecl(AST, "foo")).getParamDecl(0);
   ASSERT_TRUE(X->getOriginalType()->getNullability() ==
@@ -968,7 +968,7 @@
       return $bar[[TEN]];
     }
     )cpp");
-  EXPECT_THAT(*TestTU::withCode(Test.code()).build().getDiagnostics(),
+  EXPECT_THAT(TestTU::withCode(Test.code()).build().getDiagnostics(),
               ElementsAre(Diag(Test.range("foo"),
                                "cannot initialize return object of type "
                                "'int *' with an rvalue of type 'int'"),
@@ -984,7 +984,7 @@
     [[Define]](main) // error-ok
   )cpp");
   auto TU = TestTU::withCode(Test.code());
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               ElementsAre(AllOf(Diag(Test.range(), "'main' must return 'int'"),
                                 Not(withFix(_)))));
 }
@@ -993,11 +993,11 @@
   Annotations Test("#pragma clang [[system_header]]\n");
   auto TU = TestTU::withCode(Test.code());
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ElementsAre(AllOf(
           Diag(Test.range(), "#pragma system_header ignored in main file"))));
   TU.Filename = "TestTU.h";
-  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
 }
 
 TEST(ClangdTest, MSAsm) {
@@ -1006,7 +1006,7 @@
   llvm::InitializeAllTargetInfos(); // As in ClangdMain
   auto TU = TestTU::withCode("void fn() { __asm { cmp cl,64 } }");
   TU.ExtraArgs = {"-fms-extensions"};
-  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
 }
 
 TEST(DiagnosticsTest, ToLSP) {
@@ -1177,7 +1177,7 @@
     Annotations Main(Case.second);
     TU.Code = Main.code().str() + "\n // error-ok";
     EXPECT_THAT(
-        *TU.build().getDiagnostics(),
+        TU.build().getDiagnostics(),
         ElementsAre(AllOf(diagName(Case.first), hasRange(Main.range()),
                           withFix(Fix(Range{}, "#include \"x.h\"\n",
                                       "Include \"x.h\" for symbol ns::X")))))
@@ -1208,7 +1208,7 @@
   for (auto Case : Tests) {
     Annotations Main(Case.second);
     TU.Code = Main.code().str() + "\n // error-ok";
-    EXPECT_THAT(*TU.build().getDiagnostics(),
+    EXPECT_THAT(TU.build().getDiagnostics(),
                 Contains(AllOf(diagName(Case.first), hasRange(Main.range()),
                                withFix(Fix(Range{}, "#include \"x.h\"\n",
                                            "Include \"x.h\" for symbol X")))))
@@ -1240,7 +1240,7 @@
       MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
   TU.ExternalIndex = Index.get();
 
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               UnorderedElementsAre(
                   Diag(Test.range("base"), "base class has incomplete type"),
                   Diag(Test.range("access"),
@@ -1274,7 +1274,7 @@
   TU.ExternalIndex = Index.get();
 
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       UnorderedElementsAre(
           AllOf(Diag(Test.range("unqualified1"), "unknown type name 'X'"),
                 diagName("unknown_typename"),
@@ -1325,7 +1325,7 @@
   // FIXME: -fms-compatibility (which is default on windows) breaks the
   // ns::X cases when the namespace is undeclared. Find out why!
   TU.ExtraArgs = {"-fno-ms-compatibility"};
-  EXPECT_THAT(*TU.build().getDiagnostics(), Each(withFix(_)));
+  EXPECT_THAT(TU.build().getDiagnostics(), Each(withFix(_)));
 }
 
 TEST(IncludeFixerTest, MultipleMatchedSymbols) {
@@ -1344,7 +1344,7 @@
        SymbolWithHeader{"na::nb::X", "unittest:///b.h", "\"b.h\""}});
   TU.ExternalIndex = Index.get();
 
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               UnorderedElementsAre(AllOf(
                   Diag(Test.range("unqualified"), "unknown type name 'X'"),
                   diagName("unknown_typename"),
@@ -1365,7 +1365,7 @@
   TU.ExternalIndex = Index.get();
 
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       UnorderedElementsAre(Diag(Test.range(), "no member named 'xy' in 'X'")));
 }
 
@@ -1400,7 +1400,7 @@
   TU.ExternalIndex = Index.get();
 
   auto Parsed = TU.build();
-  for (const auto &D : *Parsed.getDiagnostics()) {
+  for (const auto &D : Parsed.getDiagnostics()) {
     if (D.Fixes.size() != 1) {
       ADD_FAILURE() << "D.Fixes.size() != 1";
       continue;
@@ -1424,7 +1424,7 @@
   TU.ExternalIndex = Index.get();
 
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       UnorderedElementsAre(
           AllOf(Diag(Test.range(), "no member named 'scope' in namespace 'ns'"),
                 diagName("no_member"),
@@ -1452,7 +1452,7 @@
   TU.ExternalIndex = Index.get();
 
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       UnorderedElementsAre(
           AllOf(Diag(Test.range("q1"), "use of undeclared identifier 'clangd'; "
                                        "did you mean 'clang'?"),
@@ -1492,7 +1492,7 @@
       SymbolWithHeader{"a::X", "unittest:///x.h", "\"x.h\""});
   TU.ExternalIndex = Index.get();
 
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               UnorderedElementsAre(AllOf(
                   Diag(Test.range(), "no type named 'X' in namespace 'a'"),
                   diagName("typename_nested_not_found"),
@@ -1518,7 +1518,7 @@
   TU.ExternalIndex = Index.get();
 
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ElementsAre(Diag(Test.range(), "use of undeclared identifier 'a'")));
 }
 
@@ -1535,7 +1535,7 @@
   TU.ExternalIndex = Index.get();
 
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ElementsAre(AllOf(
           Diag(Test.range(), "call to undeclared library function 'printf' "
                              "with type 'int (const char *, ...)'; ISO C99 "
@@ -1546,7 +1546,7 @@
 
   TU.ExtraArgs = {"-xc", "-std=c89"};
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ElementsAre(AllOf(
           Diag(Test.range(), "implicitly declaring library function 'printf' "
                              "with type 'int (const char *, ...)'"),
@@ -1572,7 +1572,7 @@
   TU.ExternalIndex = Index.get();
 
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ElementsAre(AllOf(
           Diag(Test.range(),
                "call to undeclared function 'foo'; ISO C99 and later do not "
@@ -1581,7 +1581,7 @@
                       "Include \"foo.h\" for symbol foo")))));
 
   TU.ExtraArgs = {"-std=c89", "-Wall"};
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               ElementsAre(AllOf(
                   Diag(Test.range(), "implicit declaration of function 'foo'"),
                   withFix(Fix(Range{}, "#include \"foo.h\"\n",
@@ -1595,7 +1595,7 @@
   Annotations Header("[[no_type_spec]]; // error-ok");
   TestTU TU = TestTU::withCode(Main.code());
   TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               UnorderedElementsAre(AllOf(
                   Diag(Main.range(), "in included file: a type specifier is "
                                      "required for all declarations"),
@@ -1609,7 +1609,7 @@
   TestTU TU = TestTU::withCode(Main.code());
   TU.AdditionalFiles = {{"a.h", "#include \"b.h\""},
                         {"b.h", "no_type_spec; // error-ok"}};
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               UnorderedElementsAre(Diag(Main.range(),
                                         "in included file: a type specifier is "
                                         "required for all declarations")));
@@ -1623,7 +1623,7 @@
   TestTU TU = TestTU::withCode(Main.code());
   TU.AdditionalFiles = {{"a.h", "no_type_spec; // error-ok"},
                         {"b.h", "no_type_spec; // error-ok"}};
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               UnorderedElementsAre(
                   Diag(Main.range("a"), "in included file: a type specifier is "
                                         "required for all declarations"),
@@ -1640,7 +1640,7 @@
   TU.AdditionalFiles = {
       {"a.h", "#include \"b.h\"\n"},
       {"b.h", "#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               Contains(Diag(Main.range(), "in included file: a type specifier "
                                           "is required for all declarations")));
 }
@@ -1657,7 +1657,7 @@
       {"a.h", "#include \"c.h\"\n"},
       {"b.h", "#include \"c.h\"\n"},
       {"c.h", "#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               UnorderedElementsAre(Diag(Main.range(),
                                         "in included file: a type specifier is "
                                         "required for all declarations")));
@@ -1686,7 +1686,7 @@
       no_type_spec_9;
       no_type_spec_10;
       #endif)cpp"}};
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               UnorderedElementsAre(Diag(Main.range(),
                                         "in included file: a type specifier is "
                                         "required for all declarations")));
@@ -1701,7 +1701,7 @@
     int x = 5/0;)cpp");
   TestTU TU = TestTU::withCode(Main.code());
   TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               UnorderedElementsAre(AllOf(
                   Diag(Main.range(), "in included file: a type specifier is "
                                      "required for all declarations"),
@@ -1719,7 +1719,7 @@
   TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
   // promote warnings to errors.
   TU.ExtraArgs = {"-Werror", "-Wunused"};
-  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
 }
 
 TEST(DiagsInHeaders, FromNonWrittenSources) {
@@ -1732,7 +1732,7 @@
   TestTU TU = TestTU::withCode(Main.code());
   TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
   TU.ExtraArgs = {"-DFOO=NOOO"};
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               UnorderedElementsAre(AllOf(
                   Diag(Main.range(),
                        "in included file: use of undeclared identifier 'NOOO'"),
@@ -1750,7 +1750,7 @@
   X;)cpp");
   TestTU TU = TestTU::withCode(Main.code());
   TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               UnorderedElementsAre(
                   Diag(Main.range(), "in included file: use of undeclared "
                                      "identifier 'foo'; did you mean 'fo'?")));
@@ -1767,7 +1767,7 @@
   X(foo);)cpp");
   TestTU TU = TestTU::withCode(Main.code());
   TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               UnorderedElementsAre(
                   Diag(Main.range(), "in included file: use of undeclared "
                                      "identifier 'foo'; did you mean 'fo'?")));
@@ -1779,7 +1779,7 @@
   TU.AdditionalFiles = {{"a.h", "void main();"}};
   // The diagnostic "main must return int" is from the header, we don't attempt
   // to render it in the main file as there is no written location there.
-  EXPECT_THAT(*TU.build().getDiagnostics(), UnorderedElementsAre());
+  EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
 }
 
 TEST(ToLSPDiag, RangeIsInMain) {
@@ -1827,7 +1827,7 @@
   TU.FeatureModules = &FMS;
 
   auto AST = TU.build();
-  EXPECT_THAT(*AST.getDiagnostics(),
+  EXPECT_THAT(AST.getDiagnostics(),
               testing::Contains(Diag(Code.range(), KDiagMsg.str())));
 }
 
@@ -1838,14 +1838,14 @@
   {
     TU.Code = "#define FOO\n  void bar();\n";
     auto AST = TU.build();
-    EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+    EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
   }
   {
     Annotations Code("#define FOO[[]]");
     TU.Code = Code.code().str();
     auto AST = TU.build();
     EXPECT_THAT(
-        *AST.getDiagnostics(),
+        AST.getDiagnostics(),
         testing::Contains(Diag(Code.range(), "no newline at end of file")));
   }
 }
@@ -1860,7 +1860,7 @@
     $deprecated[[bar]]();
   })cpp");
   TU.Code = Test.code().str();
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               UnorderedElementsAre(
                   AllOf(Diag(Test.range("unused"), "unused variable 'x'"),
                         withTag(DiagnosticTag::Unnecessary)),
@@ -1873,7 +1873,7 @@
   TU.Code = Test.code();
   TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       ifTidyChecks(UnorderedElementsAre(
           AllOf(Diag(Test.range("typedef"), "use 'using' instead of 'typedef'"),
                 withTag(DiagnosticTag::Deprecated)))));
@@ -1917,22 +1917,22 @@
   WithContextValue WithCfg(Config::Key, std::move(Cfg));
   auto AST = TU.build();
   EXPECT_THAT(
-      *AST.getDiagnostics(),
+      AST.getDiagnostics(),
       Contains(AllOf(
           Diag(Test.range("diag"),
                "included header unused.h is not used directly"),
           withTag(DiagnosticTag::Unnecessary), diagSource(Diag::Clangd),
           withFix(Fix(Test.range("fix"), "", "remove #include directive")))));
-  auto &Diag = AST.getDiagnostics()->front();
+  auto &Diag = AST.getDiagnostics().front();
   EXPECT_EQ(getDiagnosticDocURI(Diag.Source, Diag.ID, Diag.Name),
             std::string("https://clangd.llvm.org/guides/include-cleaner";));
   Cfg.Diagnostics.SuppressAll = true;
   WithContextValue SuppressAllWithCfg(Config::Key, std::move(Cfg));
-  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
   Cfg.Diagnostics.SuppressAll = false;
   Cfg.Diagnostics.Suppress = {"unused-includes"};
   WithContextValue SuppressFilterWithCfg(Config::Key, std::move(Cfg));
-  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
 }
 
 TEST(DiagnosticsTest, FixItFromHeader) {
@@ -1949,7 +1949,7 @@
   TU.Code = Source.code().str();
   TU.HeaderCode = Header.str();
   EXPECT_THAT(
-      *TU.build().getDiagnostics(),
+      TU.build().getDiagnostics(),
       UnorderedElementsAre(AllOf(
           Diag(Source.range("diag"), "no matching function for call to 'foo'"),
           withFix(Fix(Source.range("fix"), "&",
@@ -1963,12 +1963,12 @@
   auto TU = TestTU::withCode("static inline void foo(void) {}");
   TU.ExtraArgs.push_back("-Wunused-function");
   TU.Filename = "test.c";
-  EXPECT_THAT(*TU.build().getDiagnostics(),
+  EXPECT_THAT(TU.build().getDiagnostics(),
               ElementsAre(withID(diag::warn_unused_function)));
   // Sema should recognize a *.h file open in clangd as a header.
   // https://github.com/clangd/vscode-clangd/issues/360
   TU.Filename = "test.h";
-  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
 }
 
 } // namespace
Index: clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
+++ clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
@@ -290,33 +290,6 @@
   EXPECT_THAT(Results[0].Style.FullyQualifiedNamespaces,
               ElementsAre(val("foo"), val("bar")));
 }
-
-TEST(ParseYAML, DiagnosticsMode) {
-  CapturedDiags Diags;
-  {
-    Annotations YAML(R"yaml(
-Diagnostics:
-  AllowStalePreamble: Yes)yaml");
-    auto Results =
-        Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());
-    ASSERT_THAT(Diags.Diagnostics, IsEmpty());
-    ASSERT_EQ(Results.size(), 1u);
-    EXPECT_THAT(Results[0].Diagnostics.AllowStalePreamble,
-                llvm::ValueIs(val(true)));
-  }
-
-  {
-    Annotations YAML(R"yaml(
-Diagnostics:
-  AllowStalePreamble: No)yaml");
-    auto Results =
-        Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());
-    ASSERT_THAT(Diags.Diagnostics, IsEmpty());
-    ASSERT_EQ(Results.size(), 1u);
-    EXPECT_THAT(Results[0].Diagnostics.AllowStalePreamble,
-                llvm::ValueIs(val(false)));
-  }
-}
 } // namespace
 } // namespace config
 } // namespace clangd
Index: clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
+++ clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
@@ -540,21 +540,6 @@
   EXPECT_TRUE(compileAndApply());
   EXPECT_THAT(Conf.Style.FullyQualifiedNamespaces, ElementsAre("foo", "bar"));
 }
-
-TEST_F(ConfigCompileTests, AllowDiagsFromStalePreamble) {
-  Frag = {};
-  EXPECT_TRUE(compileAndApply());
-  // Off by default.
-  EXPECT_EQ(Conf.Diagnostics.AllowStalePreamble, false);
-
-  Frag.Diagnostics.AllowStalePreamble.emplace(true);
-  EXPECT_TRUE(compileAndApply());
-  EXPECT_EQ(Conf.Diagnostics.AllowStalePreamble, true);
-
-  Frag.Diagnostics.AllowStalePreamble.emplace(false);
-  EXPECT_TRUE(compileAndApply());
-  EXPECT_EQ(Conf.Diagnostics.AllowStalePreamble, false);
-}
 } // namespace
 } // namespace config
 } // namespace clangd
Index: clang-tools-extra/clangd/unittests/ClangdTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ClangdTests.cpp
+++ clang-tools-extra/clangd/unittests/ClangdTests.cpp
@@ -25,6 +25,7 @@
 #include "clang/Sema/CodeCompleteConsumer.h"
 #include "clang/Tooling/ArgumentsAdjusters.h"
 #include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
@@ -75,7 +76,7 @@
 class ErrorCheckingCallbacks : public ClangdServer::Callbacks {
 public:
   void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
-                          std::vector<Diag> Diagnostics) override {
+                          llvm::ArrayRef<Diag> Diagnostics) override {
     bool HadError = diagsContainErrors(Diagnostics);
     std::lock_guard<std::mutex> Lock(Mutex);
     HadErrorInLastDiags = HadError;
@@ -96,7 +97,7 @@
 class MultipleErrorCheckingCallbacks : public ClangdServer::Callbacks {
 public:
   void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
-                          std::vector<Diag> Diagnostics) override {
+                          llvm::ArrayRef<Diag> Diagnostics) override {
     bool HadError = diagsContainErrors(Diagnostics);
 
     std::lock_guard<std::mutex> Lock(Mutex);
@@ -305,7 +306,7 @@
   } FS;
   struct Callbacks : public ClangdServer::Callbacks {
     void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
-                            std::vector<Diag> Diagnostics) override {
+                            llvm::ArrayRef<Diag> Diagnostics) override {
       Got = Context::current().getExisting(Secret);
     }
     int Got;
@@ -371,7 +372,7 @@
   MockFS FS;
   struct Callbacks : public ClangdServer::Callbacks {
     void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
-                            std::vector<Diag> Diagnostics) override {
+                            llvm::ArrayRef<Diag> Diagnostics) override {
       Got = Version.str();
     }
     std::string Got = "";
@@ -688,7 +689,7 @@
     TestDiagConsumer() : Stats(FilesCount, FileStat()) {}
 
     void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
-                            std::vector<Diag> Diagnostics) override {
+                            llvm::ArrayRef<Diag> Diagnostics) override {
       StringRef FileIndexStr = llvm::sys::path::stem(File);
       ASSERT_TRUE(FileIndexStr.consume_front("Foo"));
 
@@ -867,7 +868,7 @@
         : StartSecondReparse(std::move(StartSecondReparse)) {}
 
     void onDiagnosticsReady(PathRef, llvm::StringRef,
-                            std::vector<Diag>) override {
+                            llvm::ArrayRef<Diag>) override {
       ++Count;
       std::unique_lock<std::mutex> Lock(Mutex, std::try_to_lock_t());
       ASSERT_TRUE(Lock.owns_lock())
@@ -1204,7 +1205,7 @@
   struct DiagsCheckingCallback : public ClangdServer::Callbacks {
   public:
     void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
-                            std::vector<Diag> Diagnostics) override {
+                            llvm::ArrayRef<Diag> Diagnostics) override {
       std::lock_guard<std::mutex> Lock(Mutex);
       HadDiagsInLastCallback = !Diagnostics.empty();
     }
Index: clang-tools-extra/clangd/tool/Check.cpp
===================================================================
--- clang-tools-extra/clangd/tool/Check.cpp
+++ clang-tools-extra/clangd/tool/Check.cpp
@@ -251,8 +251,8 @@
       elog("Failed to build AST");
       return false;
     }
-    ErrCount += showErrors(llvm::ArrayRef(*AST->getDiagnostics())
-                               .drop_front(Preamble->Diags.size()));
+    ErrCount +=
+        showErrors(AST->getDiagnostics().drop_front(Preamble->Diags.size()));
 
     if (Opts.BuildDynamicSymbolIndex) {
       log("Indexing AST...");
Index: clang-tools-extra/clangd/TUScheduler.cpp
===================================================================
--- clang-tools-extra/clangd/TUScheduler.cpp
+++ clang-tools-extra/clangd/TUScheduler.cpp
@@ -944,8 +944,7 @@
     // rebuild. Newly built preamble cannot emit diagnostics before this call
     // finishes (ast callbacks are called from astpeer thread), hence we
     // gurantee eventual consistency.
-    if (LatestPreamble && WantDiags != WantDiagnostics::No &&
-        Config::current().Diagnostics.AllowStalePreamble)
+    if (LatestPreamble && WantDiags != WantDiagnostics::No)
       generateDiagnostics(std::move(Invocation), std::move(Inputs),
                           std::move(CompilerInvocationDiags));
 
@@ -1101,7 +1100,7 @@
   llvm::StringLiteral TaskName = "Build AST";
   // Store preamble and build diagnostics with new preamble if requested.
   auto Task = [this, Preamble = std::move(Preamble), CI = std::move(CI),
-               PI = std::move(PI), CIDiags = std::move(CIDiags),
+               CIDiags = std::move(CIDiags),
                WantDiags = std::move(WantDiags)]() mutable {
     // Update the preamble inside ASTWorker queue to ensure atomicity. As a task
     // running inside ASTWorker assumes internals won't change until it
@@ -1127,22 +1126,17 @@
     // We only need to build the AST if diagnostics were requested.
     if (WantDiags == WantDiagnostics::No)
       return;
-    // The file may have been edited since we started building this preamble.
-    // If diagnostics need a fresh preamble, we must use the old version that
-    // matches the preamble. We make forward progress as updatePreamble()
-    // receives increasing versions, and this is the only place we emit
-    // diagnostics.
-    // If diagnostics can use a stale preamble, we use the current contents of
-    // the file instead. This provides more up-to-date diagnostics, and avoids
-    // diagnostics going backwards (we may have already emitted staler-preamble
-    // diagnostics for the new version). We still have eventual consistency: at
-    // some point updatePreamble() will catch up to the current file.
-    if (Config::current().Diagnostics.AllowStalePreamble)
-      PI = FileInputs;
+    // Since the file may have been edited since we started building this
+    // preamble, we use the current contents of the file instead. This provides
+    // more up-to-date diagnostics, and avoids diagnostics going backwards (we
+    // may have already emitted staler-preamble diagnostics for the new
+    // version).
+    // We still have eventual consistency: at some point updatePreamble() will
+    // catch up to the current file.
     // Report diagnostics with the new preamble to ensure progress. Otherwise
     // diagnostics might get stale indefinitely if user keeps invalidating the
     // preamble.
-    generateDiagnostics(std::move(CI), std::move(PI), std::move(CIDiags));
+    generateDiagnostics(std::move(CI), FileInputs, std::move(CIDiags));
   };
   if (RunSync) {
     runTask(TaskName, Task);
Index: clang-tools-extra/clangd/Preamble.h
===================================================================
--- clang-tools-extra/clangd/Preamble.h
+++ clang-tools-extra/clangd/Preamble.h
@@ -204,9 +204,6 @@
   /// Returns textual patch contents.
   llvm::StringRef text() const { return PatchContents; }
 
-  /// Whether diagnostics generated using this patch are trustable.
-  bool preserveDiagnostics() const;
-
   /// Returns diag locations for Modified contents.
   llvm::ArrayRef<Diag> patchedDiags() const { return PatchedDiags; }
 
Index: clang-tools-extra/clangd/Preamble.cpp
===================================================================
--- clang-tools-extra/clangd/Preamble.cpp
+++ clang-tools-extra/clangd/Preamble.cpp
@@ -933,10 +933,6 @@
   return PP;
 }
 
-bool PreamblePatch::preserveDiagnostics() const {
-  return PatchContents.empty() ||
-         Config::current().Diagnostics.AllowStalePreamble;
-}
 llvm::ArrayRef<PragmaMark> PreamblePatch::marks() const {
   if (PatchContents.empty())
     return Baseline->Marks;
Index: clang-tools-extra/clangd/ParsedAST.h
===================================================================
--- clang-tools-extra/clangd/ParsedAST.h
+++ clang-tools-extra/clangd/ParsedAST.h
@@ -89,9 +89,7 @@
   ArrayRef<Decl *> getLocalTopLevelDecls();
   ArrayRef<const Decl *> getLocalTopLevelDecls() const;
 
-  const std::optional<std::vector<Diag>> &getDiagnostics() const {
-    return Diags;
-  }
+  llvm::ArrayRef<Diag> getDiagnostics() const;
 
   /// Returns the estimated size of the AST and the accessory structures, in
   /// bytes. Does not include the size of the preamble.
@@ -131,9 +129,8 @@
             std::unique_ptr<CompilerInstance> Clang,
             std::unique_ptr<FrontendAction> Action, syntax::TokenBuffer Tokens,
             MainFileMacros Macros, std::vector<PragmaMark> Marks,
-            std::vector<Decl *> LocalTopLevelDecls,
-            std::optional<std::vector<Diag>> Diags, IncludeStructure Includes,
-            CanonicalIncludes CanonIncludes);
+            std::vector<Decl *> LocalTopLevelDecls, std::vector<Diag> Diags,
+            IncludeStructure Includes, CanonicalIncludes CanonIncludes);
 
   Path TUPath;
   std::string Version;
@@ -159,7 +156,7 @@
   std::vector<PragmaMark> Marks;
   // Data, stored after parsing. std::nullopt if AST was built with a stale
   // preamble.
-  std::optional<std::vector<Diag>> Diags;
+  std::vector<Diag> Diags;
   // Top-level decls inside the current file. Not that this does not include
   // top-level decls from the preamble.
   std::vector<Decl *> LocalTopLevelDecls;
Index: clang-tools-extra/clangd/ParsedAST.cpp
===================================================================
--- clang-tools-extra/clangd/ParsedAST.cpp
+++ clang-tools-extra/clangd/ParsedAST.cpp
@@ -422,7 +422,6 @@
       });
 
   std::optional<PreamblePatch> Patch;
-  bool PreserveDiags = true;
   // We might use an ignoring diagnostic consumer if they are going to be
   // dropped later on to not pay for extra latency by processing them.
   DiagnosticConsumer *DiagConsumer = &ASTDiags;
@@ -430,9 +429,6 @@
   if (Preamble) {
     Patch = PreamblePatch::createFullPatch(Filename, Inputs, *Preamble);
     Patch->apply(*CI);
-    PreserveDiags = Patch->preserveDiagnostics();
-    if (!PreserveDiags)
-      DiagConsumer = &DropDiags;
   }
   auto Clang = prepareCompilerInstance(
       std::move(CI), PreamblePCH,
@@ -448,7 +444,7 @@
     return std::nullopt;
   }
   tidy::ClangTidyOptions ClangTidyOpts;
-  if (PreserveDiags) {
+  {
     trace::Span Tracer("ClangTidyOpts");
     ClangTidyOpts = getTidyOptionsForFile(Inputs.ClangTidyProvider, Filename);
     dlog("ClangTidy configuration for file {0}: {1}", Filename,
@@ -479,9 +475,6 @@
       applyWarningOptions(*ClangTidyOpts.ExtraArgsBefore, TidyGroups, Diags);
     if (ClangTidyOpts.ExtraArgs)
       applyWarningOptions(*ClangTidyOpts.ExtraArgs, TidyGroups, Diags);
-  } else {
-    // Skips some analysis.
-    Clang->getDiagnosticOpts().IgnoreWarnings = true;
   }
 
   auto Action = std::make_unique<ClangdFrontendAction>();
@@ -517,7 +510,7 @@
   llvm::DenseMap<diag::kind, DiagnosticsEngine::Level> OverriddenSeverity;
   // No need to run clang-tidy or IncludeFixerif we are not going to surface
   // diagnostics.
-  if (PreserveDiags) {
+  {
     trace::Span Tracer("ClangTidyInit");
     static const auto *CTFactories = [] {
       auto *CTFactories = new tidy::ClangTidyCheckFactories;
@@ -712,28 +705,24 @@
   // CompilerInstance won't run this callback, do it directly.
   ASTDiags.EndSourceFile();
 
-  std::optional<std::vector<Diag>> Diags;
+  std::vector<Diag> Diags = CompilerInvocationDiags;
   // FIXME: Also skip generation of diagnostics alltogether to speed up ast
   // builds when we are patching a stale preamble.
-  if (PreserveDiags) {
-    Diags = CompilerInvocationDiags;
-    // Add diagnostics from the preamble, if any.
-    if (Preamble)
-      llvm::append_range(*Diags, Patch->patchedDiags());
-    // Finally, add diagnostics coming from the AST.
-    {
-      std::vector<Diag> D = ASTDiags.take(&*CTContext);
-      Diags->insert(Diags->end(), D.begin(), D.end());
-    }
+  // Add diagnostics from the preamble, if any.
+  if (Preamble)
+    llvm::append_range(Diags, Patch->patchedDiags());
+  // Finally, add diagnostics coming from the AST.
+  {
+    std::vector<Diag> D = ASTDiags.take(&*CTContext);
+    Diags.insert(Diags.end(), D.begin(), D.end());
   }
   ParsedAST Result(Filename, Inputs.Version, std::move(Preamble),
                    std::move(Clang), std::move(Action), std::move(Tokens),
                    std::move(Macros), std::move(Marks), std::move(ParsedDecls),
                    std::move(Diags), std::move(Includes),
                    std::move(CanonIncludes));
-  if (Result.Diags)
-    llvm::move(getIncludeCleanerDiags(Result, Inputs.Contents),
-               std::back_inserter(*Result.Diags));
+  llvm::move(getIncludeCleanerDiags(Result, Inputs.Contents),
+             std::back_inserter(Result.Diags));
   return std::move(Result);
 }
 
@@ -785,8 +774,8 @@
   auto &AST = getASTContext();
   // FIXME(ibiryukov): we do not account for the dynamically allocated part of
   // Message and Fixes inside each diagnostic.
-  std::size_t Total = clangd::getUsedBytes(LocalTopLevelDecls) +
-                      (Diags ? clangd::getUsedBytes(*Diags) : 0);
+  std::size_t Total =
+      clangd::getUsedBytes(LocalTopLevelDecls) + clangd::getUsedBytes(Diags);
 
   // FIXME: the rest of the function is almost a direct copy-paste from
   // libclang's clang_getCXTUResourceUsage. We could share the implementation.
@@ -828,8 +817,8 @@
                      syntax::TokenBuffer Tokens, MainFileMacros Macros,
                      std::vector<PragmaMark> Marks,
                      std::vector<Decl *> LocalTopLevelDecls,
-                     std::optional<std::vector<Diag>> Diags,
-                     IncludeStructure Includes, CanonicalIncludes CanonIncludes)
+                     std::vector<Diag> Diags, IncludeStructure Includes,
+                     CanonicalIncludes CanonIncludes)
     : TUPath(TUPath), Version(Version), Preamble(std::move(Preamble)),
       Clang(std::move(Clang)), Action(std::move(Action)),
       Tokens(std::move(Tokens)), Macros(std::move(Macros)),
@@ -853,5 +842,6 @@
   return llvm::StringRef(Preamble->Version);
 }
 
+llvm::ArrayRef<Diag> ParsedAST::getDiagnostics() const { return Diags; }
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/ConfigYAML.cpp
===================================================================
--- clang-tools-extra/clangd/ConfigYAML.cpp
+++ clang-tools-extra/clangd/ConfigYAML.cpp
@@ -134,9 +134,6 @@
     });
     Dict.handle("Includes", [&](Node &N) { parse(F.Includes, N); });
     Dict.handle("ClangTidy", [&](Node &N) { parse(F.ClangTidy, N); });
-    Dict.handle("AllowStalePreamble", [&](Node &N) {
-      F.AllowStalePreamble = boolValue(N, "AllowStalePreamble");
-    });
     Dict.parse(N);
   }
 
Index: clang-tools-extra/clangd/ConfigFragment.h
===================================================================
--- clang-tools-extra/clangd/ConfigFragment.h
+++ clang-tools-extra/clangd/ConfigFragment.h
@@ -235,9 +235,6 @@
     /// - std::nullopt
     std::optional<Located<std::string>> UnusedIncludes;
 
-    /// Enable emitting diagnostics using stale preambles.
-    std::optional<Located<bool>> AllowStalePreamble;
-
     /// Controls if clangd should analyze missing #include directives.
     /// clangd will warn if no header providing a symbol is `#include`d
     /// (missing) directly, and suggest adding it.
Index: clang-tools-extra/clangd/ConfigCompile.cpp
===================================================================
--- clang-tools-extra/clangd/ConfigCompile.cpp
+++ clang-tools-extra/clangd/ConfigCompile.cpp
@@ -451,13 +451,6 @@
       }
     }
 
-    if (F.AllowStalePreamble) {
-      if (auto Val = F.AllowStalePreamble)
-        Out.Apply.push_back([Val](const Params &, Config &C) {
-          C.Diagnostics.AllowStalePreamble = **Val;
-        });
-    }
-
     if (F.MissingIncludes)
       if (auto Val = compileEnum<Config::IncludesPolicy>("MissingIncludes",
                                                          **F.MissingIncludes)
Index: clang-tools-extra/clangd/Config.h
===================================================================
--- clang-tools-extra/clangd/Config.h
+++ clang-tools-extra/clangd/Config.h
@@ -105,9 +105,6 @@
       llvm::StringMap<std::string> CheckOptions;
     } ClangTidy;
 
-    /// Enable emitting diagnostics using stale preambles.
-    bool AllowStalePreamble = true;
-
     IncludesPolicy UnusedIncludes = IncludesPolicy::Strict;
     IncludesPolicy MissingIncludes = IncludesPolicy::None;
 
Index: clang-tools-extra/clangd/ClangdServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdServer.h
+++ clang-tools-extra/clangd/ClangdServer.h
@@ -30,6 +30,7 @@
 #include "support/Path.h"
 #include "support/ThreadsafeFS.h"
 #include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/FunctionExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include <functional>
@@ -67,7 +68,7 @@
     /// file, they do not interfere with "pull-based" ClangdServer::diagnostics.
     /// May be called concurrently for separate files, not for a single file.
     virtual void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
-                                    std::vector<Diag> Diagnostics) {}
+                                    llvm::ArrayRef<Diag> Diagnostics) {}
     /// Called whenever the file status is updated.
     /// May be called concurrently for separate files, not for a single file.
     virtual void onFileUpdated(PathRef File, const TUStatus &Status) {}
Index: clang-tools-extra/clangd/ClangdServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdServer.cpp
+++ clang-tools-extra/clangd/ClangdServer.cpp
@@ -124,13 +124,10 @@
     if (FIndex)
       FIndex->updateMain(Path, AST);
 
-    assert(AST.getDiagnostics() &&
-           "We issue callback only with fresh preambles");
-    std::vector<Diag> Diagnostics = *AST.getDiagnostics();
     if (ServerCallbacks)
       Publish([&]() {
         ServerCallbacks->onDiagnosticsReady(Path, AST.version(),
-                                            std::move(Diagnostics));
+                                            AST.getDiagnostics());
         if (CollectInactiveRegions) {
           ServerCallbacks->onInactiveRegionsReady(Path,
                                                   getInactiveRegions(AST));
@@ -366,7 +363,7 @@
         std::lock_guard<std::mutex> Lock(PublishMu);
         for (auto &Entry : ReportableDiagnostics)
           Publish->onDiagnosticsReady(Entry.first(), /*Version=*/"",
-                                      std::move(Entry.second));
+                                      Entry.second);
       }
       return Context::current().derive(Config::Key, std::move(C));
     }
@@ -1046,11 +1043,7 @@
       [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable {
         if (!InpAST)
           return CB(InpAST.takeError());
-        if (auto Diags = InpAST->AST.getDiagnostics())
-          return CB(*Diags);
-        // FIXME: Use ServerCancelled error once it is settled in LSP-3.17.
-        return CB(llvm::make_error<LSPError>("server is busy parsing includes",
-                                             ErrorCode::InternalError));
+        return CB(InpAST->AST.getDiagnostics());
       };
 
   WorkScheduler->runWithAST("Diagnostics", File, std::move(Action));
Index: clang-tools-extra/clangd/ClangdLSPServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.h
+++ clang-tools-extra/clangd/ClangdLSPServer.h
@@ -10,6 +10,7 @@
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H
 
 #include "ClangdServer.h"
+#include "Diagnostics.h"
 #include "GlobalCompilationDatabase.h"
 #include "LSPBinder.h"
 #include "Protocol.h"
@@ -18,6 +19,7 @@
 #include "support/MemoryTree.h"
 #include "support/Path.h"
 #include "support/Threading.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/Support/JSON.h"
 #include <chrono>
 #include <cstddef>
@@ -80,7 +82,7 @@
 private:
   // Implement ClangdServer::Callbacks.
   void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
-                          std::vector<Diag> Diagnostics) override;
+                          llvm::ArrayRef<Diag> Diagnostics) override;
   void onFileUpdated(PathRef File, const TUStatus &Status) override;
   void onBackgroundIndexProgress(const BackgroundQueue::Stats &Stats) override;
   void onSemanticsMaybeChanged(PathRef File) override;
@@ -197,7 +199,6 @@
   void bindMethods(LSPBinder &, const ClientCapabilities &Caps);
   std::vector<CodeAction> getFixes(StringRef File, const clangd::Diagnostic &D);
 
-
   /// Checks if completion request should be ignored. We need this due to the
   /// limitation of the LSP. Per LSP, a client sends requests for all "trigger
   /// character" we specify, but for '>' and ':' we need to check they actually
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -650,7 +650,6 @@
                                  CodeAction::INFO_KIND}}}
           : llvm::json::Value(true);
 
-
   std::vector<llvm::StringRef> Commands;
   for (llvm::StringRef Command : Handlers.CommandHandlers.keys())
     Commands.push_back(Command);
@@ -1744,7 +1743,7 @@
 }
 
 void ClangdLSPServer::onDiagnosticsReady(PathRef File, llvm::StringRef Version,
-                                         std::vector<Diag> Diagnostics) {
+                                         llvm::ArrayRef<Diag> Diagnostics) {
   PublishDiagnosticsParams Notification;
   Notification.version = decodeVersion(Version);
   Notification.uri = URIForFile::canonicalize(File, /*TUPath=*/File);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to