brintos

brintos / llvm-project-archived public Read only

0
0
Text · 22.5 KiB · a66680d Raw
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