clang-tools  15.0.0git
FileIndexTests.cpp
Go to the documentation of this file.
1 //===-- FileIndexTests.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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Annotations.h"
10 #include "Compiler.h"
11 #include "Headers.h"
12 #include "ParsedAST.h"
13 #include "SyncAPI.h"
14 #include "TestFS.h"
15 #include "TestTU.h"
16 #include "TestWorkspace.h"
17 #include "URI.h"
19 #include "index/FileIndex.h"
20 #include "index/Index.h"
21 #include "index/Ref.h"
22 #include "index/Relation.h"
23 #include "index/Serialization.h"
24 #include "index/Symbol.h"
25 #include "index/SymbolID.h"
26 #include "support/Threading.h"
27 #include "clang/Frontend/CompilerInvocation.h"
28 #include "clang/Lex/Preprocessor.h"
29 #include "clang/Tooling/CompilationDatabase.h"
30 #include "llvm/ADT/ArrayRef.h"
31 #include "llvm/Support/Allocator.h"
32 #include "gmock/gmock.h"
33 #include "gtest/gtest.h"
34 #include <utility>
35 #include <vector>
36 
37 using ::testing::_;
38 using ::testing::AllOf;
39 using ::testing::Contains;
40 using ::testing::ElementsAre;
41 using ::testing::Gt;
42 using ::testing::IsEmpty;
43 using ::testing::Pair;
44 using ::testing::UnorderedElementsAre;
45 
46 MATCHER_P(refRange, Range, "") {
47  return std::make_tuple(arg.Location.Start.line(), arg.Location.Start.column(),
48  arg.Location.End.line(), arg.Location.End.column()) ==
49  std::make_tuple(Range.start.line, Range.start.character,
50  Range.end.line, Range.end.character);
51 }
52 MATCHER_P(fileURI, F, "") { return llvm::StringRef(arg.Location.FileURI) == F; }
53 MATCHER_P(declURI, U, "") {
54  return llvm::StringRef(arg.CanonicalDeclaration.FileURI) == U;
55 }
56 MATCHER_P(defURI, U, "") {
57  return llvm::StringRef(arg.Definition.FileURI) == U;
58 }
59 MATCHER_P(qName, N, "") { return (arg.Scope + arg.Name).str() == N; }
60 MATCHER_P(numReferences, N, "") { return arg.References == N; }
61 MATCHER_P(hasOrign, O, "") { return bool(arg.Origin & O); }
62 
63 namespace clang {
64 namespace clangd {
65 namespace {
66 ::testing::Matcher<const RefSlab &>
67 refsAre(std::vector<::testing::Matcher<Ref>> Matchers) {
68  return ElementsAre(::testing::Pair(_, UnorderedElementsAreArray(Matchers)));
69 }
70 
71 Symbol symbol(llvm::StringRef ID) {
72  Symbol Sym;
73  Sym.ID = SymbolID(ID);
74  Sym.Name = ID;
75  return Sym;
76 }
77 
78 std::unique_ptr<SymbolSlab> numSlab(int Begin, int End) {
80  for (int I = Begin; I <= End; I++)
81  Slab.insert(symbol(std::to_string(I)));
82  return std::make_unique<SymbolSlab>(std::move(Slab).build());
83 }
84 
85 std::unique_ptr<RefSlab> refSlab(const SymbolID &ID, const char *Path) {
86  RefSlab::Builder Slab;
87  Ref R;
88  R.Location.FileURI = Path;
89  R.Kind = RefKind::Reference;
90  Slab.insert(ID, R);
91  return std::make_unique<RefSlab>(std::move(Slab).build());
92 }
93 
94 std::unique_ptr<RelationSlab> relSlab(llvm::ArrayRef<const Relation> Rels) {
95  RelationSlab::Builder RelBuilder;
96  for (auto &Rel : Rels)
97  RelBuilder.insert(Rel);
98  return std::make_unique<RelationSlab>(std::move(RelBuilder).build());
99 }
100 
101 TEST(FileSymbolsTest, UpdateAndGet) {
102  FileSymbols FS(IndexContents::All);
103  EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""), IsEmpty());
104 
105  FS.update("f1", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cc"), nullptr,
106  false);
107  EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""),
108  UnorderedElementsAre(qName("1"), qName("2"), qName("3")));
109  EXPECT_THAT(getRefs(*FS.buildIndex(IndexType::Light), SymbolID("1")),
110  refsAre({fileURI("f1.cc")}));
111 }
112 
113 TEST(FileSymbolsTest, Overlap) {
114  FileSymbols FS(IndexContents::All);
115  FS.update("f1", numSlab(1, 3), nullptr, nullptr, false);
116  FS.update("f2", numSlab(3, 5), nullptr, nullptr, false);
117  for (auto Type : {IndexType::Light, IndexType::Heavy})
118  EXPECT_THAT(runFuzzyFind(*FS.buildIndex(Type), ""),
119  UnorderedElementsAre(qName("1"), qName("2"), qName("3"),
120  qName("4"), qName("5")));
121 }
122 
123 TEST(FileSymbolsTest, MergeOverlap) {
124  FileSymbols FS(IndexContents::All);
125  auto OneSymboSlab = [](Symbol Sym) {
127  S.insert(Sym);
128  return std::make_unique<SymbolSlab>(std::move(S).build());
129  };
130  auto X1 = symbol("x");
131  X1.CanonicalDeclaration.FileURI = "file:///x1";
132  auto X2 = symbol("x");
133  X2.Definition.FileURI = "file:///x2";
134 
135  FS.update("f1", OneSymboSlab(X1), nullptr, nullptr, false);
136  FS.update("f2", OneSymboSlab(X2), nullptr, nullptr, false);
137  for (auto Type : {IndexType::Light, IndexType::Heavy})
138  EXPECT_THAT(
139  runFuzzyFind(*FS.buildIndex(Type, DuplicateHandling::Merge), "x"),
140  UnorderedElementsAre(
141  AllOf(qName("x"), declURI("file:///x1"), defURI("file:///x2"))));
142 }
143 
144 TEST(FileSymbolsTest, SnapshotAliveAfterRemove) {
145  FileSymbols FS(IndexContents::All);
146 
147  SymbolID ID("1");
148  FS.update("f1", numSlab(1, 3), refSlab(ID, "f1.cc"), nullptr, false);
149 
150  auto Symbols = FS.buildIndex(IndexType::Light);
151  EXPECT_THAT(runFuzzyFind(*Symbols, ""),
152  UnorderedElementsAre(qName("1"), qName("2"), qName("3")));
153  EXPECT_THAT(getRefs(*Symbols, ID), refsAre({fileURI("f1.cc")}));
154 
155  FS.update("f1", nullptr, nullptr, nullptr, false);
156  auto Empty = FS.buildIndex(IndexType::Light);
157  EXPECT_THAT(runFuzzyFind(*Empty, ""), IsEmpty());
158  EXPECT_THAT(getRefs(*Empty, ID), ElementsAre());
159 
160  EXPECT_THAT(runFuzzyFind(*Symbols, ""),
161  UnorderedElementsAre(qName("1"), qName("2"), qName("3")));
162  EXPECT_THAT(getRefs(*Symbols, ID), refsAre({fileURI("f1.cc")}));
163 }
164 
165 // Adds Basename.cpp, which includes Basename.h, which contains Code.
166 void update(FileIndex &M, llvm::StringRef Basename, llvm::StringRef Code) {
167  TestTU File;
168  File.Filename = (Basename + ".cpp").str();
169  File.HeaderFilename = (Basename + ".h").str();
170  File.HeaderCode = std::string(Code);
171  auto AST = File.build();
172  M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
173  AST.getASTContext(), AST.getPreprocessor(),
174  AST.getCanonicalIncludes());
175 }
176 
177 TEST(FileIndexTest, CustomizedURIScheme) {
178  FileIndex M;
179  update(M, "f", "class string {};");
180 
181  EXPECT_THAT(runFuzzyFind(M, ""), ElementsAre(declURI("unittest:///f.h")));
182 }
183 
184 TEST(FileIndexTest, IndexAST) {
185  FileIndex M;
186  update(M, "f1", "namespace ns { void f() {} class X {}; }");
187 
188  FuzzyFindRequest Req;
189  Req.Query = "";
190  Req.Scopes = {"ns::"};
191  EXPECT_THAT(runFuzzyFind(M, Req),
192  UnorderedElementsAre(qName("ns::f"), qName("ns::X")));
193 }
194 
195 TEST(FileIndexTest, NoLocal) {
196  FileIndex M;
197  update(M, "f1", "namespace ns { void f() { int local = 0; } class X {}; }");
198 
199  EXPECT_THAT(
200  runFuzzyFind(M, ""),
201  UnorderedElementsAre(qName("ns"), qName("ns::f"), qName("ns::X")));
202 }
203 
204 TEST(FileIndexTest, IndexMultiASTAndDeduplicate) {
205  FileIndex M;
206  update(M, "f1", "namespace ns { void f() {} class X {}; }");
207  update(M, "f2", "namespace ns { void ff() {} class X {}; }");
208 
209  FuzzyFindRequest Req;
210  Req.Scopes = {"ns::"};
211  EXPECT_THAT(
212  runFuzzyFind(M, Req),
213  UnorderedElementsAre(qName("ns::f"), qName("ns::X"), qName("ns::ff")));
214 }
215 
216 TEST(FileIndexTest, ClassMembers) {
217  FileIndex M;
218  update(M, "f1", "class X { static int m1; int m2; static void f(); };");
219 
220  EXPECT_THAT(runFuzzyFind(M, ""),
221  UnorderedElementsAre(qName("X"), qName("X::m1"), qName("X::m2"),
222  qName("X::f")));
223 }
224 
225 TEST(FileIndexTest, IncludeCollected) {
226  FileIndex M;
227  update(
228  M, "f",
229  "// IWYU pragma: private, include <the/good/header.h>\nclass string {};");
230 
231  auto Symbols = runFuzzyFind(M, "");
232  EXPECT_THAT(Symbols, ElementsAre(_));
233  EXPECT_THAT(Symbols.begin()->IncludeHeaders.front().IncludeHeader,
234  "<the/good/header.h>");
235 }
236 
237 TEST(FileIndexTest, HasSystemHeaderMappingsInPreamble) {
238  TestTU TU;
239  TU.HeaderCode = "class Foo{};";
240  TU.HeaderFilename = "algorithm";
241 
242  auto Symbols = runFuzzyFind(*TU.index(), "");
243  EXPECT_THAT(Symbols, ElementsAre(_));
244  EXPECT_THAT(Symbols.begin()->IncludeHeaders.front().IncludeHeader,
245  "<algorithm>");
246 }
247 
248 TEST(FileIndexTest, TemplateParamsInLabel) {
249  auto *Source = R"cpp(
250 template <class Ty>
251 class vector {
252 };
253 
254 template <class Ty, class Arg>
255 vector<Ty> make_vector(Arg A) {}
256 )cpp";
257 
258  FileIndex M;
259  update(M, "f", Source);
260 
261  auto Symbols = runFuzzyFind(M, "");
262  EXPECT_THAT(Symbols,
263  UnorderedElementsAre(qName("vector"), qName("make_vector")));
264  auto It = Symbols.begin();
265  Symbol Vector = *It++;
266  Symbol MakeVector = *It++;
267  if (MakeVector.Name == "vector")
268  std::swap(MakeVector, Vector);
269 
270  EXPECT_EQ(Vector.Signature, "<class Ty>");
271  EXPECT_EQ(Vector.CompletionSnippetSuffix, "<${1:class Ty}>");
272 
273  EXPECT_EQ(MakeVector.Signature, "<class Ty>(Arg A)");
274  EXPECT_EQ(MakeVector.CompletionSnippetSuffix, "<${1:class Ty}>(${2:Arg A})");
275 }
276 
277 TEST(FileIndexTest, RebuildWithPreamble) {
278  auto FooCpp = testPath("foo.cpp");
279  auto FooH = testPath("foo.h");
280  // Preparse ParseInputs.
281  ParseInputs PI;
282  PI.CompileCommand.Directory = testRoot();
283  PI.CompileCommand.Filename = FooCpp;
284  PI.CompileCommand.CommandLine = {"clang", "-xc++", FooCpp};
285 
286  MockFS FS;
287  FS.Files[FooCpp] = "";
288  FS.Files[FooH] = R"cpp(
289  namespace ns_in_header {
290  int func_in_header();
291  }
292  )cpp";
293  PI.TFS = &FS;
294 
295  PI.Contents = R"cpp(
296  #include "foo.h"
297  namespace ns_in_source {
298  int func_in_source();
299  }
300  )cpp";
301 
302  // Rebuild the file.
303  IgnoreDiagnostics IgnoreDiags;
305 
306  FileIndex Index;
307  bool IndexUpdated = false;
308  buildPreamble(FooCpp, *CI, PI,
309  /*StoreInMemory=*/true,
310  [&](ASTContext &Ctx, Preprocessor &PP,
311  const CanonicalIncludes &CanonIncludes) {
312  EXPECT_FALSE(IndexUpdated)
313  << "Expected only a single index update";
314  IndexUpdated = true;
315  Index.updatePreamble(FooCpp, /*Version=*/"null", Ctx, PP,
316  CanonIncludes);
317  });
318  ASSERT_TRUE(IndexUpdated);
319 
320  // Check the index contains symbols from the preamble, but not from the main
321  // file.
322  FuzzyFindRequest Req;
323  Req.Query = "";
324  Req.Scopes = {"", "ns_in_header::"};
325 
326  EXPECT_THAT(runFuzzyFind(Index, Req),
327  UnorderedElementsAre(qName("ns_in_header"),
328  qName("ns_in_header::func_in_header")));
329 }
330 
331 TEST(FileIndexTest, Refs) {
332  const char *HeaderCode = "class Foo {};";
333  Annotations MainCode(R"cpp(
334  void f() {
335  $foo[[Foo]] foo;
336  }
337  )cpp");
338 
339  auto Foo =
340  findSymbol(TestTU::withHeaderCode(HeaderCode).headerSymbols(), "Foo");
341 
342  RefsRequest Request;
343  Request.IDs = {Foo.ID};
344 
345  FileIndex Index;
346  // Add test.cc
347  TestTU Test;
348  Test.HeaderCode = HeaderCode;
349  Test.Code = std::string(MainCode.code());
350  Test.Filename = "test.cc";
351  auto AST = Test.build();
352  Index.updateMain(testPath(Test.Filename), AST);
353  // Add test2.cc
354  TestTU Test2;
355  Test2.HeaderCode = HeaderCode;
356  Test2.Code = std::string(MainCode.code());
357  Test2.Filename = "test2.cc";
358  AST = Test2.build();
359  Index.updateMain(testPath(Test2.Filename), AST);
360 
361  EXPECT_THAT(getRefs(Index, Foo.ID),
362  refsAre({AllOf(refRange(MainCode.range("foo")),
363  fileURI("unittest:///test.cc")),
364  AllOf(refRange(MainCode.range("foo")),
365  fileURI("unittest:///test2.cc"))}));
366 }
367 
368 TEST(FileIndexTest, MacroRefs) {
369  Annotations HeaderCode(R"cpp(
370  #define $def1[[HEADER_MACRO]](X) (X+1)
371  )cpp");
372  Annotations MainCode(R"cpp(
373  #define $def2[[MAINFILE_MACRO]](X) (X+1)
374  void f() {
375  int a = $ref1[[HEADER_MACRO]](2);
376  int b = $ref2[[MAINFILE_MACRO]](1);
377  }
378  )cpp");
379 
380  FileIndex Index;
381  // Add test.cc
382  TestTU Test;
383  Test.HeaderCode = std::string(HeaderCode.code());
384  Test.Code = std::string(MainCode.code());
385  Test.Filename = "test.cc";
386  auto AST = Test.build();
387  Index.updateMain(testPath(Test.Filename), AST);
388 
389  auto HeaderMacro = findSymbol(Test.headerSymbols(), "HEADER_MACRO");
390  EXPECT_THAT(getRefs(Index, HeaderMacro.ID),
391  refsAre({AllOf(refRange(MainCode.range("ref1")),
392  fileURI("unittest:///test.cc"))}));
393 
394  auto MainFileMacro = findSymbol(Test.headerSymbols(), "MAINFILE_MACRO");
395  EXPECT_THAT(getRefs(Index, MainFileMacro.ID),
396  refsAre({AllOf(refRange(MainCode.range("def2")),
397  fileURI("unittest:///test.cc")),
398  AllOf(refRange(MainCode.range("ref2")),
399  fileURI("unittest:///test.cc"))}));
400 }
401 
402 TEST(FileIndexTest, CollectMacros) {
403  FileIndex M;
404  update(M, "f", "#define CLANGD 1");
405  EXPECT_THAT(runFuzzyFind(M, ""), Contains(qName("CLANGD")));
406 }
407 
408 TEST(FileIndexTest, Relations) {
409  TestTU TU;
410  TU.Filename = "f.cpp";
411  TU.HeaderFilename = "f.h";
412  TU.HeaderCode = "class A {}; class B : public A {};";
413  auto AST = TU.build();
414  FileIndex Index;
415  Index.updatePreamble(testPath(TU.Filename), /*Version=*/"null",
416  AST.getASTContext(), AST.getPreprocessor(),
417  AST.getCanonicalIncludes());
418  SymbolID A = findSymbol(TU.headerSymbols(), "A").ID;
419  uint32_t Results = 0;
420  RelationsRequest Req;
421  Req.Subjects.insert(A);
422  Req.Predicate = RelationKind::BaseOf;
423  Index.relations(Req, [&](const SymbolID &, const Symbol &) { ++Results; });
424  EXPECT_EQ(Results, 1u);
425 }
426 
427 TEST(FileIndexTest, RelationsMultiFile) {
428  TestWorkspace Workspace;
429  Workspace.addSource("Base.h", "class Base {};");
430  Workspace.addMainFile("A.cpp", R"cpp(
431  #include "Base.h"
432  class A : public Base {};
433  )cpp");
434  Workspace.addMainFile("B.cpp", R"cpp(
435  #include "Base.h"
436  class B : public Base {};
437  )cpp");
438 
439  auto Index = Workspace.index();
440  FuzzyFindRequest FFReq;
441  FFReq.Query = "Base";
442  FFReq.AnyScope = true;
443  SymbolID Base;
444  Index->fuzzyFind(FFReq, [&](const Symbol &S) { Base = S.ID; });
445 
446  RelationsRequest Req;
447  Req.Subjects.insert(Base);
448  Req.Predicate = RelationKind::BaseOf;
449  uint32_t Results = 0;
450  Index->relations(Req, [&](const SymbolID &, const Symbol &) { ++Results; });
451  EXPECT_EQ(Results, 2u);
452 }
453 
454 TEST(FileIndexTest, ReferencesInMainFileWithPreamble) {
455  TestTU TU;
456  TU.HeaderCode = "class Foo{};";
457  Annotations Main(R"cpp(
458  void f() {
459  [[Foo]] foo;
460  }
461  )cpp");
462  TU.Code = std::string(Main.code());
463  auto AST = TU.build();
464  FileIndex Index;
465  Index.updateMain(testPath(TU.Filename), AST);
466 
467  // Expect to see references in main file, references in headers are excluded
468  // because we only index main AST.
469  EXPECT_THAT(getRefs(Index, findSymbol(TU.headerSymbols(), "Foo").ID),
470  refsAre({refRange(Main.range())}));
471 }
472 
473 TEST(FileIndexTest, MergeMainFileSymbols) {
474  const char *CommonHeader = "void foo();";
475  TestTU Header = TestTU::withCode(CommonHeader);
476  TestTU Cpp = TestTU::withCode("void foo() {}");
477  Cpp.Filename = "foo.cpp";
478  Cpp.HeaderFilename = "foo.h";
479  Cpp.HeaderCode = CommonHeader;
480 
481  FileIndex Index;
482  auto HeaderAST = Header.build();
483  auto CppAST = Cpp.build();
484  Index.updateMain(testPath("foo.h"), HeaderAST);
485  Index.updateMain(testPath("foo.cpp"), CppAST);
486 
487  auto Symbols = runFuzzyFind(Index, "");
488  // Check foo is merged, foo in Cpp wins (as we see the definition there).
489  EXPECT_THAT(Symbols, ElementsAre(AllOf(declURI("unittest:///foo.h"),
490  defURI("unittest:///foo.cpp"),
491  hasOrign(SymbolOrigin::Merge))));
492 }
493 
494 TEST(FileSymbolsTest, CountReferencesNoRefSlabs) {
495  FileSymbols FS(IndexContents::All);
496  FS.update("f1", numSlab(1, 3), nullptr, nullptr, true);
497  FS.update("f2", numSlab(1, 3), nullptr, nullptr, false);
498  EXPECT_THAT(
500  ""),
501  UnorderedElementsAre(AllOf(qName("1"), numReferences(0u)),
502  AllOf(qName("2"), numReferences(0u)),
503  AllOf(qName("3"), numReferences(0u))));
504 }
505 
506 TEST(FileSymbolsTest, CountReferencesWithRefSlabs) {
507  FileSymbols FS(IndexContents::All);
508  FS.update("f1cpp", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cpp"), nullptr,
509  true);
510  FS.update("f1h", numSlab(1, 3), refSlab(SymbolID("1"), "f1.h"), nullptr,
511  false);
512  FS.update("f2cpp", numSlab(1, 3), refSlab(SymbolID("2"), "f2.cpp"), nullptr,
513  true);
514  FS.update("f2h", numSlab(1, 3), refSlab(SymbolID("2"), "f2.h"), nullptr,
515  false);
516  FS.update("f3cpp", numSlab(1, 3), refSlab(SymbolID("3"), "f3.cpp"), nullptr,
517  true);
518  FS.update("f3h", numSlab(1, 3), refSlab(SymbolID("3"), "f3.h"), nullptr,
519  false);
520  EXPECT_THAT(
522  ""),
523  UnorderedElementsAre(AllOf(qName("1"), numReferences(1u)),
524  AllOf(qName("2"), numReferences(1u)),
525  AllOf(qName("3"), numReferences(1u))));
526 }
527 
528 TEST(FileIndexTest, StalePreambleSymbolsDeleted) {
529  FileIndex M;
530  TestTU File;
531  File.HeaderFilename = "a.h";
532 
533  File.Filename = "f1.cpp";
534  File.HeaderCode = "int a;";
535  auto AST = File.build();
536  M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
537  AST.getASTContext(), AST.getPreprocessor(),
538  AST.getCanonicalIncludes());
539  EXPECT_THAT(runFuzzyFind(M, ""), UnorderedElementsAre(qName("a")));
540 
541  File.Filename = "f2.cpp";
542  File.HeaderCode = "int b;";
543  AST = File.build();
544  M.updatePreamble(testPath(File.Filename), /*Version=*/"null",
545  AST.getASTContext(), AST.getPreprocessor(),
546  AST.getCanonicalIncludes());
547  EXPECT_THAT(runFuzzyFind(M, ""), UnorderedElementsAre(qName("b")));
548 }
549 
550 // Verifies that concurrent calls to updateMain don't "lose" any updates.
551 TEST(FileIndexTest, Threadsafety) {
552  FileIndex M;
553  Notification Go;
554 
555  constexpr int Count = 10;
556  {
557  // Set up workers to concurrently call updateMain() with separate files.
558  AsyncTaskRunner Pool;
559  for (unsigned I = 0; I < Count; ++I) {
560  auto TU = TestTU::withCode(llvm::formatv("int xxx{0};", I).str());
561  TU.Filename = llvm::formatv("x{0}.c", I).str();
562  Pool.runAsync(TU.Filename, [&, Filename(testPath(TU.Filename)),
563  AST(TU.build())]() mutable {
564  Go.wait();
565  M.updateMain(Filename, AST);
566  });
567  }
568  // On your marks, get set...
569  Go.notify();
570  }
571 
572  EXPECT_THAT(runFuzzyFind(M, "xxx"), ::testing::SizeIs(Count));
573 }
574 
575 TEST(FileShardedIndexTest, Sharding) {
576  auto AHeaderUri = URI::create(testPath("a.h")).toString();
577  auto BHeaderUri = URI::create(testPath("b.h")).toString();
578  auto BSourceUri = URI::create(testPath("b.cc")).toString();
579 
580  auto Sym1 = symbol("1");
581  Sym1.CanonicalDeclaration.FileURI = AHeaderUri.c_str();
582 
583  auto Sym2 = symbol("2");
584  Sym2.CanonicalDeclaration.FileURI = BHeaderUri.c_str();
585  Sym2.Definition.FileURI = BSourceUri.c_str();
586 
587  auto Sym3 = symbol("3"); // not stored
588 
589  IndexFileIn IF;
590  {
592  // Should be stored in only a.h
593  B.insert(Sym1);
594  // Should be stored in both b.h and b.cc
595  B.insert(Sym2);
596  IF.Symbols.emplace(std::move(B).build());
597  }
598  {
599  // Should be stored in b.cc
600  IF.Refs.emplace(std::move(*refSlab(Sym1.ID, BSourceUri.c_str())));
601  }
602  {
604  // Should be stored in a.h and b.h
605  B.insert(Relation{Sym1.ID, RelationKind::BaseOf, Sym2.ID});
606  // Should be stored in a.h and b.h
607  B.insert(Relation{Sym2.ID, RelationKind::BaseOf, Sym1.ID});
608  // Should be stored in a.h (where Sym1 is stored) even though
609  // the relation is dangling as Sym3 is unknown.
610  B.insert(Relation{Sym3.ID, RelationKind::BaseOf, Sym1.ID});
611  IF.Relations.emplace(std::move(B).build());
612  }
613 
614  IF.Sources.emplace();
615  IncludeGraph &IG = *IF.Sources;
616  {
617  // b.cc includes b.h
618  auto &Node = IG[BSourceUri];
619  Node.DirectIncludes = {BHeaderUri};
620  Node.URI = BSourceUri;
621  }
622  {
623  // b.h includes a.h
624  auto &Node = IG[BHeaderUri];
625  Node.DirectIncludes = {AHeaderUri};
626  Node.URI = BHeaderUri;
627  }
628  {
629  // a.h includes nothing.
630  auto &Node = IG[AHeaderUri];
631  Node.DirectIncludes = {};
632  Node.URI = AHeaderUri;
633  }
634 
635  IF.Cmd = tooling::CompileCommand(testRoot(), "b.cc", {"clang"}, "out");
636 
637  FileShardedIndex ShardedIndex(std::move(IF));
638  ASSERT_THAT(ShardedIndex.getAllSources(),
639  UnorderedElementsAre(AHeaderUri, BHeaderUri, BSourceUri));
640 
641  {
642  auto Shard = ShardedIndex.getShard(AHeaderUri);
643  ASSERT_TRUE(Shard);
644  EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(qName("1")));
645  EXPECT_THAT(*Shard->Refs, IsEmpty());
646  EXPECT_THAT(
647  *Shard->Relations,
648  UnorderedElementsAre(Relation{Sym1.ID, RelationKind::BaseOf, Sym2.ID},
649  Relation{Sym2.ID, RelationKind::BaseOf, Sym1.ID},
650  Relation{Sym3.ID, RelationKind::BaseOf, Sym1.ID}));
651  ASSERT_THAT(Shard->Sources->keys(), UnorderedElementsAre(AHeaderUri));
652  EXPECT_THAT(Shard->Sources->lookup(AHeaderUri).DirectIncludes, IsEmpty());
653  EXPECT_TRUE(Shard->Cmd);
654  }
655  {
656  auto Shard = ShardedIndex.getShard(BHeaderUri);
657  ASSERT_TRUE(Shard);
658  EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(qName("2")));
659  EXPECT_THAT(*Shard->Refs, IsEmpty());
660  EXPECT_THAT(
661  *Shard->Relations,
662  UnorderedElementsAre(Relation{Sym1.ID, RelationKind::BaseOf, Sym2.ID},
663  Relation{Sym2.ID, RelationKind::BaseOf, Sym1.ID}));
664  ASSERT_THAT(Shard->Sources->keys(),
665  UnorderedElementsAre(BHeaderUri, AHeaderUri));
666  EXPECT_THAT(Shard->Sources->lookup(BHeaderUri).DirectIncludes,
667  UnorderedElementsAre(AHeaderUri));
668  EXPECT_TRUE(Shard->Cmd);
669  }
670  {
671  auto Shard = ShardedIndex.getShard(BSourceUri);
672  ASSERT_TRUE(Shard);
673  EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(qName("2")));
674  EXPECT_THAT(*Shard->Refs, UnorderedElementsAre(Pair(Sym1.ID, _)));
675  EXPECT_THAT(*Shard->Relations, IsEmpty());
676  ASSERT_THAT(Shard->Sources->keys(),
677  UnorderedElementsAre(BSourceUri, BHeaderUri));
678  EXPECT_THAT(Shard->Sources->lookup(BSourceUri).DirectIncludes,
679  UnorderedElementsAre(BHeaderUri));
680  EXPECT_TRUE(Shard->Cmd);
681  }
682 }
683 
684 TEST(FileIndexTest, Profile) {
685  FileIndex FI;
686 
687  auto FileName = testPath("foo.cpp");
688  auto AST = TestTU::withHeaderCode("int a;").build();
689  FI.updateMain(FileName, AST);
690  FI.updatePreamble(FileName, "v1", AST.getASTContext(), AST.getPreprocessor(),
691  AST.getCanonicalIncludes());
692 
693  llvm::BumpPtrAllocator Alloc;
694  MemoryTree MT(&Alloc);
695  FI.profile(MT);
696  ASSERT_THAT(MT.children(),
697  UnorderedElementsAre(Pair("preamble", _), Pair("main_file", _)));
698 
699  ASSERT_THAT(MT.child("preamble").children(),
700  UnorderedElementsAre(Pair("index", _), Pair("slabs", _)));
701  ASSERT_THAT(MT.child("main_file").children(),
702  UnorderedElementsAre(Pair("index", _), Pair("slabs", _)));
703 
704  ASSERT_THAT(MT.child("preamble").child("index").total(), Gt(0U));
705  ASSERT_THAT(MT.child("main_file").child("index").total(), Gt(0U));
706 }
707 
708 TEST(FileSymbolsTest, Profile) {
709  FileSymbols FS(IndexContents::All);
710  FS.update("f1", numSlab(1, 2), nullptr, nullptr, false);
711  FS.update("f2", nullptr, refSlab(SymbolID("1"), "f1"), nullptr, false);
712  FS.update("f3", nullptr, nullptr,
713  relSlab({{SymbolID("1"), RelationKind::BaseOf, SymbolID("2")}}),
714  false);
715  llvm::BumpPtrAllocator Alloc;
716  MemoryTree MT(&Alloc);
717  FS.profile(MT);
718  ASSERT_THAT(MT.children(), UnorderedElementsAre(Pair("f1", _), Pair("f2", _),
719  Pair("f3", _)));
720  EXPECT_THAT(MT.child("f1").children(), ElementsAre(Pair("symbols", _)));
721  EXPECT_THAT(MT.child("f1").total(), Gt(0U));
722  EXPECT_THAT(MT.child("f2").children(), ElementsAre(Pair("references", _)));
723  EXPECT_THAT(MT.child("f2").total(), Gt(0U));
724  EXPECT_THAT(MT.child("f3").children(), ElementsAre(Pair("relations", _)));
725  EXPECT_THAT(MT.child("f3").total(), Gt(0U));
726 }
727 
728 TEST(FileIndexTest, MacrosFromMainFile) {
729  FileIndex Idx;
730  TestTU TU;
731  TU.Code = "#pragma once\n#define FOO";
732  TU.Filename = "foo.h";
733  auto AST = TU.build();
734  Idx.updateMain(testPath(TU.Filename), AST);
735 
736  auto Slab = runFuzzyFind(Idx, "");
737  auto &FooSymbol = findSymbol(Slab, "FOO");
738  EXPECT_TRUE(FooSymbol.Flags & Symbol::IndexedForCodeCompletion);
739 }
740 
741 } // namespace
742 } // namespace clangd
743 } // namespace clang
clang::clangd::buildPreamble
std::shared_ptr< const PreambleData > buildPreamble(PathRef FileName, CompilerInvocation CI, const ParseInputs &Inputs, bool StoreInMemory, PreambleParsedCallback PreambleCallback, PreambleBuildStats *Stats)
Build a preamble for the new inputs unless an old one can be reused.
Definition: Preamble.cpp:466
clang::clangd::Symbol::IndexedForCodeCompletion
@ IndexedForCodeCompletion
Whether or not this symbol is meant to be used for the code completion.
Definition: Symbol.h:119
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
MATCHER_P
MATCHER_P(refRange, Range, "")
Definition: FileIndexTests.cpp:46
Base
std::unique_ptr< GlobalCompilationDatabase > Base
Definition: GlobalCompilationDatabaseTests.cpp:85
SymbolID.h
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:750
Headers.h
clang::clangd::runFuzzyFind
SymbolSlab runFuzzyFind(const SymbolIndex &Index, llvm::StringRef Query)
Definition: SyncAPI.cpp:124
clang::clangd::RelationKind::BaseOf
@ BaseOf
clang::clangd::testPath
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:94
clang::clangd::Symbol::ID
SymbolID ID
The ID of the symbol.
Definition: Symbol.h:38
Refs
RefSlab Refs
Definition: SymbolCollectorTests.cpp:312
clang::clangd::Path
std::string Path
A typedef to represent a file path.
Definition: Path.h:26
clang::clangd::TestTU::build
ParsedAST build() const
Definition: TestTU.cpp:109
clang::clangd::ParsedAST::getASTContext
ASTContext & getASTContext()
Note that the returned ast will not contain decls from the preamble that were not deserialized during...
Definition: ParsedAST.cpp:697
Index.h
CI
std::unique_ptr< CompilerInvocation > CI
Definition: TUScheduler.cpp:491
clang::clangd::URI::create
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition: URI.cpp:209
TestTU.h
Filename
std::string Filename
Filename as a string.
Definition: IncludeOrderCheck.cpp:39
clang::clangd::ParsedAST::getCanonicalIncludes
const CanonicalIncludes & getCanonicalIncludes() const
Definition: ParsedAST.cpp:758
Ctx
Context Ctx
Definition: TUScheduler.cpp:495
TestWorkspace.h
M
const google::protobuf::Message & M
Definition: Server.cpp:309
clang::clangd::ParsedAST::build
static llvm::Optional< ParsedAST > build(llvm::StringRef Filename, const ParseInputs &Inputs, std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble)
Attempts to run Clang and store the parsed AST.
Definition: ParsedAST.cpp:342
clang::clangd::TestTU::withHeaderCode
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
Definition: TestTU.h:42
Relation.h
clang::clangd::getRefs
RefSlab getRefs(const SymbolIndex &Index, SymbolID ID)
Definition: SyncAPI.cpp:137
ns1::ns2::A
@ A
Definition: CategoricalFeature.h:3
Code
std::string Code
Definition: FindTargetTests.cpp:67
CanonicalIncludes.h
Foo
Definition: sample.h:4
clang::clangd::testRoot
const char * testRoot()
Definition: TestFS.cpp:86
clang::clangd::SymbolIndex::relations
virtual void relations(const RelationsRequest &Req, llvm::function_ref< void(const SymbolID &Subject, const Symbol &Object)> Callback) const =0
Finds all relations (S, P, O) stored in the index such that S is among Req.Subjects and P is Req....
clang::clangd::SymbolOrigin::Merge
@ Merge
Builder
CodeCompletionBuilder Builder
Definition: CodeCompletionStringsTests.cpp:36
TestFS.h
Threading.h
SyncAPI.h
clang::clangd::refsAre
::testing::Matcher< const RefSlab & > refsAre(std::vector<::testing::Matcher< Ref >> Matchers)
Definition: BackgroundIndexTests.cpp:38
clang::clangd::RefKind::Reference
@ Reference
Serialization.h
Results
std::vector< CodeCompletionResult > Results
Definition: CodeComplete.cpp:784
clang::doc::SymbolID
std::array< uint8_t, 20 > SymbolID
Definition: Representation.h:30
FileName
StringRef FileName
Definition: KernelNameRestrictionCheck.cpp:46
clang::clangd::TestTU::withCode
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:36
clang::clangd::DuplicateHandling::Merge
@ Merge
FileIndex.h
Annotations.h
clang::clangd::ParsedAST::getPreprocessor
Preprocessor & getPreprocessor()
Definition: ParsedAST.cpp:705
Symbol.h
clang::tidy::bugprone::PP
static Preprocessor * PP
Definition: BadSignalToKillThreadCheck.cpp:29
clang::clangd::buildCompilerInvocation
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, std::vector< std::string > *CC1Args)
Builds compiler invocation that could be used to build AST or preamble.
Definition: Compiler.cpp:86
clang::clangd::IndexContents::All
@ All
Index
const SymbolIndex * Index
Definition: Dexp.cpp:98
Compiler.h
clang::clangd::Empty
@ Empty
Definition: FuzzyMatch.h:42
ID
static char ID
Definition: Logger.cpp:74
Ref.h
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::IncludeGraph
llvm::StringMap< IncludeGraphNode > IncludeGraph
Definition: Headers.h:92
clang::clangd::symbol
Symbol symbol(llvm::StringRef QName)
Definition: TestIndex.cpp:16
URI.h
clang::clangd::IndexType::Heavy
@ Heavy
ns1::ns2::B
@ B
Definition: CategoricalFeature.h:3
clang::clangd::findSymbol
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
Definition: TestTU.cpp:182
IgnoreDiags
IgnoringDiagConsumer IgnoreDiags
Definition: HeadersTests.cpp:137
clang::clangd::IndexType::Light
@ Light
clang::clangd::SymbolIndex::fuzzyFind
virtual bool fuzzyFind(const FuzzyFindRequest &Req, llvm::function_ref< void(const Symbol &)> Callback) const =0
Matches symbols in the index fuzzily and applies Callback on each matched symbol before returning.
ParsedAST.h