642 lines · cpp
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-exception6//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 23using ::testing::_;24using ::testing::AllOf;25using ::testing::ElementsAre;26using ::testing::IsEmpty;27using ::testing::Pair;28using ::testing::Pointee;29using ::testing::UnorderedElementsAre;30 31namespace clang {32namespace clangd {33namespace {34 35MATCHER_P(named, N, "") { return arg.Name == N; }36MATCHER_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);41}42MATCHER_P(fileURI, F, "") { return StringRef(arg.Location.FileURI) == F; }43 44TEST(SymbolLocation, Position) {45 using Position = SymbolLocation::Position;46 Position Pos;47 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());53 54 Pos.setLine(Position::MaxLine + 1); // overflow55 EXPECT_TRUE(Pos.hasOverflow());56 EXPECT_EQ(Pos.line(), Position::MaxLine);57 Pos.setLine(1); // reset the overflowed line.58 59 Pos.setColumn(Position::MaxColumn + 1); // overflow60 EXPECT_TRUE(Pos.hasOverflow());61 EXPECT_EQ(Pos.column(), Position::MaxColumn);62}63 64TEST(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)));72 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));78}79 80TEST(RelationSlab, Lookup) {81 SymbolID A{"A"};82 SymbolID B{"B"};83 SymbolID C{"C"};84 SymbolID D{"D"};85 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});91 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}));96}97 98TEST(RelationSlab, Duplicates) {99 SymbolID A{"A"};100 SymbolID B{"B"};101 SymbolID C{"C"};102 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});107 108 RelationSlab Slab = std::move(Builder).build();109 EXPECT_THAT(Slab, UnorderedElementsAre(Relation{A, RelationKind::BaseOf, B},110 Relation{A, RelationKind::BaseOf, C}));111}112 113TEST(SwapIndexTest, OldIndexRecycled) {114 auto Token = std::make_shared<int>();115 std::weak_ptr<int> WeakToken = Token;116 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.123}124 125TEST(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"));133}134 135TEST(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);147}148 149TEST(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"));159}160 161TEST(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"));168}169 170TEST(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"));177}178 179TEST(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"));187}188 189TEST(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"));197}198 199TEST(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"));206}207 208TEST(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"));215}216 217TEST(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());226}227 228TEST(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);240}241 242TEST(MemIndexTest, TemplateSpecialization) {243 SymbolSlab::Builder B;244 245 Symbol S = symbol("TempSpec");246 S.ID = SymbolID("1");247 B.insert(S);248 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);255 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);262 263 auto I = MemIndex::build(std::move(B).build(), RefSlab(), RelationSlab());264 FuzzyFindRequest Req;265 Req.AnyScope = true;266 267 Req.Query = "TempSpec";268 EXPECT_THAT(match(*I, Req),269 UnorderedElementsAre("TempSpec", "TempSpec<int, bool>",270 "TempSpec<int, U>"));271 272 // FIXME: Add filtering for template argument list.273 Req.Query = "TempSpec<int";274 EXPECT_THAT(match(*I, Req), IsEmpty());275}276 277TEST(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());292}293 294TEST(MergeIndexTest, LookupRemovedDefinition) {295 FileIndex DynamicIndex(true), StaticIndex(true);296 MergedIndex Merge(&DynamicIndex, &StaticIndex);297 298 const char *HeaderCode = "class Foo;";299 auto HeaderSymbols = TestTU::withHeaderCode(HeaderCode).headerSymbols();300 auto Foo = findSymbol(HeaderSymbols, "Foo");301 302 // Build static index for test.cc with Foo definition303 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);309 310 // Remove Foo definition from test.cc, i.e. build dynamic index for test.cc311 // without Foo definition.312 Test.Code = "class Foo;";313 AST = Test.build();314 DynamicIndex.updateMain(testPath(Test.Filename), AST);315 316 // Even though the definition is actually deleted in the newer version of the317 // 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 headers319 // which are always missing from the dynamic index320 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);326 });327 EXPECT_EQ(SymbolCounter, 1u);328 329 // Drop the symbol completely.330 Test.Code = "class Bar {};";331 AST = Test.build();332 DynamicIndex.updateMain(testPath(Test.Filename), AST);333 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);338}339 340TEST(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"));349}350 351TEST(MergeIndexTest, FuzzyFindRemovedSymbol) {352 FileIndex DynamicIndex(true), StaticIndex(true);353 MergedIndex Merge(&DynamicIndex, &StaticIndex);354 355 const char *HeaderCode = "class Foo;";356 auto HeaderSymbols = TestTU::withHeaderCode(HeaderCode).headerSymbols();357 auto Foo = findSymbol(HeaderSymbols, "Foo");358 359 // Build static index for test.cc with Foo symbol360 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);366 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);372 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);382}383 384TEST(MergeTest, Merge) {385 Symbol L, R;386 L.ID = R.ID = SymbolID("hello");387 L.Name = R.Name = "Foo"; // same in both388 L.CanonicalDeclaration.FileURI = "file:///left.h"; // differs389 R.CanonicalDeclaration.FileURI = "file:///right.h";390 L.References = 1;391 R.References = 2;392 L.Signature = "()"; // present in left only393 R.CompletionSnippetSuffix = "{$1:0}"; // present in right only394 R.Documentation = "--doc--";395 L.Origin = SymbolOrigin::Preamble;396 R.Origin = SymbolOrigin::Static;397 R.Type = "expectedType";398 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);409}410 411TEST(MergeTest, PreferSymbolWithDefn) {412 Symbol L, R;413 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";419 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");424 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");430}431 432TEST(MergeTest, PreferSymbolLocationInCodegenFile) {433 Symbol L, R;434 435 L.ID = R.ID = SymbolID("hello");436 L.CanonicalDeclaration.FileURI = "file:/x.proto.h";437 R.CanonicalDeclaration.FileURI = "file:/x.proto";438 439 Symbol M = mergeSymbol(L, R);440 EXPECT_EQ(StringRef(M.CanonicalDeclaration.FileURI), "file:/x.proto");441 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");446}447 448TEST(MergeIndexTest, Refs) {449 FileIndex Dyn(true);450 FileIndex StaticIndex(true);451 MergedIndex Merge(&Dyn, &StaticIndex);452 453 const char *HeaderCode = "class Foo;";454 auto HeaderSymbols = TestTU::withHeaderCode("class Foo;").headerSymbols();455 auto Foo = findSymbol(HeaderSymbols, "Foo");456 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);465 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);473 474 // Add refs for test2.cc475 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);482 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"))))));495 496 Request.Limit = 1;497 RefSlab::Builder Results2;498 EXPECT_TRUE(499 Merge.refs(Request, [&](const Ref &O) { Results2.insert(Foo.ID, O); }));500 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);506 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"))))));515}516 517TEST(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);536 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);542}543 544TEST(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, "");554 }555 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);559}560 561MATCHER_P2(IncludeHeaderWithRef, IncludeHeader, References, "") {562 return (arg.IncludeHeader == IncludeHeader) && (arg.References == References);563}564 565TEST(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);573 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)));579 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)));585 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)));592 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)));599}600 601TEST(MergeIndexTest, IncludeHeadersMerged) {602 auto S = symbol("Z");603 S.Definition.FileURI = "unittest:///foo.cc";604 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);616 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());623 624 EXPECT_THAT(runFuzzyFind(Merge, S.Name),625 ElementsAre(testing::Field(626 &Symbol::IncludeHeaders,627 ElementsAre(IncludeHeaderWithRef("<header>", 0u)))));628 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();636 });637 EXPECT_EQ(IncludeHeader, "<header>");638}639} // namespace640} // namespace clangd641} // namespace clang642