1 //===-- IndexTests.cpp -------------------------------*- C++ -*-----------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "Annotations.h"
11 #include "TestIndex.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"
24 using ::testing::AllOf
;
25 using ::testing::ElementsAre
;
26 using ::testing::IsEmpty
;
27 using ::testing::Pair
;
28 using ::testing::Pointee
;
29 using ::testing::UnorderedElementsAre
;
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
;
49 EXPECT_EQ(1u, Pos
.line());
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
) {
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
) {
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
;
131 MemIndex
I(Symbols
, RefSlab(), RelationSlab());
132 EXPECT_THAT(match(I
, Req
), ElementsAre("2"));
135 TEST(MemIndexTest
, MemIndexLimitedNumMatches
) {
137 MemIndex::build(generateNumSymbols(0, 100), RefSlab(), RelationSlab());
138 FuzzyFindRequest Req
;
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
;
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(),
164 FuzzyFindRequest Req
;
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(),
173 FuzzyFindRequest Req
;
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(),
183 FuzzyFindRequest Req
;
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(),
193 FuzzyFindRequest Req
;
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(),
202 FuzzyFindRequest Req
;
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(),
211 FuzzyFindRequest Req
;
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(),
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
) {
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");
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
);
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
);
263 auto I
= MemIndex::build(std::move(B
).build(), RefSlab(), RelationSlab());
264 FuzzyFindRequest Req
;
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(),
280 J
= MemIndex::build(generateSymbols({"ns::B", "ns::C"}), RefSlab(),
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
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;";
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
) {
325 EXPECT_TRUE(Sym
.Definition
);
327 EXPECT_EQ(SymbolCounter
, 1u);
329 // Drop the symbol completely.
330 Test
.Code
= "class Bar {};";
332 DynamicIndex
.updateMain(testPath(Test
.Filename
), AST
);
334 // Now we don't expect to see the symbol at all.
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(),
343 J
= MemIndex::build(generateSymbols({"ns::B", "ns::C"}), RefSlab(),
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
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
= "";
371 DynamicIndex
.updateMain(testPath(Test
.Filename
), AST
);
373 // Merged index should not return removed symbol.
374 FuzzyFindRequest Req
;
377 unsigned SymbolCounter
= 0;
379 Merge
.fuzzyFind(Req
, [&](const Symbol
&) { ++SymbolCounter
; });
380 EXPECT_FALSE(IsIncomplete
);
381 EXPECT_EQ(SymbolCounter
, 0u);
384 TEST(MergeTest
, Merge
) {
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";
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
) {
414 L
.ID
= R
.ID
= SymbolID("hello");
415 L
.CanonicalDeclaration
.FileURI
= "file:/left.h";
416 R
.CanonicalDeclaration
.FileURI
= "file:/right.h";
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
) {
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
) {
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]];)");
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]] {};)");
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
);
484 Request
.IDs
= {Foo
.ID
};
485 RefSlab::Builder Results
;
487 Merge
.refs(Request
, [&](const Ref
&O
) { Results
.insert(Foo
.ID
, O
); }));
489 std::move(Results
).build(),
491 _
, UnorderedElementsAre(AllOf(refRange(Test1Code
.range("Foo")),
492 fileURI("unittest:///test.cc")),
493 AllOf(refRange(Test2Code
.range("Foo")),
494 fileURI("unittest:///test2.cc"))))));
497 RefSlab::Builder Results2
;
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.
505 Dyn
.updateMain(testPath(Test
.Filename
), AST
);
507 Request
.Limit
= std::nullopt
;
508 RefSlab::Builder Results3
;
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
;
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
;
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
;
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
) {
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();
608 SymbolSlab DynSymbols
= std::move(DynB
).build();
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
});
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)))));
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>");
640 } // namespace clangd