[AMDGPU][AsmParser][NFC] Translate parsed MIMG instructions to MCInsts automatically.
[llvm-project.git] / clang-tools-extra / clangd / unittests / FileIndexTests.cpp
blobaedd21821e3a68059bce807fba1619391d90f5e1
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 //===----------------------------------------------------------------------===//
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>
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;
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);
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;
56 MATCHER_P(defURI, U, "") {
57 return llvm::StringRef(arg.Definition.FileURI) == U;
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); }
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)));
71 Symbol symbol(llvm::StringRef ID) {
72 Symbol Sym;
73 Sym.ID = SymbolID(ID);
74 Sym.Name = ID;
75 return Sym;
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());
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());
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());
101 TEST(FileSymbolsTest, UpdateAndGet) {
102 FileSymbols FS(IndexContents::All);
103 EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""), IsEmpty());
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")}));
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")));
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());
130 auto X1 = symbol("x");
131 X1.CanonicalDeclaration.FileURI = "file:///x1";
132 auto X2 = symbol("x");
133 X2.Definition.FileURI = "file:///x2";
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"))));
144 TEST(FileSymbolsTest, SnapshotAliveAfterRemove) {
145 FileSymbols FS(IndexContents::All);
147 SymbolID ID("1");
148 FS.update("f1", numSlab(1, 3), refSlab(ID, "f1.cc"), nullptr, false);
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")}));
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());
160 EXPECT_THAT(runFuzzyFind(*Symbols, ""),
161 UnorderedElementsAre(qName("1"), qName("2"), qName("3")));
162 EXPECT_THAT(getRefs(*Symbols, ID), refsAre({fileURI("f1.cc")}));
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());
177 TEST(FileIndexTest, CustomizedURIScheme) {
178 FileIndex M;
179 update(M, "f", "class string {};");
181 EXPECT_THAT(runFuzzyFind(M, ""), ElementsAre(declURI("unittest:///f.h")));
184 TEST(FileIndexTest, IndexAST) {
185 FileIndex M;
186 update(M, "f1", "namespace ns { void f() {} class X {}; }");
188 FuzzyFindRequest Req;
189 Req.Query = "";
190 Req.Scopes = {"ns::"};
191 EXPECT_THAT(runFuzzyFind(M, Req),
192 UnorderedElementsAre(qName("ns::f"), qName("ns::X")));
195 TEST(FileIndexTest, NoLocal) {
196 FileIndex M;
197 update(M, "f1", "namespace ns { void f() { int local = 0; } class X {}; }");
199 EXPECT_THAT(
200 runFuzzyFind(M, ""),
201 UnorderedElementsAre(qName("ns"), qName("ns::f"), qName("ns::X")));
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 {}; }");
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")));
216 TEST(FileIndexTest, ClassMembers) {
217 FileIndex M;
218 update(M, "f1", "class X { static int m1; int m2; static void f(); };");
220 EXPECT_THAT(runFuzzyFind(M, ""),
221 UnorderedElementsAre(qName("X"), qName("X::m1"), qName("X::m2"),
222 qName("X::f")));
225 TEST(FileIndexTest, IncludeCollected) {
226 FileIndex M;
227 update(
228 M, "f",
229 "// IWYU pragma: private, include <the/good/header.h>\nclass string {};");
231 auto Symbols = runFuzzyFind(M, "");
232 EXPECT_THAT(Symbols, ElementsAre(_));
233 EXPECT_THAT(Symbols.begin()->IncludeHeaders.front().IncludeHeader,
234 "<the/good/header.h>");
237 TEST(FileIndexTest, HasSystemHeaderMappingsInPreamble) {
238 TestTU TU;
239 TU.HeaderCode = "class Foo{};";
240 TU.HeaderFilename = "algorithm";
242 auto Symbols = runFuzzyFind(*TU.index(), "");
243 EXPECT_THAT(Symbols, ElementsAre(_));
244 EXPECT_THAT(Symbols.begin()->IncludeHeaders.front().IncludeHeader,
245 "<algorithm>");
248 TEST(FileIndexTest, TemplateParamsInLabel) {
249 auto *Source = R"cpp(
250 template <class Ty>
251 class vector {
254 template <class Ty, class Arg>
255 vector<Ty> make_vector(Arg A) {}
256 )cpp";
258 FileIndex M;
259 update(M, "f", Source);
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);
270 EXPECT_EQ(Vector.Signature, "<class Ty>");
271 EXPECT_EQ(Vector.CompletionSnippetSuffix, "<${1:class Ty}>");
273 EXPECT_EQ(MakeVector.Signature, "<class Ty>(Arg A)");
274 EXPECT_EQ(MakeVector.CompletionSnippetSuffix, "<${1:class Ty}>(${2:Arg A})");
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};
286 MockFS FS;
287 FS.Files[FooCpp] = "";
288 FS.Files[FooH] = R"cpp(
289 namespace ns_in_header {
290 int func_in_header();
292 )cpp";
293 PI.TFS = &FS;
295 PI.Contents = R"cpp(
296 #include "foo.h"
297 namespace ns_in_source {
298 int func_in_source();
300 )cpp";
302 // Rebuild the file.
303 IgnoreDiagnostics IgnoreDiags;
304 auto CI = buildCompilerInvocation(PI, IgnoreDiags);
306 FileIndex Index;
307 bool IndexUpdated = false;
308 buildPreamble(
309 FooCpp, *CI, PI,
310 /*StoreInMemory=*/true,
311 [&](CapturedASTCtx ASTCtx,
312 const std::shared_ptr<const CanonicalIncludes> CanonIncludes) {
313 auto &Ctx = ASTCtx.getASTContext();
314 auto &PP = ASTCtx.getPreprocessor();
315 EXPECT_FALSE(IndexUpdated) << "Expected only a single index update";
316 IndexUpdated = true;
317 Index.updatePreamble(FooCpp, /*Version=*/"null", Ctx, PP,
318 *CanonIncludes);
320 ASSERT_TRUE(IndexUpdated);
322 // Check the index contains symbols from the preamble, but not from the main
323 // file.
324 FuzzyFindRequest Req;
325 Req.Query = "";
326 Req.Scopes = {"", "ns_in_header::"};
328 EXPECT_THAT(runFuzzyFind(Index, Req),
329 UnorderedElementsAre(qName("ns_in_header"),
330 qName("ns_in_header::func_in_header")));
333 TEST(FileIndexTest, Refs) {
334 const char *HeaderCode = "class Foo {};";
335 Annotations MainCode(R"cpp(
336 void f() {
337 $foo[[Foo]] foo;
339 )cpp");
341 auto Foo =
342 findSymbol(TestTU::withHeaderCode(HeaderCode).headerSymbols(), "Foo");
344 RefsRequest Request;
345 Request.IDs = {Foo.ID};
347 FileIndex Index;
348 // Add test.cc
349 TestTU Test;
350 Test.HeaderCode = HeaderCode;
351 Test.Code = std::string(MainCode.code());
352 Test.Filename = "test.cc";
353 auto AST = Test.build();
354 Index.updateMain(testPath(Test.Filename), AST);
355 // Add test2.cc
356 TestTU Test2;
357 Test2.HeaderCode = HeaderCode;
358 Test2.Code = std::string(MainCode.code());
359 Test2.Filename = "test2.cc";
360 AST = Test2.build();
361 Index.updateMain(testPath(Test2.Filename), AST);
363 EXPECT_THAT(getRefs(Index, Foo.ID),
364 refsAre({AllOf(refRange(MainCode.range("foo")),
365 fileURI("unittest:///test.cc")),
366 AllOf(refRange(MainCode.range("foo")),
367 fileURI("unittest:///test2.cc"))}));
370 TEST(FileIndexTest, MacroRefs) {
371 Annotations HeaderCode(R"cpp(
372 #define $def1[[HEADER_MACRO]](X) (X+1)
373 )cpp");
374 Annotations MainCode(R"cpp(
375 #define $def2[[MAINFILE_MACRO]](X) (X+1)
376 void f() {
377 int a = $ref1[[HEADER_MACRO]](2);
378 int b = $ref2[[MAINFILE_MACRO]](1);
380 )cpp");
382 FileIndex Index;
383 // Add test.cc
384 TestTU Test;
385 Test.HeaderCode = std::string(HeaderCode.code());
386 Test.Code = std::string(MainCode.code());
387 Test.Filename = "test.cc";
388 auto AST = Test.build();
389 Index.updateMain(testPath(Test.Filename), AST);
391 auto HeaderMacro = findSymbol(Test.headerSymbols(), "HEADER_MACRO");
392 EXPECT_THAT(getRefs(Index, HeaderMacro.ID),
393 refsAre({AllOf(refRange(MainCode.range("ref1")),
394 fileURI("unittest:///test.cc"))}));
396 auto MainFileMacro = findSymbol(Test.headerSymbols(), "MAINFILE_MACRO");
397 EXPECT_THAT(getRefs(Index, MainFileMacro.ID),
398 refsAre({AllOf(refRange(MainCode.range("def2")),
399 fileURI("unittest:///test.cc")),
400 AllOf(refRange(MainCode.range("ref2")),
401 fileURI("unittest:///test.cc"))}));
404 TEST(FileIndexTest, CollectMacros) {
405 FileIndex M;
406 update(M, "f", "#define CLANGD 1");
407 EXPECT_THAT(runFuzzyFind(M, ""), Contains(qName("CLANGD")));
410 TEST(FileIndexTest, Relations) {
411 TestTU TU;
412 TU.Filename = "f.cpp";
413 TU.HeaderFilename = "f.h";
414 TU.HeaderCode = "class A {}; class B : public A {};";
415 auto AST = TU.build();
416 FileIndex Index;
417 Index.updatePreamble(testPath(TU.Filename), /*Version=*/"null",
418 AST.getASTContext(), AST.getPreprocessor(),
419 AST.getCanonicalIncludes());
420 SymbolID A = findSymbol(TU.headerSymbols(), "A").ID;
421 uint32_t Results = 0;
422 RelationsRequest Req;
423 Req.Subjects.insert(A);
424 Req.Predicate = RelationKind::BaseOf;
425 Index.relations(Req, [&](const SymbolID &, const Symbol &) { ++Results; });
426 EXPECT_EQ(Results, 1u);
429 TEST(FileIndexTest, RelationsMultiFile) {
430 TestWorkspace Workspace;
431 Workspace.addSource("Base.h", "class Base {};");
432 Workspace.addMainFile("A.cpp", R"cpp(
433 #include "Base.h"
434 class A : public Base {};
435 )cpp");
436 Workspace.addMainFile("B.cpp", R"cpp(
437 #include "Base.h"
438 class B : public Base {};
439 )cpp");
441 auto Index = Workspace.index();
442 FuzzyFindRequest FFReq;
443 FFReq.Query = "Base";
444 FFReq.AnyScope = true;
445 SymbolID Base;
446 Index->fuzzyFind(FFReq, [&](const Symbol &S) { Base = S.ID; });
448 RelationsRequest Req;
449 Req.Subjects.insert(Base);
450 Req.Predicate = RelationKind::BaseOf;
451 uint32_t Results = 0;
452 Index->relations(Req, [&](const SymbolID &, const Symbol &) { ++Results; });
453 EXPECT_EQ(Results, 2u);
456 TEST(FileIndexTest, ReferencesInMainFileWithPreamble) {
457 TestTU TU;
458 TU.HeaderCode = "class Foo{};";
459 Annotations Main(R"cpp(
460 void f() {
461 [[Foo]] foo;
463 )cpp");
464 TU.Code = std::string(Main.code());
465 auto AST = TU.build();
466 FileIndex Index;
467 Index.updateMain(testPath(TU.Filename), AST);
469 // Expect to see references in main file, references in headers are excluded
470 // because we only index main AST.
471 EXPECT_THAT(getRefs(Index, findSymbol(TU.headerSymbols(), "Foo").ID),
472 refsAre({refRange(Main.range())}));
475 TEST(FileIndexTest, MergeMainFileSymbols) {
476 const char *CommonHeader = "void foo();";
477 TestTU Header = TestTU::withCode(CommonHeader);
478 TestTU Cpp = TestTU::withCode("void foo() {}");
479 Cpp.Filename = "foo.cpp";
480 Cpp.HeaderFilename = "foo.h";
481 Cpp.HeaderCode = CommonHeader;
483 FileIndex Index;
484 auto HeaderAST = Header.build();
485 auto CppAST = Cpp.build();
486 Index.updateMain(testPath("foo.h"), HeaderAST);
487 Index.updateMain(testPath("foo.cpp"), CppAST);
489 auto Symbols = runFuzzyFind(Index, "");
490 // Check foo is merged, foo in Cpp wins (as we see the definition there).
491 EXPECT_THAT(Symbols, ElementsAre(AllOf(declURI("unittest:///foo.h"),
492 defURI("unittest:///foo.cpp"),
493 hasOrign(SymbolOrigin::Merge))));
496 TEST(FileSymbolsTest, CountReferencesNoRefSlabs) {
497 FileSymbols FS(IndexContents::All);
498 FS.update("f1", numSlab(1, 3), nullptr, nullptr, true);
499 FS.update("f2", numSlab(1, 3), nullptr, nullptr, false);
500 EXPECT_THAT(
501 runFuzzyFind(*FS.buildIndex(IndexType::Light, DuplicateHandling::Merge),
502 ""),
503 UnorderedElementsAre(AllOf(qName("1"), numReferences(0u)),
504 AllOf(qName("2"), numReferences(0u)),
505 AllOf(qName("3"), numReferences(0u))));
508 TEST(FileSymbolsTest, CountReferencesWithRefSlabs) {
509 FileSymbols FS(IndexContents::All);
510 FS.update("f1cpp", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cpp"), nullptr,
511 true);
512 FS.update("f1h", numSlab(1, 3), refSlab(SymbolID("1"), "f1.h"), nullptr,
513 false);
514 FS.update("f2cpp", numSlab(1, 3), refSlab(SymbolID("2"), "f2.cpp"), nullptr,
515 true);
516 FS.update("f2h", numSlab(1, 3), refSlab(SymbolID("2"), "f2.h"), nullptr,
517 false);
518 FS.update("f3cpp", numSlab(1, 3), refSlab(SymbolID("3"), "f3.cpp"), nullptr,
519 true);
520 FS.update("f3h", numSlab(1, 3), refSlab(SymbolID("3"), "f3.h"), nullptr,
521 false);
522 EXPECT_THAT(
523 runFuzzyFind(*FS.buildIndex(IndexType::Light, DuplicateHandling::Merge),
524 ""),
525 UnorderedElementsAre(AllOf(qName("1"), numReferences(1u)),
526 AllOf(qName("2"), numReferences(1u)),
527 AllOf(qName("3"), numReferences(1u))));
530 TEST(FileIndexTest, StalePreambleSymbolsDeleted) {
531 FileIndex M;
532 TestTU File;
533 File.HeaderFilename = "a.h";
535 File.Filename = "f1.cpp";
536 File.HeaderCode = "int a;";
537 auto AST = File.build();
538 M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
539 AST.getASTContext(), AST.getPreprocessor(),
540 AST.getCanonicalIncludes());
541 EXPECT_THAT(runFuzzyFind(M, ""), UnorderedElementsAre(qName("a")));
543 File.Filename = "f2.cpp";
544 File.HeaderCode = "int b;";
545 AST = File.build();
546 M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
547 AST.getASTContext(), AST.getPreprocessor(),
548 AST.getCanonicalIncludes());
549 EXPECT_THAT(runFuzzyFind(M, ""), UnorderedElementsAre(qName("b")));
552 // Verifies that concurrent calls to updateMain don't "lose" any updates.
553 TEST(FileIndexTest, Threadsafety) {
554 FileIndex M;
555 Notification Go;
557 constexpr int Count = 10;
559 // Set up workers to concurrently call updateMain() with separate files.
560 AsyncTaskRunner Pool;
561 for (unsigned I = 0; I < Count; ++I) {
562 auto TU = TestTU::withCode(llvm::formatv("int xxx{0};", I).str());
563 TU.Filename = llvm::formatv("x{0}.c", I).str();
564 Pool.runAsync(TU.Filename, [&, Filename(testPath(TU.Filename)),
565 AST(TU.build())]() mutable {
566 Go.wait();
567 M.updateMain(Filename, AST);
570 // On your marks, get set...
571 Go.notify();
574 EXPECT_THAT(runFuzzyFind(M, "xxx"), ::testing::SizeIs(Count));
577 TEST(FileShardedIndexTest, Sharding) {
578 auto AHeaderUri = URI::create(testPath("a.h")).toString();
579 auto BHeaderUri = URI::create(testPath("b.h")).toString();
580 auto BSourceUri = URI::create(testPath("b.cc")).toString();
582 auto Sym1 = symbol("1");
583 Sym1.CanonicalDeclaration.FileURI = AHeaderUri.c_str();
585 auto Sym2 = symbol("2");
586 Sym2.CanonicalDeclaration.FileURI = BHeaderUri.c_str();
587 Sym2.Definition.FileURI = BSourceUri.c_str();
589 auto Sym3 = symbol("3"); // not stored
591 IndexFileIn IF;
593 SymbolSlab::Builder B;
594 // Should be stored in only a.h
595 B.insert(Sym1);
596 // Should be stored in both b.h and b.cc
597 B.insert(Sym2);
598 IF.Symbols.emplace(std::move(B).build());
601 // Should be stored in b.cc
602 IF.Refs.emplace(std::move(*refSlab(Sym1.ID, BSourceUri.c_str())));
605 RelationSlab::Builder B;
606 // Should be stored in a.h and b.h
607 B.insert(Relation{Sym1.ID, RelationKind::BaseOf, Sym2.ID});
608 // Should be stored in a.h and b.h
609 B.insert(Relation{Sym2.ID, RelationKind::BaseOf, Sym1.ID});
610 // Should be stored in a.h (where Sym1 is stored) even though
611 // the relation is dangling as Sym3 is unknown.
612 B.insert(Relation{Sym3.ID, RelationKind::BaseOf, Sym1.ID});
613 IF.Relations.emplace(std::move(B).build());
616 IF.Sources.emplace();
617 IncludeGraph &IG = *IF.Sources;
619 // b.cc includes b.h
620 auto &Node = IG[BSourceUri];
621 Node.DirectIncludes = {BHeaderUri};
622 Node.URI = BSourceUri;
625 // b.h includes a.h
626 auto &Node = IG[BHeaderUri];
627 Node.DirectIncludes = {AHeaderUri};
628 Node.URI = BHeaderUri;
631 // a.h includes nothing.
632 auto &Node = IG[AHeaderUri];
633 Node.DirectIncludes = {};
634 Node.URI = AHeaderUri;
637 IF.Cmd = tooling::CompileCommand(testRoot(), "b.cc", {"clang"}, "out");
639 FileShardedIndex ShardedIndex(std::move(IF));
640 ASSERT_THAT(ShardedIndex.getAllSources(),
641 UnorderedElementsAre(AHeaderUri, BHeaderUri, BSourceUri));
644 auto Shard = ShardedIndex.getShard(AHeaderUri);
645 ASSERT_TRUE(Shard);
646 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(qName("1")));
647 EXPECT_THAT(*Shard->Refs, IsEmpty());
648 EXPECT_THAT(
649 *Shard->Relations,
650 UnorderedElementsAre(Relation{Sym1.ID, RelationKind::BaseOf, Sym2.ID},
651 Relation{Sym2.ID, RelationKind::BaseOf, Sym1.ID},
652 Relation{Sym3.ID, RelationKind::BaseOf, Sym1.ID}));
653 ASSERT_THAT(Shard->Sources->keys(), UnorderedElementsAre(AHeaderUri));
654 EXPECT_THAT(Shard->Sources->lookup(AHeaderUri).DirectIncludes, IsEmpty());
655 EXPECT_TRUE(Shard->Cmd);
658 auto Shard = ShardedIndex.getShard(BHeaderUri);
659 ASSERT_TRUE(Shard);
660 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(qName("2")));
661 EXPECT_THAT(*Shard->Refs, IsEmpty());
662 EXPECT_THAT(
663 *Shard->Relations,
664 UnorderedElementsAre(Relation{Sym1.ID, RelationKind::BaseOf, Sym2.ID},
665 Relation{Sym2.ID, RelationKind::BaseOf, Sym1.ID}));
666 ASSERT_THAT(Shard->Sources->keys(),
667 UnorderedElementsAre(BHeaderUri, AHeaderUri));
668 EXPECT_THAT(Shard->Sources->lookup(BHeaderUri).DirectIncludes,
669 UnorderedElementsAre(AHeaderUri));
670 EXPECT_TRUE(Shard->Cmd);
673 auto Shard = ShardedIndex.getShard(BSourceUri);
674 ASSERT_TRUE(Shard);
675 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(qName("2")));
676 EXPECT_THAT(*Shard->Refs, UnorderedElementsAre(Pair(Sym1.ID, _)));
677 EXPECT_THAT(*Shard->Relations, IsEmpty());
678 ASSERT_THAT(Shard->Sources->keys(),
679 UnorderedElementsAre(BSourceUri, BHeaderUri));
680 EXPECT_THAT(Shard->Sources->lookup(BSourceUri).DirectIncludes,
681 UnorderedElementsAre(BHeaderUri));
682 EXPECT_TRUE(Shard->Cmd);
686 TEST(FileIndexTest, Profile) {
687 FileIndex FI;
689 auto FileName = testPath("foo.cpp");
690 auto AST = TestTU::withHeaderCode("int a;").build();
691 FI.updateMain(FileName, AST);
692 FI.updatePreamble(FileName, "v1", AST.getASTContext(), AST.getPreprocessor(),
693 AST.getCanonicalIncludes());
695 llvm::BumpPtrAllocator Alloc;
696 MemoryTree MT(&Alloc);
697 FI.profile(MT);
698 ASSERT_THAT(MT.children(),
699 UnorderedElementsAre(Pair("preamble", _), Pair("main_file", _)));
701 ASSERT_THAT(MT.child("preamble").children(),
702 UnorderedElementsAre(Pair("index", _), Pair("slabs", _)));
703 ASSERT_THAT(MT.child("main_file").children(),
704 UnorderedElementsAre(Pair("index", _), Pair("slabs", _)));
706 ASSERT_THAT(MT.child("preamble").child("index").total(), Gt(0U));
707 ASSERT_THAT(MT.child("main_file").child("index").total(), Gt(0U));
710 TEST(FileSymbolsTest, Profile) {
711 FileSymbols FS(IndexContents::All);
712 FS.update("f1", numSlab(1, 2), nullptr, nullptr, false);
713 FS.update("f2", nullptr, refSlab(SymbolID("1"), "f1"), nullptr, false);
714 FS.update("f3", nullptr, nullptr,
715 relSlab({{SymbolID("1"), RelationKind::BaseOf, SymbolID("2")}}),
716 false);
717 llvm::BumpPtrAllocator Alloc;
718 MemoryTree MT(&Alloc);
719 FS.profile(MT);
720 ASSERT_THAT(MT.children(), UnorderedElementsAre(Pair("f1", _), Pair("f2", _),
721 Pair("f3", _)));
722 EXPECT_THAT(MT.child("f1").children(), ElementsAre(Pair("symbols", _)));
723 EXPECT_THAT(MT.child("f1").total(), Gt(0U));
724 EXPECT_THAT(MT.child("f2").children(), ElementsAre(Pair("references", _)));
725 EXPECT_THAT(MT.child("f2").total(), Gt(0U));
726 EXPECT_THAT(MT.child("f3").children(), ElementsAre(Pair("relations", _)));
727 EXPECT_THAT(MT.child("f3").total(), Gt(0U));
730 TEST(FileIndexTest, MacrosFromMainFile) {
731 FileIndex Idx;
732 TestTU TU;
733 TU.Code = "#pragma once\n#define FOO";
734 TU.Filename = "foo.h";
735 auto AST = TU.build();
736 Idx.updateMain(testPath(TU.Filename), AST);
738 auto Slab = runFuzzyFind(Idx, "");
739 auto &FooSymbol = findSymbol(Slab, "FOO");
740 EXPECT_TRUE(FooSymbol.Flags & Symbol::IndexedForCodeCompletion);
743 } // namespace
744 } // namespace clangd
745 } // namespace clang