xref: /llvm-project/clang-tools-extra/clangd/unittests/FileIndexTests.cpp (revision 61fe67a4017375fd675f75652e857e837f77fa51)
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 "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 
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 MATCHER_P(includeHeader, P, "") {
64   return (arg.IncludeHeaders.size() == 1) &&
65          (arg.IncludeHeaders.begin()->IncludeHeader == P);
66 }
67 
68 namespace clang {
69 namespace clangd {
70 namespace {
71 ::testing::Matcher<const RefSlab &>
72 refsAre(std::vector<::testing::Matcher<Ref>> Matchers) {
73   return ElementsAre(::testing::Pair(_, UnorderedElementsAreArray(Matchers)));
74 }
75 
76 Symbol symbol(llvm::StringRef ID) {
77   Symbol Sym;
78   Sym.ID = SymbolID(ID);
79   Sym.Name = ID;
80   return Sym;
81 }
82 
83 std::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 
90 std::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 
99 std::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 
106 TEST(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 
118 TEST(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 
128 TEST(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 
149 TEST(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.
171 void 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 
182 TEST(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 
189 TEST(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 
200 TEST(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 
209 TEST(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 
221 TEST(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 
230 TEST(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 
242 TEST(FileIndexTest, IWYUPragmaExport) {
243   FileIndex M(true);
244 
245   TestTU File;
246   File.Code = R"cpp(#pragma once
247     #include "exporter.h"
248   )cpp";
249   File.HeaderFilename = "exporter.h";
250   File.HeaderCode = R"cpp(#pragma once
251     #include "private.h" // IWYU pragma: export
252   )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 
268 TEST(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 
279 TEST(FileIndexTest, TemplateParamsInLabel) {
280   auto *Source = R"cpp(
281 template <class Ty>
282 class vector {
283 };
284 
285 template <class Ty, class Arg>
286 vector<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 
308 TEST(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 main
353   // 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 
363 TEST(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.cc
379   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.cc
386   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 
400 TEST(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.cc
414   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 
434 TEST(FileIndexTest, CollectMacros) {
435   FileIndex M(true);
436   update(M, "f", "#define CLANGD 1");
437   EXPECT_THAT(runFuzzyFind(M, ""), Contains(qName("CLANGD")));
438 }
439 
440 TEST(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 
459 TEST(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 
486 TEST(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 excluded
500   // because we only index main AST.
501   EXPECT_THAT(getRefs(Index, findSymbol(TU.headerSymbols(), "Foo").ID),
502               refsAre({refRange(Main.range())}));
503 }
504 
505 TEST(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 
526 TEST(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 
538 TEST(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 
560 TEST(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.
583 TEST(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 
607 TEST(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 stored
620 
621   IndexFileIn IF;
622   {
623     SymbolSlab::Builder B;
624     // Should be stored in only a.h
625     B.insert(Sym1);
626     // Should be stored in both b.h and b.cc
627     B.insert(Sym2);
628     IF.Symbols.emplace(std::move(B).build());
629   }
630   {
631     // Should be stored in b.cc
632     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.h
637     B.insert(Relation{Sym1.ID, RelationKind::BaseOf, Sym2.ID});
638     // Should be stored in a.h and b.h
639     B.insert(Relation{Sym2.ID, RelationKind::BaseOf, Sym1.ID});
640     // Should be stored in a.h (where Sym1 is stored) even though
641     // 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.h
650     auto &Node = IG[BSourceUri];
651     Node.DirectIncludes = {BHeaderUri};
652     Node.URI = BSourceUri;
653   }
654   {
655     // b.h includes a.h
656     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 
716 TEST(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 
740 TEST(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 
760 TEST(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 } // namespace
774 } // namespace clangd
775 } // namespace clang
776