clang-tools  14.0.0git
IndexTests.cpp
Go to the documentation of this file.
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) {
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 
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 
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) {
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,
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};
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"),
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(
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
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:751
clang::clangd::MATCHER_P2
MATCHER_P2(hasFlag, Flag, Path, "")
Definition: GlobalCompilationDatabaseTests.cpp:451
References
unsigned References
Definition: CodeComplete.cpp:163
clang::clangd::runFuzzyFind
SymbolSlab runFuzzyFind(const SymbolIndex &Index, llvm::StringRef Query)
Definition: SyncAPI.cpp:124
clang::clangd::RelationKind::BaseOf
@ BaseOf
clang::clangd::testPath
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:82
Refs
RefSlab Refs
Definition: SymbolCollectorTests.cpp:311
clang::clangd::TestTU::headerSymbols
SymbolSlab headerSymbols() const
Definition: TestTU.cpp:159
Index.h
TestTU.h
clang::tidy::utils::fixit::QualifierTarget::Pointee
@ Pointee
clang::clangd::IndexContents::Symbols
@ Symbols
clang::clangd::SymbolKind::Class
@ Class
clang::clangd::generateNumSymbols
SymbolSlab generateNumSymbols(int Begin, int End)
Definition: TestIndex.cpp:83
clang::clangd::SymbolOrigin::Dynamic
@ Dynamic
clang::clangd::Position::line
int line
Line position in a document (zero-based).
Definition: Protocol.h:150
clang::clangd::match
std::vector< std::string > match(const SymbolIndex &I, const FuzzyFindRequest &Req, bool *Incomplete)
Definition: TestIndex.cpp:94
M
const google::protobuf::Message & M
Definition: Server.cpp:309
clang::clangd::ParsedAST::build
static llvm::Optional< ParsedAST > build(llvm::StringRef Filename, const ParseInputs &Inputs, std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble)
Attempts to run Clang and store the parsed AST.
Definition: ParsedAST.cpp:248
clang::clangd::TestTU::withHeaderCode
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
Definition: TestTU.h:43
ns1::ns2::A
@ A
Definition: CategoricalFeature.h:3
MemIndex.h
clang::find_all_symbols::Merge
bool Merge(llvm::StringRef MergeDir, llvm::StringRef OutputFile)
Definition: FindAllSymbolsMain.cpp:76
Foo
Definition: sample.h:4
ns1::ns2::D
@ D
Definition: CategoricalFeature.h:3
clang::clangd::SymbolKind::Function
@ Function
clang::clangd::SymbolOrigin::Static
@ Static
clang::clangd::generateSymbols
SymbolSlab generateSymbols(std::vector< std::string > QualifiedNames)
Definition: TestIndex.cpp:76
Builder
CodeCompletionBuilder Builder
Definition: CodeCompletionStringsTests.cpp:36
SyncAPI.h
clang::clangd::mergeSymbol
Symbol mergeSymbol(const Symbol &L, const Symbol &R)
Definition: Merge.cpp:213
Results
std::vector< CodeCompletionResult > Results
Definition: CodeComplete.cpp:771
clang::doc::SymbolID
std::array< uint8_t, 20 > SymbolID
Definition: Representation.h:30
clang::clangd::MemIndex::build
static std::unique_ptr< SymbolIndex > build(SymbolSlab Symbols, RefSlab Refs, RelationSlab Relations)
Builds an index from slabs. The index takes ownership of the data.
Definition: MemIndex.cpp:19
FileIndex.h
Files
llvm::DenseSet< FileID > Files
Definition: IncludeCleaner.cpp:106
Annotations.h
clang::clangd::lookup
std::vector< std::string > lookup(const SymbolIndex &I, llvm::ArrayRef< SymbolID > IDs)
Definition: TestIndex.cpp:106
SymbolKind
clang::find_all_symbols::SymbolInfo::SymbolKind SymbolKind
Definition: SymbolInfo.cpp:21
Symbol.h
clang::clangd::IndexContents::All
@ All
clang::clangd::SymbolOrigin::Merge
@ Merge
clang::clangd::IndexContents::References
@ References
clang::clangd::IndexContents::None
@ None
TestIndex.h
clang::clangd::MATCHER_P
MATCHER_P(Named, N, "")
Definition: BackgroundIndexTests.cpp:31
C
const Criteria C
Definition: FunctionCognitiveComplexityCheck.cpp:93
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
Pos
Position Pos
Definition: SourceCode.cpp:657
Merge.h
clang::clangd::symbol
Symbol symbol(llvm::StringRef QName)
Definition: TestIndex.cpp:16
ns1::ns2::B
@ B
Definition: CategoricalFeature.h:3
clang::clangd::Symbol::IncludeHeaders
llvm::SmallVector< IncludeHeaderWithReferences, 1 > IncludeHeaders
One Symbol can potentially be included via different headers.
Definition: Symbol.h:111
clang::clangd::Symbol::Documentation
llvm::StringRef Documentation
Documentation including comment for the symbol declaration.
Definition: Symbol.h:76
clang::clangd::findSymbol
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
Definition: TestTU.cpp:181
clang::clangd::RefSlab::bytes
size_t bytes() const
Definition: Ref.h:128
clang::clangd::SymbolKind::Struct
@ Struct