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 {
onDiagnosticsReady(PathRef File,std::vector<Diag> Diagnostics)29   void onDiagnosticsReady(PathRef File,
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>
Children(ChildMatchers...ChildrenM)46 ::testing::Matcher<DocumentSymbol> Children(ChildMatchers... ChildrenM) {
47   return Field(&DocumentSymbol::children, ElementsAre(ChildrenM...));
48 }
49 
optsForTests()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:
WorkspaceSymbolsTest()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 
getSymbols(llvm::StringRef Query)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 
addFile(llvm::StringRef FileName,llvm::StringRef Contents)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 
TEST_F(WorkspaceSymbolsTest,Macros)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 
TEST_F(WorkspaceSymbolsTest,NoLocals)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 
TEST_F(WorkspaceSymbolsTest,Globals)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 
TEST_F(WorkspaceSymbolsTest,Unnamed)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 
TEST_F(WorkspaceSymbolsTest,InMainFile)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 
TEST_F(WorkspaceSymbolsTest,Namespaces)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 
TEST_F(WorkspaceSymbolsTest,AnonymousNamespace)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 
TEST_F(WorkspaceSymbolsTest,MultiFile)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 
TEST_F(WorkspaceSymbolsTest,GlobalNamespaceQueries)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 
TEST_F(WorkspaceSymbolsTest,Enums)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 
TEST_F(WorkspaceSymbolsTest,Ranking)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 
TEST_F(WorkspaceSymbolsTest,WithLimit)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 
TEST_F(WorkspaceSymbolsTest,TempSpecs)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:
DocumentSymbolsTest()319   DocumentSymbolsTest()
320       : Server(CDB, FSProvider, DiagConsumer, optsForTests()) {}
321 
322 protected:
323   MockFSProvider FSProvider;
324   MockCompilationDatabase CDB;
325   IgnoreDiagnostics DiagConsumer;
326   ClangdServer Server;
327 
getSymbols(PathRef File)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 
addFile(llvm::StringRef FilePath,llvm::StringRef Contents)335   void addFile(llvm::StringRef FilePath, llvm::StringRef Contents) {
336     FSProvider.Files[FilePath] = Contents;
337     Server.addDocument(FilePath, Contents);
338   }
339 };
340 } // namespace
341 
TEST_F(DocumentSymbolsTest,BasicSymbols)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 
TEST_F(DocumentSymbolsTest,DeclarationDefinition)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 
TEST_F(DocumentSymbolsTest,Concepts)452 TEST_F(DocumentSymbolsTest, Concepts) {
453   CDB.ExtraClangFlags = {"-std=c++2a"};
454   std::string FilePath = testPath("foo.cpp");
455   addFile(FilePath,
456           "template <typename T> concept C = requires(T t) { t.foo(); };");
457 
458   EXPECT_THAT(getSymbols(FilePath), ElementsAre(WithName("C")));
459 }
460 
TEST_F(DocumentSymbolsTest,ExternSymbol)461 TEST_F(DocumentSymbolsTest, ExternSymbol) {
462   std::string FilePath = testPath("foo.cpp");
463   addFile(testPath("foo.h"), R"cpp(
464       extern int var;
465       )cpp");
466   addFile(FilePath, R"cpp(
467       #include "foo.h"
468       )cpp");
469 
470   EXPECT_THAT(getSymbols(FilePath), IsEmpty());
471 }
472 
TEST_F(DocumentSymbolsTest,NoLocals)473 TEST_F(DocumentSymbolsTest, NoLocals) {
474   std::string FilePath = testPath("foo.cpp");
475   addFile(FilePath,
476           R"cpp(
477       void test(int FirstParam, int SecondParam) {
478         struct LocalClass {};
479         int local_var;
480       })cpp");
481   EXPECT_THAT(getSymbols(FilePath), ElementsAre(WithName("test")));
482 }
483 
TEST_F(DocumentSymbolsTest,Unnamed)484 TEST_F(DocumentSymbolsTest, Unnamed) {
485   std::string FilePath = testPath("foo.h");
486   addFile(FilePath,
487           R"cpp(
488       struct {
489         int InUnnamed;
490       } UnnamedStruct;
491       )cpp");
492   EXPECT_THAT(
493       getSymbols(FilePath),
494       ElementsAre(
495           AllOf(WithName("(anonymous struct)"), WithKind(SymbolKind::Struct),
496                 Children(AllOf(WithName("InUnnamed"),
497                                WithKind(SymbolKind::Field), Children()))),
498           AllOf(WithName("UnnamedStruct"), WithKind(SymbolKind::Variable),
499                 Children())));
500 }
501 
TEST_F(DocumentSymbolsTest,InHeaderFile)502 TEST_F(DocumentSymbolsTest, InHeaderFile) {
503   addFile(testPath("bar.h"), R"cpp(
504       int foo() {
505       }
506       )cpp");
507   std::string FilePath = testPath("foo.h");
508   addFile(FilePath, R"cpp(
509       #include "bar.h"
510       int test() {
511       }
512       )cpp");
513   addFile(testPath("foo.cpp"), R"cpp(
514       #include "foo.h"
515       )cpp");
516   EXPECT_THAT(getSymbols(FilePath), ElementsAre(WithName("test")));
517 }
518 
TEST_F(DocumentSymbolsTest,Template)519 TEST_F(DocumentSymbolsTest, Template) {
520   std::string FilePath = testPath("foo.cpp");
521   addFile(FilePath, R"(
522     template <class T> struct Tmpl {T x = 0;};
523     template <> struct Tmpl<int> {
524       int y = 0;
525     };
526     extern template struct Tmpl<float>;
527     template struct Tmpl<double>;
528 
529     template <class T, class U, class Z = float>
530     int funcTmpl(U a);
531     template <>
532     int funcTmpl<int>(double a);
533 
534     template <class T, class U = double>
535     int varTmpl = T();
536     template <>
537     double varTmpl<int> = 10.0;
538   )");
539   EXPECT_THAT(
540       getSymbols(FilePath),
541       ElementsAre(
542           AllOf(WithName("Tmpl"), WithKind(SymbolKind::Struct),
543                 Children(AllOf(WithName("x"), WithKind(SymbolKind::Field)))),
544           AllOf(WithName("Tmpl<int>"), WithKind(SymbolKind::Struct),
545                 Children(WithName("y"))),
546           AllOf(WithName("Tmpl<float>"), WithKind(SymbolKind::Struct),
547                 Children()),
548           AllOf(WithName("Tmpl<double>"), WithKind(SymbolKind::Struct),
549                 Children()),
550           AllOf(WithName("funcTmpl"), Children()),
551           AllOf(WithName("funcTmpl<int>"), Children()),
552           AllOf(WithName("varTmpl"), Children()),
553           AllOf(WithName("varTmpl<int>"), Children())));
554 }
555 
TEST_F(DocumentSymbolsTest,Namespaces)556 TEST_F(DocumentSymbolsTest, Namespaces) {
557   std::string FilePath = testPath("foo.cpp");
558   addFile(FilePath, R"cpp(
559       namespace ans1 {
560         int ai1;
561       namespace ans2 {
562         int ai2;
563       }
564       }
565       namespace {
566       void test() {}
567       }
568 
569       namespace na {
570       inline namespace nb {
571       class Foo {};
572       }
573       }
574       namespace na {
575       // This is still inlined.
576       namespace nb {
577       class Bar {};
578       }
579       }
580       )cpp");
581   EXPECT_THAT(
582       getSymbols(FilePath),
583       ElementsAreArray<::testing::Matcher<DocumentSymbol>>(
584           {AllOf(WithName("ans1"),
585                  Children(AllOf(WithName("ai1"), Children()),
586                           AllOf(WithName("ans2"), Children(WithName("ai2"))))),
587            AllOf(WithName("(anonymous namespace)"), Children(WithName("test"))),
588            AllOf(WithName("na"),
589                  Children(AllOf(WithName("nb"), Children(WithName("Foo"))))),
590            AllOf(WithName("na"),
591                  Children(AllOf(WithName("nb"), Children(WithName("Bar")))))}));
592 }
593 
TEST_F(DocumentSymbolsTest,Enums)594 TEST_F(DocumentSymbolsTest, Enums) {
595   std::string FilePath = testPath("foo.cpp");
596   addFile(FilePath, R"(
597       enum {
598         Red
599       };
600       enum Color {
601         Green
602       };
603       enum class Color2 {
604         Yellow
605       };
606       namespace ns {
607       enum {
608         Black
609       };
610       }
611     )");
612   EXPECT_THAT(
613       getSymbols(FilePath),
614       ElementsAre(
615           AllOf(WithName("(anonymous enum)"), Children(WithName("Red"))),
616           AllOf(WithName("Color"), Children(WithName("Green"))),
617           AllOf(WithName("Color2"), Children(WithName("Yellow"))),
618           AllOf(WithName("ns"), Children(AllOf(WithName("(anonymous enum)"),
619                                                Children(WithName("Black")))))));
620 }
621 
TEST_F(DocumentSymbolsTest,FromMacro)622 TEST_F(DocumentSymbolsTest, FromMacro) {
623   std::string FilePath = testPath("foo.cpp");
624   Annotations Main(R"(
625     #define FF(name) \
626       class name##_Test {};
627 
628     $expansion[[FF]](abc);
629 
630     #define FF2() \
631       class $spelling[[Test]] {};
632 
633     FF2();
634   )");
635   addFile(FilePath, Main.code());
636   EXPECT_THAT(
637       getSymbols(FilePath),
638       ElementsAre(
639           AllOf(WithName("abc_Test"), SymNameRange(Main.range("expansion"))),
640           AllOf(WithName("Test"), SymNameRange(Main.range("spelling")))));
641 }
642 
TEST_F(DocumentSymbolsTest,FuncTemplates)643 TEST_F(DocumentSymbolsTest, FuncTemplates) {
644   std::string FilePath = testPath("foo.cpp");
645   Annotations Source(R"cpp(
646     template <class T>
647     T foo() {}
648 
649     auto x = foo<int>();
650     auto y = foo<double>()
651   )cpp");
652   addFile(FilePath, Source.code());
653   // Make sure we only see the template declaration, not instantiations.
654   EXPECT_THAT(getSymbols(FilePath),
655               ElementsAre(WithName("foo"), WithName("x"), WithName("y")));
656 }
657 
TEST_F(DocumentSymbolsTest,UsingDirectives)658 TEST_F(DocumentSymbolsTest, UsingDirectives) {
659   std::string FilePath = testPath("foo.cpp");
660   Annotations Source(R"cpp(
661     namespace ns {
662       int foo;
663     }
664 
665     namespace ns_alias = ns;
666 
667     using namespace ::ns;     // check we don't loose qualifiers.
668     using namespace ns_alias; // and namespace aliases.
669   )cpp");
670   addFile(FilePath, Source.code());
671   EXPECT_THAT(getSymbols(FilePath),
672               ElementsAre(WithName("ns"), WithName("ns_alias"),
673                           WithName("using namespace ::ns"),
674                           WithName("using namespace ns_alias")));
675 }
676 
TEST_F(DocumentSymbolsTest,TempSpecs)677 TEST_F(DocumentSymbolsTest, TempSpecs) {
678   addFile("foo.cpp", R"cpp(
679       template <typename T, typename U, int X = 5> class Foo {};
680       template <typename T> class Foo<int, T> {};
681       template <> class Foo<bool, int> {};
682       template <> class Foo<bool, int, 3> {};
683       )cpp");
684   // Foo is higher ranked because of exact name match.
685   EXPECT_THAT(
686       getSymbols("foo.cpp"),
687       UnorderedElementsAre(
688           AllOf(WithName("Foo"), WithKind(SymbolKind::Class)),
689           AllOf(WithName("Foo<int, T>"), WithKind(SymbolKind::Class)),
690           AllOf(WithName("Foo<bool, int>"), WithKind(SymbolKind::Class)),
691           AllOf(WithName("Foo<bool, int, 3>"), WithKind(SymbolKind::Class))));
692 }
693 
TEST_F(DocumentSymbolsTest,Qualifiers)694 TEST_F(DocumentSymbolsTest, Qualifiers) {
695   addFile("foo.cpp", R"cpp(
696     namespace foo { namespace bar {
697       struct Cls;
698 
699       int func1();
700       int func2();
701       int func3();
702       int func4();
703     }}
704 
705     struct foo::bar::Cls { };
706 
707     int foo::bar::func1() { return 10; }
708     int ::foo::bar::func2() { return 20; }
709 
710     using namespace foo;
711     int bar::func3() { return 30; }
712 
713     namespace alias = foo::bar;
714     int ::alias::func4() { return 40; }
715   )cpp");
716 
717   // All the qualifiers should be preserved exactly as written.
718   EXPECT_THAT(getSymbols("foo.cpp"),
719               UnorderedElementsAre(
720                   WithName("foo"), WithName("foo::bar::Cls"),
721                   WithName("foo::bar::func1"), WithName("::foo::bar::func2"),
722                   WithName("using namespace foo"), WithName("bar::func3"),
723                   WithName("alias"), WithName("::alias::func4")));
724 }
725 
TEST_F(DocumentSymbolsTest,QualifiersWithTemplateArgs)726 TEST_F(DocumentSymbolsTest, QualifiersWithTemplateArgs) {
727   addFile("foo.cpp", R"cpp(
728       template <typename T, typename U = double> class Foo;
729 
730       template <>
731       class Foo<int, double> {
732         int method1();
733         int method2();
734         int method3();
735       };
736 
737       using int_type = int;
738 
739       // Typedefs should be preserved!
740       int Foo<int_type, double>::method1() { return 10; }
741 
742       // Default arguments should not be shown!
743       int Foo<int>::method2() { return 20; }
744 
745       using Foo_type = Foo<int>;
746       // If the whole type is aliased, this should be preserved too!
747       int Foo_type::method3() { return 30; }
748       )cpp");
749   EXPECT_THAT(
750       getSymbols("foo.cpp"),
751       UnorderedElementsAre(WithName("Foo"), WithName("Foo<int, double>"),
752                            WithName("int_type"),
753                            WithName("Foo<int_type, double>::method1"),
754                            WithName("Foo<int>::method2"), WithName("Foo_type"),
755                            WithName("Foo_type::method3")));
756 }
757 
758 } // namespace clangd
759 } // namespace clang
760