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