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