Index: clangd/index/Background.cpp =================================================================== --- clangd/index/Background.cpp +++ clangd/index/Background.cpp @@ -180,6 +180,25 @@ llvm::StringMap URIToPathCache; }; +// We keep only the node "Path" and its edges. +IncludeGraph getSubGraph(llvm::StringRef Path, const IncludeGraph &FullGraph) { + IncludeGraph IG; + + std::string FileURI = URI::create(Path).toString(); + auto Entry = IG.try_emplace(FileURI).first; + auto &Node = Entry->getValue(); + Node = FullGraph.lookup(Entry->getKey()); + Node.URI = Entry->getKey(); + + for (auto &Include : Node.DirectIncludes) { + auto I = IG.try_emplace(Include).first; + I->getValue().URI = I->getKey(); + Include = I->getKey(); + } + + return IG; +} + /// Given index results from a TU, only update files in \p FilesToUpdate. void BackgroundIndex::update(StringRef MainFile, IndexFileIn Index, const StringMap &FilesToUpdate, @@ -233,6 +252,10 @@ auto SS = llvm::make_unique(std::move(Syms).build()); auto RS = llvm::make_unique(std::move(Refs).build()); + std::unique_ptr IG = + Index.Sources ? llvm::make_unique( + getSubGraph(Path, Index.Sources.getValue())) + : nullptr; auto Hash = FilesToUpdate.lookup(Path); // We need to store shards before updating the index, since the latter @@ -241,6 +264,7 @@ IndexFileOut Shard; Shard.Symbols = SS.get(); Shard.Refs = RS.get(); + Shard.Sources = IG.get(); if (auto Error = IndexStorage->storeShard(Path, Shard)) elog("Failed to write background-index shard for file {0}: {1}", Path, @@ -260,9 +284,9 @@ // digests. // \p FileDigests contains file digests for the current indexed files, and all // changed files will be added to \p FilesToUpdate. -decltype(SymbolCollector::Options::FileFilter) createFileFilter( - const llvm::StringMap &FileDigests, - llvm::StringMap &FilesToUpdate) { +decltype(SymbolCollector::Options::FileFilter) +createFileFilter(const llvm::StringMap &FileDigests, + llvm::StringMap &FilesToUpdate) { return [&FileDigests, &FilesToUpdate](const SourceManager &SM, FileID FID) { StringRef Path; if (const auto *F = SM.getFileEntryForID(FID)) @@ -338,11 +362,11 @@ SymbolCollector::Options IndexOpts; StringMap FilesToUpdate; IndexOpts.FileFilter = createFileFilter(DigestsSnapshot, FilesToUpdate); - SymbolSlab Symbols; - RefSlab Refs; + IndexFileIn Index; auto Action = createStaticIndexingAction( - IndexOpts, [&](SymbolSlab S) { Symbols = std::move(S); }, - [&](RefSlab R) { Refs = std::move(R); }); + IndexOpts, [&](SymbolSlab S) { Index.Symbols = std::move(S); }, + [&](RefSlab R) { Index.Refs = std::move(R); }, + [&](IncludeGraph IG) { Index.Sources = std::move(IG); }); // We're going to run clang here, and it could potentially crash. // We could use CrashRecoveryContext to try to make indexing crashes nonfatal, @@ -358,12 +382,9 @@ Action->EndSourceFile(); log("Indexed {0} ({1} symbols, {2} refs)", Inputs.CompileCommand.Filename, - Symbols.size(), Refs.numRefs()); - SPAN_ATTACH(Tracer, "symbols", int(Symbols.size())); - SPAN_ATTACH(Tracer, "refs", int(Refs.numRefs())); - IndexFileIn Index; - Index.Symbols = std::move(Symbols); - Index.Refs = std::move(Refs); + Index.Symbols->size(), Index.Refs->numRefs()); + SPAN_ATTACH(Tracer, "symbols", int(Index.Symbols->size())); + SPAN_ATTACH(Tracer, "refs", int(Index.Refs->numRefs())); update(AbsolutePath, std::move(Index), FilesToUpdate, IndexStorage); { Index: unittests/clangd/BackgroundIndexTests.cpp =================================================================== --- unittests/clangd/BackgroundIndexTests.cpp +++ unittests/clangd/BackgroundIndexTests.cpp @@ -165,5 +165,48 @@ EXPECT_THAT(*ShardSource->Refs, RefsAre({FileURI("unittest:///root/A.cc")})); } +TEST_F(BackgroundIndexTest, DirectIncludesTest) { + MockFSProvider FS; + FS.Files[testPath("root/B.h")] = ""; + FS.Files[testPath("root/A.h")] = R"cpp( + #include "B.h" + void common(); + void f_b(); + class A_CC {}; + )cpp"; + std::string A_CC = "#include \"A.h\"\nvoid g() { (void)common; }"; + FS.Files[testPath("root/A.cc")] = A_CC; + + 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")}; + { + OverlayCDB CDB(/*Base=*/nullptr); + BackgroundIndex Idx(Context::empty(), "", FS, CDB, + [&](llvm::StringRef) { return &MSS; }); + CDB.setCompileCommand(testPath("root"), Cmd); + ASSERT_TRUE(Idx.blockUntilIdleForTest()); + } + + auto ShardSource = MSS.loadShard(testPath("root/A.cc")); + EXPECT_TRUE(ShardSource->Sources); + EXPECT_EQ(ShardSource->Sources->size(), 2U); // A.cc, A.h + EXPECT_THAT( + ShardSource->Sources->lookup("unittest:///root/A.cc").DirectIncludes, + UnorderedElementsAre("unittest:///root/A.h")); + + auto ShardHeader = MSS.loadShard(testPath("root/A.h")); + EXPECT_TRUE(ShardHeader->Sources); + EXPECT_EQ(ShardHeader->Sources->size(), 2U); // A.h B.h + EXPECT_THAT( + ShardHeader->Sources->lookup("unittest:///root/A.h").DirectIncludes, + UnorderedElementsAre("unittest:///root/B.h")); +} + } // namespace clangd } // namespace clang