1 //===--- FileIndex.cpp - Indexes for files. ------------------------ C++-*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "FileIndex.h" 10 #include "CollectMacros.h" 11 #include "ParsedAST.h" 12 #include "SymbolCollector.h" 13 #include "index/CanonicalIncludes.h" 14 #include "index/Index.h" 15 #include "index/MemIndex.h" 16 #include "index/Merge.h" 17 #include "index/Ref.h" 18 #include "index/Relation.h" 19 #include "index/Serialization.h" 20 #include "index/Symbol.h" 21 #include "index/SymbolID.h" 22 #include "index/SymbolOrigin.h" 23 #include "index/dex/Dex.h" 24 #include "support/Logger.h" 25 #include "support/Path.h" 26 #include "clang/AST/ASTContext.h" 27 #include "clang/Index/IndexingAction.h" 28 #include "clang/Index/IndexingOptions.h" 29 #include "clang/Lex/MacroInfo.h" 30 #include "clang/Lex/Preprocessor.h" 31 #include "llvm/ADT/DenseMap.h" 32 #include "llvm/ADT/Optional.h" 33 #include "llvm/ADT/STLExtras.h" 34 #include "llvm/ADT/StringMap.h" 35 #include "llvm/ADT/StringRef.h" 36 #include "llvm/Support/Error.h" 37 #include <memory> 38 #include <tuple> 39 #include <utility> 40 #include <vector> 41 42 namespace clang { 43 namespace clangd { 44 namespace { 45 46 SlabTuple indexSymbols(ASTContext &AST, std::shared_ptr<Preprocessor> PP, 47 llvm::ArrayRef<Decl *> DeclsToIndex, 48 const MainFileMacros *MacroRefsToIndex, 49 const CanonicalIncludes &Includes, bool IsIndexMainAST, 50 llvm::StringRef Version) { 51 SymbolCollector::Options CollectorOpts; 52 CollectorOpts.CollectIncludePath = true; 53 CollectorOpts.Includes = &Includes; 54 CollectorOpts.CountReferences = false; 55 CollectorOpts.Origin = SymbolOrigin::Dynamic; 56 57 index::IndexingOptions IndexOpts; 58 // We only need declarations, because we don't count references. 59 IndexOpts.SystemSymbolFilter = 60 index::IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly; 61 IndexOpts.IndexFunctionLocals = false; 62 if (IsIndexMainAST) { 63 // We only collect refs when indexing main AST. 64 CollectorOpts.RefFilter = RefKind::All; 65 // Comments for main file can always be obtained from sema, do not store 66 // them in the index. 67 CollectorOpts.StoreAllDocumentation = false; 68 } else { 69 IndexOpts.IndexMacrosInPreprocessor = true; 70 CollectorOpts.CollectMacro = true; 71 CollectorOpts.StoreAllDocumentation = true; 72 } 73 74 SymbolCollector Collector(std::move(CollectorOpts)); 75 Collector.setPreprocessor(PP); 76 if (MacroRefsToIndex) 77 Collector.handleMacros(*MacroRefsToIndex); 78 index::indexTopLevelDecls(AST, *PP, DeclsToIndex, Collector, IndexOpts); 79 80 const auto &SM = AST.getSourceManager(); 81 const auto *MainFileEntry = SM.getFileEntryForID(SM.getMainFileID()); 82 std::string FileName = 83 std::string(MainFileEntry ? MainFileEntry->getName() : ""); 84 85 auto Syms = Collector.takeSymbols(); 86 auto Refs = Collector.takeRefs(); 87 auto Relations = Collector.takeRelations(); 88 89 vlog("indexed {0} AST for {1} version {2}:\n" 90 " symbol slab: {3} symbols, {4} bytes\n" 91 " ref slab: {5} symbols, {6} refs, {7} bytes\n" 92 " relations slab: {8} relations, {9} bytes", 93 IsIndexMainAST ? "file" : "preamble", FileName, Version, Syms.size(), 94 Syms.bytes(), Refs.size(), Refs.numRefs(), Refs.bytes(), 95 Relations.size(), Relations.bytes()); 96 return std::make_tuple(std::move(Syms), std::move(Refs), 97 std::move(Relations)); 98 } 99 100 // We keep only the node "U" and its edges. Any node other than "U" will be 101 // empty in the resultant graph. 102 IncludeGraph getSubGraph(llvm::StringRef URI, const IncludeGraph &FullGraph) { 103 IncludeGraph IG; 104 105 auto Entry = IG.try_emplace(URI).first; 106 auto &Node = Entry->getValue(); 107 Node = FullGraph.lookup(Entry->getKey()); 108 Node.URI = Entry->getKey(); 109 110 // URIs inside nodes must point into the keys of the same IncludeGraph. 111 for (auto &Include : Node.DirectIncludes) { 112 auto I = IG.try_emplace(Include).first; 113 I->getValue().URI = I->getKey(); 114 Include = I->getKey(); 115 } 116 return IG; 117 } 118 } // namespace 119 120 FileShardedIndex::FileShardedIndex(IndexFileIn Input) 121 : Index(std::move(Input)) { 122 // Used to build RelationSlabs. 123 llvm::DenseMap<SymbolID, FileShard *> SymbolIDToFile; 124 125 // Attribute each Symbol to both their declaration and definition locations. 126 if (Index.Symbols) { 127 for (const auto &S : *Index.Symbols) { 128 auto It = Shards.try_emplace(S.CanonicalDeclaration.FileURI); 129 It.first->getValue().Symbols.insert(&S); 130 SymbolIDToFile[S.ID] = &It.first->getValue(); 131 // Only bother if definition file is different than declaration file. 132 if (S.Definition && 133 S.Definition.FileURI != S.CanonicalDeclaration.FileURI) { 134 auto It = Shards.try_emplace(S.Definition.FileURI); 135 It.first->getValue().Symbols.insert(&S); 136 } 137 } 138 } 139 // Attribute references into each file they occured in. 140 if (Index.Refs) { 141 for (const auto &SymRefs : *Index.Refs) { 142 for (const auto &R : SymRefs.second) { 143 const auto It = Shards.try_emplace(R.Location.FileURI); 144 It.first->getValue().Refs.insert(&R); 145 RefToSymID[&R] = SymRefs.first; 146 } 147 } 148 } 149 // Attribute relations to the file declaraing their Subject as Object might 150 // not have been indexed, see SymbolCollector::processRelations for details. 151 if (Index.Relations) { 152 for (const auto &R : *Index.Relations) { 153 // FIXME: RelationSlab shouldn't contain dangling relations. 154 if (auto *File = SymbolIDToFile.lookup(R.Subject)) 155 File->Relations.insert(&R); 156 } 157 } 158 // Store only the direct includes of a file in a shard. 159 if (Index.Sources) { 160 const auto &FullGraph = *Index.Sources; 161 for (const auto &It : FullGraph) { 162 auto ShardIt = Shards.try_emplace(It.first()); 163 ShardIt.first->getValue().IG = getSubGraph(It.first(), FullGraph); 164 } 165 } 166 } 167 std::vector<llvm::StringRef> FileShardedIndex::getAllSources() const { 168 // It should be enough to construct a vector with {Shards.keys().begin(), 169 // Shards.keys().end()} but MSVC fails to compile that. 170 std::vector<PathRef> Result; 171 Result.reserve(Shards.size()); 172 for (auto Key : Shards.keys()) 173 Result.push_back(Key); 174 return Result; 175 } 176 177 llvm::Optional<IndexFileIn> 178 FileShardedIndex::getShard(llvm::StringRef Uri) const { 179 auto It = Shards.find(Uri); 180 if (It == Shards.end()) 181 return llvm::None; 182 183 IndexFileIn IF; 184 IF.Sources = It->getValue().IG; 185 IF.Cmd = Index.Cmd; 186 187 SymbolSlab::Builder SymB; 188 for (const auto *S : It->getValue().Symbols) 189 SymB.insert(*S); 190 IF.Symbols = std::move(SymB).build(); 191 192 RefSlab::Builder RefB; 193 for (const auto *Ref : It->getValue().Refs) { 194 auto SID = RefToSymID.lookup(Ref); 195 RefB.insert(SID, *Ref); 196 } 197 IF.Refs = std::move(RefB).build(); 198 199 RelationSlab::Builder RelB; 200 for (const auto *Rel : It->getValue().Relations) { 201 RelB.insert(*Rel); 202 } 203 IF.Relations = std::move(RelB).build(); 204 return IF; 205 } 206 207 SlabTuple indexMainDecls(ParsedAST &AST) { 208 return indexSymbols(AST.getASTContext(), AST.getPreprocessorPtr(), 209 AST.getLocalTopLevelDecls(), &AST.getMacros(), 210 AST.getCanonicalIncludes(), 211 /*IsIndexMainAST=*/true, AST.version()); 212 } 213 214 SlabTuple indexHeaderSymbols(llvm::StringRef Version, ASTContext &AST, 215 std::shared_ptr<Preprocessor> PP, 216 const CanonicalIncludes &Includes) { 217 std::vector<Decl *> DeclsToIndex( 218 AST.getTranslationUnitDecl()->decls().begin(), 219 AST.getTranslationUnitDecl()->decls().end()); 220 return indexSymbols(AST, std::move(PP), DeclsToIndex, 221 /*MainFileMacros=*/nullptr, Includes, 222 /*IsIndexMainAST=*/false, Version); 223 } 224 225 void FileSymbols::update(llvm::StringRef Key, 226 std::unique_ptr<SymbolSlab> Symbols, 227 std::unique_ptr<RefSlab> Refs, 228 std::unique_ptr<RelationSlab> Relations, 229 bool CountReferences) { 230 std::lock_guard<std::mutex> Lock(Mutex); 231 if (!Symbols) 232 SymbolsSnapshot.erase(Key); 233 else 234 SymbolsSnapshot[Key] = std::move(Symbols); 235 if (!Refs) { 236 RefsSnapshot.erase(Key); 237 } else { 238 RefSlabAndCountReferences Item; 239 Item.CountReferences = CountReferences; 240 Item.Slab = std::move(Refs); 241 RefsSnapshot[Key] = std::move(Item); 242 } 243 if (!Relations) 244 RelatiosSnapshot.erase(Key); 245 else 246 RelatiosSnapshot[Key] = std::move(Relations); 247 } 248 249 std::unique_ptr<SymbolIndex> 250 FileSymbols::buildIndex(IndexType Type, DuplicateHandling DuplicateHandle) { 251 std::vector<std::shared_ptr<SymbolSlab>> SymbolSlabs; 252 std::vector<std::shared_ptr<RefSlab>> RefSlabs; 253 std::vector<std::shared_ptr<RelationSlab>> RelationSlabs; 254 std::vector<RefSlab *> MainFileRefs; 255 { 256 std::lock_guard<std::mutex> Lock(Mutex); 257 for (const auto &FileAndSymbols : SymbolsSnapshot) 258 SymbolSlabs.push_back(FileAndSymbols.second); 259 for (const auto &FileAndRefs : RefsSnapshot) { 260 RefSlabs.push_back(FileAndRefs.second.Slab); 261 if (FileAndRefs.second.CountReferences) 262 MainFileRefs.push_back(RefSlabs.back().get()); 263 } 264 for (const auto &FileAndRelations : RelatiosSnapshot) 265 RelationSlabs.push_back(FileAndRelations.second); 266 } 267 std::vector<const Symbol *> AllSymbols; 268 std::vector<Symbol> SymsStorage; 269 switch (DuplicateHandle) { 270 case DuplicateHandling::Merge: { 271 llvm::DenseMap<SymbolID, Symbol> Merged; 272 for (const auto &Slab : SymbolSlabs) { 273 for (const auto &Sym : *Slab) { 274 assert(Sym.References == 0 && 275 "Symbol with non-zero references sent to FileSymbols"); 276 auto I = Merged.try_emplace(Sym.ID, Sym); 277 if (!I.second) 278 I.first->second = mergeSymbol(I.first->second, Sym); 279 } 280 } 281 for (const RefSlab *Refs : MainFileRefs) 282 for (const auto &Sym : *Refs) { 283 auto It = Merged.find(Sym.first); 284 // This might happen while background-index is still running. 285 if (It == Merged.end()) 286 continue; 287 It->getSecond().References += Sym.second.size(); 288 } 289 SymsStorage.reserve(Merged.size()); 290 for (auto &Sym : Merged) { 291 SymsStorage.push_back(std::move(Sym.second)); 292 AllSymbols.push_back(&SymsStorage.back()); 293 } 294 break; 295 } 296 case DuplicateHandling::PickOne: { 297 llvm::DenseSet<SymbolID> AddedSymbols; 298 for (const auto &Slab : SymbolSlabs) 299 for (const auto &Sym : *Slab) { 300 assert(Sym.References == 0 && 301 "Symbol with non-zero references sent to FileSymbols"); 302 if (AddedSymbols.insert(Sym.ID).second) 303 AllSymbols.push_back(&Sym); 304 } 305 break; 306 } 307 } 308 309 std::vector<Ref> RefsStorage; // Contiguous ranges for each SymbolID. 310 llvm::DenseMap<SymbolID, llvm::ArrayRef<Ref>> AllRefs; 311 { 312 llvm::DenseMap<SymbolID, llvm::SmallVector<Ref, 4>> MergedRefs; 313 size_t Count = 0; 314 for (const auto &RefSlab : RefSlabs) 315 for (const auto &Sym : *RefSlab) { 316 MergedRefs[Sym.first].append(Sym.second.begin(), Sym.second.end()); 317 Count += Sym.second.size(); 318 } 319 RefsStorage.reserve(Count); 320 AllRefs.reserve(MergedRefs.size()); 321 for (auto &Sym : MergedRefs) { 322 auto &SymRefs = Sym.second; 323 // Sorting isn't required, but yields more stable results over rebuilds. 324 llvm::sort(SymRefs); 325 llvm::copy(SymRefs, back_inserter(RefsStorage)); 326 AllRefs.try_emplace( 327 Sym.first, 328 llvm::ArrayRef<Ref>(&RefsStorage[RefsStorage.size() - SymRefs.size()], 329 SymRefs.size())); 330 } 331 } 332 333 std::vector<Relation> AllRelations; 334 for (const auto &RelationSlab : RelationSlabs) { 335 for (const auto &R : *RelationSlab) 336 AllRelations.push_back(R); 337 } 338 339 size_t StorageSize = 340 RefsStorage.size() * sizeof(Ref) + SymsStorage.size() * sizeof(Symbol); 341 for (const auto &Slab : SymbolSlabs) 342 StorageSize += Slab->bytes(); 343 for (const auto &RefSlab : RefSlabs) 344 StorageSize += RefSlab->bytes(); 345 for (const auto &RelationSlab : RelationSlabs) 346 StorageSize += RelationSlab->bytes(); 347 348 // Index must keep the slabs and contiguous ranges alive. 349 switch (Type) { 350 case IndexType::Light: 351 return std::make_unique<MemIndex>( 352 llvm::make_pointee_range(AllSymbols), std::move(AllRefs), 353 std::move(AllRelations), 354 std::make_tuple(std::move(SymbolSlabs), std::move(RefSlabs), 355 std::move(RefsStorage), std::move(SymsStorage)), 356 StorageSize); 357 case IndexType::Heavy: 358 return std::make_unique<dex::Dex>( 359 llvm::make_pointee_range(AllSymbols), std::move(AllRefs), 360 std::move(AllRelations), 361 std::make_tuple(std::move(SymbolSlabs), std::move(RefSlabs), 362 std::move(RefsStorage), std::move(SymsStorage)), 363 StorageSize); 364 } 365 llvm_unreachable("Unknown clangd::IndexType"); 366 } 367 368 FileIndex::FileIndex(bool UseDex) 369 : MergedIndex(&MainFileIndex, &PreambleIndex), UseDex(UseDex), 370 PreambleIndex(std::make_unique<MemIndex>()), 371 MainFileIndex(std::make_unique<MemIndex>()) {} 372 373 void FileIndex::updatePreamble(PathRef Path, llvm::StringRef Version, 374 ASTContext &AST, 375 std::shared_ptr<Preprocessor> PP, 376 const CanonicalIncludes &Includes) { 377 IndexFileIn IF; 378 std::tie(IF.Symbols, std::ignore, IF.Relations) = 379 indexHeaderSymbols(Version, AST, std::move(PP), Includes); 380 FileShardedIndex ShardedIndex(std::move(IF)); 381 for (auto Uri : ShardedIndex.getAllSources()) { 382 auto IF = ShardedIndex.getShard(Uri); 383 // We are using the key received from ShardedIndex, so it should always 384 // exist. 385 assert(IF); 386 PreambleSymbols.update( 387 Uri, std::make_unique<SymbolSlab>(std::move(*IF->Symbols)), 388 std::make_unique<RefSlab>(), 389 std::make_unique<RelationSlab>(std::move(*IF->Relations)), 390 /*CountReferences=*/false); 391 } 392 PreambleIndex.reset( 393 PreambleSymbols.buildIndex(UseDex ? IndexType::Heavy : IndexType::Light, 394 DuplicateHandling::PickOne)); 395 vlog("Build dynamic index for header symbols with estimated memory usage of " 396 "{0} bytes", 397 PreambleIndex.estimateMemoryUsage()); 398 } 399 400 void FileIndex::updateMain(PathRef Path, ParsedAST &AST) { 401 auto Contents = indexMainDecls(AST); 402 MainFileSymbols.update( 403 Path, std::make_unique<SymbolSlab>(std::move(std::get<0>(Contents))), 404 std::make_unique<RefSlab>(std::move(std::get<1>(Contents))), 405 std::make_unique<RelationSlab>(std::move(std::get<2>(Contents))), 406 /*CountReferences=*/true); 407 MainFileIndex.reset( 408 MainFileSymbols.buildIndex(IndexType::Light, DuplicateHandling::Merge)); 409 vlog("Build dynamic index for main-file symbols with estimated memory usage " 410 "of {0} bytes", 411 MainFileIndex.estimateMemoryUsage()); 412 } 413 414 } // namespace clangd 415 } // namespace clang 416