sammccall updated this revision to Diff 248069.
sammccall added a comment.

Fix accidental default.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D75582/new/

https://reviews.llvm.org/D75582

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/Compiler.h
  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/Protocol.cpp
  clang-tools-extra/clangd/Protocol.h
  clang-tools-extra/clangd/TUScheduler.cpp
  clang-tools-extra/clangd/TUScheduler.h
  clang-tools-extra/clangd/index/FileIndex.cpp
  clang-tools-extra/clangd/index/FileIndex.h
  clang-tools-extra/clangd/test/diagnostic-category.test
  clang-tools-extra/clangd/test/diagnostics-no-tidy.test
  clang-tools-extra/clangd/test/diagnostics-notes.test
  clang-tools-extra/clangd/test/diagnostics.test
  clang-tools-extra/clangd/test/did-change-configuration-params.test
  clang-tools-extra/clangd/test/execute-command.test
  clang-tools-extra/clangd/test/fixits-codeaction.test
  clang-tools-extra/clangd/test/fixits-command.test
  clang-tools-extra/clangd/test/fixits-embed-in-diagnostic.test
  clang-tools-extra/clangd/test/path-mappings.test
  clang-tools-extra/clangd/test/semantic-highlighting.test
  clang-tools-extra/clangd/test/version.test
  clang-tools-extra/clangd/unittests/ClangdTests.cpp
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang-tools-extra/clangd/unittests/FileIndexTests.cpp
  clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
  clang-tools-extra/clangd/unittests/SyncAPI.cpp
  clang-tools-extra/clangd/unittests/SyncAPI.h
  clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
  clang-tools-extra/clangd/unittests/TestTU.cpp
  clang-tools-extra/clangd/unittests/XRefsTests.cpp

Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -838,7 +838,8 @@
       ElementsAre(Sym("foo.h", FooHeader.range())));
 
   // Only preamble is built, and no AST is built in this request.
-  Server.addDocument(FooCpp, FooWithoutHeader.code(), WantDiagnostics::No);
+  Server.addDocument(FooCpp, FooWithoutHeader.code(), nullptr,
+                     WantDiagnostics::No);
   // We build AST here, and it should use the latest preamble rather than the
   // stale one.
   EXPECT_THAT(
@@ -848,7 +849,8 @@
   // Reset test environment.
   runAddDocument(Server, FooCpp, FooWithHeader.code());
   // Both preamble and AST are built in this request.
-  Server.addDocument(FooCpp, FooWithoutHeader.code(), WantDiagnostics::Yes);
+  Server.addDocument(FooCpp, FooWithoutHeader.code(), nullptr,
+                     WantDiagnostics::Yes);
   // Use the AST being built in above request.
   EXPECT_THAT(
       cantFail(runLocateSymbolAt(Server, FooCpp, FooWithoutHeader.point())),
Index: clang-tools-extra/clangd/unittests/TestTU.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TestTU.cpp
+++ clang-tools-extra/clangd/unittests/TestTU.cpp
@@ -97,16 +97,16 @@
 
 SymbolSlab TestTU::headerSymbols() const {
   auto AST = build();
-  return std::get<0>(indexHeaderSymbols(AST.getASTContext(),
-                                        AST.getPreprocessorPtr(),
-                                        AST.getCanonicalIncludes()));
+  return std::get<0>(
+      indexHeaderSymbols(/*Version=*/nullptr, AST.getASTContext(),
+                         AST.getPreprocessorPtr(), AST.getCanonicalIncludes()));
 }
 
 std::unique_ptr<SymbolIndex> TestTU::index() const {
   auto AST = build();
   auto Idx = std::make_unique<FileIndex>(/*UseDex=*/true);
-  Idx->updatePreamble(Filename, AST.getASTContext(), AST.getPreprocessorPtr(),
-                      AST.getCanonicalIncludes());
+  Idx->updatePreamble(Filename, /*Version=*/nullptr, AST.getASTContext(),
+                      AST.getPreprocessorPtr(), AST.getCanonicalIncludes());
   Idx->updateMain(Filename, AST);
   return std::move(Idx);
 }
Index: clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
+++ clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
@@ -40,7 +40,15 @@
 using ::testing::UnorderedElementsAre;
 
 MATCHER_P2(TUState, State, ActionName, "") {
-  return arg.Action.S == State && arg.Action.Name == ActionName;
+  if (arg.Action.S != State) {
+    *result_listener << "state is " << arg.Action.S;
+    return false;
+  }
+  if (arg.Action.Name != ActionName) {
+    *result_listener << "name is " << arg.Action.Name;
+    return false;
+  }
+  return true;
 }
 
 TUScheduler::Options optsForTest() {
@@ -61,8 +69,15 @@
   void updateWithCallback(TUScheduler &S, PathRef File,
                           llvm::StringRef Contents, WantDiagnostics WD,
                           llvm::unique_function<void()> CB) {
+    updateWithCallback(S, File, getInputs(File, std::string(Contents)), WD,
+                       std::move(CB));
+  }
+
+  void updateWithCallback(TUScheduler &S, PathRef File, ParseInputs Inputs,
+                          WantDiagnostics WD,
+                          llvm::unique_function<void()> CB) {
     WithContextValue Ctx(llvm::make_scope_exit(std::move(CB)));
-    S.update(File, getInputs(File, std::string(Contents)), WD);
+    S.update(File, Inputs, WD);
   }
 
   static Key<llvm::unique_function<void(PathRef File, std::vector<Diag>)>>
@@ -77,8 +92,8 @@
         reportDiagnostics(File, AST.getDiagnostics(), Publish);
       }
 
-      void onFailedAST(PathRef File, std::vector<Diag> Diags,
-                       PublishFn Publish) override {
+      void onFailedAST(PathRef File, const llvm::json::Value &Version,
+                       std::vector<Diag> Diags, PublishFn Publish) override {
         reportDiagnostics(File, Diags, Publish);
       }
 
@@ -243,7 +258,9 @@
     // Schedule two updates (A, B) and two preamble reads (stale, consistent).
     // The stale read should see A, and the consistent read should see B.
     // (We recognize the preambles by their included files).
-    updateWithCallback(S, Path, "#include <A>", WantDiagnostics::Yes, [&]() {
+    auto Inputs = getInputs(Path, "#include <A>");
+    Inputs.Version = "A";
+    updateWithCallback(S, Path, Inputs, WantDiagnostics::Yes, [&]() {
       // This callback runs in between the two preamble updates.
 
       // This blocks update B, preventing it from winning the race
@@ -256,12 +273,14 @@
       // If the second read was stale, it would usually see A.
       std::this_thread::sleep_for(std::chrono::milliseconds(100));
     });
-    S.update(Path, getInputs(Path, "#include <B>"), WantDiagnostics::Yes);
+    Inputs.Contents = "#include <B>";
+    Inputs.Version = "B";
+    S.update(Path, Inputs, WantDiagnostics::Yes);
 
     S.runWithPreamble("StaleRead", Path, TUScheduler::Stale,
                       [&](Expected<InputsAndPreamble> Pre) {
                         ASSERT_TRUE(bool(Pre));
-                        assert(bool(Pre));
+                        EXPECT_EQ(Pre->Preamble->Version, "A");
                         EXPECT_THAT(includes(Pre->Preamble),
                                     ElementsAre("<A>"));
                         InconsistentReadDone.notify();
@@ -270,6 +289,7 @@
     S.runWithPreamble("ConsistentRead", Path, TUScheduler::Consistent,
                       [&](Expected<InputsAndPreamble> Pre) {
                         ASSERT_TRUE(bool(Pre));
+                        EXPECT_EQ(Pre->Preamble->Version, "B");
                         EXPECT_THAT(includes(Pre->Preamble),
                                     ElementsAre("<B>"));
                         ++CallbackCount;
@@ -388,6 +408,7 @@
         auto Inputs = getInputs(File, Contents.str());
         {
           WithContextValue WithNonce(NonceKey, ++Nonce);
+          Inputs.Version = Nonce;
           updateWithDiags(
               S, File, Inputs, WantDiagnostics::Auto,
               [File, Nonce, &Mut, &TotalUpdates](std::vector<Diag>) {
@@ -409,6 +430,8 @@
                 ASSERT_TRUE((bool)AST);
                 EXPECT_EQ(AST->Inputs.FS, Inputs.FS);
                 EXPECT_EQ(AST->Inputs.Contents, Inputs.Contents);
+                EXPECT_EQ(AST->Inputs.Version, Inputs.Version);
+                EXPECT_EQ(AST->AST.version(), Inputs.Version);
 
                 std::lock_guard<std::mutex> Lock(Mut);
                 ++TotalASTReads;
@@ -711,9 +734,6 @@
 TEST_F(TUSchedulerTests, TUStatus) {
   class CaptureTUStatus : public ClangdServer::Callbacks {
   public:
-    void onDiagnosticsReady(PathRef File,
-                            std::vector<Diag> Diagnostics) override {}
-
     void onFileUpdated(PathRef File, const TUStatus &Status) override {
       std::lock_guard<std::mutex> Lock(Mutex);
       AllStatus.push_back(Status);
@@ -735,7 +755,7 @@
 
   // We schedule the following tasks in the queue:
   //   [Update] [GoToDefinition]
-  Server.addDocument(testPath("foo.cpp"), Code.code(), WantDiagnostics::Yes);
+  Server.addDocument(testPath("foo.cpp"), Code.code(), 1, WantDiagnostics::Yes);
   Server.locateSymbolAt(testPath("foo.cpp"), Code.point(),
                         [](Expected<std::vector<LocatedSymbol>> Result) {
                           ASSERT_TRUE((bool)Result);
@@ -746,9 +766,9 @@
   EXPECT_THAT(CaptureTUStatus.allStatus(),
               ElementsAre(
                   // Statuses of "Update" action.
-                  TUState(TUAction::RunningAction, "Update"),
-                  TUState(TUAction::BuildingPreamble, "Update"),
-                  TUState(TUAction::BuildingFile, "Update"),
+                  TUState(TUAction::RunningAction, "Update (1)"),
+                  TUState(TUAction::BuildingPreamble, "Update (1)"),
+                  TUState(TUAction::BuildingFile, "Update (1)"),
 
                   // Statuses of "Definitions" action
                   TUState(TUAction::RunningAction, "Definitions"),
Index: clang-tools-extra/clangd/unittests/SyncAPI.h
===================================================================
--- clang-tools-extra/clangd/unittests/SyncAPI.h
+++ clang-tools-extra/clangd/unittests/SyncAPI.h
@@ -23,7 +23,9 @@
 
 // Calls addDocument and then blockUntilIdleForTest.
 void runAddDocument(ClangdServer &Server, PathRef File, StringRef Contents,
-                    WantDiagnostics WantDiags = WantDiagnostics::Auto);
+                    llvm::json::Value Version = nullptr,
+                    WantDiagnostics WantDiags = WantDiagnostics::Auto,
+                    bool ForceRebuild = false);
 
 llvm::Expected<CodeCompleteResult>
 runCodeComplete(ClangdServer &Server, PathRef File, Position Pos,
Index: clang-tools-extra/clangd/unittests/SyncAPI.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/SyncAPI.cpp
+++ clang-tools-extra/clangd/unittests/SyncAPI.cpp
@@ -13,8 +13,10 @@
 namespace clangd {
 
 void runAddDocument(ClangdServer &Server, PathRef File,
-                    llvm::StringRef Contents, WantDiagnostics WantDiags) {
-  Server.addDocument(File, Contents, WantDiags);
+                    llvm::StringRef Contents, llvm::json::Value Version,
+                    WantDiagnostics WantDiags, bool ForceRebuild) {
+  Server.addDocument(File, Contents, std::move(Version), WantDiags,
+                     ForceRebuild);
   if (!Server.blockUntilIdleForTest())
     llvm_unreachable("not idle after addDocument");
 }
Index: clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
+++ clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
@@ -702,7 +702,8 @@
     std::atomic<int> Count = {0};
 
     void onHighlightingsReady(
-        PathRef File, std::vector<HighlightingToken> Highlightings) override {
+        PathRef File, const llvm::json::Value &Version,
+        std::vector<HighlightingToken> Highlightings) override {
       ++Count;
     }
   };
Index: clang-tools-extra/clangd/unittests/FileIndexTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/FileIndexTests.cpp
+++ clang-tools-extra/clangd/unittests/FileIndexTests.cpp
@@ -151,8 +151,8 @@
   File.HeaderFilename = (Basename + ".h").str();
   File.HeaderCode = std::string(Code);
   auto AST = File.build();
-  M.updatePreamble(File.Filename, AST.getASTContext(), AST.getPreprocessorPtr(),
-                   AST.getCanonicalIncludes());
+  M.updatePreamble(File.Filename, /*Version=*/nullptr, AST.getASTContext(),
+                   AST.getPreprocessorPtr(), AST.getCanonicalIncludes());
 }
 
 TEST(FileIndexTest, CustomizedURIScheme) {
@@ -293,7 +293,8 @@
           const CanonicalIncludes &CanonIncludes) {
         EXPECT_FALSE(IndexUpdated) << "Expected only a single index update";
         IndexUpdated = true;
-        Index.updatePreamble(FooCpp, Ctx, std::move(PP), CanonIncludes);
+        Index.updatePreamble(FooCpp, /*Version=*/nullptr, Ctx, std::move(PP),
+                             CanonIncludes);
       });
   ASSERT_TRUE(IndexUpdated);
 
@@ -392,7 +393,7 @@
   TU.HeaderCode = "class A {}; class B : public A {};";
   auto AST = TU.build();
   FileIndex Index;
-  Index.updatePreamble(TU.Filename, AST.getASTContext(),
+  Index.updatePreamble(TU.Filename, /*Version=*/nullptr, AST.getASTContext(),
                        AST.getPreprocessorPtr(), AST.getCanonicalIncludes());
   SymbolID A = findSymbol(TU.headerSymbols(), "A").ID;
   uint32_t Results = 0;
Index: clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -1509,7 +1509,7 @@
     }
     int a = fun^
   )cpp");
-  Server.addDocument(FooCpp, Source.code(), WantDiagnostics::Yes);
+  Server.addDocument(FooCpp, Source.code(), nullptr, WantDiagnostics::Yes);
   // We need to wait for preamble to build.
   ASSERT_TRUE(Server.blockUntilIdleForTest());
 
@@ -1575,7 +1575,7 @@
   // FIXME: Auto-completion in a template requires disabling delayed template
   // parsing.
   CDB.ExtraClangFlags.push_back("-fno-delayed-template-parsing");
-  runAddDocument(Server, FooCpp, Source.code(), WantDiagnostics::Yes);
+  runAddDocument(Server, FooCpp, Source.code(), nullptr, WantDiagnostics::Yes);
   CodeCompleteResult Completions = cantFail(runCodeComplete(
       Server, FooCpp, Source.point(), clangd::CodeCompleteOptions()));
 
Index: clang-tools-extra/clangd/unittests/ClangdTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ClangdTests.cpp
+++ clang-tools-extra/clangd/unittests/ClangdTests.cpp
@@ -61,7 +61,7 @@
 
 class ErrorCheckingCallbacks : public ClangdServer::Callbacks {
 public:
-  void onDiagnosticsReady(PathRef File,
+  void onDiagnosticsReady(PathRef File, const llvm::json::Value &Version,
                           std::vector<Diag> Diagnostics) override {
     bool HadError = diagsContainErrors(Diagnostics);
     std::lock_guard<std::mutex> Lock(Mutex);
@@ -82,7 +82,7 @@
 /// least one error.
 class MultipleErrorCheckingCallbacks : public ClangdServer::Callbacks {
 public:
-  void onDiagnosticsReady(PathRef File,
+  void onDiagnosticsReady(PathRef File, const llvm::json::Value &Version,
                           std::vector<Diag> Diagnostics) override {
     bool HadError = diagsContainErrors(Diagnostics);
 
@@ -276,7 +276,7 @@
     mutable int Got;
   } FS;
   struct Callbacks : public ClangdServer::Callbacks {
-    void onDiagnosticsReady(PathRef File,
+    void onDiagnosticsReady(PathRef File, const llvm::json::Value &Version,
                             std::vector<Diag> Diagnostics) override {
       Got = Context::current().getExisting(Secret);
     }
@@ -295,6 +295,23 @@
   EXPECT_EQ(Callbacks.Got, 42);
 }
 
+TEST_F(ClangdVFSTest, PropagatesVersion) {
+  MockCompilationDatabase CDB;
+  MockFSProvider FS;
+  struct Callbacks : public ClangdServer::Callbacks {
+    void onDiagnosticsReady(PathRef File, const llvm::json::Value &Version,
+                            std::vector<Diag> Diagnostics) override {
+      Got = Version;
+    }
+    llvm::json::Value Got = nullptr;
+  } Callbacks;
+
+  // Verify that the version is plumbed to diagnostics.
+  ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &Callbacks);
+  runAddDocument(Server, testPath("foo.cpp"), "void main(){}", 42);
+  EXPECT_EQ(Callbacks.Got, 42);
+}
+
 // Only enable this test on Unix
 #ifdef LLVM_ON_UNIX
 TEST_F(ClangdVFSTest, SearchLibDir) {
@@ -374,7 +391,7 @@
 
   // Now switch to C++ mode.
   CDB.ExtraClangFlags = {"-xc++"};
-  runAddDocument(Server, FooCpp, SourceContents2, WantDiagnostics::Auto);
+  runAddDocument(Server, FooCpp, SourceContents2);
   EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
   // Subsequent addDocument calls should finish without errors too.
   runAddDocument(Server, FooCpp, SourceContents1);
@@ -406,7 +423,7 @@
 
   // Parse without the define, no errors should be produced.
   CDB.ExtraClangFlags = {};
-  runAddDocument(Server, FooCpp, SourceContents, WantDiagnostics::Auto);
+  runAddDocument(Server, FooCpp, SourceContents);
   ASSERT_TRUE(Server.blockUntilIdleForTest());
   EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
   // Subsequent addDocument call should finish without errors too.
@@ -467,8 +484,8 @@
   CDB.ExtraClangFlags.clear();
   DiagConsumer.clear();
   Server.removeDocument(BazCpp);
-  Server.addDocument(FooCpp, FooSource.code(), WantDiagnostics::Auto);
-  Server.addDocument(BarCpp, BarSource.code(), WantDiagnostics::Auto);
+  Server.addDocument(FooCpp, FooSource.code());
+  Server.addDocument(BarCpp, BarSource.code());
   ASSERT_TRUE(Server.blockUntilIdleForTest());
 
   EXPECT_THAT(DiagConsumer.filesWithDiags(),
@@ -595,7 +612,7 @@
   public:
     TestDiagConsumer() : Stats(FilesCount, FileStat()) {}
 
-    void onDiagnosticsReady(PathRef File,
+    void onDiagnosticsReady(PathRef File, const llvm::json::Value &Version,
                             std::vector<Diag> Diagnostics) override {
       StringRef FileIndexStr = llvm::sys::path::stem(File);
       ASSERT_TRUE(FileIndexStr.consume_front("Foo"));
@@ -672,8 +689,7 @@
       bool ShouldHaveErrors = ShouldHaveErrorsDist(RandGen);
       Server.addDocument(FilePaths[FileIndex],
                          ShouldHaveErrors ? SourceContentsWithErrors
-                                          : SourceContentsWithoutErrors,
-                         WantDiagnostics::Auto);
+                                          : SourceContentsWithoutErrors);
       UpdateStatsOnAddDocument(FileIndex, ShouldHaveErrors);
     };
 
@@ -775,7 +791,8 @@
     NoConcurrentAccessDiagConsumer(std::promise<void> StartSecondReparse)
         : StartSecondReparse(std::move(StartSecondReparse)) {}
 
-    void onDiagnosticsReady(PathRef, std::vector<Diag>) override {
+    void onDiagnosticsReady(PathRef, const llvm::json::Value &,
+                            std::vector<Diag>) override {
       ++Count;
       std::unique_lock<std::mutex> Lock(Mutex, std::try_to_lock_t());
       ASSERT_TRUE(Lock.owns_lock())
Index: clang-tools-extra/clangd/test/version.test
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/test/version.test
@@ -0,0 +1,25 @@
+# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
+# Verify versions get recorded/inferred, and are reported in publishDiagnostics.
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{}}
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":""}}}
+# CHECK:    "version": 0
+---
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.c","version":5},"contentChanges":[{"text":"a"}]}}
+# CHECK:    "version": 5
+---
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.c"},"contentChanges":[{"text":"b"}]}}
+# CHECK:    "version": 6
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///bar.c","version": 42, "languageId":"c","text":""}}}
+# CHECK:    "version": 42
+---
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///bar.c"},"contentChanges":[{"text":"c"}]}}
+# CHECK:    "version": 43
+---
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///bar.c", "version": 123},"contentChanges":[{"text":"d"}]}}
+# CHECK:    "version": 123
+---
+{"jsonrpc":"2.0","id":6,"method":"shutdown"}
+---
+{"jsonrpc":"2.0","method":"exit"}
Index: clang-tools-extra/clangd/test/semantic-highlighting.test
===================================================================
--- clang-tools-extra/clangd/test/semantic-highlighting.test
+++ clang-tools-extra/clangd/test/semantic-highlighting.test
@@ -67,7 +67,7 @@
 # CHECK-NEXT:        ]
 # CHECK-NEXT:      },
 ---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.cpp","languageId":"cpp","version":1,"text":"int x = 2;"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.cpp","languageId":"cpp","text":"int x = 2;"}}}
 #      CHECK:  "method": "textDocument/semanticHighlighting",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "lines": [
@@ -78,12 +78,13 @@
 # CHECK-NEXT:      }
 # CHECK-NEXT:    ],
 # CHECK-NEXT:    "textDocument": {
-# CHECK-NEXT:      "uri": "file://{{.*}}/clangd-test/foo.cpp"
+# CHECK-NEXT:      "uri": "file://{{.*}}/clangd-test/foo.cpp",
+# CHECK-NEXT:      "version": 0
 # CHECK-NEXT:    }
 # CHECK-NEXT:  }
 # CHECK-NEXT:}
 ---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo2.cpp","languageId":"cpp","version":1,"text":"int x = 2;\nint y = 2;"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo2.cpp","languageId":"cpp","text":"int x = 2;\nint y = 2;"}}}
 #      CHECK:  "method": "textDocument/semanticHighlighting",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "lines": [
@@ -99,12 +100,13 @@
 # CHECK-NEXT:      }
 # CHECK-NEXT:    ],
 # CHECK-NEXT:    "textDocument": {
-# CHECK-NEXT:      "uri": "file://{{.*}}/clangd-test/foo2.cpp"
+# CHECK-NEXT:      "uri": "file://{{.*}}/clangd-test/foo2.cpp",
+# CHECK-NEXT:      "version": 0
 # CHECK-NEXT:    }
 # CHECK-NEXT:  }
 # CHECK-NEXT:}
 ---
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.cpp","version":2},"contentChanges": [{"range":{"start": {"line": 0,"character": 10},"end": {"line": 0,"character": 10}},"rangeLength": 0,"text": "\nint y = 2;"}]}}
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.cpp"},"contentChanges": [{"range":{"start": {"line": 0,"character": 10},"end": {"line": 0,"character": 10}},"rangeLength": 0,"text": "\nint y = 2;"}]}}
 #      CHECK:  "method": "textDocument/semanticHighlighting",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "lines": [
@@ -115,12 +117,13 @@
 # CHECK-NEXT:      }
 # CHECK-NEXT:   ],
 # CHECK-NEXT:    "textDocument": {
-# CHECK-NEXT:      "uri": "file://{{.*}}/clangd-test/foo.cpp"
+# CHECK-NEXT:      "uri": "file://{{.*}}/clangd-test/foo.cpp",
+# CHECK-NEXT:      "version": 1
 # CHECK-NEXT:    }
 # CHECK-NEXT:  }
 # CHECK-NEXT:}
 ---
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.cpp","version":2},"contentChanges": [{"range":{"start": {"line": 0,"character": 10},"end": {"line": 1,"character": 10}},"rangeLength": 11,"text": ""}]}}
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.cpp"},"contentChanges": [{"range":{"start": {"line": 0,"character": 10},"end": {"line": 1,"character": 10}},"rangeLength": 11,"text": ""}]}}
 #      CHECK:  "method": "textDocument/semanticHighlighting",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "lines": [
@@ -131,7 +134,8 @@
 # CHECK-NEXT:      }
 # CHECK-NEXT:   ],
 # CHECK-NEXT:    "textDocument": {
-# CHECK-NEXT:      "uri": "file://{{.*}}/clangd-test/foo.cpp"
+# CHECK-NEXT:      "uri": "file://{{.*}}/clangd-test/foo.cpp",
+# CHECK-NEXT:      "version": 2
 # CHECK-NEXT:    }
 # CHECK-NEXT:  }
 # CHECK-NEXT:}
Index: clang-tools-extra/clangd/test/path-mappings.test
===================================================================
--- clang-tools-extra/clangd/test/path-mappings.test
+++ clang-tools-extra/clangd/test/path-mappings.test
@@ -12,7 +12,6 @@
     "textDocument": {
       "uri": "file:///C:/client/bar.cpp",
       "languageId": "cpp",
-      "version": 1,
       "text": "#include \"foo.h\"\nint main(){\nreturn foo();\n}"
     }
   }
@@ -21,7 +20,8 @@
 #      CHECK:  "method": "textDocument/publishDiagnostics",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [],
-# CHECK-NEXT:    "uri": "file:///C:/client/bar.cpp"
+# CHECK-NEXT:    "uri": "file:///C:/client/bar.cpp",
+# CHECK-NEXT:    "version": 0
 # CHECK-NEXT:  }
 ---
 # We're editing bar.cpp, which includes foo.h, where foo.h "exists" at a server location 
@@ -47,7 +47,7 @@
 # CHECK-NEXT:      "range": {
 # CHECK-NEXT:        "end": {
 # CHECK-NEXT:          "character": {{[0-9]+}},
-# CHECK-NEXT:          "line": {{[0-9]+}} 
+# CHECK-NEXT:          "line": {{[0-9]+}}
 # CHECK-NEXT:        },
 # CHECK-NEXT:        "start": {
 # CHECK-NEXT:          "character": {{[0-9]+}},
Index: clang-tools-extra/clangd/test/fixits-embed-in-diagnostic.test
===================================================================
--- clang-tools-extra/clangd/test/fixits-embed-in-diagnostic.test
+++ clang-tools-extra/clangd/test/fixits-embed-in-diagnostic.test
@@ -1,7 +1,7 @@
 # RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"textDocument":{"publishDiagnostics":{"codeActionsInline":true}}},"trace":"off"}}
 ---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"struct Point {}; union Point p;"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"struct Point {}; union Point p;"}}}
 #      CHECK:  "method": "textDocument/publishDiagnostics",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [
@@ -61,7 +61,8 @@
 # CHECK-NEXT:        "severity": 3
 # CHECK-NEXT:      }
 # CHECK-NEXT:    ],
-# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT:    "version": 0
 # CHECK-NEXT:  }
 ---
 {"jsonrpc":"2.0","id":4,"method":"shutdown"}
Index: clang-tools-extra/clangd/test/fixits-command.test
===================================================================
--- clang-tools-extra/clangd/test/fixits-command.test
+++ clang-tools-extra/clangd/test/fixits-command.test
@@ -1,7 +1,7 @@
 # RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
 ---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"int main(int i, char **a) { if (i = 2) {}}"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"int main(int i, char **a) { if (i = 2) {}}"}}}
 #      CHECK:    "method": "textDocument/publishDiagnostics",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [
@@ -22,7 +22,8 @@
 # CHECK-NEXT:        "source": "clang"
 # CHECK-NEXT:      }
 # CHECK-NEXT:    ],
-# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT:    "version": 0
 # CHECK-NEXT:  }
 ---
 {"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":0,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses (fixes available)"}]}}}
Index: clang-tools-extra/clangd/test/fixits-codeaction.test
===================================================================
--- clang-tools-extra/clangd/test/fixits-codeaction.test
+++ clang-tools-extra/clangd/test/fixits-codeaction.test
@@ -1,7 +1,7 @@
 # RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"textDocument":{"codeAction":{"codeActionLiteralSupport":{}}}},"trace":"off"}}
 ---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"int main(int i, char **a) { if (i = 2) {}}"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"int main(int i, char **a) { if (i = 2) {}}"}}}
 #      CHECK:    "method": "textDocument/publishDiagnostics",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [
@@ -22,7 +22,8 @@
 # CHECK-NEXT:        "source": "clang"
 # CHECK-NEXT:      }
 # CHECK-NEXT:    ],
-# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT:    "version": 0
 # CHECK-NEXT:  }
 ---
 {"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":0,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses (fixes available)", "code": "-Wparentheses", "source": "clang"}]}}}
Index: clang-tools-extra/clangd/test/execute-command.test
===================================================================
--- clang-tools-extra/clangd/test/execute-command.test
+++ clang-tools-extra/clangd/test/execute-command.test
@@ -1,7 +1,7 @@
 # RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
 ---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"int main(int i, char **a) { if (i = 2) {}}"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"int main(int i, char **a) { if (i = 2) {}}"}}}
 #      CHECK:  "method": "textDocument/publishDiagnostics",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [
@@ -22,7 +22,8 @@
 # CHECK-NEXT:        "source": "clang"
 # CHECK-NEXT:      }
 # CHECK-NEXT:    ],
-# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT:    "version": 0
 # CHECK-NEXT:  }
 ---
 # No command name
Index: clang-tools-extra/clangd/test/did-change-configuration-params.test
===================================================================
--- clang-tools-extra/clangd/test/did-change-configuration-params.test
+++ clang-tools-extra/clangd/test/did-change-configuration-params.test
@@ -5,18 +5,20 @@
 ---
 {"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabaseChanges":{"/clangd-test/foo.c": {"workingDirectory":"/clangd-test", "compilationCommand": ["clang", "-c", "foo.c"]}}}}}
 ---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"int main() { int i; return i; }"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"int main() { int i; return i; }"}}}
 #      CHECK:  "method": "textDocument/publishDiagnostics",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [],
-# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT:    "version": 0
 # CHECK-NEXT:  }
 ---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///bar.c","languageId":"c","version":1,"text":"int main() { int i; return i; }"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///bar.c","languageId":"c","text":"int main() { int i; return i; }"}}}
 #      CHECK:  "method": "textDocument/publishDiagnostics",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [],
-# CHECK-NEXT:    "uri": "file://{{.*}}/bar.c"
+# CHECK-NEXT:    "uri": "file://{{.*}}/bar.c",
+# CHECK-NEXT:    "version": 0
 # CHECK-NEXT:  }
 ---
 {"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabaseChanges":{"/clangd-test/foo.c": {"workingDirectory":"/clangd-test2", "compilationCommand": ["clang", "-c", "foo.c", "-Wall", "-Werror"]}}}}}
@@ -40,10 +42,11 @@
 # CHECK-NEXT:        "source": "clang"
 # CHECK-NEXT:      }
 # CHECK-NEXT:    ],
-# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT:    "version": 0
 # CHECK-NEXT:  }
 #
-# ERR: Updating file {{.*}}foo.c with command
+# ERR: ASTWorker building file {{.*}}foo.c version 0 with command
 # ERR: [{{.*}}clangd-test2]
 # ERR: clang -c foo.c -Wall -Werror
 ---
Index: clang-tools-extra/clangd/test/diagnostics.test
===================================================================
--- clang-tools-extra/clangd/test/diagnostics.test
+++ clang-tools-extra/clangd/test/diagnostics.test
@@ -1,7 +1,7 @@
 # RUN: clangd -lit-test -clang-tidy-checks=bugprone-sizeof-expression < %s | FileCheck -strict-whitespace %s
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
 ---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"void main() {\n(void)sizeof(42);\n}"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"void main() {\n(void)sizeof(42);\n}"}}}
 #      CHECK:  "method": "textDocument/publishDiagnostics",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [
@@ -38,7 +38,8 @@
 # CHECK-NEXT:        "source": "clang-tidy"
 # CHECK-NEXT:      }
 # CHECK-NEXT:    ],
-# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT:    "version": 0
 # CHECK-NEXT:  }
 ---
 {"jsonrpc":"2.0","id":2,"method":"sync","params":null}
@@ -47,7 +48,8 @@
 #      CHECK:  "method": "textDocument/publishDiagnostics",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [],
-# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT:    "version": null
 # CHECK-NEXT:  }
 ---
 {"jsonrpc":"2.0","id":5,"method":"shutdown"}
Index: clang-tools-extra/clangd/test/diagnostics-notes.test
===================================================================
--- clang-tools-extra/clangd/test/diagnostics-notes.test
+++ clang-tools-extra/clangd/test/diagnostics-notes.test
@@ -1,7 +1,7 @@
 # RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"textDocument":{"publishDiagnostics":{"relatedInformation":true}}},"trace":"off"}}
 ---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.cc","languageId":"cpp","version":1,"text":"int x;\nint x;"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.cc","languageId":"cpp","text":"int x;\nint x;"}}}
 #      CHECK:  "method": "textDocument/publishDiagnostics",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [
@@ -40,7 +40,8 @@
 # CHECK-NEXT:        "source": "clang"
 # CHECK-NEXT:      }
 # CHECK-NEXT:    ],
-# CHECK-NEXT:    "uri": "file://{{.*}}/foo.cc"
+# CHECK-NEXT:    "uri": "file://{{.*}}/foo.cc",
+# CHECK-NEXT:    "version": 0
 # CHECK-NEXT:  }
 ---
 {"jsonrpc":"2.0","id":5,"method":"shutdown"}
Index: clang-tools-extra/clangd/test/diagnostics-no-tidy.test
===================================================================
--- clang-tools-extra/clangd/test/diagnostics-no-tidy.test
+++ clang-tools-extra/clangd/test/diagnostics-no-tidy.test
@@ -1,7 +1,7 @@
 # RUN: clangd -lit-test -clang-tidy=false < %s | FileCheck -strict-whitespace %s
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
 ---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"void main() {\n(void)sizeof(42);\n}"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"void main() {\n(void)sizeof(42);\n}"}}}
 #      CHECK:  "method": "textDocument/publishDiagnostics",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [
@@ -22,7 +22,8 @@
 # CHECK-NEXT:        "source": "clang"
 # CHECK-NEXT:      }
 # CHECK-NEXT:    ],
-# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT:    "version": 0
 # CHECK-NEXT:  }
 ---
 {"jsonrpc":"2.0","id":2,"method":"sync","params":null}
@@ -31,7 +32,8 @@
 #      CHECK:  "method": "textDocument/publishDiagnostics",
 # CHECK-NEXT:  "params": {
 # CHECK-NEXT:    "diagnostics": [],
-# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT:    "version": null
 # CHECK-NEXT:  }
 ---
 {"jsonrpc":"2.0","id":5,"method":"shutdown"}
Index: clang-tools-extra/clangd/test/diagnostic-category.test
===================================================================
--- clang-tools-extra/clangd/test/diagnostic-category.test
+++ clang-tools-extra/clangd/test/diagnostic-category.test
@@ -1,7 +1,7 @@
 # RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"textDocument":{"publishDiagnostics":{"categorySupport":true}}},"trace":"off"}}
 ---
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"struct Point {}; union Point p;"}}}
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"struct Point {}; union Point p;"}}}
 #      CHECK:    "method": "textDocument/publishDiagnostics",
 # CHECK-NEXT:    "params": {
 # CHECK-NEXT:     "diagnostics": [
@@ -37,7 +37,8 @@
 # CHECK-NEXT:        "severity": 3
 # CHECK-NEXT:      }
 # CHECK-NEXT:    ],
-# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT:    "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT:    "version": 0
 # CHECK-NEXT:  }
 ---
 {"jsonrpc":"2.0","id":4,"method":"shutdown"}
Index: clang-tools-extra/clangd/index/FileIndex.h
===================================================================
--- clang-tools-extra/clangd/index/FileIndex.h
+++ clang-tools-extra/clangd/index/FileIndex.h
@@ -98,8 +98,8 @@
 
   /// Update preamble symbols of file \p Path with all declarations in \p AST
   /// and macros in \p PP.
-  void updatePreamble(PathRef Path, ASTContext &AST,
-                      std::shared_ptr<Preprocessor> PP,
+  void updatePreamble(PathRef Path, const llvm::json::Value &Version,
+                      ASTContext &AST, std::shared_ptr<Preprocessor> PP,
                       const CanonicalIncludes &Includes);
 
   /// Update symbols and references from main file \p Path with
@@ -142,7 +142,8 @@
 
 /// Index declarations from \p AST and macros from \p PP that are declared in
 /// included headers.
-SlabTuple indexHeaderSymbols(ASTContext &AST, std::shared_ptr<Preprocessor> PP,
+SlabTuple indexHeaderSymbols(const llvm::json::Value &Version, ASTContext &AST,
+                             std::shared_ptr<Preprocessor> PP,
                              const CanonicalIncludes &Includes);
 
 } // namespace clangd
Index: clang-tools-extra/clangd/index/FileIndex.cpp
===================================================================
--- clang-tools-extra/clangd/index/FileIndex.cpp
+++ clang-tools-extra/clangd/index/FileIndex.cpp
@@ -35,7 +35,8 @@
                               llvm::ArrayRef<Decl *> DeclsToIndex,
                               const MainFileMacros *MacroRefsToIndex,
                               const CanonicalIncludes &Includes,
-                              bool IsIndexMainAST) {
+                              bool IsIndexMainAST,
+                              const llvm::json::Value &Version) {
   SymbolCollector::Options CollectorOpts;
   CollectorOpts.CollectIncludePath = true;
   CollectorOpts.Includes = &Includes;
@@ -74,12 +75,13 @@
   auto Refs = Collector.takeRefs();
   auto Relations = Collector.takeRelations();
 
-  vlog("index AST for {0} (main={1}): \n"
-       "  symbol slab: {2} symbols, {3} bytes\n"
-       "  ref slab: {4} symbols, {5} refs, {6} bytes\n"
-       "  relations slab: {7} relations, {8} bytes",
-       FileName, IsIndexMainAST, Syms.size(), Syms.bytes(), Refs.size(),
-       Refs.numRefs(), Refs.bytes(), Relations.size(), Relations.bytes());
+  vlog("indexed {0} AST for {1} version {2}:\n"
+       "  symbol slab: {3} symbols, {4} bytes\n"
+       "  ref slab: {5} symbols, {6} refs, {7} bytes\n"
+       "  relations slab: {8} relations, {9} bytes",
+       IsIndexMainAST ? "file" : "preamble", FileName, Version, Syms.size(),
+       Syms.bytes(), Refs.size(), Refs.numRefs(), Refs.bytes(),
+       Relations.size(), Relations.bytes());
   return std::make_tuple(std::move(Syms), std::move(Refs),
                          std::move(Relations));
 }
@@ -88,17 +90,18 @@
   return indexSymbols(AST.getASTContext(), AST.getPreprocessorPtr(),
                       AST.getLocalTopLevelDecls(), &AST.getMacros(),
                       AST.getCanonicalIncludes(),
-                      /*IsIndexMainAST=*/true);
+                      /*IsIndexMainAST=*/true, AST.version());
 }
 
-SlabTuple indexHeaderSymbols(ASTContext &AST, std::shared_ptr<Preprocessor> PP,
+SlabTuple indexHeaderSymbols(const llvm::json::Value &Version, ASTContext &AST,
+                             std::shared_ptr<Preprocessor> PP,
                              const CanonicalIncludes &Includes) {
   std::vector<Decl *> DeclsToIndex(
       AST.getTranslationUnitDecl()->decls().begin(),
       AST.getTranslationUnitDecl()->decls().end());
   return indexSymbols(AST, std::move(PP), DeclsToIndex,
                       /*MainFileMacros=*/nullptr, Includes,
-                      /*IsIndexMainAST=*/false);
+                      /*IsIndexMainAST=*/false, Version);
 }
 
 void FileSymbols::update(PathRef Path, std::unique_ptr<SymbolSlab> Symbols,
@@ -248,10 +251,11 @@
       PreambleIndex(std::make_unique<MemIndex>()),
       MainFileIndex(std::make_unique<MemIndex>()) {}
 
-void FileIndex::updatePreamble(PathRef Path, ASTContext &AST,
+void FileIndex::updatePreamble(PathRef Path, const llvm::json::Value &Version,
+                               ASTContext &AST,
                                std::shared_ptr<Preprocessor> PP,
                                const CanonicalIncludes &Includes) {
-  auto Slabs = indexHeaderSymbols(AST, std::move(PP), Includes);
+  auto Slabs = indexHeaderSymbols(Version, AST, std::move(PP), Includes);
   PreambleSymbols.update(
       Path, std::make_unique<SymbolSlab>(std::move(std::get<0>(Slabs))),
       std::make_unique<RefSlab>(),
Index: clang-tools-extra/clangd/TUScheduler.h
===================================================================
--- clang-tools-extra/clangd/TUScheduler.h
+++ clang-tools-extra/clangd/TUScheduler.h
@@ -122,7 +122,8 @@
   /// Called on the AST that was built for emitting the preamble. The built AST
   /// contains only AST nodes from the #include directives at the start of the
   /// file. AST node in the current file should be observed on onMainAST call.
-  virtual void onPreambleAST(PathRef Path, ASTContext &Ctx,
+  virtual void onPreambleAST(PathRef Path, const llvm::json::Value &Version,
+                             ASTContext &Ctx,
                              std::shared_ptr<clang::Preprocessor> PP,
                              const CanonicalIncludes &) {}
 
@@ -153,8 +154,8 @@
 
   /// Called whenever the AST fails to build. \p Diags will have the diagnostics
   /// that led to failure.
-  virtual void onFailedAST(PathRef Path, std::vector<Diag> Diags,
-                           PublishFn Publish) {}
+  virtual void onFailedAST(PathRef Path, const llvm::json::Value &Version,
+                           std::vector<Diag> Diags, PublishFn Publish) {}
 
   /// Called whenever the TU status is updated.
   virtual void onFileUpdated(PathRef File, const TUStatus &Status) {}
Index: clang-tools-extra/clangd/TUScheduler.cpp
===================================================================
--- clang-tools-extra/clangd/TUScheduler.cpp
+++ clang-tools-extra/clangd/TUScheduler.cpp
@@ -371,7 +371,7 @@
 }
 
 void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) {
-  llvm::StringRef TaskName = "Update";
+  std::string TaskName = llvm::formatv("Update ({0})", Inputs.Version);
   auto Task = [=]() mutable {
     auto RunPublish = [&](llvm::function_ref<void()> Publish) {
       // Ensure we only publish results from the worker if the file was not
@@ -405,8 +405,8 @@
     }
     RanASTCallback = false;
     emitTUStatus({TUAction::BuildingPreamble, TaskName});
-    log("Updating file {0} with command {1}\n[{2}]\n{3}", FileName,
-        Inputs.CompileCommand.Heuristic,
+    log("ASTWorker building file {0} version {1} with command {2}\n[{3}]\n{4}",
+        FileName, Inputs.Version, Inputs.CompileCommand.Heuristic,
         Inputs.CompileCommand.Directory,
         llvm::join(Inputs.CompileCommand.CommandLine, " "));
     // Rebuild the preamble and the AST.
@@ -427,8 +427,8 @@
       Details.BuildFailed = true;
       emitTUStatus({TUAction::BuildingPreamble, TaskName}, &Details);
       // Report the diagnostics we collected when parsing the command line.
-      Callbacks.onFailedAST(FileName, std::move(CompilerInvocationDiags),
-                            RunPublish);
+      Callbacks.onFailedAST(FileName, Inputs.Version,
+                            std::move(CompilerInvocationDiags), RunPublish);
       // Make sure anyone waiting for the preamble gets notified it could not
       // be built.
       PreambleWasBuilt.notify();
@@ -441,9 +441,11 @@
     std::shared_ptr<const PreambleData> NewPreamble = buildPreamble(
         FileName, *Invocation, OldPreamble, OldCommand, Inputs,
         StorePreambleInMemory,
-        [this](ASTContext &Ctx, std::shared_ptr<clang::Preprocessor> PP,
-               const CanonicalIncludes &CanonIncludes) {
-          Callbacks.onPreambleAST(FileName, Ctx, std::move(PP), CanonIncludes);
+        [this, Version(Inputs.Version)](
+            ASTContext &Ctx, std::shared_ptr<clang::Preprocessor> PP,
+            const CanonicalIncludes &CanonIncludes) {
+          Callbacks.onPreambleAST(FileName, Version, Ctx, std::move(PP),
+                                  CanonIncludes);
         });
 
     bool CanReuseAST = InputsAreTheSame && (OldPreamble == NewPreamble);
@@ -537,7 +539,8 @@
       // line if there were any.
       // FIXME: we might have got more errors while trying to build the AST,
       //        surface them too.
-      Callbacks.onFailedAST(FileName, CompilerInvocationDiags, RunPublish);
+      Callbacks.onFailedAST(FileName, Inputs.Version, CompilerInvocationDiags,
+                            RunPublish);
     }
     // Stash the AST in the cache for further use.
     IdleASTs.put(this, std::move(*AST));
@@ -558,6 +561,8 @@
       std::unique_ptr<CompilerInvocation> Invocation = buildCompilerInvocation(
           *CurrentInputs, CompilerInvocationDiagConsumer);
       // Try rebuilding the AST.
+      vlog("ASTWorker rebuilding evicted AST to run {0}: {1} version {2}", Name,
+           FileName, CurrentInputs->Version);
       llvm::Optional<ParsedAST> NewAST =
           Invocation
               ? buildAST(FileName,
@@ -574,6 +579,8 @@
     if (!*AST)
       return Action(llvm::make_error<llvm::StringError>(
           "invalid AST", llvm::errc::invalid_argument));
+    vlog("ASTWorker running {0} on version {2} of {1}", Name, FileName,
+         CurrentInputs->Version);
     Action(InputsAndAST{*CurrentInputs, **AST});
   };
   startTask(Name, std::move(Task), /*UpdateType=*/None);
@@ -763,8 +770,10 @@
       I->UpdateType = WantDiagnostics::Auto;
   }
 
-  while (shouldSkipHeadLocked())
+  while (shouldSkipHeadLocked()) {
+    vlog("ASTWorker skipping {0}", Requests.front().Name);
     Requests.pop_front();
+  }
   assert(!Requests.empty() && "skipped the whole queue");
   // Some updates aren't dead yet, but never end up being used.
   // e.g. the first keystroke is live until obsoleted by the second.
Index: clang-tools-extra/clangd/Protocol.h
===================================================================
--- clang-tools-extra/clangd/Protocol.h
+++ clang-tools-extra/clangd/Protocol.h
@@ -125,14 +125,16 @@
 bool fromJSON(const llvm::json::Value &, TextDocumentIdentifier &);
 
 struct VersionedTextDocumentIdentifier : public TextDocumentIdentifier {
-  // The version number of this document. If a versioned text document
-  // identifier is sent from the server to the client and the file is not open
-  // in the editor (the server has not received an open notification before) the
-  // server can send `null` to indicate that the version is known and the
-  // content on disk is the master (as speced with document content ownership).
-  //
-  // The version number of a document will increase after each change, including
-  // undo/redo. The number doesn't need to be consecutive.
+  /// The version number of this document. If a versioned text document
+  /// identifier is sent from the server to the client and the file is not open
+  /// in the editor (the server has not received an open notification before)
+  /// the server can send `null` to indicate that the version is known and the
+  /// content on disk is the master (as speced with document content ownership).
+  ///
+  /// The version number of a document will increase after each change,
+  /// including undo/redo. The number doesn't need to be consecutive.
+  ///
+  /// clangd extension: versions are optional, and synthesized if missing.
   llvm::Optional<std::int64_t> version;
 };
 llvm::json::Value toJSON(const VersionedTextDocumentIdentifier &);
@@ -237,7 +239,10 @@
   std::string languageId;
 
   /// The version number of this document (it will strictly increase after each
-  std::int64_t version = 0;
+  /// change, including undo/redo.
+  ///
+  /// clangd extension: versions are optional, and synthesized if missing.
+  llvm::Optional<int64_t> version;
 
   /// The content of the opened text document.
   std::string text;
@@ -811,6 +816,8 @@
   URIForFile uri;
   /// An array of diagnostic information items.
   std::vector<Diagnostic> diagnostics;
+  /// The version number of the document the diagnostics are published for.
+  llvm::Optional<int64_t> version;
 };
 llvm::json::Value toJSON(const PublishDiagnosticsParams &);
 
@@ -1353,7 +1360,7 @@
 /// Parameters for the semantic highlighting (server-side) push notification.
 struct SemanticHighlightingParams {
   /// The textdocument these highlightings belong to.
-  TextDocumentIdentifier TextDocument;
+  VersionedTextDocumentIdentifier TextDocument;
   /// The lines of highlightings that should be sent.
   std::vector<SemanticHighlightingInformation> Lines;
 };
Index: clang-tools-extra/clangd/Protocol.cpp
===================================================================
--- clang-tools-extra/clangd/Protocol.cpp
+++ clang-tools-extra/clangd/Protocol.cpp
@@ -92,8 +92,7 @@
 
 llvm::json::Value toJSON(const VersionedTextDocumentIdentifier &R) {
   auto Result = toJSON(static_cast<const TextDocumentIdentifier &>(R));
-  if (R.version)
-    Result.getAsObject()->try_emplace("version", R.version);
+  Result.getAsObject()->try_emplace("version", R.version);
   return Result;
 }
 
@@ -547,8 +546,9 @@
 
 llvm::json::Value toJSON(const PublishDiagnosticsParams &PDP) {
   return llvm::json::Object{
-    {"uri", PDP.uri},
-    {"diagnostics", PDP.diagnostics},
+      {"uri", PDP.uri},
+      {"diagnostics", PDP.diagnostics},
+      {"version", PDP.version},
   };
 }
 
Index: clang-tools-extra/clangd/Preamble.h
===================================================================
--- clang-tools-extra/clangd/Preamble.h
+++ clang-tools-extra/clangd/Preamble.h
@@ -43,11 +43,14 @@
 /// As we must avoid re-parsing the preamble, any information that can only
 /// be obtained during parsing must be eagerly captured and stored here.
 struct PreambleData {
-  PreambleData(PrecompiledPreamble Preamble, std::vector<Diag> Diags,
-               IncludeStructure Includes, MainFileMacros Macros,
+  PreambleData(llvm::json::Value Version, PrecompiledPreamble Preamble,
+               std::vector<Diag> Diags, IncludeStructure Includes,
+               MainFileMacros Macros,
                std::unique_ptr<PreambleFileStatusCache> StatCache,
                CanonicalIncludes CanonIncludes);
 
+  // Version of the ParseInputs this preamble was built from.
+  llvm::json::Value Version;
   tooling::CompileCommand CompileCommand;
   PrecompiledPreamble Preamble;
   std::vector<Diag> Diags;
Index: clang-tools-extra/clangd/Preamble.cpp
===================================================================
--- clang-tools-extra/clangd/Preamble.cpp
+++ clang-tools-extra/clangd/Preamble.cpp
@@ -75,15 +75,16 @@
 
 } // namespace
 
-PreambleData::PreambleData(PrecompiledPreamble Preamble,
+PreambleData::PreambleData(llvm::json::Value Version,
+                           PrecompiledPreamble Preamble,
                            std::vector<Diag> Diags, IncludeStructure Includes,
                            MainFileMacros Macros,
                            std::unique_ptr<PreambleFileStatusCache> StatCache,
                            CanonicalIncludes CanonIncludes)
-    : Preamble(std::move(Preamble)), Diags(std::move(Diags)),
-      Includes(std::move(Includes)), Macros(std::move(Macros)),
-      StatCache(std::move(StatCache)), CanonIncludes(std::move(CanonIncludes)) {
-}
+    : Version(std::move(Version)), Preamble(std::move(Preamble)),
+      Diags(std::move(Diags)), Includes(std::move(Includes)),
+      Macros(std::move(Macros)), StatCache(std::move(StatCache)),
+      CanonIncludes(std::move(CanonIncludes)) {}
 
 std::shared_ptr<const PreambleData>
 buildPreamble(PathRef FileName, CompilerInvocation &CI,
@@ -102,12 +103,17 @@
       compileCommandsAreEqual(Inputs.CompileCommand, OldCompileCommand) &&
       OldPreamble->Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds,
                                      Inputs.FS.get())) {
-    vlog("Reusing preamble for {0}", FileName);
+    vlog("Reusing preamble version {0} for version {1} of {2}",
+         OldPreamble->Version, Inputs.Version, FileName);
     return OldPreamble;
   }
-  vlog(OldPreamble ? "Rebuilding invalidated preamble for {0}"
-                   : "Building first preamble for {0}",
-       FileName);
+  if (OldPreamble)
+    vlog("Rebuilding invalidated preamble for {0} version {1} "
+         "(previous was version {2})",
+         FileName, Inputs.Version, OldPreamble->Version);
+  else
+    vlog("Building first preamble for {0} verson {1}", FileName,
+         Inputs.Version);
 
   trace::Span Tracer("BuildPreamble");
   SPAN_ATTACH(Tracer, "File", FileName);
@@ -145,16 +151,17 @@
   CI.getFrontendOpts().SkipFunctionBodies = false;
 
   if (BuiltPreamble) {
-    vlog("Built preamble of size {0} for file {1}", BuiltPreamble->getSize(),
-         FileName);
+    vlog("Built preamble of size {0} for file {1} version {2}",
+         BuiltPreamble->getSize(), FileName, Inputs.Version);
     std::vector<Diag> Diags = PreambleDiagnostics.take();
     return std::make_shared<PreambleData>(
-        std::move(*BuiltPreamble), std::move(Diags),
+        Inputs.Version, std::move(*BuiltPreamble), std::move(Diags),
         SerializedDeclsCollector.takeIncludes(),
         SerializedDeclsCollector.takeMacros(), std::move(StatCache),
         SerializedDeclsCollector.takeCanonicalIncludes());
   } else {
-    elog("Could not build a preamble for file {0}", FileName);
+    elog("Could not build a preamble for file {0} version {2}", FileName,
+         Inputs.Version);
     return nullptr;
   }
 }
Index: clang-tools-extra/clangd/ParsedAST.h
===================================================================
--- clang-tools-extra/clangd/ParsedAST.h
+++ clang-tools-extra/clangd/ParsedAST.h
@@ -48,7 +48,8 @@
   /// Attempts to run Clang and store parsed AST. If \p Preamble is non-null
   /// it is reused during parsing.
   static llvm::Optional<ParsedAST>
-  build(std::unique_ptr<clang::CompilerInvocation> CI,
+  build(llvm::json::Value Version,
+        std::unique_ptr<clang::CompilerInvocation> CI,
         llvm::ArrayRef<Diag> CompilerInvocationDiags,
         std::shared_ptr<const PreambleData> Preamble,
         std::unique_ptr<llvm::MemoryBuffer> Buffer,
@@ -101,14 +102,19 @@
   /// (!) does not have tokens from the preamble.
   const syntax::TokenBuffer &getTokens() const { return Tokens; }
 
+  /// Returns the version of the ParseInputs this AST was built from.
+  const llvm::json::Value &version() const { return Version; }
+
 private:
-  ParsedAST(std::shared_ptr<const PreambleData> Preamble,
+  ParsedAST(llvm::json::Value Version,
+            std::shared_ptr<const PreambleData> Preamble,
             std::unique_ptr<CompilerInstance> Clang,
             std::unique_ptr<FrontendAction> Action, syntax::TokenBuffer Tokens,
             MainFileMacros Macros, std::vector<Decl *> LocalTopLevelDecls,
             std::vector<Diag> Diags, IncludeStructure Includes,
             CanonicalIncludes CanonIncludes);
 
+  llvm::json::Value Version;
   // In-memory preambles must outlive the AST, it is important that this member
   // goes before Clang and Action.
   std::shared_ptr<const PreambleData> Preamble;
Index: clang-tools-extra/clangd/ParsedAST.cpp
===================================================================
--- clang-tools-extra/clangd/ParsedAST.cpp
+++ clang-tools-extra/clangd/ParsedAST.cpp
@@ -215,7 +215,8 @@
 }
 
 llvm::Optional<ParsedAST>
-ParsedAST::build(std::unique_ptr<clang::CompilerInvocation> CI,
+ParsedAST::build(llvm::json::Value Version,
+                 std::unique_ptr<clang::CompilerInvocation> CI,
                  llvm::ArrayRef<Diag> CompilerInvocationDiags,
                  std::shared_ptr<const PreambleData> Preamble,
                  std::unique_ptr<llvm::MemoryBuffer> Buffer,
@@ -403,10 +404,10 @@
     std::vector<Diag> D = ASTDiags.take(CTContext.getPointer());
     Diags.insert(Diags.end(), D.begin(), D.end());
   }
-  return ParsedAST(std::move(Preamble), std::move(Clang), std::move(Action),
-                   std::move(Tokens), std::move(Macros), std::move(ParsedDecls),
-                   std::move(Diags), std::move(Includes),
-                   std::move(CanonIncludes));
+  return ParsedAST(std::move(Version), std::move(Preamble), std::move(Clang),
+                   std::move(Action), std::move(Tokens), std::move(Macros),
+                   std::move(ParsedDecls), std::move(Diags),
+                   std::move(Includes), std::move(CanonIncludes));
 }
 
 ParsedAST::ParsedAST(ParsedAST &&Other) = default;
@@ -488,16 +489,18 @@
   return CanonIncludes;
 }
 
-ParsedAST::ParsedAST(std::shared_ptr<const PreambleData> Preamble,
+ParsedAST::ParsedAST(llvm::json::Value Version,
+                     std::shared_ptr<const PreambleData> Preamble,
                      std::unique_ptr<CompilerInstance> Clang,
                      std::unique_ptr<FrontendAction> Action,
                      syntax::TokenBuffer Tokens, MainFileMacros Macros,
                      std::vector<Decl *> LocalTopLevelDecls,
                      std::vector<Diag> Diags, IncludeStructure Includes,
                      CanonicalIncludes CanonIncludes)
-    : Preamble(std::move(Preamble)), Clang(std::move(Clang)),
-      Action(std::move(Action)), Tokens(std::move(Tokens)),
-      Macros(std::move(Macros)), Diags(std::move(Diags)),
+    : Version(std::move(Version)), Preamble(std::move(Preamble)),
+      Clang(std::move(Clang)), Action(std::move(Action)),
+      Tokens(std::move(Tokens)), Macros(std::move(Macros)),
+      Diags(std::move(Diags)),
       LocalTopLevelDecls(std::move(LocalTopLevelDecls)),
       Includes(std::move(Includes)), CanonIncludes(std::move(CanonIncludes)) {
   assert(this->Clang);
@@ -522,7 +525,7 @@
   }
 
   return ParsedAST::build(
-      std::make_unique<CompilerInvocation>(*Invocation),
+      Inputs.Version, std::make_unique<CompilerInvocation>(*Invocation),
       CompilerInvocationDiags, Preamble,
       llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents, FileName),
       std::move(VFS), Inputs.Index, Inputs.Opts);
Index: clang-tools-extra/clangd/Compiler.h
===================================================================
--- clang-tools-extra/clangd/Compiler.h
+++ clang-tools-extra/clangd/Compiler.h
@@ -45,6 +45,8 @@
   tooling::CompileCommand CompileCommand;
   IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
   std::string Contents;
+  // Version identifier for Contents, provided by the client and opaque to us.
+  llvm::json::Value Version = nullptr;
   // Prevent reuse of the cached preamble/AST. Slow! Useful to workaround
   // clangd's assumption that missing header files will stay missing.
   bool ForceRebuild = false;
Index: clang-tools-extra/clangd/ClangdServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdServer.h
+++ clang-tools-extra/clangd/ClangdServer.h
@@ -70,6 +70,7 @@
     /// Called by ClangdServer when \p Diagnostics for \p File are ready.
     /// May be called concurrently for separate files, not for a single file.
     virtual void onDiagnosticsReady(PathRef File,
+                                    const llvm::json::Value &Version,
                                     std::vector<Diag> Diagnostics) {}
     /// Called whenever the file status is updated.
     /// May be called concurrently for separate files, not for a single file.
@@ -78,7 +79,7 @@
     /// Called by ClangdServer when some \p Highlightings for \p File are ready.
     /// May be called concurrently for separate files, not for a single file.
     virtual void
-    onHighlightingsReady(PathRef File,
+    onHighlightingsReady(PathRef File, const llvm::json::Value &Version,
                          std::vector<HighlightingToken> Highlightings) {}
 
     /// Called when background indexing tasks are enqueued/started/completed.
@@ -171,13 +172,17 @@
   /// \p File is already tracked. Also schedules parsing of the AST for it on a
   /// separate thread. When the parsing is complete, DiagConsumer passed in
   /// constructor will receive onDiagnosticsReady callback.
+  /// Version identifies this snapshot and is propagated to ASTs, preambles,
+  /// diagnostics etc built from it.
   void addDocument(PathRef File, StringRef Contents,
+                   llvm::json::Value Version = nullptr,
                    WantDiagnostics WD = WantDiagnostics::Auto,
                    bool ForceRebuild = false);
 
   /// Remove \p File from list of tracked files, schedule a request to free
   /// resources associated with it. Pending diagnostics for closed files may not
   /// be delivered, even if requested with WantDiags::Auto or WantDiags::Yes.
+  /// An empty set of diagnostics will be delivered, with Version = nullptr.
   void removeDocument(PathRef File);
 
   /// Run code completion for \p File at \p Pos.
Index: clang-tools-extra/clangd/ClangdServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdServer.cpp
+++ clang-tools-extra/clangd/ClangdServer.cpp
@@ -62,11 +62,11 @@
       : FIndex(FIndex), ServerCallbacks(ServerCallbacks),
         SemanticHighlighting(SemanticHighlighting) {}
 
-  void onPreambleAST(PathRef Path, ASTContext &Ctx,
-                     std::shared_ptr<clang::Preprocessor> PP,
+  void onPreambleAST(PathRef Path, const llvm::json::Value &Version,
+                     ASTContext &Ctx, std::shared_ptr<clang::Preprocessor> PP,
                      const CanonicalIncludes &CanonIncludes) override {
     if (FIndex)
-      FIndex->updatePreamble(Path, Ctx, std::move(PP), CanonIncludes);
+      FIndex->updatePreamble(Path, Version, Ctx, std::move(PP), CanonIncludes);
   }
 
   void onMainAST(PathRef Path, ParsedAST &AST, PublishFn Publish) override {
@@ -80,16 +80,19 @@
 
     if (ServerCallbacks)
       Publish([&]() {
-        ServerCallbacks->onDiagnosticsReady(Path, std::move(Diagnostics));
+        ServerCallbacks->onDiagnosticsReady(Path, AST.version(),
+                                            std::move(Diagnostics));
         if (SemanticHighlighting)
-          ServerCallbacks->onHighlightingsReady(Path, std::move(Highlightings));
+          ServerCallbacks->onHighlightingsReady(Path, AST.version(),
+                                                std::move(Highlightings));
       });
   }
 
-  void onFailedAST(PathRef Path, std::vector<Diag> Diags,
-                   PublishFn Publish) override {
+  void onFailedAST(PathRef Path, const llvm::json::Value &Version,
+                   std::vector<Diag> Diags, PublishFn Publish) override {
     if (ServerCallbacks)
-      Publish([&]() { ServerCallbacks->onDiagnosticsReady(Path, Diags); });
+      Publish(
+          [&]() { ServerCallbacks->onDiagnosticsReady(Path, Version, Diags); });
   }
 
   void onFileUpdated(PathRef File, const TUStatus &Status) override {
@@ -169,6 +172,7 @@
 }
 
 void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents,
+                               llvm::json::Value Version,
                                WantDiagnostics WantDiags, bool ForceRebuild) {
   auto FS = FSProvider.getFileSystem();
 
@@ -183,6 +187,7 @@
   ParseInputs Inputs;
   Inputs.FS = FS;
   Inputs.Contents = std::string(Contents);
+  Inputs.Version = std::move(Version);
   Inputs.ForceRebuild = ForceRebuild;
   Inputs.Opts = std::move(Opts);
   Inputs.Index = Index;
Index: clang-tools-extra/clangd/ClangdLSPServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.h
+++ clang-tools-extra/clangd/ClangdLSPServer.h
@@ -21,6 +21,7 @@
 #include "clang/Tooling/Core/Replacement.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringSet.h"
+#include "llvm/Support/JSON.h"
 #include <memory>
 
 namespace clang {
@@ -57,10 +58,11 @@
 
 private:
   // Implement ClangdServer::Callbacks.
-  void onDiagnosticsReady(PathRef File, std::vector<Diag> Diagnostics) override;
+  void onDiagnosticsReady(PathRef File, const llvm::json::Value &Version,
+                          std::vector<Diag> Diagnostics) override;
   void onFileUpdated(PathRef File, const TUStatus &Status) override;
   void
-  onHighlightingsReady(PathRef File,
+  onHighlightingsReady(PathRef File, const llvm::json::Value &Version,
                        std::vector<HighlightingToken> Highlightings) override;
   void onBackgroundIndexProgress(const BackgroundQueue::Stats &Stats) override;
 
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -630,8 +630,8 @@
 
   const std::string &Contents = Params.textDocument.text;
 
-  DraftMgr.addDraft(File, Params.textDocument.version, Contents);
-  Server->addDocument(File, Contents, WantDiagnostics::Yes);
+  auto Version = DraftMgr.addDraft(File, Params.textDocument.version, Contents);
+  Server->addDocument(File, Contents, Version, WantDiagnostics::Yes);
 }
 
 void ClangdLSPServer::onDocumentDidChange(
@@ -654,7 +654,8 @@
     return;
   }
 
-  Server->addDocument(File, Draft->Contents, WantDiags, Params.forceRebuild);
+  Server->addDocument(File, Draft->Contents, Draft->Version, WantDiags,
+                      Params.forceRebuild);
 }
 
 void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
@@ -1347,7 +1348,8 @@
 }
 
 void ClangdLSPServer::onHighlightingsReady(
-    PathRef File, std::vector<HighlightingToken> Highlightings) {
+    PathRef File, const llvm::json::Value &Version,
+    std::vector<HighlightingToken> Highlightings) {
   std::vector<HighlightingToken> Old;
   std::vector<HighlightingToken> HighlightingsCopy = Highlightings;
   {
@@ -1358,14 +1360,19 @@
   // LSP allows us to send incremental edits of highlightings. Also need to diff
   // to remove highlightings from tokens that should no longer have them.
   std::vector<LineHighlightings> Diffed = diffHighlightings(Highlightings, Old);
-  publishSemanticHighlighting(
-      {{URIForFile::canonicalize(File, /*TUPath=*/File)},
-       toSemanticHighlightingInformation(Diffed)});
+  SemanticHighlightingParams Notification;
+  Notification.TextDocument.uri =
+      URIForFile::canonicalize(File, /*TUPath=*/File);
+  Notification.TextDocument.version = Version.getAsInteger();
+  Notification.Lines = toSemanticHighlightingInformation(Diffed);
+  publishSemanticHighlighting(Notification);
 }
 
 void ClangdLSPServer::onDiagnosticsReady(PathRef File,
+                                         const llvm::json::Value &Version,
                                          std::vector<Diag> Diagnostics) {
   PublishDiagnosticsParams Notification;
+  Notification.version = Version.getAsInteger();
   Notification.uri = URIForFile::canonicalize(File, /*TUPath=*/File);
   DiagnosticToReplacementMap LocalFixIts; // Temporary storage
   for (auto &Diag : Diagnostics) {
@@ -1475,8 +1482,9 @@
   // Reparse only opened files that were modified.
   for (const Path &FilePath : DraftMgr.getActiveFiles())
     if (ModifiedFiles.find(FilePath) != ModifiedFiles.end())
-      Server->addDocument(FilePath, DraftMgr.getDraft(FilePath)->Contents,
-                          WantDiagnostics::Auto);
+      if (auto Draft = DraftMgr.getDraft(FilePath)) // else disappeared in race?
+        Server->addDocument(FilePath, std::move(Draft->Contents),
+                            Draft->Version, WantDiagnostics::Auto);
 }
 
 } // namespace clangd
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to