1 //===-- FileIndexTests.cpp ---------------------------*- 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 "Annotations.h" 10 #include "Compiler.h" 11 #include "Headers.h" 12 #include "ParsedAST.h" 13 #include "SyncAPI.h" 14 #include "TestFS.h" 15 #include "TestTU.h" 16 #include "TestWorkspace.h" 17 #include "URI.h" 18 #include "index/CanonicalIncludes.h" 19 #include "index/FileIndex.h" 20 #include "index/Index.h" 21 #include "index/Ref.h" 22 #include "index/Relation.h" 23 #include "index/Serialization.h" 24 #include "index/Symbol.h" 25 #include "index/SymbolID.h" 26 #include "support/Threading.h" 27 #include "clang/Frontend/CompilerInvocation.h" 28 #include "clang/Lex/Preprocessor.h" 29 #include "clang/Tooling/CompilationDatabase.h" 30 #include "llvm/ADT/ArrayRef.h" 31 #include "llvm/Support/Allocator.h" 32 #include "gmock/gmock.h" 33 #include "gtest/gtest.h" 34 #include <utility> 35 #include <vector> 36 37 using ::testing::_; 38 using ::testing::AllOf; 39 using ::testing::Contains; 40 using ::testing::ElementsAre; 41 using ::testing::Gt; 42 using ::testing::IsEmpty; 43 using ::testing::Pair; 44 using ::testing::UnorderedElementsAre; 45 46 MATCHER_P(refRange, Range, "") { 47 return std::make_tuple(arg.Location.Start.line(), arg.Location.Start.column(), 48 arg.Location.End.line(), arg.Location.End.column()) == 49 std::make_tuple(Range.start.line, Range.start.character, 50 Range.end.line, Range.end.character); 51 } 52 MATCHER_P(fileURI, F, "") { return llvm::StringRef(arg.Location.FileURI) == F; } 53 MATCHER_P(declURI, U, "") { 54 return llvm::StringRef(arg.CanonicalDeclaration.FileURI) == U; 55 } 56 MATCHER_P(defURI, U, "") { 57 return llvm::StringRef(arg.Definition.FileURI) == U; 58 } 59 MATCHER_P(qName, N, "") { return (arg.Scope + arg.Name).str() == N; } 60 MATCHER_P(numReferences, N, "") { return arg.References == N; } 61 MATCHER_P(hasOrign, O, "") { return bool(arg.Origin & O); } 62 63 namespace clang { 64 namespace clangd { 65 namespace { 66 ::testing::Matcher<const RefSlab &> 67 refsAre(std::vector<::testing::Matcher<Ref>> Matchers) { 68 return ElementsAre(::testing::Pair(_, UnorderedElementsAreArray(Matchers))); 69 } 70 71 Symbol symbol(llvm::StringRef ID) { 72 Symbol Sym; 73 Sym.ID = SymbolID(ID); 74 Sym.Name = ID; 75 return Sym; 76 } 77 78 std::unique_ptr<SymbolSlab> numSlab(int Begin, int End) { 79 SymbolSlab::Builder Slab; 80 for (int I = Begin; I <= End; I++) 81 Slab.insert(symbol(std::to_string(I))); 82 return std::make_unique<SymbolSlab>(std::move(Slab).build()); 83 } 84 85 std::unique_ptr<RefSlab> refSlab(const SymbolID &ID, const char *Path) { 86 RefSlab::Builder Slab; 87 Ref R; 88 R.Location.FileURI = Path; 89 R.Kind = RefKind::Reference; 90 Slab.insert(ID, R); 91 return std::make_unique<RefSlab>(std::move(Slab).build()); 92 } 93 94 std::unique_ptr<RelationSlab> relSlab(llvm::ArrayRef<const Relation> Rels) { 95 RelationSlab::Builder RelBuilder; 96 for (auto &Rel : Rels) 97 RelBuilder.insert(Rel); 98 return std::make_unique<RelationSlab>(std::move(RelBuilder).build()); 99 } 100 101 TEST(FileSymbolsTest, UpdateAndGet) { 102 FileSymbols FS(IndexContents::All); 103 EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""), IsEmpty()); 104 105 FS.update("f1", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cc"), nullptr, 106 false); 107 EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""), 108 UnorderedElementsAre(qName("1"), qName("2"), qName("3"))); 109 EXPECT_THAT(getRefs(*FS.buildIndex(IndexType::Light), SymbolID("1")), 110 refsAre({fileURI("f1.cc")})); 111 } 112 113 TEST(FileSymbolsTest, Overlap) { 114 FileSymbols FS(IndexContents::All); 115 FS.update("f1", numSlab(1, 3), nullptr, nullptr, false); 116 FS.update("f2", numSlab(3, 5), nullptr, nullptr, false); 117 for (auto Type : {IndexType::Light, IndexType::Heavy}) 118 EXPECT_THAT(runFuzzyFind(*FS.buildIndex(Type), ""), 119 UnorderedElementsAre(qName("1"), qName("2"), qName("3"), 120 qName("4"), qName("5"))); 121 } 122 123 TEST(FileSymbolsTest, MergeOverlap) { 124 FileSymbols FS(IndexContents::All); 125 auto OneSymboSlab = [](Symbol Sym) { 126 SymbolSlab::Builder S; 127 S.insert(Sym); 128 return std::make_unique<SymbolSlab>(std::move(S).build()); 129 }; 130 auto X1 = symbol("x"); 131 X1.CanonicalDeclaration.FileURI = "file:///x1"; 132 auto X2 = symbol("x"); 133 X2.Definition.FileURI = "file:///x2"; 134 135 FS.update("f1", OneSymboSlab(X1), nullptr, nullptr, false); 136 FS.update("f2", OneSymboSlab(X2), nullptr, nullptr, false); 137 for (auto Type : {IndexType::Light, IndexType::Heavy}) 138 EXPECT_THAT( 139 runFuzzyFind(*FS.buildIndex(Type, DuplicateHandling::Merge), "x"), 140 UnorderedElementsAre( 141 AllOf(qName("x"), declURI("file:///x1"), defURI("file:///x2")))); 142 } 143 144 TEST(FileSymbolsTest, SnapshotAliveAfterRemove) { 145 FileSymbols FS(IndexContents::All); 146 147 SymbolID ID("1"); 148 FS.update("f1", numSlab(1, 3), refSlab(ID, "f1.cc"), nullptr, false); 149 150 auto Symbols = FS.buildIndex(IndexType::Light); 151 EXPECT_THAT(runFuzzyFind(*Symbols, ""), 152 UnorderedElementsAre(qName("1"), qName("2"), qName("3"))); 153 EXPECT_THAT(getRefs(*Symbols, ID), refsAre({fileURI("f1.cc")})); 154 155 FS.update("f1", nullptr, nullptr, nullptr, false); 156 auto Empty = FS.buildIndex(IndexType::Light); 157 EXPECT_THAT(runFuzzyFind(*Empty, ""), IsEmpty()); 158 EXPECT_THAT(getRefs(*Empty, ID), ElementsAre()); 159 160 EXPECT_THAT(runFuzzyFind(*Symbols, ""), 161 UnorderedElementsAre(qName("1"), qName("2"), qName("3"))); 162 EXPECT_THAT(getRefs(*Symbols, ID), refsAre({fileURI("f1.cc")})); 163 } 164 165 // Adds Basename.cpp, which includes Basename.h, which contains Code. 166 void update(FileIndex &M, llvm::StringRef Basename, llvm::StringRef Code) { 167 TestTU File; 168 File.Filename = (Basename + ".cpp").str(); 169 File.HeaderFilename = (Basename + ".h").str(); 170 File.HeaderCode = std::string(Code); 171 auto AST = File.build(); 172 M.updatePreamble(testPath(File.Filename), /*Version=*/"null", 173 AST.getASTContext(), AST.getPreprocessor(), 174 AST.getCanonicalIncludes()); 175 } 176 177 TEST(FileIndexTest, CustomizedURIScheme) { 178 FileIndex M; 179 update(M, "f", "class string {};"); 180 181 EXPECT_THAT(runFuzzyFind(M, ""), ElementsAre(declURI("unittest:///f.h"))); 182 } 183 184 TEST(FileIndexTest, IndexAST) { 185 FileIndex M; 186 update(M, "f1", "namespace ns { void f() {} class X {}; }"); 187 188 FuzzyFindRequest Req; 189 Req.Query = ""; 190 Req.Scopes = {"ns::"}; 191 EXPECT_THAT(runFuzzyFind(M, Req), 192 UnorderedElementsAre(qName("ns::f"), qName("ns::X"))); 193 } 194 195 TEST(FileIndexTest, NoLocal) { 196 FileIndex M; 197 update(M, "f1", "namespace ns { void f() { int local = 0; } class X {}; }"); 198 199 EXPECT_THAT( 200 runFuzzyFind(M, ""), 201 UnorderedElementsAre(qName("ns"), qName("ns::f"), qName("ns::X"))); 202 } 203 204 TEST(FileIndexTest, IndexMultiASTAndDeduplicate) { 205 FileIndex M; 206 update(M, "f1", "namespace ns { void f() {} class X {}; }"); 207 update(M, "f2", "namespace ns { void ff() {} class X {}; }"); 208 209 FuzzyFindRequest Req; 210 Req.Scopes = {"ns::"}; 211 EXPECT_THAT( 212 runFuzzyFind(M, Req), 213 UnorderedElementsAre(qName("ns::f"), qName("ns::X"), qName("ns::ff"))); 214 } 215 216 TEST(FileIndexTest, ClassMembers) { 217 FileIndex M; 218 update(M, "f1", "class X { static int m1; int m2; static void f(); };"); 219 220 EXPECT_THAT(runFuzzyFind(M, ""), 221 UnorderedElementsAre(qName("X"), qName("X::m1"), qName("X::m2"), 222 qName("X::f"))); 223 } 224 225 TEST(FileIndexTest, IncludeCollected) { 226 FileIndex M; 227 update( 228 M, "f", 229 "// IWYU pragma: private, include <the/good/header.h>\nclass string {};"); 230 231 auto Symbols = runFuzzyFind(M, ""); 232 EXPECT_THAT(Symbols, ElementsAre(_)); 233 EXPECT_THAT(Symbols.begin()->IncludeHeaders.front().IncludeHeader, 234 "<the/good/header.h>"); 235 } 236 237 TEST(FileIndexTest, HasSystemHeaderMappingsInPreamble) { 238 TestTU TU; 239 TU.HeaderCode = "class Foo{};"; 240 TU.HeaderFilename = "algorithm"; 241 242 auto Symbols = runFuzzyFind(*TU.index(), ""); 243 EXPECT_THAT(Symbols, ElementsAre(_)); 244 EXPECT_THAT(Symbols.begin()->IncludeHeaders.front().IncludeHeader, 245 "<algorithm>"); 246 } 247 248 TEST(FileIndexTest, TemplateParamsInLabel) { 249 auto *Source = R"cpp( 250 template <class Ty> 251 class vector { 252 }; 253 254 template <class Ty, class Arg> 255 vector<Ty> make_vector(Arg A) {} 256 )cpp"; 257 258 FileIndex M; 259 update(M, "f", Source); 260 261 auto Symbols = runFuzzyFind(M, ""); 262 EXPECT_THAT(Symbols, 263 UnorderedElementsAre(qName("vector"), qName("make_vector"))); 264 auto It = Symbols.begin(); 265 Symbol Vector = *It++; 266 Symbol MakeVector = *It++; 267 if (MakeVector.Name == "vector") 268 std::swap(MakeVector, Vector); 269 270 EXPECT_EQ(Vector.Signature, "<class Ty>"); 271 EXPECT_EQ(Vector.CompletionSnippetSuffix, "<${1:class Ty}>"); 272 273 EXPECT_EQ(MakeVector.Signature, "<class Ty>(Arg A)"); 274 EXPECT_EQ(MakeVector.CompletionSnippetSuffix, "<${1:class Ty}>(${2:Arg A})"); 275 } 276 277 TEST(FileIndexTest, RebuildWithPreamble) { 278 auto FooCpp = testPath("foo.cpp"); 279 auto FooH = testPath("foo.h"); 280 // Preparse ParseInputs. 281 ParseInputs PI; 282 PI.CompileCommand.Directory = testRoot(); 283 PI.CompileCommand.Filename = FooCpp; 284 PI.CompileCommand.CommandLine = {"clang", "-xc++", FooCpp}; 285 286 MockFS FS; 287 FS.Files[FooCpp] = ""; 288 FS.Files[FooH] = R"cpp( 289 namespace ns_in_header { 290 int func_in_header(); 291 } 292 )cpp"; 293 PI.TFS = &FS; 294 295 PI.Contents = R"cpp( 296 #include "foo.h" 297 namespace ns_in_source { 298 int func_in_source(); 299 } 300 )cpp"; 301 302 // Rebuild the file. 303 IgnoreDiagnostics IgnoreDiags; 304 auto CI = buildCompilerInvocation(PI, IgnoreDiags); 305 306 FileIndex Index; 307 bool IndexUpdated = false; 308 buildPreamble(FooCpp, *CI, PI, 309 /*StoreInMemory=*/true, 310 [&](ASTContext &Ctx, Preprocessor &PP, 311 const CanonicalIncludes &CanonIncludes) { 312 EXPECT_FALSE(IndexUpdated) 313 << "Expected only a single index update"; 314 IndexUpdated = true; 315 Index.updatePreamble(FooCpp, /*Version=*/"null", Ctx, PP, 316 CanonIncludes); 317 }); 318 ASSERT_TRUE(IndexUpdated); 319 320 // Check the index contains symbols from the preamble, but not from the main 321 // file. 322 FuzzyFindRequest Req; 323 Req.Query = ""; 324 Req.Scopes = {"", "ns_in_header::"}; 325 326 EXPECT_THAT(runFuzzyFind(Index, Req), 327 UnorderedElementsAre(qName("ns_in_header"), 328 qName("ns_in_header::func_in_header"))); 329 } 330 331 TEST(FileIndexTest, Refs) { 332 const char *HeaderCode = "class Foo {};"; 333 Annotations MainCode(R"cpp( 334 void f() { 335 $foo[[Foo]] foo; 336 } 337 )cpp"); 338 339 auto Foo = 340 findSymbol(TestTU::withHeaderCode(HeaderCode).headerSymbols(), "Foo"); 341 342 RefsRequest Request; 343 Request.IDs = {Foo.ID}; 344 345 FileIndex Index; 346 // Add test.cc 347 TestTU Test; 348 Test.HeaderCode = HeaderCode; 349 Test.Code = std::string(MainCode.code()); 350 Test.Filename = "test.cc"; 351 auto AST = Test.build(); 352 Index.updateMain(testPath(Test.Filename), AST); 353 // Add test2.cc 354 TestTU Test2; 355 Test2.HeaderCode = HeaderCode; 356 Test2.Code = std::string(MainCode.code()); 357 Test2.Filename = "test2.cc"; 358 AST = Test2.build(); 359 Index.updateMain(testPath(Test2.Filename), AST); 360 361 EXPECT_THAT(getRefs(Index, Foo.ID), 362 refsAre({AllOf(refRange(MainCode.range("foo")), 363 fileURI("unittest:///test.cc")), 364 AllOf(refRange(MainCode.range("foo")), 365 fileURI("unittest:///test2.cc"))})); 366 } 367 368 TEST(FileIndexTest, MacroRefs) { 369 Annotations HeaderCode(R"cpp( 370 #define $def1[[HEADER_MACRO]](X) (X+1) 371 )cpp"); 372 Annotations MainCode(R"cpp( 373 #define $def2[[MAINFILE_MACRO]](X) (X+1) 374 void f() { 375 int a = $ref1[[HEADER_MACRO]](2); 376 int b = $ref2[[MAINFILE_MACRO]](1); 377 } 378 )cpp"); 379 380 FileIndex Index; 381 // Add test.cc 382 TestTU Test; 383 Test.HeaderCode = std::string(HeaderCode.code()); 384 Test.Code = std::string(MainCode.code()); 385 Test.Filename = "test.cc"; 386 auto AST = Test.build(); 387 Index.updateMain(testPath(Test.Filename), AST); 388 389 auto HeaderMacro = findSymbol(Test.headerSymbols(), "HEADER_MACRO"); 390 EXPECT_THAT(getRefs(Index, HeaderMacro.ID), 391 refsAre({AllOf(refRange(MainCode.range("ref1")), 392 fileURI("unittest:///test.cc"))})); 393 394 auto MainFileMacro = findSymbol(Test.headerSymbols(), "MAINFILE_MACRO"); 395 EXPECT_THAT(getRefs(Index, MainFileMacro.ID), 396 refsAre({AllOf(refRange(MainCode.range("def2")), 397 fileURI("unittest:///test.cc")), 398 AllOf(refRange(MainCode.range("ref2")), 399 fileURI("unittest:///test.cc"))})); 400 } 401 402 TEST(FileIndexTest, CollectMacros) { 403 FileIndex M; 404 update(M, "f", "#define CLANGD 1"); 405 EXPECT_THAT(runFuzzyFind(M, ""), Contains(qName("CLANGD"))); 406 } 407 408 TEST(FileIndexTest, Relations) { 409 TestTU TU; 410 TU.Filename = "f.cpp"; 411 TU.HeaderFilename = "f.h"; 412 TU.HeaderCode = "class A {}; class B : public A {};"; 413 auto AST = TU.build(); 414 FileIndex Index; 415 Index.updatePreamble(testPath(TU.Filename), /*Version=*/"null", 416 AST.getASTContext(), AST.getPreprocessor(), 417 AST.getCanonicalIncludes()); 418 SymbolID A = findSymbol(TU.headerSymbols(), "A").ID; 419 uint32_t Results = 0; 420 RelationsRequest Req; 421 Req.Subjects.insert(A); 422 Req.Predicate = RelationKind::BaseOf; 423 Index.relations(Req, [&](const SymbolID &, const Symbol &) { ++Results; }); 424 EXPECT_EQ(Results, 1u); 425 } 426 427 TEST(FileIndexTest, RelationsMultiFile) { 428 TestWorkspace Workspace; 429 Workspace.addSource("Base.h", "class Base {};"); 430 Workspace.addMainFile("A.cpp", R"cpp( 431 #include "Base.h" 432 class A : public Base {}; 433 )cpp"); 434 Workspace.addMainFile("B.cpp", R"cpp( 435 #include "Base.h" 436 class B : public Base {}; 437 )cpp"); 438 439 auto Index = Workspace.index(); 440 FuzzyFindRequest FFReq; 441 FFReq.Query = "Base"; 442 FFReq.AnyScope = true; 443 SymbolID Base; 444 Index->fuzzyFind(FFReq, [&](const Symbol &S) { Base = S.ID; }); 445 446 RelationsRequest Req; 447 Req.Subjects.insert(Base); 448 Req.Predicate = RelationKind::BaseOf; 449 uint32_t Results = 0; 450 Index->relations(Req, [&](const SymbolID &, const Symbol &) { ++Results; }); 451 EXPECT_EQ(Results, 2u); 452 } 453 454 TEST(FileIndexTest, ReferencesInMainFileWithPreamble) { 455 TestTU TU; 456 TU.HeaderCode = "class Foo{};"; 457 Annotations Main(R"cpp( 458 void f() { 459 [[Foo]] foo; 460 } 461 )cpp"); 462 TU.Code = std::string(Main.code()); 463 auto AST = TU.build(); 464 FileIndex Index; 465 Index.updateMain(testPath(TU.Filename), AST); 466 467 // Expect to see references in main file, references in headers are excluded 468 // because we only index main AST. 469 EXPECT_THAT(getRefs(Index, findSymbol(TU.headerSymbols(), "Foo").ID), 470 refsAre({refRange(Main.range())})); 471 } 472 473 TEST(FileIndexTest, MergeMainFileSymbols) { 474 const char *CommonHeader = "void foo();"; 475 TestTU Header = TestTU::withCode(CommonHeader); 476 TestTU Cpp = TestTU::withCode("void foo() {}"); 477 Cpp.Filename = "foo.cpp"; 478 Cpp.HeaderFilename = "foo.h"; 479 Cpp.HeaderCode = CommonHeader; 480 481 FileIndex Index; 482 auto HeaderAST = Header.build(); 483 auto CppAST = Cpp.build(); 484 Index.updateMain(testPath("foo.h"), HeaderAST); 485 Index.updateMain(testPath("foo.cpp"), CppAST); 486 487 auto Symbols = runFuzzyFind(Index, ""); 488 // Check foo is merged, foo in Cpp wins (as we see the definition there). 489 EXPECT_THAT(Symbols, ElementsAre(AllOf(declURI("unittest:///foo.h"), 490 defURI("unittest:///foo.cpp"), 491 hasOrign(SymbolOrigin::Merge)))); 492 } 493 494 TEST(FileSymbolsTest, CountReferencesNoRefSlabs) { 495 FileSymbols FS(IndexContents::All); 496 FS.update("f1", numSlab(1, 3), nullptr, nullptr, true); 497 FS.update("f2", numSlab(1, 3), nullptr, nullptr, false); 498 EXPECT_THAT( 499 runFuzzyFind(*FS.buildIndex(IndexType::Light, DuplicateHandling::Merge), 500 ""), 501 UnorderedElementsAre(AllOf(qName("1"), numReferences(0u)), 502 AllOf(qName("2"), numReferences(0u)), 503 AllOf(qName("3"), numReferences(0u)))); 504 } 505 506 TEST(FileSymbolsTest, CountReferencesWithRefSlabs) { 507 FileSymbols FS(IndexContents::All); 508 FS.update("f1cpp", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cpp"), nullptr, 509 true); 510 FS.update("f1h", numSlab(1, 3), refSlab(SymbolID("1"), "f1.h"), nullptr, 511 false); 512 FS.update("f2cpp", numSlab(1, 3), refSlab(SymbolID("2"), "f2.cpp"), nullptr, 513 true); 514 FS.update("f2h", numSlab(1, 3), refSlab(SymbolID("2"), "f2.h"), nullptr, 515 false); 516 FS.update("f3cpp", numSlab(1, 3), refSlab(SymbolID("3"), "f3.cpp"), nullptr, 517 true); 518 FS.update("f3h", numSlab(1, 3), refSlab(SymbolID("3"), "f3.h"), nullptr, 519 false); 520 EXPECT_THAT( 521 runFuzzyFind(*FS.buildIndex(IndexType::Light, DuplicateHandling::Merge), 522 ""), 523 UnorderedElementsAre(AllOf(qName("1"), numReferences(1u)), 524 AllOf(qName("2"), numReferences(1u)), 525 AllOf(qName("3"), numReferences(1u)))); 526 } 527 528 TEST(FileIndexTest, StalePreambleSymbolsDeleted) { 529 FileIndex M; 530 TestTU File; 531 File.HeaderFilename = "a.h"; 532 533 File.Filename = "f1.cpp"; 534 File.HeaderCode = "int a;"; 535 auto AST = File.build(); 536 M.updatePreamble(testPath(File.Filename), /*Version=*/"null", 537 AST.getASTContext(), AST.getPreprocessor(), 538 AST.getCanonicalIncludes()); 539 EXPECT_THAT(runFuzzyFind(M, ""), UnorderedElementsAre(qName("a"))); 540 541 File.Filename = "f2.cpp"; 542 File.HeaderCode = "int b;"; 543 AST = File.build(); 544 M.updatePreamble(testPath(File.Filename), /*Version=*/"null", 545 AST.getASTContext(), AST.getPreprocessor(), 546 AST.getCanonicalIncludes()); 547 EXPECT_THAT(runFuzzyFind(M, ""), UnorderedElementsAre(qName("b"))); 548 } 549 550 // Verifies that concurrent calls to updateMain don't "lose" any updates. 551 TEST(FileIndexTest, Threadsafety) { 552 FileIndex M; 553 Notification Go; 554 555 constexpr int Count = 10; 556 { 557 // Set up workers to concurrently call updateMain() with separate files. 558 AsyncTaskRunner Pool; 559 for (unsigned I = 0; I < Count; ++I) { 560 auto TU = TestTU::withCode(llvm::formatv("int xxx{0};", I).str()); 561 TU.Filename = llvm::formatv("x{0}.c", I).str(); 562 Pool.runAsync(TU.Filename, [&, Filename(testPath(TU.Filename)), 563 AST(TU.build())]() mutable { 564 Go.wait(); 565 M.updateMain(Filename, AST); 566 }); 567 } 568 // On your marks, get set... 569 Go.notify(); 570 } 571 572 EXPECT_THAT(runFuzzyFind(M, "xxx"), ::testing::SizeIs(Count)); 573 } 574 575 TEST(FileShardedIndexTest, Sharding) { 576 auto AHeaderUri = URI::create(testPath("a.h")).toString(); 577 auto BHeaderUri = URI::create(testPath("b.h")).toString(); 578 auto BSourceUri = URI::create(testPath("b.cc")).toString(); 579 580 auto Sym1 = symbol("1"); 581 Sym1.CanonicalDeclaration.FileURI = AHeaderUri.c_str(); 582 583 auto Sym2 = symbol("2"); 584 Sym2.CanonicalDeclaration.FileURI = BHeaderUri.c_str(); 585 Sym2.Definition.FileURI = BSourceUri.c_str(); 586 587 auto Sym3 = symbol("3"); // not stored 588 589 IndexFileIn IF; 590 { 591 SymbolSlab::Builder B; 592 // Should be stored in only a.h 593 B.insert(Sym1); 594 // Should be stored in both b.h and b.cc 595 B.insert(Sym2); 596 IF.Symbols.emplace(std::move(B).build()); 597 } 598 { 599 // Should be stored in b.cc 600 IF.Refs.emplace(std::move(*refSlab(Sym1.ID, BSourceUri.c_str()))); 601 } 602 { 603 RelationSlab::Builder B; 604 // Should be stored in a.h and b.h 605 B.insert(Relation{Sym1.ID, RelationKind::BaseOf, Sym2.ID}); 606 // Should be stored in a.h and b.h 607 B.insert(Relation{Sym2.ID, RelationKind::BaseOf, Sym1.ID}); 608 // Should be stored in a.h (where Sym1 is stored) even though 609 // the relation is dangling as Sym3 is unknown. 610 B.insert(Relation{Sym3.ID, RelationKind::BaseOf, Sym1.ID}); 611 IF.Relations.emplace(std::move(B).build()); 612 } 613 614 IF.Sources.emplace(); 615 IncludeGraph &IG = *IF.Sources; 616 { 617 // b.cc includes b.h 618 auto &Node = IG[BSourceUri]; 619 Node.DirectIncludes = {BHeaderUri}; 620 Node.URI = BSourceUri; 621 } 622 { 623 // b.h includes a.h 624 auto &Node = IG[BHeaderUri]; 625 Node.DirectIncludes = {AHeaderUri}; 626 Node.URI = BHeaderUri; 627 } 628 { 629 // a.h includes nothing. 630 auto &Node = IG[AHeaderUri]; 631 Node.DirectIncludes = {}; 632 Node.URI = AHeaderUri; 633 } 634 635 IF.Cmd = tooling::CompileCommand(testRoot(), "b.cc", {"clang"}, "out"); 636 637 FileShardedIndex ShardedIndex(std::move(IF)); 638 ASSERT_THAT(ShardedIndex.getAllSources(), 639 UnorderedElementsAre(AHeaderUri, BHeaderUri, BSourceUri)); 640 641 { 642 auto Shard = ShardedIndex.getShard(AHeaderUri); 643 ASSERT_TRUE(Shard); 644 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(qName("1"))); 645 EXPECT_THAT(*Shard->Refs, IsEmpty()); 646 EXPECT_THAT( 647 *Shard->Relations, 648 UnorderedElementsAre(Relation{Sym1.ID, RelationKind::BaseOf, Sym2.ID}, 649 Relation{Sym2.ID, RelationKind::BaseOf, Sym1.ID}, 650 Relation{Sym3.ID, RelationKind::BaseOf, Sym1.ID})); 651 ASSERT_THAT(Shard->Sources->keys(), UnorderedElementsAre(AHeaderUri)); 652 EXPECT_THAT(Shard->Sources->lookup(AHeaderUri).DirectIncludes, IsEmpty()); 653 EXPECT_TRUE(Shard->Cmd); 654 } 655 { 656 auto Shard = ShardedIndex.getShard(BHeaderUri); 657 ASSERT_TRUE(Shard); 658 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(qName("2"))); 659 EXPECT_THAT(*Shard->Refs, IsEmpty()); 660 EXPECT_THAT( 661 *Shard->Relations, 662 UnorderedElementsAre(Relation{Sym1.ID, RelationKind::BaseOf, Sym2.ID}, 663 Relation{Sym2.ID, RelationKind::BaseOf, Sym1.ID})); 664 ASSERT_THAT(Shard->Sources->keys(), 665 UnorderedElementsAre(BHeaderUri, AHeaderUri)); 666 EXPECT_THAT(Shard->Sources->lookup(BHeaderUri).DirectIncludes, 667 UnorderedElementsAre(AHeaderUri)); 668 EXPECT_TRUE(Shard->Cmd); 669 } 670 { 671 auto Shard = ShardedIndex.getShard(BSourceUri); 672 ASSERT_TRUE(Shard); 673 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(qName("2"))); 674 EXPECT_THAT(*Shard->Refs, UnorderedElementsAre(Pair(Sym1.ID, _))); 675 EXPECT_THAT(*Shard->Relations, IsEmpty()); 676 ASSERT_THAT(Shard->Sources->keys(), 677 UnorderedElementsAre(BSourceUri, BHeaderUri)); 678 EXPECT_THAT(Shard->Sources->lookup(BSourceUri).DirectIncludes, 679 UnorderedElementsAre(BHeaderUri)); 680 EXPECT_TRUE(Shard->Cmd); 681 } 682 } 683 684 TEST(FileIndexTest, Profile) { 685 FileIndex FI; 686 687 auto FileName = testPath("foo.cpp"); 688 auto AST = TestTU::withHeaderCode("int a;").build(); 689 FI.updateMain(FileName, AST); 690 FI.updatePreamble(FileName, "v1", AST.getASTContext(), AST.getPreprocessor(), 691 AST.getCanonicalIncludes()); 692 693 llvm::BumpPtrAllocator Alloc; 694 MemoryTree MT(&Alloc); 695 FI.profile(MT); 696 ASSERT_THAT(MT.children(), 697 UnorderedElementsAre(Pair("preamble", _), Pair("main_file", _))); 698 699 ASSERT_THAT(MT.child("preamble").children(), 700 UnorderedElementsAre(Pair("index", _), Pair("slabs", _))); 701 ASSERT_THAT(MT.child("main_file").children(), 702 UnorderedElementsAre(Pair("index", _), Pair("slabs", _))); 703 704 ASSERT_THAT(MT.child("preamble").child("index").total(), Gt(0U)); 705 ASSERT_THAT(MT.child("main_file").child("index").total(), Gt(0U)); 706 } 707 708 TEST(FileSymbolsTest, Profile) { 709 FileSymbols FS(IndexContents::All); 710 FS.update("f1", numSlab(1, 2), nullptr, nullptr, false); 711 FS.update("f2", nullptr, refSlab(SymbolID("1"), "f1"), nullptr, false); 712 FS.update("f3", nullptr, nullptr, 713 relSlab({{SymbolID("1"), RelationKind::BaseOf, SymbolID("2")}}), 714 false); 715 llvm::BumpPtrAllocator Alloc; 716 MemoryTree MT(&Alloc); 717 FS.profile(MT); 718 ASSERT_THAT(MT.children(), UnorderedElementsAre(Pair("f1", _), Pair("f2", _), 719 Pair("f3", _))); 720 EXPECT_THAT(MT.child("f1").children(), ElementsAre(Pair("symbols", _))); 721 EXPECT_THAT(MT.child("f1").total(), Gt(0U)); 722 EXPECT_THAT(MT.child("f2").children(), ElementsAre(Pair("references", _))); 723 EXPECT_THAT(MT.child("f2").total(), Gt(0U)); 724 EXPECT_THAT(MT.child("f3").children(), ElementsAre(Pair("relations", _))); 725 EXPECT_THAT(MT.child("f3").total(), Gt(0U)); 726 } 727 728 TEST(FileIndexTest, MacrosFromMainFile) { 729 FileIndex Idx; 730 TestTU TU; 731 TU.Code = "#pragma once\n#define FOO"; 732 TU.Filename = "foo.h"; 733 auto AST = TU.build(); 734 Idx.updateMain(testPath(TU.Filename), AST); 735 736 auto Slab = runFuzzyFind(Idx, ""); 737 auto &FooSymbol = findSymbol(Slab, "FOO"); 738 EXPECT_TRUE(FooSymbol.Flags & Symbol::IndexedForCodeCompletion); 739 } 740 741 } // namespace 742 } // namespace clangd 743 } // namespace clang 744