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