Index: clangd/index/Background.h =================================================================== --- clangd/index/Background.h +++ clangd/index/Background.h @@ -91,10 +91,12 @@ blockUntilIdleForTest(llvm::Optional TimeoutSeconds = 10); private: - /// Given index results from a TU, only update files in \p FilesToUpdate. - /// Also stores new index information on IndexStorage. + /// Given index results from a TU, only update symbols coming from files in \p + /// FilesToUpdate and skips empty files if they are up-to-date. Also stores + /// new index information on IndexStorage. void update(llvm::StringRef MainFile, IndexFileIn Index, const llvm::StringMap &FilesToUpdate, + const llvm::StringMap &DigestsSnapshot, BackgroundIndexStorage *IndexStorage); // configuration Index: clangd/index/Background.cpp =================================================================== --- clangd/index/Background.cpp +++ clangd/index/Background.cpp @@ -264,9 +264,12 @@ QueueCV.notify_all(); } -/// Given index results from a TU, only update files in \p FilesToUpdate. +/// Given index results from a TU, only update symbols coming from files in \p +/// FilesToUpdate and skips empty files if they are up-to-date. Also stores new +/// index information on IndexStorage. void BackgroundIndex::update(llvm::StringRef MainFile, IndexFileIn Index, const llvm::StringMap &FilesToUpdate, + const llvm::StringMap &DigestsSnapshot, BackgroundIndexStorage *IndexStorage) { // Partition symbols/references into files. struct File { @@ -316,7 +319,9 @@ Syms.insert(*S); for (const auto *R : F.second.Refs) Refs.insert(RefToIDs[R], *R); - } + } else if (DigestsSnapshot.count(Path)) + // File has been seen before and it is up-to-date, so we can skip it. + continue; auto SS = llvm::make_unique(std::move(Syms).build()); auto RS = llvm::make_unique(std::move(Refs).build()); auto IG = llvm::make_unique( @@ -448,7 +453,8 @@ SPAN_ATTACH(Tracer, "refs", int(Index.Refs->numRefs())); SPAN_ATTACH(Tracer, "sources", int(Index.Sources->size())); - update(AbsolutePath, std::move(Index), FilesToUpdate, IndexStorage); + update(AbsolutePath, std::move(Index), FilesToUpdate, DigestsSnapshot, + IndexStorage); if (BuildIndexPeriodMs > 0) SymbolsUpdatedSinceLastIndex = true; Index: unittests/clangd/BackgroundIndexTests.cpp =================================================================== --- unittests/clangd/BackgroundIndexTests.cpp +++ unittests/clangd/BackgroundIndexTests.cpp @@ -351,11 +351,82 @@ EXPECT_EQ(CacheHits, 2U); // Check both A.cc and A.h loaded from cache. // Check if the new symbol has arrived. - auto ShardSource = MSS.loadShard(testPath("root/A.cc")); + ShardHeader = MSS.loadShard(testPath("root/A.h")); EXPECT_NE(ShardHeader, nullptr); + EXPECT_THAT(*ShardHeader->Symbols, Contains(Named("A_CCnew"))); + auto ShardSource = MSS.loadShard(testPath("root/A.cc")); + EXPECT_NE(ShardSource, nullptr); EXPECT_THAT(*ShardSource->Symbols, Contains(AllOf(Named("f_b"), Declared(), Defined()))); } +TEST_F(BackgroundIndexTest, ShardStorageEmptyFile) { + MockFSProvider FS; + FS.Files[testPath("root/A.h")] = R"cpp( + void common(); + void f_b(); + class A_CC {}; + )cpp"; + FS.Files[testPath("root/B.h")] = R"cpp( + #include "A.h" + )cpp"; + FS.Files[testPath("root/A.cc")] = + "#include \"B.h\"\nvoid g() { (void)common; }"; + + llvm::StringMap Storage; + size_t CacheHits = 0; + MemoryShardStorage MSS(Storage, CacheHits); + + tooling::CompileCommand Cmd; + Cmd.Filename = testPath("root/A.cc"); + Cmd.Directory = testPath("root"); + Cmd.CommandLine = {"clang++", testPath("root/A.cc")}; + // Check that A.cc, A.h and B.h has been stored. + { + OverlayCDB CDB(/*Base=*/nullptr); + BackgroundIndex Idx(Context::empty(), "", FS, CDB, + [&](llvm::StringRef) { return &MSS; }); + CDB.setCompileCommand(testPath("root/A.cc"), Cmd); + ASSERT_TRUE(Idx.blockUntilIdleForTest()); + } + EXPECT_THAT(Storage.keys(), + UnorderedElementsAre(testPath("root/A.cc"), testPath("root/A.h"), + testPath("root/B.h"))); + auto ShardHeader = MSS.loadShard(testPath("root/B.h")); + EXPECT_NE(ShardHeader, nullptr); + EXPECT_TRUE(ShardHeader->Symbols->empty()); + + // Check that A.cc, A.h and B.h has been loaded. + { + CacheHits = 0; + OverlayCDB CDB(/*Base=*/nullptr); + BackgroundIndex Idx(Context::empty(), "", FS, CDB, + [&](llvm::StringRef) { return &MSS; }); + CDB.setCompileCommand(testPath("root/A.cc"), Cmd); + ASSERT_TRUE(Idx.blockUntilIdleForTest()); + } + EXPECT_EQ(CacheHits, 3U); + + // Update B.h to contain some symbols. + FS.Files[testPath("root/B.h")] = R"cpp( + #include "A.h" + void new_func(); + )cpp"; + // Check that B.h has been stored with new contents. + { + CacheHits = 0; + OverlayCDB CDB(/*Base=*/nullptr); + BackgroundIndex Idx(Context::empty(), "", FS, CDB, + [&](llvm::StringRef) { return &MSS; }); + CDB.setCompileCommand(testPath("root/A.cc"), Cmd); + ASSERT_TRUE(Idx.blockUntilIdleForTest()); + } + EXPECT_EQ(CacheHits, 3U); + ShardHeader = MSS.loadShard(testPath("root/B.h")); + EXPECT_NE(ShardHeader, nullptr); + EXPECT_THAT(*ShardHeader->Symbols, + Contains(AllOf(Named("new_func"), Declared(), Not(Defined())))); +} + } // namespace clangd } // namespace clang