xref: /llvm-project/clang-tools-extra/clangd/unittests/IndexTests.cpp (revision 6d2fb3cefba618be0326bb3da85d7568a72fefc4)
1 //===-- IndexTests.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 "SyncAPI.h"
11 #include "TestIndex.h"
12 #include "TestTU.h"
13 #include "index/FileIndex.h"
14 #include "index/Index.h"
15 #include "index/MemIndex.h"
16 #include "index/Merge.h"
17 #include "index/Symbol.h"
18 #include "clang/Index/IndexSymbol.h"
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21 #include <utility>
22 
23 using ::testing::_;
24 using ::testing::AllOf;
25 using ::testing::AnyOf;
26 using ::testing::ElementsAre;
27 using ::testing::IsEmpty;
28 using ::testing::Pair;
29 using ::testing::Pointee;
30 using ::testing::UnorderedElementsAre;
31 
32 namespace clang {
33 namespace clangd {
34 namespace {
35 
36 MATCHER_P(Named, N, "") { return arg.Name == N; }
37 MATCHER_P(RefRange, Range, "") {
38   return std::make_tuple(arg.Location.Start.line(), arg.Location.Start.column(),
39                          arg.Location.End.line(), arg.Location.End.column()) ==
40          std::make_tuple(Range.start.line, Range.start.character,
41                          Range.end.line, Range.end.character);
42 }
43 MATCHER_P(FileURI, F, "") { return StringRef(arg.Location.FileURI) == F; }
44 
45 TEST(SymbolLocation, Position) {
46   using Position = SymbolLocation::Position;
47   Position Pos;
48 
49   Pos.setLine(1);
50   EXPECT_EQ(1u, Pos.line());
51   Pos.setColumn(2);
52   EXPECT_EQ(2u, Pos.column());
53   EXPECT_FALSE(Pos.hasOverflow());
54 
55   Pos.setLine(Position::MaxLine + 1); // overflow
56   EXPECT_TRUE(Pos.hasOverflow());
57   EXPECT_EQ(Pos.line(), Position::MaxLine);
58   Pos.setLine(1); // reset the overflowed line.
59 
60   Pos.setColumn(Position::MaxColumn + 1); // overflow
61   EXPECT_TRUE(Pos.hasOverflow());
62   EXPECT_EQ(Pos.column(), Position::MaxColumn);
63 }
64 
65 TEST(SymbolSlab, FindAndIterate) {
66   SymbolSlab::Builder B;
67   B.insert(symbol("Z"));
68   B.insert(symbol("Y"));
69   B.insert(symbol("X"));
70   EXPECT_EQ(nullptr, B.find(SymbolID("W")));
71   for (const char *Sym : {"X", "Y", "Z"})
72     EXPECT_THAT(B.find(SymbolID(Sym)), Pointee(Named(Sym)));
73 
74   SymbolSlab S = std::move(B).build();
75   EXPECT_THAT(S, UnorderedElementsAre(Named("X"), Named("Y"), Named("Z")));
76   EXPECT_EQ(S.end(), S.find(SymbolID("W")));
77   for (const char *Sym : {"X", "Y", "Z"})
78     EXPECT_THAT(*S.find(SymbolID(Sym)), Named(Sym));
79 }
80 
81 TEST(RelationSlab, Lookup) {
82   SymbolID A{"A"};
83   SymbolID B{"B"};
84   SymbolID C{"C"};
85   SymbolID D{"D"};
86 
87   RelationSlab::Builder Builder;
88   Builder.insert(Relation{A, RelationKind::BaseOf, B});
89   Builder.insert(Relation{A, RelationKind::BaseOf, C});
90   Builder.insert(Relation{B, RelationKind::BaseOf, D});
91   Builder.insert(Relation{C, RelationKind::BaseOf, D});
92 
93   RelationSlab Slab = std::move(Builder).build();
94   EXPECT_THAT(Slab.lookup(A, RelationKind::BaseOf),
95               UnorderedElementsAre(Relation{A, RelationKind::BaseOf, B},
96                                    Relation{A, RelationKind::BaseOf, C}));
97 }
98 
99 TEST(RelationSlab, Duplicates) {
100   SymbolID A{"A"};
101   SymbolID B{"B"};
102   SymbolID C{"C"};
103 
104   RelationSlab::Builder Builder;
105   Builder.insert(Relation{A, RelationKind::BaseOf, B});
106   Builder.insert(Relation{A, RelationKind::BaseOf, C});
107   Builder.insert(Relation{A, RelationKind::BaseOf, B});
108 
109   RelationSlab Slab = std::move(Builder).build();
110   EXPECT_THAT(Slab, UnorderedElementsAre(Relation{A, RelationKind::BaseOf, B},
111                                          Relation{A, RelationKind::BaseOf, C}));
112 }
113 
114 TEST(SwapIndexTest, OldIndexRecycled) {
115   auto Token = std::make_shared<int>();
116   std::weak_ptr<int> WeakToken = Token;
117 
118   SwapIndex S(std::make_unique<MemIndex>(SymbolSlab(), RefSlab(),
119                                           RelationSlab(), std::move(Token),
120                                           /*BackingDataSize=*/0));
121   EXPECT_FALSE(WeakToken.expired());      // Current MemIndex keeps it alive.
122   S.reset(std::make_unique<MemIndex>()); // Now the MemIndex is destroyed.
123   EXPECT_TRUE(WeakToken.expired());       // So the token is too.
124 }
125 
126 TEST(MemIndexTest, MemIndexDeduplicate) {
127   std::vector<Symbol> Symbols = {symbol("1"), symbol("2"), symbol("3"),
128                                  symbol("2") /* duplicate */};
129   FuzzyFindRequest Req;
130   Req.Query = "2";
131   Req.AnyScope = true;
132   MemIndex I(Symbols, RefSlab(), RelationSlab());
133   EXPECT_THAT(match(I, Req), ElementsAre("2"));
134 }
135 
136 TEST(MemIndexTest, MemIndexLimitedNumMatches) {
137   auto I =
138       MemIndex::build(generateNumSymbols(0, 100), RefSlab(), RelationSlab());
139   FuzzyFindRequest Req;
140   Req.Query = "5";
141   Req.AnyScope = true;
142   Req.Limit = 3;
143   bool Incomplete;
144   auto Matches = match(*I, Req, &Incomplete);
145   EXPECT_TRUE(Req.Limit);
146   EXPECT_EQ(Matches.size(), *Req.Limit);
147   EXPECT_TRUE(Incomplete);
148 }
149 
150 TEST(MemIndexTest, FuzzyMatch) {
151   auto I = MemIndex::build(
152       generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"}),
153       RefSlab(), RelationSlab());
154   FuzzyFindRequest Req;
155   Req.Query = "lol";
156   Req.AnyScope = true;
157   Req.Limit = 2;
158   EXPECT_THAT(match(*I, Req),
159               UnorderedElementsAre("LaughingOutLoud", "LittleOldLady"));
160 }
161 
162 TEST(MemIndexTest, MatchQualifiedNamesWithoutSpecificScope) {
163   auto I = MemIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab(),
164                            RelationSlab());
165   FuzzyFindRequest Req;
166   Req.Query = "y";
167   Req.AnyScope = true;
168   EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "b::y2", "y3"));
169 }
170 
171 TEST(MemIndexTest, MatchQualifiedNamesWithGlobalScope) {
172   auto I = MemIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab(),
173                            RelationSlab());
174   FuzzyFindRequest Req;
175   Req.Query = "y";
176   Req.Scopes = {""};
177   EXPECT_THAT(match(*I, Req), UnorderedElementsAre("y3"));
178 }
179 
180 TEST(MemIndexTest, MatchQualifiedNamesWithOneScope) {
181   auto I = MemIndex::build(
182       generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}), RefSlab(),
183       RelationSlab());
184   FuzzyFindRequest Req;
185   Req.Query = "y";
186   Req.Scopes = {"a::"};
187   EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "a::y2"));
188 }
189 
190 TEST(MemIndexTest, MatchQualifiedNamesWithMultipleScopes) {
191   auto I = MemIndex::build(
192       generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}), RefSlab(),
193       RelationSlab());
194   FuzzyFindRequest Req;
195   Req.Query = "y";
196   Req.Scopes = {"a::", "b::"};
197   EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "a::y2", "b::y3"));
198 }
199 
200 TEST(MemIndexTest, NoMatchNestedScopes) {
201   auto I = MemIndex::build(generateSymbols({"a::y1", "a::b::y2"}), RefSlab(),
202                            RelationSlab());
203   FuzzyFindRequest Req;
204   Req.Query = "y";
205   Req.Scopes = {"a::"};
206   EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1"));
207 }
208 
209 TEST(MemIndexTest, IgnoreCases) {
210   auto I = MemIndex::build(generateSymbols({"ns::ABC", "ns::abc"}), RefSlab(),
211                            RelationSlab());
212   FuzzyFindRequest Req;
213   Req.Query = "AB";
214   Req.Scopes = {"ns::"};
215   EXPECT_THAT(match(*I, Req), UnorderedElementsAre("ns::ABC", "ns::abc"));
216 }
217 
218 TEST(MemIndexTest, Lookup) {
219   auto I = MemIndex::build(generateSymbols({"ns::abc", "ns::xyz"}), RefSlab(),
220                            RelationSlab());
221   EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc"));
222   EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}),
223               UnorderedElementsAre("ns::abc", "ns::xyz"));
224   EXPECT_THAT(lookup(*I, {SymbolID("ns::nonono"), SymbolID("ns::xyz")}),
225               UnorderedElementsAre("ns::xyz"));
226   EXPECT_THAT(lookup(*I, SymbolID("ns::nonono")), UnorderedElementsAre());
227 }
228 
229 TEST(MemIndexTest, IndexedFiles) {
230   SymbolSlab Symbols;
231   RefSlab Refs;
232   auto Size = Symbols.bytes() + Refs.bytes();
233   auto Data = std::make_pair(std::move(Symbols), std::move(Refs));
234   llvm::StringSet<> Files = {"unittest:///foo.cc", "unittest:///bar.cc"};
235   MemIndex I(std::move(Data.first), std::move(Data.second), RelationSlab(),
236              std::move(Files), IndexContents::All, std::move(Data), Size);
237   auto ContainsFile = I.indexedFiles();
238   EXPECT_EQ(ContainsFile("unittest:///foo.cc"), IndexContents::All);
239   EXPECT_EQ(ContainsFile("unittest:///bar.cc"), IndexContents::All);
240   EXPECT_EQ(ContainsFile("unittest:///foobar.cc"), IndexContents::None);
241 }
242 
243 TEST(MemIndexTest, TemplateSpecialization) {
244   SymbolSlab::Builder B;
245 
246   Symbol S = symbol("TempSpec");
247   S.ID = SymbolID("1");
248   B.insert(S);
249 
250   S = symbol("TempSpec");
251   S.ID = SymbolID("2");
252   S.TemplateSpecializationArgs = "<int, bool>";
253   S.SymInfo.Properties = static_cast<index::SymbolPropertySet>(
254       index::SymbolProperty::TemplateSpecialization);
255   B.insert(S);
256 
257   S = symbol("TempSpec");
258   S.ID = SymbolID("3");
259   S.TemplateSpecializationArgs = "<int, U>";
260   S.SymInfo.Properties = static_cast<index::SymbolPropertySet>(
261       index::SymbolProperty::TemplatePartialSpecialization);
262   B.insert(S);
263 
264   auto I = MemIndex::build(std::move(B).build(), RefSlab(), RelationSlab());
265   FuzzyFindRequest Req;
266   Req.AnyScope = true;
267 
268   Req.Query = "TempSpec";
269   EXPECT_THAT(match(*I, Req),
270               UnorderedElementsAre("TempSpec", "TempSpec<int, bool>",
271                                    "TempSpec<int, U>"));
272 
273   // FIXME: Add filtering for template argument list.
274   Req.Query = "TempSpec<int";
275   EXPECT_THAT(match(*I, Req), IsEmpty());
276 }
277 
278 TEST(MergeIndexTest, Lookup) {
279   auto I = MemIndex::build(generateSymbols({"ns::A", "ns::B"}), RefSlab(),
280                            RelationSlab()),
281        J = MemIndex::build(generateSymbols({"ns::B", "ns::C"}), RefSlab(),
282                            RelationSlab());
283   MergedIndex M(I.get(), J.get());
284   EXPECT_THAT(lookup(M, SymbolID("ns::A")), UnorderedElementsAre("ns::A"));
285   EXPECT_THAT(lookup(M, SymbolID("ns::B")), UnorderedElementsAre("ns::B"));
286   EXPECT_THAT(lookup(M, SymbolID("ns::C")), UnorderedElementsAre("ns::C"));
287   EXPECT_THAT(lookup(M, {SymbolID("ns::A"), SymbolID("ns::B")}),
288               UnorderedElementsAre("ns::A", "ns::B"));
289   EXPECT_THAT(lookup(M, {SymbolID("ns::A"), SymbolID("ns::C")}),
290               UnorderedElementsAre("ns::A", "ns::C"));
291   EXPECT_THAT(lookup(M, SymbolID("ns::D")), UnorderedElementsAre());
292   EXPECT_THAT(lookup(M, {}), UnorderedElementsAre());
293 }
294 
295 TEST(MergeIndexTest, LookupRemovedDefinition) {
296   FileIndex DynamicIndex, StaticIndex;
297   MergedIndex Merge(&DynamicIndex, &StaticIndex);
298 
299   const char *HeaderCode = "class Foo;";
300   auto HeaderSymbols = TestTU::withHeaderCode(HeaderCode).headerSymbols();
301   auto Foo = findSymbol(HeaderSymbols, "Foo");
302 
303   // Build static index for test.cc with Foo definition
304   TestTU Test;
305   Test.HeaderCode = HeaderCode;
306   Test.Code = "class Foo {};";
307   Test.Filename = "test.cc";
308   auto AST = Test.build();
309   StaticIndex.updateMain(testPath(Test.Filename), AST);
310 
311   // Remove Foo definition from test.cc, i.e. build dynamic index for test.cc
312   // without Foo definition.
313   Test.Code = "class Foo;";
314   AST = Test.build();
315   DynamicIndex.updateMain(testPath(Test.Filename), AST);
316 
317   // Even though the definition is actually deleted in the newer version of the
318   // file, we still chose to merge with information coming from static index.
319   // This seems wrong, but is generic behavior we want for e.g. include headers
320   // which are always missing from the dynamic index
321   LookupRequest LookupReq;
322   LookupReq.IDs = {Foo.ID};
323   unsigned SymbolCounter = 0;
324   Merge.lookup(LookupReq, [&](const Symbol &Sym) {
325     ++SymbolCounter;
326     EXPECT_TRUE(Sym.Definition);
327   });
328   EXPECT_EQ(SymbolCounter, 1u);
329 
330   // Drop the symbol completely.
331   Test.Code = "class Bar {};";
332   AST = Test.build();
333   DynamicIndex.updateMain(testPath(Test.Filename), AST);
334 
335   // Now we don't expect to see the symbol at all.
336   SymbolCounter = 0;
337   Merge.lookup(LookupReq, [&](const Symbol &Sym) { ++SymbolCounter; });
338   EXPECT_EQ(SymbolCounter, 0u);
339 }
340 
341 TEST(MergeIndexTest, FuzzyFind) {
342   auto I = MemIndex::build(generateSymbols({"ns::A", "ns::B"}), RefSlab(),
343                            RelationSlab()),
344        J = MemIndex::build(generateSymbols({"ns::B", "ns::C"}), RefSlab(),
345                            RelationSlab());
346   FuzzyFindRequest Req;
347   Req.Scopes = {"ns::"};
348   EXPECT_THAT(match(MergedIndex(I.get(), J.get()), Req),
349               UnorderedElementsAre("ns::A", "ns::B", "ns::C"));
350 }
351 
352 TEST(MergeIndexTest, FuzzyFindRemovedSymbol) {
353   FileIndex DynamicIndex, StaticIndex;
354   MergedIndex Merge(&DynamicIndex, &StaticIndex);
355 
356   const char *HeaderCode = "class Foo;";
357   auto HeaderSymbols = TestTU::withHeaderCode(HeaderCode).headerSymbols();
358   auto Foo = findSymbol(HeaderSymbols, "Foo");
359 
360   // Build static index for test.cc with Foo symbol
361   TestTU Test;
362   Test.HeaderCode = HeaderCode;
363   Test.Code = "class Foo {};";
364   Test.Filename = "test.cc";
365   auto AST = Test.build();
366   StaticIndex.updateMain(testPath(Test.Filename), AST);
367 
368   // Remove Foo symbol, i.e. build dynamic index for test.cc, which is empty.
369   Test.HeaderCode = "";
370   Test.Code = "";
371   AST = Test.build();
372   DynamicIndex.updateMain(testPath(Test.Filename), AST);
373 
374   // Merged index should not return removed symbol.
375   FuzzyFindRequest Req;
376   Req.AnyScope = true;
377   Req.Query = "Foo";
378   unsigned SymbolCounter = 0;
379   bool IsIncomplete =
380       Merge.fuzzyFind(Req, [&](const Symbol &) { ++SymbolCounter; });
381   EXPECT_FALSE(IsIncomplete);
382   EXPECT_EQ(SymbolCounter, 0u);
383 }
384 
385 TEST(MergeTest, Merge) {
386   Symbol L, R;
387   L.ID = R.ID = SymbolID("hello");
388   L.Name = R.Name = "Foo";                           // same in both
389   L.CanonicalDeclaration.FileURI = "file:///left.h"; // differs
390   R.CanonicalDeclaration.FileURI = "file:///right.h";
391   L.References = 1;
392   R.References = 2;
393   L.Signature = "()";                   // present in left only
394   R.CompletionSnippetSuffix = "{$1:0}"; // present in right only
395   R.Documentation = "--doc--";
396   L.Origin = SymbolOrigin::Dynamic;
397   R.Origin = SymbolOrigin::Static;
398   R.Type = "expectedType";
399 
400   Symbol M = mergeSymbol(L, R);
401   EXPECT_EQ(M.Name, "Foo");
402   EXPECT_EQ(StringRef(M.CanonicalDeclaration.FileURI), "file:///left.h");
403   EXPECT_EQ(M.References, 3u);
404   EXPECT_EQ(M.Signature, "()");
405   EXPECT_EQ(M.CompletionSnippetSuffix, "{$1:0}");
406   EXPECT_EQ(M.Documentation, "--doc--");
407   EXPECT_EQ(M.Type, "expectedType");
408   EXPECT_EQ(M.Origin,
409             SymbolOrigin::Dynamic | SymbolOrigin::Static | SymbolOrigin::Merge);
410 }
411 
412 TEST(MergeTest, PreferSymbolWithDefn) {
413   Symbol L, R;
414 
415   L.ID = R.ID = SymbolID("hello");
416   L.CanonicalDeclaration.FileURI = "file:/left.h";
417   R.CanonicalDeclaration.FileURI = "file:/right.h";
418   L.Name = "left";
419   R.Name = "right";
420 
421   Symbol M = mergeSymbol(L, R);
422   EXPECT_EQ(StringRef(M.CanonicalDeclaration.FileURI), "file:/left.h");
423   EXPECT_EQ(StringRef(M.Definition.FileURI), "");
424   EXPECT_EQ(M.Name, "left");
425 
426   R.Definition.FileURI = "file:/right.cpp"; // Now right will be favored.
427   M = mergeSymbol(L, R);
428   EXPECT_EQ(StringRef(M.CanonicalDeclaration.FileURI), "file:/right.h");
429   EXPECT_EQ(StringRef(M.Definition.FileURI), "file:/right.cpp");
430   EXPECT_EQ(M.Name, "right");
431 }
432 
433 TEST(MergeTest, PreferSymbolLocationInCodegenFile) {
434   Symbol L, R;
435 
436   L.ID = R.ID = SymbolID("hello");
437   L.CanonicalDeclaration.FileURI = "file:/x.proto.h";
438   R.CanonicalDeclaration.FileURI = "file:/x.proto";
439 
440   Symbol M = mergeSymbol(L, R);
441   EXPECT_EQ(StringRef(M.CanonicalDeclaration.FileURI), "file:/x.proto");
442 
443   // Prefer L if both have codegen suffix.
444   L.CanonicalDeclaration.FileURI = "file:/y.proto";
445   M = mergeSymbol(L, R);
446   EXPECT_EQ(StringRef(M.CanonicalDeclaration.FileURI), "file:/y.proto");
447 }
448 
449 TEST(MergeIndexTest, Refs) {
450   FileIndex Dyn;
451   FileIndex StaticIndex;
452   MergedIndex Merge(&Dyn, &StaticIndex);
453 
454   const char *HeaderCode = "class Foo;";
455   auto HeaderSymbols = TestTU::withHeaderCode("class Foo;").headerSymbols();
456   auto Foo = findSymbol(HeaderSymbols, "Foo");
457 
458   // Build dynamic index for test.cc.
459   Annotations Test1Code(R"(class $Foo[[Foo]];)");
460   TestTU Test;
461   Test.HeaderCode = HeaderCode;
462   Test.Code = std::string(Test1Code.code());
463   Test.Filename = "test.cc";
464   auto AST = Test.build();
465   Dyn.updateMain(testPath(Test.Filename), AST);
466 
467   // Build static index for test.cc.
468   Test.HeaderCode = HeaderCode;
469   Test.Code = "// static\nclass Foo {};";
470   Test.Filename = "test.cc";
471   auto StaticAST = Test.build();
472   // Add stale refs for test.cc.
473   StaticIndex.updateMain(testPath(Test.Filename), StaticAST);
474 
475   // Add refs for test2.cc
476   Annotations Test2Code(R"(class $Foo[[Foo]] {};)");
477   TestTU Test2;
478   Test2.HeaderCode = HeaderCode;
479   Test2.Code = std::string(Test2Code.code());
480   Test2.Filename = "test2.cc";
481   StaticAST = Test2.build();
482   StaticIndex.updateMain(testPath(Test2.Filename), StaticAST);
483 
484   RefsRequest Request;
485   Request.IDs = {Foo.ID};
486   RefSlab::Builder Results;
487   EXPECT_FALSE(
488       Merge.refs(Request, [&](const Ref &O) { Results.insert(Foo.ID, O); }));
489   EXPECT_THAT(
490       std::move(Results).build(),
491       ElementsAre(Pair(
492           _, UnorderedElementsAre(AllOf(RefRange(Test1Code.range("Foo")),
493                                         FileURI("unittest:///test.cc")),
494                                   AllOf(RefRange(Test2Code.range("Foo")),
495                                         FileURI("unittest:///test2.cc"))))));
496 
497   Request.Limit = 1;
498   RefSlab::Builder Results2;
499   EXPECT_TRUE(
500       Merge.refs(Request, [&](const Ref &O) { Results2.insert(Foo.ID, O); }));
501 
502   // Remove all refs for test.cc from dynamic index,
503   // merged index should not return results from static index for test.cc.
504   Test.Code = "";
505   AST = Test.build();
506   Dyn.updateMain(testPath(Test.Filename), AST);
507 
508   Request.Limit = llvm::None;
509   RefSlab::Builder Results3;
510   EXPECT_FALSE(
511       Merge.refs(Request, [&](const Ref &O) { Results3.insert(Foo.ID, O); }));
512   EXPECT_THAT(std::move(Results3).build(),
513               ElementsAre(Pair(_, UnorderedElementsAre(AllOf(
514                                       RefRange(Test2Code.range("Foo")),
515                                       FileURI("unittest:///test2.cc"))))));
516 }
517 
518 TEST(MergeIndexTest, IndexedFiles) {
519   SymbolSlab DynSymbols;
520   RefSlab DynRefs;
521   auto DynSize = DynSymbols.bytes() + DynRefs.bytes();
522   auto DynData = std::make_pair(std::move(DynSymbols), std::move(DynRefs));
523   llvm::StringSet<> DynFiles = {"unittest:///foo.cc"};
524   MemIndex DynIndex(std::move(DynData.first), std::move(DynData.second),
525                     RelationSlab(), std::move(DynFiles), IndexContents::Symbols,
526                     std::move(DynData), DynSize);
527   SymbolSlab StaticSymbols;
528   RefSlab StaticRefs;
529   auto StaticData =
530       std::make_pair(std::move(StaticSymbols), std::move(StaticRefs));
531   llvm::StringSet<> StaticFiles = {"unittest:///foo.cc", "unittest:///bar.cc"};
532   MemIndex StaticIndex(
533       std::move(StaticData.first), std::move(StaticData.second), RelationSlab(),
534       std::move(StaticFiles), IndexContents::References, std::move(StaticData),
535       StaticSymbols.bytes() + StaticRefs.bytes());
536   MergedIndex Merge(&DynIndex, &StaticIndex);
537 
538   auto ContainsFile = Merge.indexedFiles();
539   EXPECT_EQ(ContainsFile("unittest:///foo.cc"),
540             IndexContents::Symbols | IndexContents::References);
541   EXPECT_EQ(ContainsFile("unittest:///bar.cc"), IndexContents::References);
542   EXPECT_EQ(ContainsFile("unittest:///foobar.cc"), IndexContents::None);
543 }
544 
545 TEST(MergeIndexTest, NonDocumentation) {
546   using index::SymbolKind;
547   Symbol L, R;
548   L.ID = R.ID = SymbolID("x");
549   L.Definition.FileURI = "file:/x.h";
550   R.Documentation = "Forward declarations because x.h is too big to include";
551   for (auto ClassLikeKind :
552        {SymbolKind::Class, SymbolKind::Struct, SymbolKind::Union}) {
553     L.SymInfo.Kind = ClassLikeKind;
554     EXPECT_EQ(mergeSymbol(L, R).Documentation, "");
555   }
556 
557   L.SymInfo.Kind = SymbolKind::Function;
558   R.Documentation = "Documentation from non-class symbols should be included";
559   EXPECT_EQ(mergeSymbol(L, R).Documentation, R.Documentation);
560 }
561 
562 MATCHER_P2(IncludeHeaderWithRef, IncludeHeader, References, "") {
563   return (arg.IncludeHeader == IncludeHeader) && (arg.References == References);
564 }
565 
566 TEST(MergeTest, MergeIncludesOnDifferentDefinitions) {
567   Symbol L, R;
568   L.Name = "left";
569   R.Name = "right";
570   L.ID = R.ID = SymbolID("hello");
571   L.IncludeHeaders.emplace_back("common", 1);
572   R.IncludeHeaders.emplace_back("common", 1);
573   R.IncludeHeaders.emplace_back("new", 1);
574 
575   // Both have no definition.
576   Symbol M = mergeSymbol(L, R);
577   EXPECT_THAT(M.IncludeHeaders,
578               UnorderedElementsAre(IncludeHeaderWithRef("common", 2u),
579                                    IncludeHeaderWithRef("new", 1u)));
580 
581   // Only merge references of the same includes but do not merge new #includes.
582   L.Definition.FileURI = "file:/left.h";
583   M = mergeSymbol(L, R);
584   EXPECT_THAT(M.IncludeHeaders,
585               UnorderedElementsAre(IncludeHeaderWithRef("common", 2u)));
586 
587   // Definitions are the same.
588   R.Definition.FileURI = "file:/right.h";
589   M = mergeSymbol(L, R);
590   EXPECT_THAT(M.IncludeHeaders,
591               UnorderedElementsAre(IncludeHeaderWithRef("common", 2u),
592                                    IncludeHeaderWithRef("new", 1u)));
593 
594   // Definitions are different.
595   R.Definition.FileURI = "file:/right.h";
596   M = mergeSymbol(L, R);
597   EXPECT_THAT(M.IncludeHeaders,
598               UnorderedElementsAre(IncludeHeaderWithRef("common", 2u),
599                                    IncludeHeaderWithRef("new", 1u)));
600 }
601 
602 TEST(MergeIndexTest, IncludeHeadersMerged) {
603   auto S = symbol("Z");
604   S.Definition.FileURI = "unittest:///foo.cc";
605 
606   SymbolSlab::Builder DynB;
607   S.IncludeHeaders.clear();
608   DynB.insert(S);
609   SymbolSlab DynSymbols = std::move(DynB).build();
610   RefSlab DynRefs;
611   auto DynSize = DynSymbols.bytes() + DynRefs.bytes();
612   auto DynData = std::make_pair(std::move(DynSymbols), std::move(DynRefs));
613   llvm::StringSet<> DynFiles = {S.Definition.FileURI};
614   MemIndex DynIndex(std::move(DynData.first), std::move(DynData.second),
615                     RelationSlab(), std::move(DynFiles), IndexContents::Symbols,
616                     std::move(DynData), DynSize);
617 
618   SymbolSlab::Builder StaticB;
619   S.IncludeHeaders.push_back({"<header>", 0});
620   StaticB.insert(S);
621   auto StaticIndex =
622       MemIndex::build(std::move(StaticB).build(), RefSlab(), RelationSlab());
623   MergedIndex Merge(&DynIndex, StaticIndex.get());
624 
625   EXPECT_THAT(runFuzzyFind(Merge, S.Name),
626               ElementsAre(testing::Field(
627                   &Symbol::IncludeHeaders,
628                   ElementsAre(IncludeHeaderWithRef("<header>", 0u)))));
629 
630   LookupRequest Req;
631   Req.IDs = {S.ID};
632   std::string IncludeHeader;
633   Merge.lookup(Req, [&](const Symbol &S) {
634     EXPECT_TRUE(IncludeHeader.empty());
635     ASSERT_EQ(S.IncludeHeaders.size(), 1u);
636     IncludeHeader = S.IncludeHeaders.front().IncludeHeader.str();
637   });
638   EXPECT_EQ(IncludeHeader, "<header>");
639 }
640 } // namespace
641 } // namespace clangd
642 } // namespace clang
643