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