clang-tools  14.0.0git
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 "TestTU.h"
14 #include "llvm/ADT/StringRef.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17 
18 namespace clang {
19 namespace clangd {
20 
21 namespace {
22 
23 using ::testing::AllOf;
24 using ::testing::ElementsAre;
25 using ::testing::ElementsAreArray;
26 using ::testing::Field;
27 using ::testing::IsEmpty;
28 using ::testing::UnorderedElementsAre;
29 
30 // GMock helpers for matching SymbolInfos items.
31 MATCHER_P(QName, Name, "") {
32  if (arg.containerName.empty())
33  return arg.name == Name;
34  return (arg.containerName + "::" + arg.name) == Name;
35 }
36 MATCHER_P(WithName, N, "") { return arg.name == N; }
37 MATCHER_P(WithKind, Kind, "") { return arg.kind == Kind; }
38 MATCHER_P(WithDetail, Detail, "") { return arg.detail == Detail; }
39 MATCHER_P(SymRange, Range, "") { return arg.range == Range; }
40 
41 // GMock helpers for matching DocumentSymbol.
42 MATCHER_P(SymNameRange, Range, "") { return arg.selectionRange == Range; }
43 template <class... ChildMatchers>
44 ::testing::Matcher<DocumentSymbol> Children(ChildMatchers... ChildrenM) {
45  return Field(&DocumentSymbol::children, UnorderedElementsAre(ChildrenM...));
46 }
47 
48 std::vector<SymbolInformation> getSymbols(TestTU &TU, llvm::StringRef Query,
49  int Limit = 0) {
50  auto SymbolInfos = getWorkspaceSymbols(Query, Limit, TU.index().get(),
51  testPath(TU.Filename));
52  EXPECT_TRUE(bool(SymbolInfos)) << "workspaceSymbols returned an error";
53  return *SymbolInfos;
54 }
55 
56 TEST(WorkspaceSymbols, Macros) {
57  TestTU TU;
58  TU.Code = R"cpp(
59  #define MACRO X
60  )cpp";
61 
62  // LSP's SymbolKind doesn't have a "Macro" kind, and
63  // indexSymbolKindToSymbolKind() currently maps macros
64  // to SymbolKind::String.
65  EXPECT_THAT(getSymbols(TU, "macro"),
66  ElementsAre(AllOf(QName("MACRO"), WithKind(SymbolKind::String))));
67 }
68 
69 TEST(WorkspaceSymbols, NoLocals) {
70  TestTU TU;
71  TU.Code = R"cpp(
72  void test(int FirstParam, int SecondParam) {
73  struct LocalClass {};
74  int local_var;
75  })cpp";
76  EXPECT_THAT(getSymbols(TU, "l"), ElementsAre(QName("LocalClass")));
77  EXPECT_THAT(getSymbols(TU, "p"), IsEmpty());
78 }
79 
80 TEST(WorkspaceSymbols, Globals) {
81  TestTU TU;
82  TU.AdditionalFiles["foo.h"] = R"cpp(
83  int global_var;
84 
85  int global_func();
86 
87  struct GlobalStruct {};)cpp";
88  TU.Code = R"cpp(
89  #include "foo.h"
90  )cpp";
91  EXPECT_THAT(getSymbols(TU, "global"),
92  UnorderedElementsAre(
93  AllOf(QName("GlobalStruct"), WithKind(SymbolKind::Struct)),
94  AllOf(QName("global_func"), WithKind(SymbolKind::Function)),
95  AllOf(QName("global_var"), WithKind(SymbolKind::Variable))));
96 }
97 
98 TEST(WorkspaceSymbols, Unnamed) {
99  TestTU TU;
100  TU.AdditionalFiles["foo.h"] = R"cpp(
101  struct {
102  int InUnnamed;
103  } UnnamedStruct;)cpp";
104  TU.Code = R"cpp(
105  #include "foo.h"
106  )cpp";
107  EXPECT_THAT(getSymbols(TU, "UnnamedStruct"),
108  ElementsAre(AllOf(QName("UnnamedStruct"),
109  WithKind(SymbolKind::Variable))));
110  EXPECT_THAT(getSymbols(TU, "InUnnamed"),
111  ElementsAre(AllOf(QName("(anonymous struct)::InUnnamed"),
112  WithKind(SymbolKind::Field))));
113 }
114 
115 TEST(WorkspaceSymbols, InMainFile) {
116  TestTU TU;
117  TU.Code = R"cpp(
118  int test() {}
119  static void test2() {}
120  )cpp";
121  EXPECT_THAT(getSymbols(TU, "test"),
122  ElementsAre(QName("test"), QName("test2")));
123 }
124 
125 TEST(WorkspaceSymbols, Namespaces) {
126  TestTU TU;
127  TU.AdditionalFiles["foo.h"] = R"cpp(
128  namespace ans1 {
129  int ai1;
130  namespace ans2 {
131  int ai2;
132  namespace ans3 {
133  int ai3;
134  }
135  }
136  }
137  )cpp";
138  TU.Code = R"cpp(
139  #include "foo.h"
140  )cpp";
141  EXPECT_THAT(getSymbols(TU, "a"),
142  UnorderedElementsAre(
143  QName("ans1"), QName("ans1::ai1"), QName("ans1::ans2"),
144  QName("ans1::ans2::ai2"), QName("ans1::ans2::ans3"),
145  QName("ans1::ans2::ans3::ai3")));
146  EXPECT_THAT(getSymbols(TU, "::"), ElementsAre(QName("ans1")));
147  EXPECT_THAT(getSymbols(TU, "::a"), ElementsAre(QName("ans1")));
148  EXPECT_THAT(getSymbols(TU, "ans1::"),
149  UnorderedElementsAre(QName("ans1::ai1"), QName("ans1::ans2"),
150  QName("ans1::ans2::ai2"),
151  QName("ans1::ans2::ans3"),
152  QName("ans1::ans2::ans3::ai3")));
153  EXPECT_THAT(getSymbols(TU, "ans2::"),
154  UnorderedElementsAre(QName("ans1::ans2::ai2"),
155  QName("ans1::ans2::ans3"),
156  QName("ans1::ans2::ans3::ai3")));
157  EXPECT_THAT(getSymbols(TU, "::ans1"), ElementsAre(QName("ans1")));
158  EXPECT_THAT(getSymbols(TU, "::ans1::"),
159  UnorderedElementsAre(QName("ans1::ai1"), QName("ans1::ans2")));
160  EXPECT_THAT(getSymbols(TU, "::ans1::ans2"), ElementsAre(QName("ans1::ans2")));
161  EXPECT_THAT(getSymbols(TU, "::ans1::ans2::"),
162  ElementsAre(QName("ans1::ans2::ai2"), QName("ans1::ans2::ans3")));
163 
164  // Ensure sub-sequence matching works.
165  EXPECT_THAT(getSymbols(TU, "ans1::ans3::ai"),
166  UnorderedElementsAre(QName("ans1::ans2::ans3::ai3")));
167 }
168 
169 TEST(WorkspaceSymbols, AnonymousNamespace) {
170  TestTU TU;
171  TU.Code = R"cpp(
172  namespace {
173  void test() {}
174  }
175  )cpp";
176  EXPECT_THAT(getSymbols(TU, "test"), ElementsAre(QName("test")));
177 }
178 
179 TEST(WorkspaceSymbols, MultiFile) {
180  TestTU TU;
181  TU.AdditionalFiles["foo.h"] = R"cpp(
182  int foo() {
183  }
184  )cpp";
185  TU.AdditionalFiles["foo2.h"] = R"cpp(
186  int foo2() {
187  }
188  )cpp";
189  TU.Code = R"cpp(
190  #include "foo.h"
191  #include "foo2.h"
192  )cpp";
193  EXPECT_THAT(getSymbols(TU, "foo"),
194  UnorderedElementsAre(QName("foo"), QName("foo2")));
195 }
196 
197 TEST(WorkspaceSymbols, GlobalNamespaceQueries) {
198  TestTU TU;
199  TU.AdditionalFiles["foo.h"] = R"cpp(
200  int foo() {
201  }
202  class Foo {
203  int a;
204  };
205  namespace ns {
206  int foo2() {
207  }
208  }
209  )cpp";
210  TU.Code = R"cpp(
211  #include "foo.h"
212  )cpp";
213  EXPECT_THAT(getSymbols(TU, "::"),
214  UnorderedElementsAre(
215  AllOf(QName("Foo"), WithKind(SymbolKind::Class)),
216  AllOf(QName("foo"), WithKind(SymbolKind::Function)),
217  AllOf(QName("ns"), WithKind(SymbolKind::Namespace))));
218  EXPECT_THAT(getSymbols(TU, ":"), IsEmpty());
219  EXPECT_THAT(getSymbols(TU, ""),
220  UnorderedElementsAre(QName("foo"), QName("Foo"), QName("Foo::a"),
221  QName("ns"), QName("ns::foo2")));
222 }
223 
224 TEST(WorkspaceSymbols, Enums) {
225  TestTU TU;
226  TU.AdditionalFiles["foo.h"] = R"cpp(
227  enum {
228  Red
229  };
230  enum Color {
231  Green
232  };
233  enum class Color2 {
234  Yellow
235  };
236  namespace ns {
237  enum {
238  Black
239  };
240  enum Color3 {
241  Blue
242  };
243  enum class Color4 {
244  White
245  };
246  }
247  )cpp";
248  TU.Code = R"cpp(
249  #include "foo.h"
250  )cpp";
251  EXPECT_THAT(getSymbols(TU, "Red"), ElementsAre(QName("Red")));
252  EXPECT_THAT(getSymbols(TU, "::Red"), ElementsAre(QName("Red")));
253  EXPECT_THAT(getSymbols(TU, "Green"), ElementsAre(QName("Green")));
254  EXPECT_THAT(getSymbols(TU, "Green"), ElementsAre(QName("Green")));
255  EXPECT_THAT(getSymbols(TU, "Color2::Yellow"),
256  ElementsAre(QName("Color2::Yellow")));
257  EXPECT_THAT(getSymbols(TU, "Yellow"), ElementsAre(QName("Color2::Yellow")));
258 
259  EXPECT_THAT(getSymbols(TU, "ns::Black"), ElementsAre(QName("ns::Black")));
260  EXPECT_THAT(getSymbols(TU, "ns::Blue"), ElementsAre(QName("ns::Blue")));
261  EXPECT_THAT(getSymbols(TU, "ns::Color4::White"),
262  ElementsAre(QName("ns::Color4::White")));
263 }
264 
265 TEST(WorkspaceSymbols, Ranking) {
266  TestTU TU;
267  TU.AdditionalFiles["foo.h"] = R"cpp(
268  namespace ns{}
269  void func();
270  )cpp";
271  TU.Code = R"cpp(
272  #include "foo.h"
273  )cpp";
274  EXPECT_THAT(getSymbols(TU, "::"), ElementsAre(QName("func"), QName("ns")));
275 }
276 
277 TEST(WorkspaceSymbols, RankingPartialNamespace) {
278  TestTU TU;
279  TU.Code = R"cpp(
280  namespace ns1 {
281  namespace ns2 { struct Foo {}; }
282  }
283  namespace ns2 { struct FooB {}; })cpp";
284  EXPECT_THAT(getSymbols(TU, "ns2::f"),
285  ElementsAre(QName("ns2::FooB"), QName("ns1::ns2::Foo")));
286 }
287 
288 TEST(WorkspaceSymbols, WithLimit) {
289  TestTU TU;
290  TU.AdditionalFiles["foo.h"] = R"cpp(
291  int foo;
292  int foo2;
293  )cpp";
294  TU.Code = R"cpp(
295  #include "foo.h"
296  )cpp";
297  // Foo is higher ranked because of exact name match.
298  EXPECT_THAT(getSymbols(TU, "foo"),
299  UnorderedElementsAre(
300  AllOf(QName("foo"), WithKind(SymbolKind::Variable)),
301  AllOf(QName("foo2"), WithKind(SymbolKind::Variable))));
302 
303  EXPECT_THAT(getSymbols(TU, "foo", 1), ElementsAre(QName("foo")));
304 }
305 
306 TEST(WorkspaceSymbols, TempSpecs) {
307  TestTU TU;
308  TU.ExtraArgs = {"-xc++"};
309  TU.Code = R"cpp(
310  template <typename T, typename U, int X = 5> class Foo {};
311  template <typename T> class Foo<int, T> {};
312  template <> class Foo<bool, int> {};
313  template <> class Foo<bool, int, 3> {};
314  )cpp";
315  // Foo is higher ranked because of exact name match.
316  EXPECT_THAT(
317  getSymbols(TU, "Foo"),
318  UnorderedElementsAre(
319  AllOf(QName("Foo"), WithKind(SymbolKind::Class)),
320  AllOf(QName("Foo<int, T>"), WithKind(SymbolKind::Class)),
321  AllOf(QName("Foo<bool, int>"), WithKind(SymbolKind::Class)),
322  AllOf(QName("Foo<bool, int, 3>"), WithKind(SymbolKind::Class))));
323 }
324 
325 std::vector<DocumentSymbol> getSymbols(ParsedAST AST) {
326  auto SymbolInfos = getDocumentSymbols(AST);
327  EXPECT_TRUE(bool(SymbolInfos)) << "documentSymbols returned an error";
328  return *SymbolInfos;
329 }
330 
331 TEST(DocumentSymbols, BasicSymbols) {
332  TestTU TU;
333  Annotations Main(R"(
334  class Foo;
335  class Foo {
336  Foo() {}
337  Foo(int a) {}
338  void $decl[[f]]();
339  friend void f1();
340  friend class Friend;
341  Foo& operator=(const Foo&);
342  ~Foo();
343  class Nested {
344  void f();
345  };
346  };
347  class Friend {
348  };
349 
350  void f1();
351  inline void f2() {}
352  static const int KInt = 2;
353  const char* kStr = "123";
354 
355  void f1() {}
356 
357  namespace foo {
358  // Type alias
359  typedef int int32;
360  using int32_t = int32;
361 
362  // Variable
363  int v1;
364 
365  // Namespace
366  namespace bar {
367  int v2;
368  }
369  // Namespace alias
370  namespace baz = bar;
371 
372  using bar::v2;
373  } // namespace foo
374  )");
375 
376  TU.Code = Main.code().str();
377  EXPECT_THAT(
378  getSymbols(TU.build()),
379  ElementsAreArray(
380  {AllOf(WithName("Foo"), WithKind(SymbolKind::Class),
381  WithDetail("class"), Children()),
382  AllOf(WithName("Foo"), WithKind(SymbolKind::Class),
383  WithDetail("class"),
384  Children(
385  AllOf(WithName("Foo"), WithKind(SymbolKind::Constructor),
386  WithDetail("()"), Children()),
387  AllOf(WithName("Foo"), WithKind(SymbolKind::Constructor),
388  WithDetail("(int)"), Children()),
389  AllOf(WithName("f"), WithKind(SymbolKind::Method),
390  WithDetail("void ()"), Children()),
391  AllOf(WithName("operator="), WithKind(SymbolKind::Method),
392  WithDetail("Foo &(const Foo &)"), Children()),
393  AllOf(WithName("~Foo"), WithKind(SymbolKind::Constructor),
394  WithDetail(""), Children()),
395  AllOf(WithName("Nested"), WithKind(SymbolKind::Class),
396  WithDetail("class"),
397  Children(AllOf(
398  WithName("f"), WithKind(SymbolKind::Method),
399  WithDetail("void ()"), Children()))))),
400  AllOf(WithName("Friend"), WithKind(SymbolKind::Class),
401  WithDetail("class"), Children()),
402  AllOf(WithName("f1"), WithKind(SymbolKind::Function),
403  WithDetail("void ()"), Children()),
404  AllOf(WithName("f2"), WithKind(SymbolKind::Function),
405  WithDetail("void ()"), Children()),
406  AllOf(WithName("KInt"), WithKind(SymbolKind::Variable),
407  WithDetail("const int"), Children()),
408  AllOf(WithName("kStr"), WithKind(SymbolKind::Variable),
409  WithDetail("const char *"), Children()),
410  AllOf(WithName("f1"), WithKind(SymbolKind::Function),
411  WithDetail("void ()"), Children()),
412  AllOf(
413  WithName("foo"), WithKind(SymbolKind::Namespace), WithDetail(""),
414  Children(AllOf(WithName("int32"), WithKind(SymbolKind::Class),
415  WithDetail("type alias"), Children()),
416  AllOf(WithName("int32_t"), WithKind(SymbolKind::Class),
417  WithDetail("type alias"), Children()),
418  AllOf(WithName("v1"), WithKind(SymbolKind::Variable),
419  WithDetail("int"), Children()),
420  AllOf(WithName("bar"), WithKind(SymbolKind::Namespace),
421  WithDetail(""),
422  Children(AllOf(WithName("v2"),
423  WithKind(SymbolKind::Variable),
424  WithDetail("int"), Children()))),
425  AllOf(WithName("baz"), WithKind(SymbolKind::Namespace),
426  WithDetail(""), Children()),
427  AllOf(WithName("v2"), WithKind(SymbolKind::Namespace),
428  WithDetail(""))))}));
429 }
430 
431 TEST(DocumentSymbols, DeclarationDefinition) {
432  TestTU TU;
433  Annotations Main(R"(
434  class Foo {
435  void $decl[[f]]();
436  };
437  void Foo::$def[[f]]() {
438  }
439  )");
440 
441  TU.Code = Main.code().str();
442  EXPECT_THAT(
443  getSymbols(TU.build()),
444  ElementsAre(
445  AllOf(WithName("Foo"), WithKind(SymbolKind::Class),
446  WithDetail("class"),
447  Children(AllOf(WithName("f"), WithKind(SymbolKind::Method),
448  WithDetail("void ()"),
449  SymNameRange(Main.range("decl"))))),
450  AllOf(WithName("Foo::f"), WithKind(SymbolKind::Method),
451  WithDetail("void ()"), SymNameRange(Main.range("def")))));
452 }
453 
454 TEST(DocumentSymbols, Concepts) {
455  TestTU TU;
456  TU.ExtraArgs = {"-std=c++20"};
457  TU.Code = "template <typename T> concept C = requires(T t) { t.foo(); };";
458 
459  EXPECT_THAT(getSymbols(TU.build()),
460  ElementsAre(AllOf(WithName("C"), WithDetail("concept"))));
461 }
462 
463 TEST(DocumentSymbols, ExternSymbol) {
464  TestTU TU;
465  TU.AdditionalFiles["foo.h"] = R"cpp(
466  extern int var;
467  )cpp";
468  TU.Code = R"cpp(
469  #include "foo.h"
470  )cpp";
471 
472  EXPECT_THAT(getSymbols(TU.build()), IsEmpty());
473 }
474 
475 TEST(DocumentSymbols, ExternContext) {
476  TestTU TU;
477  TU.Code = R"cpp(
478  extern "C" {
479  void foo();
480  class Foo {};
481  }
482  namespace ns {
483  extern "C" {
484  void bar();
485  class Bar {};
486  }
487  })cpp";
488 
489  EXPECT_THAT(getSymbols(TU.build()),
490  ElementsAre(WithName("foo"), WithName("Foo"),
491  AllOf(WithName("ns"),
492  Children(WithName("bar"), WithName("Bar")))));
493 }
494 
495 TEST(DocumentSymbols, ExportContext) {
496  TestTU TU;
497  TU.ExtraArgs = {"-std=c++20"};
498  TU.Code = R"cpp(
499  export module test;
500  export {
501  void foo();
502  class Foo {};
503  })cpp";
504 
505  EXPECT_THAT(getSymbols(TU.build()),
506  ElementsAre(WithName("foo"), WithName("Foo")));
507 }
508 
509 TEST(DocumentSymbols, NoLocals) {
510  TestTU TU;
511  TU.Code = R"cpp(
512  void test(int FirstParam, int SecondParam) {
513  struct LocalClass {};
514  int local_var;
515  })cpp";
516  EXPECT_THAT(getSymbols(TU.build()), ElementsAre(WithName("test")));
517 }
518 
519 TEST(DocumentSymbols, Unnamed) {
520  TestTU TU;
521  TU.Code = R"cpp(
522  struct {
523  int InUnnamed;
524  } UnnamedStruct;
525  )cpp";
526  EXPECT_THAT(
527  getSymbols(TU.build()),
528  ElementsAre(AllOf(WithName("(anonymous struct)"),
529  WithKind(SymbolKind::Struct), WithDetail("struct"),
530  Children(AllOf(WithName("InUnnamed"),
531  WithKind(SymbolKind::Field),
532  WithDetail("int"), Children()))),
533  AllOf(WithName("UnnamedStruct"),
534  WithKind(SymbolKind::Variable),
535  WithDetail("struct (unnamed)"), Children())));
536 }
537 
538 TEST(DocumentSymbols, InHeaderFile) {
539  TestTU TU;
540  TU.AdditionalFiles["bar.h"] = R"cpp(
541  int foo() {
542  }
543  )cpp";
544  TU.Code = R"cpp(
545  int i; // declaration to finish preamble
546  #include "bar.h"
547  int test() {
548  }
549  )cpp";
550  EXPECT_THAT(getSymbols(TU.build()),
551  ElementsAre(WithName("i"), WithName("test")));
552 }
553 
554 TEST(DocumentSymbols, Template) {
555  TestTU TU;
556  TU.Code = R"(
557  template <class T> struct Tmpl {T x = 0;};
558  template <> struct Tmpl<int> {
559  int y = 0;
560  };
561  extern template struct Tmpl<float>;
562  template struct Tmpl<double>;
563 
564  template <class T, class U, class Z = float>
565  int funcTmpl(U a);
566  template <>
567  int funcTmpl<int>(double a);
568 
569  template <class T, class U = double>
570  int varTmpl = T();
571  template <>
572  double varTmpl<int> = 10.0;
573  )";
574  EXPECT_THAT(
575  getSymbols(TU.build()),
576  ElementsAre(
577  AllOf(WithName("Tmpl"), WithKind(SymbolKind::Struct),
578  WithDetail("template struct"),
579  Children(AllOf(WithName("x"), WithKind(SymbolKind::Field),
580  WithDetail("T")))),
581  AllOf(WithName("Tmpl<int>"), WithKind(SymbolKind::Struct),
582  WithDetail("struct"),
583  Children(AllOf(WithName("y"), WithDetail("int")))),
584  AllOf(WithName("Tmpl<float>"), WithKind(SymbolKind::Struct),
585  WithDetail("struct"), Children()),
586  AllOf(WithName("Tmpl<double>"), WithKind(SymbolKind::Struct),
587  WithDetail("struct"), Children()),
588  AllOf(WithName("funcTmpl"), WithDetail("template int (U)"),
589  Children()),
590  AllOf(WithName("funcTmpl<int>"), WithDetail("int (double)"),
591  Children()),
592  AllOf(WithName("varTmpl"), WithDetail("template int"), Children()),
593  AllOf(WithName("varTmpl<int>"), WithDetail("double"), Children())));
594 }
595 
596 TEST(DocumentSymbols, Namespaces) {
597  TestTU TU;
598  TU.Code = R"cpp(
599  namespace ans1 {
600  int ai1;
601  namespace ans2 {
602  int ai2;
603  }
604  }
605  namespace {
606  void test() {}
607  }
608 
609  namespace na {
610  inline namespace nb {
611  class Foo {};
612  }
613  }
614  namespace na {
615  // This is still inlined.
616  namespace nb {
617  class Bar {};
618  }
619  }
620  )cpp";
621  EXPECT_THAT(
622  getSymbols(TU.build()),
623  ElementsAreArray<::testing::Matcher<DocumentSymbol>>(
624  {AllOf(WithName("ans1"),
625  Children(AllOf(WithName("ai1"), Children()),
626  AllOf(WithName("ans2"), Children(WithName("ai2"))))),
627  AllOf(WithName("(anonymous namespace)"), Children(WithName("test"))),
628  AllOf(WithName("na"),
629  Children(AllOf(WithName("nb"), Children(WithName("Foo"))))),
630  AllOf(WithName("na"),
631  Children(AllOf(WithName("nb"), Children(WithName("Bar")))))}));
632 }
633 
634 TEST(DocumentSymbols, Enums) {
635  TestTU TU;
636  TU.Code = R"(
637  enum {
638  Red
639  };
640  enum Color {
641  Green
642  };
643  enum class Color2 {
644  Yellow
645  };
646  namespace ns {
647  enum {
648  Black
649  };
650  }
651  )";
652  EXPECT_THAT(
653  getSymbols(TU.build()),
654  ElementsAre(
655  AllOf(WithName("(anonymous enum)"), WithDetail("enum"),
656  Children(AllOf(WithName("Red"), WithDetail("(unnamed)")))),
657  AllOf(WithName("Color"), WithDetail("enum"),
658  Children(AllOf(WithName("Green"), WithDetail("Color")))),
659  AllOf(WithName("Color2"), WithDetail("enum"),
660  Children(AllOf(WithName("Yellow"), WithDetail("Color2")))),
661  AllOf(WithName("ns"),
662  Children(AllOf(WithName("(anonymous enum)"), WithDetail("enum"),
663  Children(AllOf(WithName("Black"),
664  WithDetail("(unnamed)"))))))));
665 }
666 
667 TEST(DocumentSymbols, Macro) {
668  struct Test {
669  const char *Code;
670  testing::Matcher<DocumentSymbol> Matcher;
671  } Tests[] = {
672  {
673  R"cpp(
674  // Basic macro that generates symbols.
675  #define DEFINE_FLAG(X) bool FLAGS_##X; bool FLAGS_no##X
676  DEFINE_FLAG(pretty);
677  )cpp",
678  AllOf(WithName("DEFINE_FLAG"), WithDetail("(pretty)"),
679  Children(WithName("FLAGS_pretty"), WithName("FLAGS_nopretty"))),
680  },
681  {
682  R"cpp(
683  // Hierarchy is determined by primary (name) location.
684  #define ID(X) X
685  namespace ID(ns) { int ID(y); }
686  )cpp",
687  AllOf(WithName("ID"), WithDetail("(ns)"),
688  Children(AllOf(WithName("ns"),
689  Children(AllOf(WithName("ID"), WithDetail("(y)"),
690  Children(WithName("y"))))))),
691  },
692  {
693  R"cpp(
694  // More typical example where macro only generates part of a decl.
695  #define TEST(A, B) class A##_##B { void go(); }; void A##_##B::go()
696  TEST(DocumentSymbols, Macro) { }
697  )cpp",
698  AllOf(WithName("TEST"), WithDetail("(DocumentSymbols, Macro)"),
699  Children(AllOf(WithName("DocumentSymbols_Macro"),
700  Children(WithName("go"))),
701  WithName("DocumentSymbols_Macro::go"))),
702  },
703  {
704  R"cpp(
705  // Nested macros.
706  #define NAMESPACE(NS, BODY) namespace NS { BODY }
707  NAMESPACE(a, NAMESPACE(b, int x;))
708  )cpp",
709  AllOf(
710  WithName("NAMESPACE"), WithDetail("(a, NAMESPACE(b, int x;))"),
711  Children(AllOf(
712  WithName("a"),
713  Children(AllOf(WithName("NAMESPACE"),
714  // FIXME: nested expansions not in TokenBuffer
715  WithDetail(""),
716  Children(AllOf(WithName("b"),
717  Children(WithName("x"))))))))),
718  },
719  {
720  R"cpp(
721  // Macro invoked from body is not exposed.
722  #define INNER(X) int X
723  #define OUTER(X) INNER(X)
724  OUTER(foo);
725  )cpp",
726  AllOf(WithName("OUTER"), WithDetail("(foo)"),
727  Children(WithName("foo"))),
728  },
729  };
730  for (const Test &T : Tests) {
731  auto TU = TestTU::withCode(T.Code);
732  EXPECT_THAT(getSymbols(TU.build()), ElementsAre(T.Matcher)) << T.Code;
733  }
734 }
735 
736 TEST(DocumentSymbols, RangeFromMacro) {
737  TestTU TU;
738  Annotations Main(R"(
739  #define FF(name) \
740  class name##_Test {};
741 
742  $expansion1[[FF]](abc);
743 
744  #define FF2() \
745  class Test {}
746 
747  $expansion2parens[[$expansion2[[FF2]]()]];
748 
749  #define FF3() \
750  void waldo()
751 
752  $fullDef[[FF3() {
753  int var = 42;
754  }]]
755  )");
756  TU.Code = Main.code().str();
757  EXPECT_THAT(
758  getSymbols(TU.build()),
759  ElementsAre(
760  AllOf(WithName("FF"), WithDetail("(abc)"),
761  Children(AllOf(WithName("abc_Test"), WithDetail("class"),
762  SymNameRange(Main.range("expansion1"))))),
763  AllOf(WithName("FF2"), WithDetail("()"),
764  SymNameRange(Main.range("expansion2")),
765  SymRange(Main.range("expansion2parens")),
766  Children(AllOf(WithName("Test"), WithDetail("class"),
767  SymNameRange(Main.range("expansion2"))))),
768  AllOf(WithName("FF3"), WithDetail("()"),
769  SymRange(Main.range("fullDef")),
770  Children(AllOf(WithName("waldo"), WithDetail("void ()"),
771  SymRange(Main.range("fullDef")))))));
772 }
773 
774 TEST(DocumentSymbols, FuncTemplates) {
775  TestTU TU;
776  Annotations Source(R"cpp(
777  template <class T>
778  T foo() {}
779 
780  auto x = foo<int>();
781  auto y = foo<double>();
782  )cpp");
783  TU.Code = Source.code().str();
784  // Make sure we only see the template declaration, not instantiations.
785  EXPECT_THAT(getSymbols(TU.build()),
786  ElementsAre(AllOf(WithName("foo"), WithDetail("template T ()")),
787  AllOf(WithName("x"), WithDetail("int")),
788  AllOf(WithName("y"), WithDetail("double"))));
789 }
790 
791 TEST(DocumentSymbols, UsingDirectives) {
792  TestTU TU;
793  Annotations Source(R"cpp(
794  namespace ns {
795  int foo;
796  }
797 
798  namespace ns_alias = ns;
799 
800  using namespace ::ns; // check we don't loose qualifiers.
801  using namespace ns_alias; // and namespace aliases.
802  )cpp");
803  TU.Code = Source.code().str();
804  EXPECT_THAT(getSymbols(TU.build()),
805  ElementsAre(WithName("ns"), WithName("ns_alias"),
806  WithName("using namespace ::ns"),
807  WithName("using namespace ns_alias")));
808 }
809 
810 TEST(DocumentSymbols, TempSpecs) {
811  TestTU TU;
812  TU.Code = R"cpp(
813  template <typename T, typename U, int X = 5> class Foo {};
814  template <typename T> class Foo<int, T> {};
815  template <> class Foo<bool, int> {};
816  template <> class Foo<bool, int, 3> {};
817  )cpp";
818  // Foo is higher ranked because of exact name match.
819  EXPECT_THAT(getSymbols(TU.build()),
820  UnorderedElementsAre(
821  AllOf(WithName("Foo"), WithKind(SymbolKind::Class),
822  WithDetail("template class")),
823  AllOf(WithName("Foo<int, T>"), WithKind(SymbolKind::Class),
824  WithDetail("template class")),
825  AllOf(WithName("Foo<bool, int>"), WithKind(SymbolKind::Class),
826  WithDetail("class")),
827  AllOf(WithName("Foo<bool, int, 3>"),
828  WithKind(SymbolKind::Class), WithDetail("class"))));
829 }
830 
831 TEST(DocumentSymbols, Qualifiers) {
832  TestTU TU;
833  TU.Code = R"cpp(
834  namespace foo { namespace bar {
835  struct Cls;
836 
837  int func1();
838  int func2();
839  int func3();
840  int func4();
841  }}
842 
843  struct foo::bar::Cls { };
844 
845  int foo::bar::func1() { return 10; }
846  int ::foo::bar::func2() { return 20; }
847 
848  using namespace foo;
849  int bar::func3() { return 30; }
850 
851  namespace alias = foo::bar;
852  int ::alias::func4() { return 40; }
853  )cpp";
854 
855  // All the qualifiers should be preserved exactly as written.
856  EXPECT_THAT(getSymbols(TU.build()),
857  UnorderedElementsAre(
858  WithName("foo"), WithName("foo::bar::Cls"),
859  WithName("foo::bar::func1"), WithName("::foo::bar::func2"),
860  WithName("using namespace foo"), WithName("bar::func3"),
861  WithName("alias"), WithName("::alias::func4")));
862 }
863 
864 TEST(DocumentSymbols, QualifiersWithTemplateArgs) {
865  TestTU TU;
866  TU.Code = R"cpp(
867  template <typename T, typename U = double> class Foo;
868 
869  template <>
870  class Foo<int, double> {
871  int method1();
872  int method2();
873  int method3();
874  };
875 
876  using int_type = int;
877 
878  // Typedefs should be preserved!
879  int Foo<int_type, double>::method1() { return 10; }
880 
881  // Default arguments should not be shown!
882  int Foo<int>::method2() { return 20; }
883 
884  using Foo_type = Foo<int>;
885  // If the whole type is aliased, this should be preserved too!
886  int Foo_type::method3() { return 30; }
887  )cpp";
888  EXPECT_THAT(getSymbols(TU.build()),
889  UnorderedElementsAre(
890  AllOf(WithName("Foo"), WithDetail("template class")),
891  AllOf(WithName("Foo<int, double>"), WithDetail("class")),
892  AllOf(WithName("int_type"), WithDetail("type alias")),
893  AllOf(WithName("Foo<int_type, double>::method1"),
894  WithDetail("int ()")),
895  AllOf(WithName("Foo<int>::method2"), WithDetail("int ()")),
896  AllOf(WithName("Foo_type"), WithDetail("type alias")),
897  AllOf(WithName("Foo_type::method3"), WithDetail("int ()"))));
898 }
899 
900 TEST(DocumentSymbolsTest, Ranges) {
901  TestTU TU;
902  Annotations Main(R"(
903  $foo[[int foo(bool Argument) {
904  return 42;
905  }]]
906 
907  $variable[[char GLOBAL_VARIABLE]];
908 
909  $ns[[namespace ns {
910  $bar[[class Bar {
911  public:
912  $ctor[[Bar() {}]]
913  $dtor[[~Bar()]];
914 
915  private:
916  $field[[unsigned Baz]];
917 
918  $getbaz[[unsigned getBaz() { return Baz; }]]
919  }]];
920  }]] // namespace ns
921 
922  $forwardclass[[class ForwardClassDecl]];
923 
924  $struct[[struct StructDefinition {
925  $structfield[[int *Pointer = nullptr]];
926  }]];
927  $forwardstruct[[struct StructDeclaration]];
928 
929  $forwardfunc[[void forwardFunctionDecl(int Something)]];
930  )");
931  TU.Code = Main.code().str();
932  EXPECT_THAT(
933  getSymbols(TU.build()),
934  UnorderedElementsAre(
935  AllOf(WithName("foo"), WithKind(SymbolKind::Function),
936  WithDetail("int (bool)"), SymRange(Main.range("foo"))),
937  AllOf(WithName("GLOBAL_VARIABLE"), WithKind(SymbolKind::Variable),
938  WithDetail("char"), SymRange(Main.range("variable"))),
939  AllOf(
940  WithName("ns"), WithKind(SymbolKind::Namespace),
941  SymRange(Main.range("ns")),
942  Children(AllOf(
943  WithName("Bar"), WithKind(SymbolKind::Class),
944  WithDetail("class"), SymRange(Main.range("bar")),
945  Children(
946  AllOf(WithName("Bar"), WithKind(SymbolKind::Constructor),
947  WithDetail("()"), SymRange(Main.range("ctor"))),
948  AllOf(WithName("~Bar"), WithKind(SymbolKind::Constructor),
949  WithDetail(""), SymRange(Main.range("dtor"))),
950  AllOf(WithName("Baz"), WithKind(SymbolKind::Field),
951  WithDetail("unsigned int"),
952  SymRange(Main.range("field"))),
953  AllOf(WithName("getBaz"), WithKind(SymbolKind::Method),
954  WithDetail("unsigned int ()"),
955  SymRange(Main.range("getbaz"))))))),
956  AllOf(WithName("ForwardClassDecl"), WithKind(SymbolKind::Class),
957  WithDetail("class"), SymRange(Main.range("forwardclass"))),
958  AllOf(WithName("StructDefinition"), WithKind(SymbolKind::Struct),
959  WithDetail("struct"), SymRange(Main.range("struct")),
960  Children(AllOf(WithName("Pointer"), WithKind(SymbolKind::Field),
961  WithDetail("int *"),
962  SymRange(Main.range("structfield"))))),
963  AllOf(WithName("StructDeclaration"), WithKind(SymbolKind::Struct),
964  WithDetail("struct"), SymRange(Main.range("forwardstruct"))),
965  AllOf(WithName("forwardFunctionDecl"), WithKind(SymbolKind::Function),
966  WithDetail("void (int)"),
967  SymRange(Main.range("forwardfunc")))));
968 }
969 
970 TEST(DocumentSymbolsTest, DependentType) {
971  TestTU TU;
972  TU.Code = R"(
973  template <typename T> auto plus(T x, T y) -> decltype(x + y) { return x + y; }
974 
975  template <typename Key, typename Value> class Pair {};
976 
977  template <typename Key, typename Value>
978  struct Context : public Pair<Key, Value> {
979  using Pair<Key, Value>::Pair;
980  };
981  )";
982  EXPECT_THAT(
983  getSymbols(TU.build()),
984  ElementsAre(
985  AllOf(WithName("plus"),
986  WithDetail("template auto (T, T) -> decltype(x + y)")),
987  AllOf(WithName("Pair"), WithDetail("template class")),
988  AllOf(WithName("Context"), WithDetail("template struct"),
989  Children(AllOf(
990  WithName("Pair<type-parameter-0-0, type-parameter-0-1>"),
991  WithDetail("<dependent type>"))))));
992 }
993 
994 TEST(DocumentSymbolsTest, ObjCCategoriesAndClassExtensions) {
995  TestTU TU;
996  TU.ExtraArgs = {"-xobjective-c++", "-Wno-objc-root-class"};
997  Annotations Main(R"cpp(
998  $Cat[[@interface Cat
999  + (id)sharedCat;
1000  @end]]
1001  $SneakyCat[[@interface Cat (Sneaky)
1002  - (id)sneak:(id)behavior;
1003  @end]]
1004 
1005  $MeowCat[[@interface Cat ()
1006  - (void)meow;
1007  @end]]
1008  $PurCat[[@interface Cat ()
1009  - (void)pur;
1010  @end]]
1011  )cpp");
1012  TU.Code = Main.code().str();
1013  EXPECT_THAT(
1014  getSymbols(TU.build()),
1015  ElementsAre(
1016  AllOf(WithName("Cat"), SymRange(Main.range("Cat")),
1017  Children(AllOf(WithName("+sharedCat"),
1018  WithKind(SymbolKind::Method)))),
1019  AllOf(WithName("Cat(Sneaky)"), SymRange(Main.range("SneakyCat")),
1020  Children(
1021  AllOf(WithName("-sneak:"), WithKind(SymbolKind::Method)))),
1022  AllOf(
1023  WithName("Cat()"), SymRange(Main.range("MeowCat")),
1024  Children(AllOf(WithName("-meow"), WithKind(SymbolKind::Method)))),
1025  AllOf(WithName("Cat()"), SymRange(Main.range("PurCat")),
1026  Children(
1027  AllOf(WithName("-pur"), WithKind(SymbolKind::Method))))));
1028 }
1029 
1030 TEST(DocumentSymbolsTest, PragmaMarkGroups) {
1031  TestTU TU;
1032  TU.ExtraArgs = {"-xobjective-c++", "-Wno-objc-root-class"};
1033  Annotations Main(R"cpp(
1034  $DogDef[[@interface Dog
1035  @end]]
1036 
1037  $DogImpl[[@implementation Dog
1038 
1039  + (id)sharedDoggo { return 0; }
1040 
1041  #pragma $Overrides[[mark - Overrides
1042 
1043  - (id)init {
1044  return self;
1045  }
1046  - (void)bark {}]]
1047 
1048  #pragma $Specifics[[mark - Dog Specifics
1049 
1050  - (int)isAGoodBoy {
1051  return 1;
1052  }]]
1053  @]]end // FIXME: Why doesn't this include the 'end'?
1054 
1055  #pragma $End[[mark - End
1056 ]]
1057  )cpp");
1058  TU.Code = Main.code().str();
1059  EXPECT_THAT(
1060  getSymbols(TU.build()),
1061  UnorderedElementsAre(
1062  AllOf(WithName("Dog"), SymRange(Main.range("DogDef"))),
1063  AllOf(WithName("Dog"), SymRange(Main.range("DogImpl")),
1064  Children(AllOf(WithName("+sharedDoggo"),
1065  WithKind(SymbolKind::Method)),
1066  AllOf(WithName("Overrides"),
1067  SymRange(Main.range("Overrides")),
1068  Children(AllOf(WithName("-init"),
1069  WithKind(SymbolKind::Method)),
1070  AllOf(WithName("-bark"),
1071  WithKind(SymbolKind::Method)))),
1072  AllOf(WithName("Dog Specifics"),
1073  SymRange(Main.range("Specifics")),
1074  Children(AllOf(WithName("-isAGoodBoy"),
1075  WithKind(SymbolKind::Method)))))),
1076  AllOf(WithName("End"), SymRange(Main.range("End")))));
1077 }
1078 
1079 TEST(DocumentSymbolsTest, PragmaMarkGroupsNesting) {
1080  TestTU TU;
1081  TU.ExtraArgs = {"-xobjective-c++", "-Wno-objc-root-class"};
1082  Annotations Main(R"cpp(
1083  #pragma mark - Foo
1084  struct Foo {
1085  #pragma mark - Bar
1086  void bar() {
1087  #pragma mark - NotTopDecl
1088  }
1089  };
1090  void bar() {}
1091  )cpp");
1092  TU.Code = Main.code().str();
1093  EXPECT_THAT(
1094  getSymbols(TU.build()),
1095  UnorderedElementsAre(AllOf(
1096  WithName("Foo"),
1097  Children(AllOf(WithName("Foo"),
1098  Children(AllOf(WithName("Bar"),
1099  Children(AllOf(WithName("bar"),
1100  Children(WithName(
1101  "NotTopDecl"))))))),
1102  WithName("bar")))));
1103 }
1104 
1105 TEST(DocumentSymbolsTest, PragmaMarkGroupsNoNesting) {
1106  TestTU TU;
1107  TU.ExtraArgs = {"-xobjective-c++", "-Wno-objc-root-class"};
1108  Annotations Main(R"cpp(
1109  #pragma mark Helpers
1110  void helpA(id obj) {}
1111 
1112  #pragma mark -
1113  #pragma mark Core
1114 
1115  void coreMethod() {}
1116  )cpp");
1117  TU.Code = Main.code().str();
1118  EXPECT_THAT(getSymbols(TU.build()),
1119  UnorderedElementsAre(WithName("Helpers"), WithName("helpA"),
1120  WithName("(unnamed group)"),
1121  WithName("Core"), WithName("coreMethod")));
1122 }
1123 
1124 } // namespace
1125 } // namespace clangd
1126 } // namespace clang
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::clangd::SymbolKind::Field
@ Field
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:751
clang::clangd::testPath
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:82
Macros
llvm::DenseSet< FileID > Macros
Definition: IncludeCleaner.cpp:107
clang::clangd::CompletionItemKind::Field
@ Field
TestTU.h
Kind
BindArgumentKind Kind
Definition: AvoidBindCheck.cpp:59
FindSymbols.h
clang::clangd::SymbolKind::Class
@ Class
clang::clangd::DocumentSymbol::children
std::vector< DocumentSymbol > children
Children of this symbol, e.g. properties of a class.
Definition: Protocol.h:1030
Children
std::vector< std::unique_ptr< HTMLNode > > Children
Definition: HTMLGenerator.cpp:91
Code
std::string Code
Definition: FindTargetTests.cpp:67
clang::clangd::SymbolKind::Method
@ Method
clang::clangd::SymbolKind::Function
@ Function
TestFS.h
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:28
SyncAPI.h
clang::clangd::TestTU::withCode
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:37
Annotations.h
clang::clangd::getDocumentSymbols
llvm::Expected< std::vector< DocumentSymbol > > getDocumentSymbols(ParsedAST &AST)
Retrieves the symbols contained in the "main file" section of an AST in the same order that they appe...
Definition: FindSymbols.cpp:657
clang::clangd::getWorkspaceSymbols
llvm::Expected< std::vector< SymbolInformation > > getWorkspaceSymbols(llvm::StringRef Query, int Limit, const SymbolIndex *const Index, llvm::StringRef HintPath)
Searches for the symbols matching Query.
Definition: FindSymbols.cpp:88
clang::clangd::SymbolKind::Variable
@ Variable
clang::clangd::MATCHER_P
MATCHER_P(Named, N, "")
Definition: BackgroundIndexTests.cpp:31
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
ClangdServer.h
clang::clangd::SymbolKind::Namespace
@ Namespace
clang::clangd::SymbolKind::Constructor
@ Constructor
clang::clangd::SymbolKind::String
@ String
clang::clangd::SymbolKind::Struct
@ Struct