Skip to content

Commit 7f48d90

Browse files
[clangd] Drop the optimization where only shards for modified files are updated in the background index
The optimization is not valid, given that the target of a symbol reference can change without the spelling of the reference (or anything else in the file containing the reference) changing, as illustrated in the added test case. Partially fixes clangd/clangd#1104
1 parent 8e67d8f commit 7f48d90

File tree

3 files changed

+83
-48
lines changed

3 files changed

+83
-48
lines changed

clang-tools-extra/clangd/index/Background.cpp

Lines changed: 6 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,10 @@ void BackgroundIndex::boostRelated(llvm::StringRef Path) {
180180
Queue.boost(filenameWithoutExtension(Path), IndexBoostedFile);
181181
}
182182

183-
/// Given index results from a TU, only update symbols coming from files that
184-
/// are different or missing from than \p ShardVersionsSnapshot. Also stores new
185-
/// index information on IndexStorage.
186-
void BackgroundIndex::update(
187-
llvm::StringRef MainFile, IndexFileIn Index,
188-
const llvm::StringMap<ShardVersion> &ShardVersionsSnapshot,
189-
bool HadErrors) {
183+
/// Given index results from a TU, update symbols coming from all files. Also
184+
/// stores new index information on IndexStorage.
185+
void BackgroundIndex::update(llvm::StringRef MainFile, IndexFileIn Index,
186+
bool HadErrors) {
190187
// Keys are URIs.
191188
llvm::StringMap<std::pair<Path, FileDigest>> FilesToUpdate;
192189
// Note that sources do not contain any information regarding missing headers,
@@ -198,12 +195,7 @@ void BackgroundIndex::update(
198195
elog("Failed to resolve URI: {0}", AbsPath.takeError());
199196
continue;
200197
}
201-
const auto DigestIt = ShardVersionsSnapshot.find(*AbsPath);
202-
// File has different contents, or indexing was successful this time.
203-
if (DigestIt == ShardVersionsSnapshot.end() ||
204-
DigestIt->getValue().Digest != IGN.Digest ||
205-
(DigestIt->getValue().HadErrors && !HadErrors))
206-
FilesToUpdate[IGN.URI] = {std::move(*AbsPath), IGN.Digest};
198+
FilesToUpdate[IGN.URI] = {std::move(*AbsPath), IGN.Digest};
207199
}
208200

209201
// Shard slabs into files.
@@ -263,13 +255,6 @@ llvm::Error BackgroundIndex::index(tooling::CompileCommand Cmd) {
263255
return llvm::errorCodeToError(Buf.getError());
264256
auto Hash = digest(Buf->get()->getBuffer());
265257

266-
// Take a snapshot of the versions to avoid locking for each file in the TU.
267-
llvm::StringMap<ShardVersion> ShardVersionsSnapshot;
268-
{
269-
std::lock_guard<std::mutex> Lock(ShardVersionsMu);
270-
ShardVersionsSnapshot = ShardVersions;
271-
}
272-
273258
vlog("Indexing {0} (digest:={1})", Cmd.Filename, llvm::toHex(Hash));
274259
ParseInputs Inputs;
275260
Inputs.TFS = &TFS;
@@ -286,25 +271,6 @@ llvm::Error BackgroundIndex::index(tooling::CompileCommand Cmd) {
286271
return error("Couldn't build compiler instance");
287272

288273
SymbolCollector::Options IndexOpts;
289-
// Creates a filter to not collect index results from files with unchanged
290-
// digests.
291-
IndexOpts.FileFilter = [&ShardVersionsSnapshot](const SourceManager &SM,
292-
FileID FID) {
293-
const auto F = SM.getFileEntryRefForID(FID);
294-
if (!F)
295-
return false; // Skip invalid files.
296-
auto AbsPath = getCanonicalPath(*F, SM.getFileManager());
297-
if (!AbsPath)
298-
return false; // Skip files without absolute path.
299-
auto Digest = digestFile(SM, FID);
300-
if (!Digest)
301-
return false;
302-
auto D = ShardVersionsSnapshot.find(*AbsPath);
303-
if (D != ShardVersionsSnapshot.end() && D->second.Digest == Digest &&
304-
!D->second.HadErrors)
305-
return false; // Skip files that haven't changed, without errors.
306-
return true;
307-
};
308274
IndexOpts.CollectMainFileRefs = true;
309275

310276
IndexFileIn Index;
@@ -345,7 +311,7 @@ llvm::Error BackgroundIndex::index(tooling::CompileCommand Cmd) {
345311
for (auto &It : *Index.Sources)
346312
It.second.Flags |= IncludeGraphNode::SourceFlag::HadErrors;
347313
}
348-
update(AbsolutePath, std::move(Index), ShardVersionsSnapshot, HadErrors);
314+
update(AbsolutePath, std::move(Index), HadErrors);
349315

350316
Rebuilder.indexedTU();
351317
return llvm::Error::success();

clang-tools-extra/clangd/index/Background.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -190,12 +190,9 @@ class BackgroundIndex : public SwapIndex {
190190
bool HadErrors = false;
191191
};
192192

193-
/// Given index results from a TU, only update symbols coming from files with
194-
/// different digests than \p ShardVersionsSnapshot. Also stores new index
195-
/// information on IndexStorage.
196-
void update(llvm::StringRef MainFile, IndexFileIn Index,
197-
const llvm::StringMap<ShardVersion> &ShardVersionsSnapshot,
198-
bool HadErrors);
193+
/// Given index results from a TU, update symbols coming from all files. Also
194+
/// stores new index information on IndexStorage.
195+
void update(llvm::StringRef MainFile, IndexFileIn Index, bool HadErrors);
199196

200197
// configuration
201198
const ThreadsafeFS &TFS;

clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "gmock/gmock.h"
1515
#include "gtest/gtest.h"
1616
#include <deque>
17+
#include <optional>
1718
#include <thread>
1819

1920
using ::testing::_;
@@ -213,10 +214,11 @@ TEST_F(BackgroundIndexTest, IndexTwoFiles) {
213214
CDB.setCompileCommand(testPath("root/B.cc"), Cmd);
214215

215216
ASSERT_TRUE(Idx.blockUntilIdleForTest());
216-
// B_CC is dropped as we don't collect symbols from A.h in this compilation.
217+
// A_CC is dropped as A.h was now most recently indexed in the context of
218+
// B.cc.
217219
EXPECT_THAT(runFuzzyFind(Idx, ""),
218220
UnorderedElementsAre(AllOf(named("common"), numReferences(5U)),
219-
AllOf(named("A_CC"), numReferences(0U)),
221+
AllOf(named("B_CC"), numReferences(0U)),
220222
AllOf(named("g"), numReferences(1U)),
221223
AllOf(named("f_b"), declared(), defined(),
222224
numReferences(1U))));
@@ -682,6 +684,76 @@ TEST_F(BackgroundIndexTest, Reindex) {
682684
EXPECT_EQ(OldShard, Storage.lookup(testPath("A.cc")));
683685
}
684686

687+
// Test that restarting clangd properly updates the index with changes
688+
// to files since the last time the index was built.
689+
TEST_F(BackgroundIndexTest, UpdateAfterRestart) {
690+
MockFS FS;
691+
llvm::StringMap<std::string> Storage;
692+
size_t CacheHits = 0;
693+
MemoryShardStorage MSS(Storage, CacheHits);
694+
auto CDB = std::make_unique<OverlayCDB>(/*Base=*/nullptr);
695+
auto Idx = std::make_unique<BackgroundIndex>(
696+
FS, *CDB, [&](llvm::StringRef) { return &MSS; },
697+
BackgroundIndex::Options{});
698+
699+
// Create a header file containing a function declaration, and a source file
700+
// containing a call to the function.
701+
FS.Files[testPath("test.h")] = R"cpp(
702+
#ifndef TEST_H
703+
#define TEST_H
704+
void waldo(int);
705+
#endif
706+
)cpp";
707+
FS.Files[testPath("test.cc")] = R"cpp(
708+
#include "test.h"
709+
int main() {
710+
waldo(42);
711+
}
712+
)cpp";
713+
714+
// Index the files in this state.
715+
tooling::CompileCommand Cmd;
716+
Cmd.Filename = "../test.cc";
717+
Cmd.Directory = testPath("build");
718+
Cmd.CommandLine = {"clang++", "../test.cc", "-fsyntax-only"};
719+
CDB->setCompileCommand(testPath("test.cc"), Cmd);
720+
ASSERT_TRUE(Idx->blockUntilIdleForTest());
721+
722+
// Verify that the function 'waldo' has two references in the index
723+
// (the declaration, and the call site).
724+
auto CheckRefCount = [&](std::string SymbolName) {
725+
auto Syms = runFuzzyFind(*Idx, SymbolName);
726+
EXPECT_THAT(Syms, UnorderedElementsAre(named(SymbolName)));
727+
auto Sym = *Syms.begin();
728+
return getRefs(*Idx, Sym.ID).numRefs();
729+
};
730+
EXPECT_EQ(CheckRefCount("waldo"), 2u);
731+
732+
// Modify the declaration of 'waldo' in a way that changes its SymbolID
733+
// without changing how existing call sites are written. Here, we add
734+
// a new parameter with a default argument.
735+
FS.Files[testPath("test.h")] = R"cpp(
736+
#ifndef TEST_H
737+
#define TEST_H
738+
void waldo(int, int = 0);
739+
#endif
740+
)cpp";
741+
742+
// Simulate clangd shutting down and restarting, and the background index
743+
// being rebuilt after restart.
744+
Idx = nullptr;
745+
CDB = std::make_unique<OverlayCDB>(/*Base=*/nullptr);
746+
Idx = std::make_unique<BackgroundIndex>(
747+
FS, *CDB, [&](llvm::StringRef) { return &MSS; },
748+
BackgroundIndex::Options{});
749+
CDB->setCompileCommand(testPath("test.cc"), Cmd);
750+
ASSERT_TRUE(Idx->blockUntilIdleForTest());
751+
752+
// The rebuild should have updated things so that 'waldo' now again has
753+
// two references in the index.
754+
EXPECT_EQ(CheckRefCount("waldo"), 2u);
755+
}
756+
685757
class BackgroundIndexRebuilderTest : public testing::Test {
686758
protected:
687759
BackgroundIndexRebuilderTest()

0 commit comments

Comments
 (0)