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

source code of clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp