clang-tools  10.0.0
FindSymbolsTests.cpp
Go to the documentation of this file.
1 //===-- FindSymbolsTests.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 #include "Annotations.h"
9 #include "ClangdServer.h"
10 #include "FindSymbols.h"
11 #include "SyncAPI.h"
12 #include "TestFS.h"
13 #include "gmock/gmock.h"
14 #include "gtest/gtest.h"
15 
16 namespace clang {
17 namespace clangd {
18 
19 namespace {
20 
21 using ::testing::AllOf;
22 using ::testing::ElementsAre;
23 using ::testing::ElementsAreArray;
24 using ::testing::Field;
25 using ::testing::IsEmpty;
26 using ::testing::UnorderedElementsAre;
27 
28 class IgnoreDiagnostics : public DiagnosticsConsumer {
30  std::vector<Diag> Diagnostics) override {}
31 };
32 
33 // GMock helpers for matching SymbolInfos items.
34 MATCHER_P(QName, Name, "") {
35  if (arg.containerName.empty())
36  return arg.name == Name;
37  return (arg.containerName + "::" + arg.name) == Name;
38 }
39 MATCHER_P(WithName, N, "") { return arg.name == N; }
40 MATCHER_P(WithKind, Kind, "") { return arg.kind == Kind; }
41 MATCHER_P(SymRange, Range, "") { return arg.location.range == Range; }
42 
43 // GMock helpers for matching DocumentSymbol.
44 MATCHER_P(SymNameRange, Range, "") { return arg.selectionRange == Range; }
45 template <class... ChildMatchers>
46 ::testing::Matcher<DocumentSymbol> Children(ChildMatchers... ChildrenM) {
47  return Field(&DocumentSymbol::children, ElementsAre(ChildrenM...));
48 }
49 
50 ClangdServer::Options optsForTests() {
51  auto ServerOpts = ClangdServer::optsForTest();
52  ServerOpts.WorkspaceRoot = testRoot();
53  ServerOpts.BuildDynamicSymbolIndex = true;
54  return ServerOpts;
55 }
56 
57 class WorkspaceSymbolsTest : public ::testing::Test {
58 public:
59  WorkspaceSymbolsTest()
60  : Server(CDB, FSProvider, DiagConsumer, optsForTests()) {
61  // Make sure the test root directory is created.
62  FSProvider.Files[testPath("unused")] = "";
63  CDB.ExtraClangFlags = {"-xc++"};
64  }
65 
66 protected:
67  MockFSProvider FSProvider;
68  MockCompilationDatabase CDB;
69  IgnoreDiagnostics DiagConsumer;
70  ClangdServer Server;
71  int Limit = 0;
72 
73  std::vector<SymbolInformation> getSymbols(llvm::StringRef Query) {
74  EXPECT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for preamble";
75  auto SymbolInfos = runWorkspaceSymbols(Server, Query, Limit);
76  EXPECT_TRUE(bool(SymbolInfos)) << "workspaceSymbols returned an error";
77  return *SymbolInfos;
78  }
79 
80  void addFile(llvm::StringRef FileName, llvm::StringRef Contents) {
81  auto Path = testPath(FileName);
82  FSProvider.Files[Path] = Contents;
83  Server.addDocument(Path, Contents);
84  }
85 };
86 
87 } // namespace
88 
89 TEST_F(WorkspaceSymbolsTest, Macros) {
90  addFile("foo.cpp", R"cpp(
91  #define MACRO X
92  )cpp");
93 
94  // LSP's SymbolKind doesn't have a "Macro" kind, and
95  // indexSymbolKindToSymbolKind() currently maps macros
96  // to SymbolKind::String.
97  EXPECT_THAT(getSymbols("macro"),
98  ElementsAre(AllOf(QName("MACRO"), WithKind(SymbolKind::String))));
99 }
100 
101 TEST_F(WorkspaceSymbolsTest, NoLocals) {
102  addFile("foo.cpp", R"cpp(
103  void test(int FirstParam, int SecondParam) {
104  struct LocalClass {};
105  int local_var;
106  })cpp");
107  EXPECT_THAT(getSymbols("l"), IsEmpty());
108  EXPECT_THAT(getSymbols("p"), IsEmpty());
109 }
110 
111 TEST_F(WorkspaceSymbolsTest, Globals) {
112  addFile("foo.h", R"cpp(
113  int global_var;
114 
115  int global_func();
116 
117  struct GlobalStruct {};)cpp");
118  addFile("foo.cpp", R"cpp(
119  #include "foo.h"
120  )cpp");
121  EXPECT_THAT(getSymbols("global"),
122  UnorderedElementsAre(
123  AllOf(QName("GlobalStruct"), WithKind(SymbolKind::Struct)),
124  AllOf(QName("global_func"), WithKind(SymbolKind::Function)),
125  AllOf(QName("global_var"), WithKind(SymbolKind::Variable))));
126 }
127 
128 TEST_F(WorkspaceSymbolsTest, Unnamed) {
129  addFile("foo.h", R"cpp(
130  struct {
131  int InUnnamed;
132  } UnnamedStruct;)cpp");
133  addFile("foo.cpp", R"cpp(
134  #include "foo.h"
135  )cpp");
136  EXPECT_THAT(getSymbols("UnnamedStruct"),
137  ElementsAre(AllOf(QName("UnnamedStruct"),
138  WithKind(SymbolKind::Variable))));
139  EXPECT_THAT(getSymbols("InUnnamed"),
140  ElementsAre(AllOf(QName("(anonymous struct)::InUnnamed"),
141  WithKind(SymbolKind::Field))));
142 }
143 
144 TEST_F(WorkspaceSymbolsTest, InMainFile) {
145  addFile("foo.cpp", R"cpp(
146  int test() {}
147  static test2() {}
148  )cpp");
149  EXPECT_THAT(getSymbols("test"), ElementsAre(QName("test"), QName("test2")));
150 }
151 
152 TEST_F(WorkspaceSymbolsTest, Namespaces) {
153  addFile("foo.h", R"cpp(
154  namespace ans1 {
155  int ai1;
156  namespace ans2 {
157  int ai2;
158  }
159  }
160  )cpp");
161  addFile("foo.cpp", R"cpp(
162  #include "foo.h"
163  )cpp");
164  EXPECT_THAT(getSymbols("a"),
165  UnorderedElementsAre(QName("ans1"), QName("ans1::ai1"),
166  QName("ans1::ans2"),
167  QName("ans1::ans2::ai2")));
168  EXPECT_THAT(getSymbols("::"), ElementsAre(QName("ans1")));
169  EXPECT_THAT(getSymbols("::a"), ElementsAre(QName("ans1")));
170  EXPECT_THAT(getSymbols("ans1::"),
171  UnorderedElementsAre(QName("ans1::ai1"), QName("ans1::ans2")));
172  EXPECT_THAT(getSymbols("::ans1"), ElementsAre(QName("ans1")));
173  EXPECT_THAT(getSymbols("::ans1::"),
174  UnorderedElementsAre(QName("ans1::ai1"), QName("ans1::ans2")));
175  EXPECT_THAT(getSymbols("::ans1::ans2"), ElementsAre(QName("ans1::ans2")));
176  EXPECT_THAT(getSymbols("::ans1::ans2::"),
177  ElementsAre(QName("ans1::ans2::ai2")));
178 }
179 
180 TEST_F(WorkspaceSymbolsTest, AnonymousNamespace) {
181  addFile("foo.cpp", R"cpp(
182  namespace {
183  void test() {}
184  }
185  )cpp");
186  EXPECT_THAT(getSymbols("test"), ElementsAre(QName("test")));
187 }
188 
189 TEST_F(WorkspaceSymbolsTest, MultiFile) {
190  addFile("foo.h", R"cpp(
191  int foo() {
192  }
193  )cpp");
194  addFile("foo2.h", R"cpp(
195  int foo2() {
196  }
197  )cpp");
198  addFile("foo.cpp", R"cpp(
199  #include "foo.h"
200  #include "foo2.h"
201  )cpp");
202  EXPECT_THAT(getSymbols("foo"),
203  UnorderedElementsAre(QName("foo"), QName("foo2")));
204 }
205 
206 TEST_F(WorkspaceSymbolsTest, GlobalNamespaceQueries) {
207  addFile("foo.h", R"cpp(
208  int foo() {
209  }
210  class Foo {
211  int a;
212  };
213  namespace ns {
214  int foo2() {
215  }
216  }
217  )cpp");
218  addFile("foo.cpp", R"cpp(
219  #include "foo.h"
220  )cpp");
221  EXPECT_THAT(getSymbols("::"),
222  UnorderedElementsAre(
223  AllOf(QName("Foo"), WithKind(SymbolKind::Class)),
224  AllOf(QName("foo"), WithKind(SymbolKind::Function)),
225  AllOf(QName("ns"), WithKind(SymbolKind::Namespace))));
226  EXPECT_THAT(getSymbols(":"), IsEmpty());
227  EXPECT_THAT(getSymbols(""), IsEmpty());
228 }
229 
230 TEST_F(WorkspaceSymbolsTest, Enums) {
231  addFile("foo.h", R"cpp(
232  enum {
233  Red
234  };
235  enum Color {
236  Green
237  };
238  enum class Color2 {
239  Yellow
240  };
241  namespace ns {
242  enum {
243  Black
244  };
245  enum Color3 {
246  Blue
247  };
248  enum class Color4 {
249  White
250  };
251  }
252  )cpp");
253  addFile("foo.cpp", R"cpp(
254  #include "foo.h"
255  )cpp");
256  EXPECT_THAT(getSymbols("Red"), ElementsAre(QName("Red")));
257  EXPECT_THAT(getSymbols("::Red"), ElementsAre(QName("Red")));
258  EXPECT_THAT(getSymbols("Green"), ElementsAre(QName("Green")));
259  EXPECT_THAT(getSymbols("Green"), ElementsAre(QName("Green")));
260  EXPECT_THAT(getSymbols("Color2::Yellow"),
261  ElementsAre(QName("Color2::Yellow")));
262  EXPECT_THAT(getSymbols("Yellow"), ElementsAre(QName("Color2::Yellow")));
263 
264  EXPECT_THAT(getSymbols("ns::Black"), ElementsAre(QName("ns::Black")));
265  EXPECT_THAT(getSymbols("ns::Blue"), ElementsAre(QName("ns::Blue")));
266  EXPECT_THAT(getSymbols("ns::Color4::White"),
267  ElementsAre(QName("ns::Color4::White")));
268 }
269 
270 TEST_F(WorkspaceSymbolsTest, Ranking) {
271  addFile("foo.h", R"cpp(
272  namespace ns{}
273  void func();
274  )cpp");
275  addFile("foo.cpp", R"cpp(
276  #include "foo.h"
277  )cpp");
278  EXPECT_THAT(getSymbols("::"), ElementsAre(QName("func"), QName("ns")));
279 }
280 
281 TEST_F(WorkspaceSymbolsTest, WithLimit) {
282  addFile("foo.h", R"cpp(
283  int foo;
284  int foo2;
285  )cpp");
286  addFile("foo.cpp", R"cpp(
287  #include "foo.h"
288  )cpp");
289  // Foo is higher ranked because of exact name match.
290  EXPECT_THAT(getSymbols("foo"),
291  UnorderedElementsAre(
292  AllOf(QName("foo"), WithKind(SymbolKind::Variable)),
293  AllOf(QName("foo2"), WithKind(SymbolKind::Variable))));
294 
295  Limit = 1;
296  EXPECT_THAT(getSymbols("foo"), ElementsAre(QName("foo")));
297 }
298 
299 TEST_F(WorkspaceSymbolsTest, TempSpecs) {
300  addFile("foo.h", R"cpp(
301  template <typename T, typename U, int X = 5> class Foo {};
302  template <typename T> class Foo<int, T> {};
303  template <> class Foo<bool, int> {};
304  template <> class Foo<bool, int, 3> {};
305  )cpp");
306  // Foo is higher ranked because of exact name match.
307  EXPECT_THAT(
308  getSymbols("Foo"),
309  UnorderedElementsAre(
310  AllOf(QName("Foo"), WithKind(SymbolKind::Class)),
311  AllOf(QName("Foo<int, T>"), WithKind(SymbolKind::Class)),
312  AllOf(QName("Foo<bool, int>"), WithKind(SymbolKind::Class)),
313  AllOf(QName("Foo<bool, int, 3>"), WithKind(SymbolKind::Class))));
314 }
315 
316 namespace {
317 class DocumentSymbolsTest : public ::testing::Test {
318 public:
319  DocumentSymbolsTest()
320  : Server(CDB, FSProvider, DiagConsumer, optsForTests()) {}
321 
322 protected:
327 
328  std::vector<DocumentSymbol> getSymbols(PathRef File) {
329  EXPECT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for preamble";
330  auto SymbolInfos = runDocumentSymbols(Server, File);
331  EXPECT_TRUE(bool(SymbolInfos)) << "documentSymbols returned an error";
332  return *SymbolInfos;
333  }
334 
335  void addFile(llvm::StringRef FilePath, llvm::StringRef Contents) {
336  FSProvider.Files[FilePath] = Contents;
337  Server.addDocument(FilePath, Contents);
338  }
339 };
340 } // namespace
341 
342 TEST_F(DocumentSymbolsTest, BasicSymbols) {
343  std::string FilePath = testPath("foo.cpp");
344  Annotations Main(R"(
345  class Foo;
346  class Foo {
347  Foo() {}
348  Foo(int a) {}
349  void $decl[[f]]();
350  friend void f1();
351  friend class Friend;
352  Foo& operator=(const Foo&);
353  ~Foo();
354  class Nested {
355  void f();
356  };
357  };
358  class Friend {
359  };
360 
361  void f1();
362  inline void f2() {}
363  static const int KInt = 2;
364  const char* kStr = "123";
365 
366  void f1() {}
367 
368  namespace foo {
369  // Type alias
370  typedef int int32;
371  using int32_t = int32;
372 
373  // Variable
374  int v1;
375 
376  // Namespace
377  namespace bar {
378  int v2;
379  }
380  // Namespace alias
381  namespace baz = bar;
382 
383  using bar::v2;
384  } // namespace foo
385  )");
386 
387  addFile(FilePath, Main.code());
388  EXPECT_THAT(
389  getSymbols(FilePath),
390  ElementsAreArray(
391  {AllOf(WithName("Foo"), WithKind(SymbolKind::Class), Children()),
392  AllOf(WithName("Foo"), WithKind(SymbolKind::Class),
393  Children(AllOf(WithName("Foo"),
394  WithKind(SymbolKind::Constructor), Children()),
395  AllOf(WithName("Foo"),
396  WithKind(SymbolKind::Constructor), Children()),
397  AllOf(WithName("f"), WithKind(SymbolKind::Method),
398  Children()),
399  AllOf(WithName("operator="),
400  WithKind(SymbolKind::Method), Children()),
401  AllOf(WithName("~Foo"),
402  WithKind(SymbolKind::Constructor), Children()),
403  AllOf(WithName("Nested"), WithKind(SymbolKind::Class),
404  Children(AllOf(WithName("f"),
405  WithKind(SymbolKind::Method),
406  Children()))))),
407  AllOf(WithName("Friend"), WithKind(SymbolKind::Class), Children()),
408  AllOf(WithName("f1"), WithKind(SymbolKind::Function), Children()),
409  AllOf(WithName("f2"), WithKind(SymbolKind::Function), Children()),
410  AllOf(WithName("KInt"), WithKind(SymbolKind::Variable), Children()),
411  AllOf(WithName("kStr"), WithKind(SymbolKind::Variable), Children()),
412  AllOf(WithName("f1"), WithKind(SymbolKind::Function), Children()),
413  AllOf(
414  WithName("foo"), WithKind(SymbolKind::Namespace),
415  Children(
416  AllOf(WithName("int32"), WithKind(SymbolKind::Class),
417  Children()),
418  AllOf(WithName("int32_t"), WithKind(SymbolKind::Class),
419  Children()),
420  AllOf(WithName("v1"), WithKind(SymbolKind::Variable),
421  Children()),
422  AllOf(WithName("bar"), WithKind(SymbolKind::Namespace),
423  Children(AllOf(WithName("v2"),
424  WithKind(SymbolKind::Variable),
425  Children()))),
426  AllOf(WithName("baz"), WithKind(SymbolKind::Namespace),
427  Children()),
428  AllOf(WithName("v2"), WithKind(SymbolKind::Namespace))))}));
429 }
430 
431 TEST_F(DocumentSymbolsTest, DeclarationDefinition) {
432  std::string FilePath = testPath("foo.cpp");
433  Annotations Main(R"(
434  class Foo {
435  void $decl[[f]]();
436  };
437  void Foo::$def[[f]]() {
438  }
439  )");
440 
441  addFile(FilePath, Main.code());
442  EXPECT_THAT(
443  getSymbols(FilePath),
444  ElementsAre(
445  AllOf(WithName("Foo"), WithKind(SymbolKind::Class),
446  Children(AllOf(WithName("f"), WithKind(SymbolKind::Method),
447  SymNameRange(Main.range("decl"))))),
448  AllOf(WithName("Foo::f"), WithKind(SymbolKind::Method),
449  SymNameRange(Main.range("def")))));
450 }
451 
452 TEST_F(DocumentSymbolsTest, ExternSymbol) {
453  std::string FilePath = testPath("foo.cpp");
454  addFile(testPath("foo.h"), R"cpp(
455  extern int var;
456  )cpp");
457  addFile(FilePath, R"cpp(
458  #include "foo.h"
459  )cpp");
460 
461  EXPECT_THAT(getSymbols(FilePath), IsEmpty());
462 }
463 
464 TEST_F(DocumentSymbolsTest, NoLocals) {
465  std::string FilePath = testPath("foo.cpp");
466  addFile(FilePath,
467  R"cpp(
468  void test(int FirstParam, int SecondParam) {
469  struct LocalClass {};
470  int local_var;
471  })cpp");
472  EXPECT_THAT(getSymbols(FilePath), ElementsAre(WithName("test")));
473 }
474 
475 TEST_F(DocumentSymbolsTest, Unnamed) {
476  std::string FilePath = testPath("foo.h");
477  addFile(FilePath,
478  R"cpp(
479  struct {
480  int InUnnamed;
481  } UnnamedStruct;
482  )cpp");
483  EXPECT_THAT(
484  getSymbols(FilePath),
485  ElementsAre(
486  AllOf(WithName("(anonymous struct)"), WithKind(SymbolKind::Struct),
487  Children(AllOf(WithName("InUnnamed"),
488  WithKind(SymbolKind::Field), Children()))),
489  AllOf(WithName("UnnamedStruct"), WithKind(SymbolKind::Variable),
490  Children())));
491 }
492 
493 TEST_F(DocumentSymbolsTest, InHeaderFile) {
494  addFile(testPath("bar.h"), R"cpp(
495  int foo() {
496  }
497  )cpp");
498  std::string FilePath = testPath("foo.h");
499  addFile(FilePath, R"cpp(
500  #include "bar.h"
501  int test() {
502  }
503  )cpp");
504  addFile(testPath("foo.cpp"), R"cpp(
505  #include "foo.h"
506  )cpp");
507  EXPECT_THAT(getSymbols(FilePath), ElementsAre(WithName("test")));
508 }
509 
510 TEST_F(DocumentSymbolsTest, Template) {
511  std::string FilePath = testPath("foo.cpp");
512  addFile(FilePath, R"(
513  template <class T> struct Tmpl {T x = 0;};
514  template <> struct Tmpl<int> {
515  int y = 0;
516  };
517  extern template struct Tmpl<float>;
518  template struct Tmpl<double>;
519 
520  template <class T, class U, class Z = float>
521  int funcTmpl(U a);
522  template <>
523  int funcTmpl<int>(double a);
524 
525  template <class T, class U = double>
526  int varTmpl = T();
527  template <>
528  double varTmpl<int> = 10.0;
529  )");
530  EXPECT_THAT(
531  getSymbols(FilePath),
532  ElementsAre(
533  AllOf(WithName("Tmpl"), WithKind(SymbolKind::Struct),
534  Children(AllOf(WithName("x"), WithKind(SymbolKind::Field)))),
535  AllOf(WithName("Tmpl<int>"), WithKind(SymbolKind::Struct),
536  Children(WithName("y"))),
537  AllOf(WithName("Tmpl<float>"), WithKind(SymbolKind::Struct),
538  Children()),
539  AllOf(WithName("Tmpl<double>"), WithKind(SymbolKind::Struct),
540  Children()),
541  AllOf(WithName("funcTmpl"), Children()),
542  AllOf(WithName("funcTmpl<int>"), Children()),
543  AllOf(WithName("varTmpl"), Children()),
544  AllOf(WithName("varTmpl<int>"), Children())));
545 }
546 
547 TEST_F(DocumentSymbolsTest, Namespaces) {
548  std::string FilePath = testPath("foo.cpp");
549  addFile(FilePath, R"cpp(
550  namespace ans1 {
551  int ai1;
552  namespace ans2 {
553  int ai2;
554  }
555  }
556  namespace {
557  void test() {}
558  }
559 
560  namespace na {
561  inline namespace nb {
562  class Foo {};
563  }
564  }
565  namespace na {
566  // This is still inlined.
567  namespace nb {
568  class Bar {};
569  }
570  }
571  )cpp");
572  EXPECT_THAT(
573  getSymbols(FilePath),
574  ElementsAreArray<::testing::Matcher<DocumentSymbol>>(
575  {AllOf(WithName("ans1"),
576  Children(AllOf(WithName("ai1"), Children()),
577  AllOf(WithName("ans2"), Children(WithName("ai2"))))),
578  AllOf(WithName("(anonymous namespace)"), Children(WithName("test"))),
579  AllOf(WithName("na"),
580  Children(AllOf(WithName("nb"), Children(WithName("Foo"))))),
581  AllOf(WithName("na"),
582  Children(AllOf(WithName("nb"), Children(WithName("Bar")))))}));
583 }
584 
585 TEST_F(DocumentSymbolsTest, Enums) {
586  std::string FilePath = testPath("foo.cpp");
587  addFile(FilePath, R"(
588  enum {
589  Red
590  };
591  enum Color {
592  Green
593  };
594  enum class Color2 {
595  Yellow
596  };
597  namespace ns {
598  enum {
599  Black
600  };
601  }
602  )");
603  EXPECT_THAT(
604  getSymbols(FilePath),
605  ElementsAre(
606  AllOf(WithName("(anonymous enum)"), Children(WithName("Red"))),
607  AllOf(WithName("Color"), Children(WithName("Green"))),
608  AllOf(WithName("Color2"), Children(WithName("Yellow"))),
609  AllOf(WithName("ns"), Children(AllOf(WithName("(anonymous enum)"),
610  Children(WithName("Black")))))));
611 }
612 
613 TEST_F(DocumentSymbolsTest, FromMacro) {
614  std::string FilePath = testPath("foo.cpp");
615  Annotations Main(R"(
616  #define FF(name) \
617  class name##_Test {};
618 
619  $expansion[[FF]](abc);
620 
621  #define FF2() \
622  class $spelling[[Test]] {};
623 
624  FF2();
625  )");
626  addFile(FilePath, Main.code());
627  EXPECT_THAT(
628  getSymbols(FilePath),
629  ElementsAre(
630  AllOf(WithName("abc_Test"), SymNameRange(Main.range("expansion"))),
631  AllOf(WithName("Test"), SymNameRange(Main.range("spelling")))));
632 }
633 
634 TEST_F(DocumentSymbolsTest, FuncTemplates) {
635  std::string FilePath = testPath("foo.cpp");
636  Annotations Source(R"cpp(
637  template <class T>
638  T foo() {}
639 
640  auto x = foo<int>();
641  auto y = foo<double>()
642  )cpp");
643  addFile(FilePath, Source.code());
644  // Make sure we only see the template declaration, not instantiations.
645  EXPECT_THAT(getSymbols(FilePath),
646  ElementsAre(WithName("foo"), WithName("x"), WithName("y")));
647 }
648 
649 TEST_F(DocumentSymbolsTest, UsingDirectives) {
650  std::string FilePath = testPath("foo.cpp");
651  Annotations Source(R"cpp(
652  namespace ns {
653  int foo;
654  }
655 
656  namespace ns_alias = ns;
657 
658  using namespace ::ns; // check we don't loose qualifiers.
659  using namespace ns_alias; // and namespace aliases.
660  )cpp");
661  addFile(FilePath, Source.code());
662  EXPECT_THAT(getSymbols(FilePath),
663  ElementsAre(WithName("ns"), WithName("ns_alias"),
664  WithName("using namespace ::ns"),
665  WithName("using namespace ns_alias")));
666 }
667 
668 TEST_F(DocumentSymbolsTest, TempSpecs) {
669  addFile("foo.cpp", R"cpp(
670  template <typename T, typename U, int X = 5> class Foo {};
671  template <typename T> class Foo<int, T> {};
672  template <> class Foo<bool, int> {};
673  template <> class Foo<bool, int, 3> {};
674  )cpp");
675  // Foo is higher ranked because of exact name match.
676  EXPECT_THAT(
677  getSymbols("foo.cpp"),
678  UnorderedElementsAre(
679  AllOf(WithName("Foo"), WithKind(SymbolKind::Class)),
680  AllOf(WithName("Foo<int, T>"), WithKind(SymbolKind::Class)),
681  AllOf(WithName("Foo<bool, int>"), WithKind(SymbolKind::Class)),
682  AllOf(WithName("Foo<bool, int, 3>"), WithKind(SymbolKind::Class))));
683 }
684 
685 TEST_F(DocumentSymbolsTest, Qualifiers) {
686  addFile("foo.cpp", R"cpp(
687  namespace foo { namespace bar {
688  struct Cls;
689 
690  int func1();
691  int func2();
692  int func3();
693  int func4();
694  }}
695 
696  struct foo::bar::Cls { };
697 
698  int foo::bar::func1() { return 10; }
699  int ::foo::bar::func2() { return 20; }
700 
701  using namespace foo;
702  int bar::func3() { return 30; }
703 
704  namespace alias = foo::bar;
705  int ::alias::func4() { return 40; }
706  )cpp");
707 
708  // All the qualifiers should be preserved exactly as written.
709  EXPECT_THAT(getSymbols("foo.cpp"),
710  UnorderedElementsAre(
711  WithName("foo"), WithName("foo::bar::Cls"),
712  WithName("foo::bar::func1"), WithName("::foo::bar::func2"),
713  WithName("using namespace foo"), WithName("bar::func3"),
714  WithName("alias"), WithName("::alias::func4")));
715 }
716 
717 TEST_F(DocumentSymbolsTest, QualifiersWithTemplateArgs) {
718  addFile("foo.cpp", R"cpp(
719  template <typename T, typename U = double> class Foo;
720 
721  template <>
722  class Foo<int, double> {
723  int method1();
724  int method2();
725  int method3();
726  };
727 
728  using int_type = int;
729 
730  // Typedefs should be preserved!
731  int Foo<int_type, double>::method1() { return 10; }
732 
733  // Default arguments should not be shown!
734  int Foo<int>::method2() { return 20; }
735 
736  using Foo_type = Foo<int>;
737  // If the whole type is aliased, this should be preserved too!
738  int Foo_type::method3() { return 30; }
739  )cpp");
740  EXPECT_THAT(
741  getSymbols("foo.cpp"),
742  UnorderedElementsAre(WithName("Foo"), WithName("Foo<int, double>"),
743  WithName("int_type"),
744  WithName("Foo<int_type, double>::method1"),
745  WithName("Foo<int>::method2"), WithName("Foo_type"),
746  WithName("Foo_type::method3")));
747 }
748 
749 } // namespace clangd
750 } // namespace clang
int Limit
MATCHER_P(Named, N, "")
void addDocument(PathRef File, StringRef Contents, WantDiagnostics WD=WantDiagnostics::Auto)
Add a File to the list of tracked C++ files or update the contents if File is already tracked...
llvm::StringRef Contents
clangd::Range range(llvm::StringRef Name="") const
Definition: Annotations.cpp:37
llvm::StringMap< std::string > Files
Definition: TestFS.h:38
llvm::Expected< std::vector< DocumentSymbol > > runDocumentSymbols(ClangdServer &Server, PathRef File)
Definition: SyncAPI.cpp:120
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:23
LLVM_NODISCARD bool blockUntilIdleForTest(llvm::Optional< double > TimeoutSeconds=10)
static Options optsForTest()
MockFSProvider FSProvider
BindArgumentKind Kind
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
std::string QName
std::string testPath(PathRef File)
Definition: TestFS.cpp:82
std::string Path
A typedef to represent a file path.
Definition: Path.h:20
virtual void onDiagnosticsReady(PathRef File, std::vector< Diag > Diagnostics)=0
Called by ClangdServer when Diagnostics for File are ready.
static constexpr llvm::StringLiteral Name
const char * testRoot()
Definition: TestFS.cpp:74
std::vector< DocumentSymbol > children
Children of this symbol, e.g. properties of a class.
Definition: Protocol.h:822
PathRef FileName
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::Expected< std::vector< SymbolInformation > > runWorkspaceSymbols(ClangdServer &Server, llvm::StringRef Query, int Limit)
Definition: SyncAPI.cpp:113
ClangdServer Server
MockCompilationDatabase CDB
CharSourceRange Range
SourceRange for the file name.
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges...
Definition: Annotations.h:23
IgnoreDiagnostics DiagConsumer
Manages a collection of source files and derived data (ASTs, indexes), and provides language-aware fe...
Definition: ClangdServer.h:80