ilya-biryukov created this revision.
ilya-biryukov added a reviewer: sammccall.
Herald added subscribers: kadircet, jfb, arphaman, jkorous, MaskRay, ioeric, 
javed.absar.

NFC on the LSP level, only produces notifications in the C++ API.
Useful for the clients of the C++ API that provide an indicator when
diagnostics finish.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53946

Files:
  clangd/ClangdLSPServer.cpp
  clangd/ClangdLSPServer.h
  clangd/ClangdServer.cpp
  clangd/ClangdServer.h
  clangd/TUScheduler.cpp
  clangd/TUScheduler.h
  unittests/clangd/ClangdTests.cpp
  unittests/clangd/CodeCompleteTests.cpp
  unittests/clangd/FindSymbolsTests.cpp
  unittests/clangd/TUSchedulerTests.cpp
  unittests/clangd/XRefsTests.cpp

Index: unittests/clangd/XRefsTests.cpp
===================================================================
--- unittests/clangd/XRefsTests.cpp
+++ unittests/clangd/XRefsTests.cpp
@@ -34,7 +34,7 @@
 
 class IgnoreDiagnostics : public DiagnosticsConsumer {
   void onDiagnosticsReady(PathRef File,
-                          std::vector<Diag> Diagnostics) override {}
+                          DiagnosticsResult Diagnostics) override {}
 };
 
 // Extracts ranges from an annotated example, and constructs a matcher for a
Index: unittests/clangd/TUSchedulerTests.cpp
===================================================================
--- unittests/clangd/TUSchedulerTests.cpp
+++ unittests/clangd/TUSchedulerTests.cpp
@@ -28,7 +28,7 @@
 using ::testing::Pointee;
 using ::testing::UnorderedElementsAre;
 
-void ignoreUpdate(Optional<std::vector<Diag>>) {}
+void ignoreUpdate(DiagnosticsResult) {}
 void ignoreError(Error Err) {
   handleAllErrors(std::move(Err), [](const ErrorInfoBase &) {});
 }
@@ -109,20 +109,20 @@
         ASTRetentionPolicy());
     auto Path = testPath("foo.cpp");
     S.update(Path, getInputs(Path, ""), WantDiagnostics::Yes,
-             [&](std::vector<Diag>) { Ready.wait(); });
+             [&](DiagnosticsResult) { Ready.wait(); });
 
     S.update(Path, getInputs(Path, "request diags"), WantDiagnostics::Yes,
-             [&](std::vector<Diag> Diags) { ++CallbackCount; });
+             [&](DiagnosticsResult) { ++CallbackCount; });
     S.update(Path, getInputs(Path, "auto (clobbered)"), WantDiagnostics::Auto,
-             [&](std::vector<Diag> Diags) {
+             [&](DiagnosticsResult) {
                ADD_FAILURE() << "auto should have been cancelled by auto";
              });
     S.update(Path, getInputs(Path, "request no diags"), WantDiagnostics::No,
-             [&](std::vector<Diag> Diags) {
+             [&](DiagnosticsResult) {
                ADD_FAILURE() << "no diags should not be called back";
              });
     S.update(Path, getInputs(Path, "auto (produces)"), WantDiagnostics::Auto,
-             [&](std::vector<Diag> Diags) { ++CallbackCount; });
+             [&](DiagnosticsResult) { ++CallbackCount; });
     Ready.notify();
   }
   EXPECT_EQ(2, CallbackCount);
@@ -138,15 +138,15 @@
     // FIXME: we could probably use timeouts lower than 1 second here.
     auto Path = testPath("foo.cpp");
     S.update(Path, getInputs(Path, "auto (debounced)"), WantDiagnostics::Auto,
-             [&](std::vector<Diag> Diags) {
+             [&](DiagnosticsResult) {
                ADD_FAILURE() << "auto should have been debounced and canceled";
              });
     std::this_thread::sleep_for(std::chrono::milliseconds(200));
     S.update(Path, getInputs(Path, "auto (timed out)"), WantDiagnostics::Auto,
-             [&](std::vector<Diag> Diags) { ++CallbackCount; });
+             [&](DiagnosticsResult) { ++CallbackCount; });
     std::this_thread::sleep_for(std::chrono::seconds(2));
     S.update(Path, getInputs(Path, "auto (shut down)"), WantDiagnostics::Auto,
-             [&](std::vector<Diag> Diags) { ++CallbackCount; });
+             [&](DiagnosticsResult) { ++CallbackCount; });
   }
   EXPECT_EQ(2, CallbackCount);
 }
@@ -173,7 +173,7 @@
     // The stale read should see A, and the consistent read should see B.
     // (We recognize the preambles by their included files).
     S.update(Path, getInputs(Path, "#include <A>"), WantDiagnostics::Yes,
-             [&](std::vector<Diag> Diags) {
+             [&](DiagnosticsResult Diags) {
                // This callback runs in between the two preamble updates.
 
                // This blocks update B, preventing it from winning the race
@@ -187,7 +187,7 @@
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
              });
     S.update(Path, getInputs(Path, "#include <B>"), WantDiagnostics::Yes,
-             [&](std::vector<Diag> Diags) {});
+             [&](DiagnosticsResult Diags) {});
 
     S.runWithPreamble("StaleRead", Path, TUScheduler::Stale,
                       [&](Expected<InputsAndPreamble> Pre) {
@@ -253,37 +253,33 @@
 
         {
           WithContextValue WithNonce(NonceKey, ++Nonce);
-          S.update(File, Inputs, WantDiagnostics::Auto,
-                   [File, Nonce, &Mut,
-                    &TotalUpdates](Optional<std::vector<Diag>> Diags) {
-                     EXPECT_THAT(Context::current().get(NonceKey),
-                                 Pointee(Nonce));
-
-                     std::lock_guard<std::mutex> Lock(Mut);
-                     ++TotalUpdates;
-                     EXPECT_EQ(File,
-                               *TUScheduler::getFileBeingProcessedInContext());
-                   });
+          S.update(
+              File, Inputs, WantDiagnostics::Auto,
+              [File, Nonce, &Mut, &TotalUpdates](DiagnosticsResult) {
+                EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce));
+
+                std::lock_guard<std::mutex> Lock(Mut);
+                ++TotalUpdates;
+                EXPECT_EQ(File, *TUScheduler::getFileBeingProcessedInContext());
+              });
         }
 
         {
           WithContextValue WithNonce(NonceKey, ++Nonce);
-          S.runWithAST("CheckAST", File,
-                       [File, Inputs, Nonce, &Mut,
-                        &TotalASTReads](Expected<InputsAndAST> AST) {
-                         EXPECT_THAT(Context::current().get(NonceKey),
-                                     Pointee(Nonce));
-
-                         ASSERT_TRUE((bool)AST);
-                         EXPECT_EQ(AST->Inputs.FS, Inputs.FS);
-                         EXPECT_EQ(AST->Inputs.Contents, Inputs.Contents);
-
-                         std::lock_guard<std::mutex> Lock(Mut);
-                         ++TotalASTReads;
-                         EXPECT_EQ(
-                             File,
-                             *TUScheduler::getFileBeingProcessedInContext());
-                       });
+          S.runWithAST(
+              "CheckAST", File,
+              [File, Inputs, Nonce, &Mut,
+               &TotalASTReads](Expected<InputsAndAST> AST) {
+                EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce));
+
+                ASSERT_TRUE((bool)AST);
+                EXPECT_EQ(AST->Inputs.FS, Inputs.FS);
+                EXPECT_EQ(AST->Inputs.Contents, Inputs.Contents);
+
+                std::lock_guard<std::mutex> Lock(Mut);
+                ++TotalASTReads;
+                EXPECT_EQ(File, *TUScheduler::getFileBeingProcessedInContext());
+              });
         }
 
         {
@@ -337,25 +333,37 @@
   // Build one file in advance. We will not access it later, so it will be the
   // one that the cache will evict.
   S.update(Foo, getInputs(Foo, SourceContents), WantDiagnostics::Yes,
-           [&BuiltASTCounter](std::vector<Diag> Diags) { ++BuiltASTCounter; });
+           [&BuiltASTCounter](DiagnosticsResult R) {
+             if (!R.isSkipped())
+               ++BuiltASTCounter;
+           });
   ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
   ASSERT_EQ(BuiltASTCounter.load(), 1);
 
   // Build two more files. Since we can retain only 2 ASTs, these should be the
   // ones we see in the cache later.
   S.update(Bar, getInputs(Bar, SourceContents), WantDiagnostics::Yes,
-           [&BuiltASTCounter](std::vector<Diag> Diags) { ++BuiltASTCounter; });
+           [&BuiltASTCounter](DiagnosticsResult R) {
+             if (!R.isSkipped())
+               ++BuiltASTCounter;
+           });
   S.update(Baz, getInputs(Baz, SourceContents), WantDiagnostics::Yes,
-           [&BuiltASTCounter](std::vector<Diag> Diags) { ++BuiltASTCounter; });
+           [&BuiltASTCounter](DiagnosticsResult R) {
+             if (!R.isSkipped())
+               ++BuiltASTCounter;
+           });
   ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
   ASSERT_EQ(BuiltASTCounter.load(), 3);
 
   // Check only the last two ASTs are retained.
   ASSERT_THAT(S.getFilesWithCachedAST(), UnorderedElementsAre(Bar, Baz));
 
   // Access the old file again.
   S.update(Foo, getInputs(Foo, OtherSourceContents), WantDiagnostics::Yes,
-           [&BuiltASTCounter](std::vector<Diag> Diags) { ++BuiltASTCounter; });
+           [&BuiltASTCounter](DiagnosticsResult R) {
+             if (!R.isSkipped())
+               ++BuiltASTCounter;
+           });
   ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
   ASSERT_EQ(BuiltASTCounter.load(), 4);
 
@@ -383,31 +391,31 @@
   )cpp";
   auto WithEmptyPreamble = R"cpp(int main() {})cpp";
   S.update(Foo, getInputs(Foo, WithPreamble), WantDiagnostics::Auto,
-           [](std::vector<Diag>) {});
-  S.runWithPreamble("getNonEmptyPreamble", Foo, TUScheduler::Stale,
-                    [&](Expected<InputsAndPreamble> Preamble) {
-                      // We expect to get a non-empty preamble.
-                      EXPECT_GT(cantFail(std::move(Preamble))
-                                    .Preamble->Preamble.getBounds()
-                                    .Size,
-                                0u);
-                    });
+           [](DiagnosticsResult) {});
+  S.runWithPreamble(
+      "getNonEmptyPreamble", Foo, TUScheduler::Stale,
+      [&](Expected<InputsAndPreamble> Preamble) {
+        // We expect to get a non-empty preamble.
+        EXPECT_GT(
+            cantFail(std::move(Preamble)).Preamble->Preamble.getBounds().Size,
+            0u);
+      });
   // Wait for the preamble is being built.
   ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
 
   // Update the file which results in an empty preamble.
   S.update(Foo, getInputs(Foo, WithEmptyPreamble), WantDiagnostics::Auto,
-           [](std::vector<Diag>) {});
+           [](DiagnosticsResult) {});
   // Wait for the preamble is being built.
   ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
-  S.runWithPreamble("getEmptyPreamble", Foo, TUScheduler::Stale,
-                    [&](Expected<InputsAndPreamble> Preamble) {
-                      // We expect to get an empty preamble.
-                      EXPECT_EQ(cantFail(std::move(Preamble))
-                                    .Preamble->Preamble.getBounds()
-                                    .Size,
-                                0u);
-                    });
+  S.runWithPreamble(
+      "getEmptyPreamble", Foo, TUScheduler::Stale,
+      [&](Expected<InputsAndPreamble> Preamble) {
+        // We expect to get an empty preamble.
+        EXPECT_EQ(
+            cantFail(std::move(Preamble)).Preamble->Preamble.getBounds().Size,
+            0u);
+      });
 }
 
 TEST_F(TUSchedulerTests, RunWaitsForPreamble) {
@@ -429,7 +437,7 @@
   std::mutex PreamblesMut;
   std::vector<const void *> Preambles(ReadsToSchedule, nullptr);
   S.update(Foo, getInputs(Foo, NonEmptyPreamble), WantDiagnostics::Auto,
-           [](std::vector<Diag>) {});
+           [](DiagnosticsResult) {});
   for (int I = 0; I < ReadsToSchedule; ++I) {
     S.runWithPreamble(
         "test", Foo, TUScheduler::Stale,
@@ -466,13 +474,18 @@
   // Return value indicates if the updated callback was received.
   auto DoUpdate = [&](ParseInputs Inputs) -> bool {
     std::atomic<bool> Updated(false);
-    Updated = false;
+    std::atomic<bool> Skipped(false);
     S.update(Source, std::move(Inputs), WantDiagnostics::Yes,
-             [&Updated](std::vector<Diag>) { Updated = true; });
+             [&](DiagnosticsResult R) {
+               Updated = true;
+               Skipped = R.isSkipped();
+             });
     bool UpdateFinished = S.blockUntilIdle(timeoutSeconds(10));
     if (!UpdateFinished)
       ADD_FAILURE() << "Updated has not finished in one second. Threading bug?";
-    return Updated;
+    if (!Updated)
+      ADD_FAILURE() << "The callback was never called";
+    return !Skipped;
   };
 
   // Test that subsequent updates with the same inputs do not cause rebuilds.
@@ -509,27 +522,34 @@
   auto Contents = "int a; int b;";
 
   S.update(FooCpp, getInputs(FooCpp, Contents), WantDiagnostics::No,
-           [](std::vector<Diag>) { ADD_FAILURE() << "Should not be called."; });
+           [](DiagnosticsResult) { ADD_FAILURE() << "Should not be called."; });
   S.runWithAST("touchAST", FooCpp, [](Expected<InputsAndAST> IA) {
     // Make sure the AST was actually built.
     cantFail(std::move(IA));
   });
   ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
 
   // Even though the inputs didn't change and AST can be reused, we need to
   // report the diagnostics, as they were not reported previously.
-  std::atomic<bool> SeenDiags(false);
+  std::atomic<bool> SeenResult(false);
   S.update(FooCpp, getInputs(FooCpp, Contents), WantDiagnostics::Auto,
-           [&](std::vector<Diag>) { SeenDiags = true; });
+           [&](DiagnosticsResult R) {
+             EXPECT_FALSE(R.isSkipped()) << "New diagnostics were not produced";
+             SeenResult = true;
+           });
   ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
-  ASSERT_TRUE(SeenDiags);
+  ASSERT_TRUE(SeenResult);
 
   // Subsequent request does not get any diagnostics callback because the same
   // diags have previously been reported and the inputs didn't change.
-  S.update(
-      FooCpp, getInputs(FooCpp, Contents), WantDiagnostics::Auto,
-      [&](std::vector<Diag>) { ADD_FAILURE() << "Should not be called."; });
+  SeenResult = false;
+  S.update(FooCpp, getInputs(FooCpp, Contents), WantDiagnostics::Auto,
+           [&](DiagnosticsResult R) {
+             EXPECT_TRUE(R.isSkipped()) << "Diagnostics should not be produced";
+             SeenResult = true;
+           });
   ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
+  ASSERT_TRUE(SeenResult);
 }
 
 TEST_F(TUSchedulerTests, Run) {
Index: unittests/clangd/FindSymbolsTests.cpp
===================================================================
--- unittests/clangd/FindSymbolsTests.cpp
+++ unittests/clangd/FindSymbolsTests.cpp
@@ -28,7 +28,7 @@
 
 class IgnoreDiagnostics : public DiagnosticsConsumer {
   void onDiagnosticsReady(PathRef File,
-                          std::vector<Diag> Diagnostics) override {}
+                          DiagnosticsResult Diagnostics) override {}
 };
 
 // GMock helpers for matching SymbolInfos items.
Index: unittests/clangd/CodeCompleteTests.cpp
===================================================================
--- unittests/clangd/CodeCompleteTests.cpp
+++ unittests/clangd/CodeCompleteTests.cpp
@@ -41,7 +41,7 @@
 
 class IgnoreDiagnostics : public DiagnosticsConsumer {
   void onDiagnosticsReady(PathRef File,
-                          std::vector<Diag> Diagnostics) override {}
+                          DiagnosticsResult Diagnostics) override {}
 };
 
 // GMock helpers for matching completion items.
Index: unittests/clangd/ClangdTests.cpp
===================================================================
--- unittests/clangd/ClangdTests.cpp
+++ unittests/clangd/ClangdTests.cpp
@@ -55,9 +55,10 @@
 
 class ErrorCheckingDiagConsumer : public DiagnosticsConsumer {
 public:
-  void onDiagnosticsReady(PathRef File,
-                          std::vector<Diag> Diagnostics) override {
-    bool HadError = diagsContainErrors(Diagnostics);
+  void onDiagnosticsReady(PathRef File, DiagnosticsResult Result) override {
+    if (Result.isSkipped())
+      return;
+    bool HadError = diagsContainErrors(Result.diags());
     std::lock_guard<std::mutex> Lock(Mutex);
     HadErrorInLastDiags = HadError;
   }
@@ -76,9 +77,8 @@
 /// least one error.
 class MultipleErrorCheckingDiagConsumer : public DiagnosticsConsumer {
 public:
-  void onDiagnosticsReady(PathRef File,
-                          std::vector<Diag> Diagnostics) override {
-    bool HadError = diagsContainErrors(Diagnostics);
+  void onDiagnosticsReady(PathRef File, DiagnosticsResult Result) override {
+    bool HadError = diagsContainErrors(Result.diags());
 
     std::lock_guard<std::mutex> Lock(Mutex);
     LastDiagsHadError[File] = HadError;
@@ -272,10 +272,10 @@
     mutable int Got;
   } FS;
   struct DiagConsumer : public DiagnosticsConsumer {
-    void onDiagnosticsReady(PathRef File,
-                            std::vector<Diag> Diagnostics) override {
+    void onDiagnosticsReady(PathRef, DiagnosticsResult) override {
       Got = Context::current().getExisting(Secret);
     }
+
     int Got;
   } DiagConsumer;
   MockCompilationDatabase CDB;
@@ -592,14 +592,16 @@
   public:
     TestDiagConsumer() : Stats(FilesCount, FileStat()) {}
 
-    void onDiagnosticsReady(PathRef File,
-                            std::vector<Diag> Diagnostics) override {
+    void onDiagnosticsReady(PathRef File, DiagnosticsResult Result) override {
+      if (Result.isSkipped())
+        return;
+
       StringRef FileIndexStr = sys::path::stem(File);
       ASSERT_TRUE(FileIndexStr.consume_front("Foo"));
 
       unsigned long FileIndex = std::stoul(FileIndexStr.str());
 
-      bool HadError = diagsContainErrors(Diagnostics);
+      bool HadError = diagsContainErrors(Result.diags());
 
       std::lock_guard<std::mutex> Lock(Mutex);
       if (HadError)
@@ -847,7 +849,10 @@
     NoConcurrentAccessDiagConsumer(std::promise<void> StartSecondReparse)
         : StartSecondReparse(std::move(StartSecondReparse)) {}
 
-    void onDiagnosticsReady(PathRef, std::vector<Diag>) override {
+    void onDiagnosticsReady(PathRef, DiagnosticsResult) override { onAccess(); }
+
+  private:
+    void onAccess() {
       ++Count;
       std::unique_lock<std::mutex> Lock(Mutex, std::try_to_lock_t());
       ASSERT_TRUE(Lock.owns_lock())
@@ -863,7 +868,6 @@
       }
     }
 
-  private:
     std::mutex Mutex;
     bool FirstRequest = true;
     std::promise<void> StartSecondReparse;
Index: clangd/TUScheduler.h
===================================================================
--- clangd/TUScheduler.h
+++ clangd/TUScheduler.h
@@ -74,6 +74,23 @@
   virtual void onMainAST(PathRef Path, ParsedAST &AST) {}
 };
 
+class DiagnosticsResult {
+public:
+  DiagnosticsResult() = default;
+  explicit DiagnosticsResult(std::vector<Diag> Diags)
+      : Diags(std::move(Diags)) {}
+
+  /// Indicates computation of diagnostics was skipped, because inputs did not
+  /// change. Previous diagnostics should be reused.
+  bool isSkipped() { return !Diags.hasValue(); }
+
+  /// Cannot be called if isSkipped is false.
+  std::vector<Diag> &diags() { return *Diags; }
+
+private:
+  llvm::Optional<std::vector<Diag>> Diags;
+};
+
 /// Handles running tasks for ClangdServer and managing the resources (e.g.,
 /// preambles and ASTs) for opened files.
 /// TUScheduler is not thread-safe, only one thread should be providing updates
@@ -102,7 +119,7 @@
   /// \p File was not part of it before.
   /// FIXME(ibiryukov): remove the callback from this function.
   void update(PathRef File, ParseInputs Inputs, WantDiagnostics WD,
-              llvm::unique_function<void(std::vector<Diag>)> OnUpdated);
+              llvm::unique_function<void(DiagnosticsResult)> OnUpdated);
 
   /// Remove \p File from the list of tracked files and schedule removal of its
   /// resources.
Index: clangd/TUScheduler.cpp
===================================================================
--- clangd/TUScheduler.cpp
+++ clangd/TUScheduler.cpp
@@ -176,7 +176,7 @@
   ~ASTWorker();
 
   void update(ParseInputs Inputs, WantDiagnostics,
-              llvm::unique_function<void(std::vector<Diag>)> OnUpdated);
+              llvm::unique_function<void(DiagnosticsResult)> OnUpdated);
   void runWithAST(StringRef Name,
                   unique_function<void(Expected<InputsAndAST>)> Action);
   bool blockUntilIdle(Deadline Timeout) const;
@@ -333,7 +333,7 @@
 }
 
 void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags,
-                       unique_function<void(std::vector<Diag>)> OnUpdated) {
+                       unique_function<void(DiagnosticsResult)> OnUpdated) {
   auto Task = [=](decltype(OnUpdated) OnUpdated) mutable {
     // Will be used to check if we can avoid rebuilding the AST.
     bool InputsAreTheSame =
@@ -358,6 +358,9 @@
       // Make sure anyone waiting for the preamble gets notified it could not
       // be built.
       PreambleWasBuilt.notify();
+      if (WantDiags != WantDiagnostics::No)
+        OnUpdated(DiagnosticsResult()); // FIXME(ibiryukov): report an error to
+                                        // the user.
       return;
     }
 
@@ -397,6 +400,9 @@
         // current file at this point?
         log("Skipping rebuild of the AST for {0}, inputs are the same.",
             FileName);
+        if (WantDiags != WantDiagnostics::No)
+          OnUpdated(DiagnosticsResult()); // Signals prev diagnostics should be
+                                          // reused.
         return;
       }
     }
@@ -417,7 +423,7 @@
     // spam us with updates.
     // Note *AST can still be null if buildAST fails.
     if (*AST) {
-      OnUpdated((*AST)->getDiagnostics());
+      OnUpdated(DiagnosticsResult((*AST)->getDiagnostics()));
       trace::Span Span("Running main AST callback");
       Callbacks.onMainAST(FileName, **AST);
       DiagsWereReported = true;
@@ -697,7 +703,7 @@
 
 void TUScheduler::update(PathRef File, ParseInputs Inputs,
                          WantDiagnostics WantDiags,
-                         unique_function<void(std::vector<Diag>)> OnUpdated) {
+                         unique_function<void(DiagnosticsResult)> OnUpdated) {
   std::unique_ptr<FileData> &FD = Files[File];
   if (!FD) {
     // Create a new worker to process the AST-related tasks.
Index: clangd/ClangdServer.h
===================================================================
--- clangd/ClangdServer.h
+++ clangd/ClangdServer.h
@@ -40,9 +40,8 @@
 public:
   virtual ~DiagnosticsConsumer() = default;
 
-  /// Called by ClangdServer when \p Diagnostics for \p File are ready.
   virtual void onDiagnosticsReady(PathRef File,
-                                  std::vector<Diag> Diagnostics) = 0;
+                                  DiagnosticsResult Diagnostics) = 0;
 };
 
 /// Manages a collection of source files and derived data (ASTs, indexes),
@@ -229,7 +228,7 @@
   typedef uint64_t DocVersion;
 
   void consumeDiagnostics(PathRef File, DocVersion Version,
-                          std::vector<Diag> Diags);
+                          DiagnosticsResult Diags);
 
   tooling::CompileCommand getCompileCommand(PathRef File);
 
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -140,7 +140,7 @@
 
   Path FileStr = File.str();
   WorkScheduler.update(File, std::move(Inputs), WantDiags,
-                       [this, FileStr, Version](std::vector<Diag> Diags) {
+                       [this, FileStr, Version](DiagnosticsResult Diags) {
                          consumeDiagnostics(FileStr, Version, std::move(Diags));
                        });
 }
@@ -446,7 +446,7 @@
 }
 
 void ClangdServer::consumeDiagnostics(PathRef File, DocVersion Version,
-                                      std::vector<Diag> Diags) {
+                                      DiagnosticsResult Diags) {
   // We need to serialize access to resulting diagnostics to avoid calling
   // `onDiagnosticsReady` in the wrong order.
   std::lock_guard<std::mutex> DiagsLock(DiagnosticsMutex);
Index: clangd/ClangdLSPServer.h
===================================================================
--- clangd/ClangdLSPServer.h
+++ clangd/ClangdLSPServer.h
@@ -49,7 +49,7 @@
 
 private:
   // Implement DiagnosticsConsumer.
-  void onDiagnosticsReady(PathRef File, std::vector<Diag> Diagnostics) override;
+  void onDiagnosticsReady(PathRef File, DiagnosticsResult Diagnostics) override;
 
   // LSP methods. Notifications have signature void(const Params&).
   // Calls have signature void(const Params&, Callback<Response>).
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -751,11 +751,14 @@
 }
 
 void ClangdLSPServer::onDiagnosticsReady(PathRef File,
-                                         std::vector<Diag> Diagnostics) {
+                                         DiagnosticsResult Diagnostics) {
+  if (Diagnostics.isSkipped())
+    return; // no action needed, previous diags can be reused.
+
   URIForFile URI(File);
   std::vector<Diagnostic> LSPDiagnostics;
   DiagnosticToReplacementMap LocalFixIts; // Temporary storage
-  for (auto &Diag : Diagnostics) {
+  for (auto &Diag : Diagnostics.diags()) {
     toLSPDiags(Diag, URI, DiagOpts,
                [&](clangd::Diagnostic Diag, ArrayRef<Fix> Fixes) {
                  auto &FixItsForDiagnostic = LocalFixIts[Diag];
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to