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()) ==
42MATCHER_P(fileURI, F,
"") {
return StringRef(arg.Location.FileURI) == F; }
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);
69 EXPECT_EQ(
nullptr,
B.find(
SymbolID(
"W")));
70 for (
const char *Sym : {
"X",
"Y",
"Z"})
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));
113TEST(SwapIndexTest, OldIndexRecycled) {
114 auto Token = std::make_shared<int>();
115 std::weak_ptr<int> WeakToken =
Token;
120 EXPECT_FALSE(WeakToken.expired());
121 S.reset(std::make_unique<MemIndex>());
122 EXPECT_TRUE(WeakToken.expired());
125TEST(MemIndexTest, MemIndexDeduplicate) {
132 EXPECT_THAT(
match(I, Req), ElementsAre(
"2"));
135TEST(MemIndexTest, MemIndexLimitedNumMatches) {
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"}),
157 EXPECT_THAT(
match(*I, Req),
158 UnorderedElementsAre(
"LaughingOutLoud",
"LittleOldLady"));
161TEST(MemIndexTest, MatchQualifiedNamesWithoutSpecificScope) {
167 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"a::y1",
"b::y2",
"y3"));
170TEST(MemIndexTest, MatchQualifiedNamesWithGlobalScope) {
176 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"y3"));
179TEST(MemIndexTest, MatchQualifiedNamesWithOneScope) {
185 Req.Scopes = {
"a::"};
186 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"a::y1",
"a::y2"));
189TEST(MemIndexTest, MatchQualifiedNamesWithMultipleScopes) {
195 Req.Scopes = {
"a::",
"b::"};
196 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"a::y1",
"a::y2",
"b::y3"));
199TEST(MemIndexTest, NoMatchNestedScopes) {
204 Req.Scopes = {
"a::"};
205 EXPECT_THAT(
match(*I, Req), UnorderedElementsAre(
"a::y1"));
208TEST(MemIndexTest, IgnoreCases) {
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"};
236 auto ContainsFile = I.indexedFiles();
242TEST(MemIndexTest, TemplateSpecialization) {
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);
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) {
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"));
290 EXPECT_THAT(
lookup(M,
SymbolID(
"ns::D")), UnorderedElementsAre());
291 EXPECT_THAT(
lookup(M, {}), UnorderedElementsAre());
294TEST(MergeIndexTest, LookupRemovedDefinition) {
295 FileIndex DynamicIndex(
true), StaticIndex(
true);
298 const char *HeaderCode =
"class Foo;";
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);
321 LookupReq.
IDs = {Foo.ID};
322 unsigned SymbolCounter = 0;
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) {
348 UnorderedElementsAre(
"ns::A",
"ns::B",
"ns::C"));
351TEST(MergeIndexTest, FuzzyFindRemovedSymbol) {
352 FileIndex DynamicIndex(
true), StaticIndex(
true);
355 const char *HeaderCode =
"class Foo;";
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);
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) {
453 const char *HeaderCode =
"class Foo;";
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);
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); }));
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"))))));
499 Merge.refs(Request, [&](
const Ref &O) { Results2.insert(Foo.ID, O); }));
507 Request.Limit = std::nullopt;
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) {
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);
529 std::make_pair(std::move(StaticSymbols), std::move(StaticRefs));
530 llvm::StringSet<> StaticFiles = {
"unittest:///foo.cc",
"unittest:///bar.cc"};
532 std::move(StaticData.first), std::move(StaticData.second),
RelationSlab(),
534 StaticSymbols.bytes() + StaticRefs.bytes());
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";
558 EXPECT_EQ(
mergeSymbol(L, R).Documentation, R.Documentation);
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";
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);
625 ElementsAre(testing::Field(
627 ElementsAre(IncludeHeaderWithRef(
"<header>", 0u)))));
631 std::string IncludeHeader;
633 EXPECT_TRUE(IncludeHeader.empty());
634 ASSERT_EQ(S.IncludeHeaders.size(), 1u);
635 IncludeHeader = S.IncludeHeaders.front().IncludeHeader.str();
637 EXPECT_EQ(IncludeHeader,
"<header>");
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
This manages symbols from files and an in-memory index on all symbols.
MemIndex is a naive in-memory index suitable for a small set of symbols.
static std::unique_ptr< SymbolIndex > build(SymbolSlab Symbols, RefSlab Refs, RelationSlab Relations)
Builds an index from slabs. The index takes ownership of the data.
RefSlab::Builder is a mutable container that can 'freeze' to RefSlab.
An efficient structure of storing large set of symbol references in memory.
RelationSlab::Builder is a mutable container that can 'freeze' to RelationSlab.
void insert(const Relation &R)
Adds a relation to the slab.
SymbolSlab::Builder is a mutable container that can 'freeze' to SymbolSlab.
An immutable symbol container that stores a set of symbols.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
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)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::vector< std::string > Scopes
If this is non-empty, symbols must be in at least one of the scopes (e.g.
std::string Query
A query string for the fuzzy find.
bool AnyScope
If set to true, allow symbols from any scope.
llvm::DenseSet< SymbolID > IDs
int line
Line position in a document (zero-based).
int character
Character offset on a line in a document (zero-based).
Position start
The range's start position.
Position end
The range's end position.
Represents a symbol occurrence in the source file.
llvm::DenseSet< SymbolID > IDs
Represents a relation between two symbols.
The class presents a C++ symbol, e.g.
@ Include
#include "header.h"
llvm::SmallVector< IncludeHeaderWithReferences, 1 > IncludeHeaders
One Symbol can potentially be included via different headers.
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
SymbolSlab headerSymbols() const
A single C++ or preprocessor token.