18#include "clang/Index/IndexSymbol.h"
19#include "gmock/gmock.h"
20#include "gtest/gtest.h"
24using ::testing::AllOf;
25using ::testing::ElementsAre;
26using ::testing::IsEmpty;
28using ::testing::Pointee;
29using ::testing::UnorderedElementsAre;
35MATCHER_P(named, N,
"") {
return arg.Name == N; }
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,
42MATCHER_P(fileURI, F,
"") {
return StringRef(arg.Location.FileURI) == F; }
44TEST(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);
55 EXPECT_TRUE(
Pos.hasOverflow());
56 EXPECT_EQ(
Pos.line(), Position::MaxLine);
59 Pos.setColumn(Position::MaxColumn + 1);
60 EXPECT_TRUE(
Pos.hasOverflow());
61 EXPECT_EQ(
Pos.column(), Position::MaxColumn);
64TEST(SymbolSlab, FindAndIterate) {
65 SymbolSlab::Builder
B;
69 EXPECT_EQ(
nullptr,
B.find(
SymbolID(
"W")));
70 for (
const char *Sym : {
"X",
"Y",
"Z"})
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));
80TEST(RelationSlab, Lookup) {
92 RelationSlab Slab = std::move(
Builder).build();
98TEST(RelationSlab, Duplicates) {
108 RelationSlab Slab = std::move(
Builder).build();
113TEST(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),
120 EXPECT_FALSE(WeakToken.expired());
121 S.reset(std::make_unique<MemIndex>());
122 EXPECT_TRUE(WeakToken.expired());
125TEST(MemIndexTest, MemIndexDeduplicate) {
128 FuzzyFindRequest Req;
131 MemIndex I(
Symbols, RefSlab(), RelationSlab());
132 EXPECT_THAT(
match(I, Req), ElementsAre(
"2"));
135TEST(MemIndexTest, MemIndexLimitedNumMatches) {
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);
149TEST(MemIndexTest, FuzzyMatch) {
151 generateSymbols({
"LaughingOutLoud",
"LionPopulation",
"LittleOldLady"}),
152 RefSlab(), RelationSlab());
153 FuzzyFindRequest Req;
157 EXPECT_THAT(
match(*I, Req),
158 UnorderedElementsAre(
"LaughingOutLoud",
"LittleOldLady"));
161TEST(MemIndexTest, MatchQualifiedNamesWithoutSpecificScope) {
164 FuzzyFindRequest Req;
167 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"a::y1",
"b::y2",
"y3"));
170TEST(MemIndexTest, MatchQualifiedNamesWithGlobalScope) {
173 FuzzyFindRequest Req;
176 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"y3"));
179TEST(MemIndexTest, MatchQualifiedNamesWithOneScope) {
183 FuzzyFindRequest Req;
185 Req.Scopes = {
"a::"};
186 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"a::y1",
"a::y2"));
189TEST(MemIndexTest, MatchQualifiedNamesWithMultipleScopes) {
193 FuzzyFindRequest Req;
195 Req.Scopes = {
"a::",
"b::"};
196 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"a::y1",
"a::y2",
"b::y3"));
199TEST(MemIndexTest, NoMatchNestedScopes) {
202 FuzzyFindRequest Req;
204 Req.Scopes = {
"a::"};
205 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"a::y1"));
208TEST(MemIndexTest, IgnoreCases) {
211 FuzzyFindRequest Req;
213 Req.Scopes = {
"ns::"};
214 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"ns::ABC",
"ns::abc"));
217TEST(MemIndexTest, Lookup) {
220 EXPECT_THAT(
lookup(*I,
SymbolID(
"ns::abc")), UnorderedElementsAre(
"ns::abc"));
222 UnorderedElementsAre(
"ns::abc",
"ns::xyz"));
224 UnorderedElementsAre(
"ns::xyz"));
225 EXPECT_THAT(
lookup(*I,
SymbolID(
"ns::nonono")), UnorderedElementsAre());
228TEST(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(),
236 auto ContainsFile = I.indexedFiles();
242TEST(MemIndexTest, TemplateSpecialization) {
243 SymbolSlab::Builder
B;
245 Symbol S =
symbol(
"TempSpec");
251 S.TemplateSpecializationArgs =
"<int, bool>";
252 S.SymInfo.Properties =
static_cast<index::SymbolPropertySet
>(
253 index::SymbolProperty::TemplateSpecialization);
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>"));
273 Req.Query =
"TempSpec<int";
274 EXPECT_THAT(
match(*I, Req), IsEmpty());
277TEST(MergeIndexTest, Lookup) {
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"));
287 UnorderedElementsAre(
"ns::A",
"ns::B"));
289 UnorderedElementsAre(
"ns::A",
"ns::C"));
291 EXPECT_THAT(
lookup(
M, {}), UnorderedElementsAre());
294TEST(MergeIndexTest, LookupRemovedDefinition) {
295 FileIndex DynamicIndex(
true), StaticIndex(
true);
296 MergedIndex
Merge(&DynamicIndex, &StaticIndex);
298 const char *HeaderCode =
"class Foo;";
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);
312 Test.Code =
"class Foo;";
314 DynamicIndex.updateMain(
testPath(Test.Filename),
AST);
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);
330 Test.Code =
"class Bar {};";
332 DynamicIndex.updateMain(
testPath(Test.Filename),
AST);
336 Merge.lookup(LookupReq, [&](
const Symbol &Sym) { ++SymbolCounter; });
337 EXPECT_EQ(SymbolCounter, 0u);
340TEST(MergeIndexTest, FuzzyFind) {
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"));
351TEST(MergeIndexTest, FuzzyFindRemovedSymbol) {
352 FileIndex DynamicIndex(
true), StaticIndex(
true);
353 MergedIndex
Merge(&DynamicIndex, &StaticIndex);
355 const char *HeaderCode =
"class Foo;";
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);
368 Test.HeaderCode =
"";
371 DynamicIndex.updateMain(
testPath(Test.Filename),
AST);
374 FuzzyFindRequest Req;
377 unsigned SymbolCounter = 0;
379 Merge.fuzzyFind(Req, [&](
const Symbol &) { ++SymbolCounter; });
380 EXPECT_FALSE(IsIncomplete);
381 EXPECT_EQ(SymbolCounter, 0u);
387 L.Name = R.Name =
"Foo";
388 L.CanonicalDeclaration.FileURI =
"file:///left.h";
389 R.CanonicalDeclaration.FileURI =
"file:///right.h";
393 R.CompletionSnippetSuffix =
"{$1:0}";
394 R.Documentation =
"--doc--";
397 R.Type =
"expectedType";
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");
411TEST(MergeTest, PreferSymbolWithDefn) {
415 L.CanonicalDeclaration.FileURI =
"file:/left.h";
416 R.CanonicalDeclaration.FileURI =
"file:/right.h";
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";
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");
432TEST(MergeTest, PreferSymbolLocationInCodegenFile) {
436 L.CanonicalDeclaration.FileURI =
"file:/x.proto.h";
437 R.CanonicalDeclaration.FileURI =
"file:/x.proto";
440 EXPECT_EQ(StringRef(
M.CanonicalDeclaration.FileURI),
"file:/x.proto");
443 L.CanonicalDeclaration.FileURI =
"file:/y.proto";
445 EXPECT_EQ(StringRef(
M.CanonicalDeclaration.FileURI),
"file:/y.proto");
448TEST(MergeIndexTest, Refs) {
450 FileIndex StaticIndex(
true);
451 MergedIndex
Merge(&Dyn, &StaticIndex);
453 const char *HeaderCode =
"class Foo;";
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();
467 Test.HeaderCode = HeaderCode;
468 Test.Code =
"// static\nclass Foo {};";
469 Test.Filename =
"test.cc";
470 auto StaticAST = Test.build();
472 StaticIndex.updateMain(
testPath(Test.Filename), StaticAST);
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};
487 Merge.refs(Request, [&](
const Ref &O) { Results.insert(Foo.ID, O); }));
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); }));
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"))))));
517TEST(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),
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(),
534 StaticSymbols.bytes() + StaticRefs.bytes());
535 MergedIndex
Merge(&DynIndex, &StaticIndex);
537 auto ContainsFile =
Merge.indexedFiles();
538 EXPECT_EQ(ContainsFile(
"unittest:///foo.cc"),
544TEST(MergeIndexTest, NonDocumentation) {
545 using index::SymbolKind;
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;
556 L.SymInfo.Kind = SymbolKind::Function;
557 R.Documentation =
"Documentation from non-class symbols should be included";
562 return (arg.IncludeHeader == IncludeHeader) && (arg.References ==
References);
565TEST(MergeTest, MergeIncludesOnDifferentDefinitions) {
576 EXPECT_THAT(
M.IncludeHeaders,
577 UnorderedElementsAre(IncludeHeaderWithRef(
"common", 2u),
578 IncludeHeaderWithRef(
"new", 1u)));
581 L.Definition.FileURI =
"file:/left.h";
583 EXPECT_THAT(
M.IncludeHeaders,
584 UnorderedElementsAre(IncludeHeaderWithRef(
"common", 2u)));
587 R.Definition.FileURI =
"file:/right.h";
589 EXPECT_THAT(
M.IncludeHeaders,
590 UnorderedElementsAre(IncludeHeaderWithRef(
"common", 2u),
591 IncludeHeaderWithRef(
"new", 1u)));
594 R.Definition.FileURI =
"file:/right.h";
596 EXPECT_THAT(
M.IncludeHeaders,
597 UnorderedElementsAre(IncludeHeaderWithRef(
"common", 2u),
598 IncludeHeaderWithRef(
"new", 1u)));
601TEST(MergeIndexTest, IncludeHeadersMerged) {
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),
615 std::move(DynData), DynSize);
617 SymbolSlab::Builder StaticB;
621 MemIndex::build(std::move(StaticB).build(), RefSlab(), RelationSlab());
622 MergedIndex
Merge(&DynIndex, StaticIndex.get());
625 ElementsAre(testing::Field(
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>");
std::vector< CodeCompletionResult > Results
CodeCompletionBuilder Builder
CharSourceRange Range
SourceRange for the file name.
const google::protobuf::Message & M
static std::unique_ptr< SymbolIndex > build(SymbolSlab Symbols, RefSlab Refs, RelationSlab Relations)
Builds an index from slabs. The index takes ownership of the data.
Symbol mergeSymbol(const Symbol &L, const Symbol &R)
MATCHER_P2(hasFlag, Flag, Path, "")
std::string testPath(PathRef File, llvm::sys::path::Style Style)
std::vector< std::string > match(const SymbolIndex &I, const FuzzyFindRequest &Req, bool *Incomplete)
TEST(BackgroundQueueTest, Priority)
Symbol symbol(llvm::StringRef QName)
SymbolSlab generateSymbols(std::vector< std::string > QualifiedNames)
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
std::vector< std::string > lookup(const SymbolIndex &I, llvm::ArrayRef< SymbolID > IDs)
SymbolSlab generateNumSymbols(int Begin, int End)
SymbolSlab runFuzzyFind(const SymbolIndex &Index, llvm::StringRef Query)
std::array< uint8_t, 20 > SymbolID
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
@ Include
#include "header.h"
llvm::StringRef Documentation
Documentation including comment for the symbol declaration.
llvm::SmallVector< IncludeHeaderWithReferences, 1 > IncludeHeaders
One Symbol can potentially be included via different headers.
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
SymbolSlab headerSymbols() const