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