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