clang-tools 22.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
46std::vector<SymbolInformation> getSymbols(TestTU &TU, llvm::StringRef Query,
47 int Limit = 0) {
48 auto SymbolInfos = getWorkspaceSymbols(Query, Limit, TU.index().get(),
49 testPath(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("(unnamed struct)::InUnnamed"),
110 withKind(SymbolKind::Field))));
111}
112
113TEST(WorkspaceSymbols, InMainFile) {
114 TestTU TU;
115 TU.Code = R"cpp(
116 int test() { return 0; }
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 void f2() {}
339 friend class Friend;
340 Foo& operator=(const Foo&);
341 ~Foo();
342 class Nested {
343 void f();
344 };
345 };
346 class Friend {
347 };
348
349 void f1();
350 void f2();
351 static const int KInt = 2;
352 const char* kStr = "123";
353
354 void f1() {}
355
356 namespace foo {
357 // Type alias
358 typedef int int32;
359 using int32_t = int32;
360
361 // Variable
362 int v1;
363
364 // Namespace
365 namespace bar {
366 int v2;
367 }
368 // Namespace alias
369 namespace baz = bar;
370
371 using bar::v2;
372 } // namespace foo
373 )");
374
375 TU.Code = Main.code().str();
376 EXPECT_THAT(
377 getSymbols(TU.build()),
378 ElementsAreArray(
379 {AllOf(withName("Foo"), withKind(SymbolKind::Class),
380 withDetail("class"), children()),
381 AllOf(withName("Foo"), withKind(SymbolKind::Class),
382 withDetail("class"),
383 children(
384 AllOf(withName("Foo"), withKind(SymbolKind::Constructor),
385 withDetail("()"), children()),
386 AllOf(withName("Foo"), withKind(SymbolKind::Constructor),
387 withDetail("(int)"), children()),
388 AllOf(withName("f"), withKind(SymbolKind::Method),
389 withDetail("void ()"), children()),
390 AllOf(withName("f2"), withKind(SymbolKind::Function),
391 withDetail("void ()"), children()),
392 AllOf(withName("operator="), withKind(SymbolKind::Method),
393 withDetail("Foo &(const Foo &)"), children()),
394 AllOf(withName("~Foo"), withKind(SymbolKind::Constructor),
395 withDetail(""), children()),
396 AllOf(withName("Nested"), withKind(SymbolKind::Class),
397 withDetail("class"),
398 children(AllOf(
399 withName("f"), withKind(SymbolKind::Method),
400 withDetail("void ()"), children()))))),
401 AllOf(withName("Friend"), withKind(SymbolKind::Class),
402 withDetail("class"), children()),
403 AllOf(withName("f1"), withKind(SymbolKind::Function),
404 withDetail("void ()"), children()),
405 AllOf(withName("f2"), withKind(SymbolKind::Function),
406 withDetail("void ()"), children()),
407 AllOf(withName("KInt"), withKind(SymbolKind::Variable),
408 withDetail("const int"), children()),
409 AllOf(withName("kStr"), withKind(SymbolKind::Variable),
410 withDetail("const char *"), children()),
411 AllOf(withName("f1"), withKind(SymbolKind::Function),
412 withDetail("void ()"), children()),
413 AllOf(
414 withName("foo"), withKind(SymbolKind::Namespace), withDetail(""),
415 children(AllOf(withName("int32"), withKind(SymbolKind::Class),
416 withDetail("type alias"), children()),
417 AllOf(withName("int32_t"), withKind(SymbolKind::Class),
418 withDetail("type alias"), children()),
419 AllOf(withName("v1"), withKind(SymbolKind::Variable),
420 withDetail("int"), children()),
421 AllOf(withName("bar"), withKind(SymbolKind::Namespace),
422 withDetail(""),
423 children(AllOf(withName("v2"),
424 withKind(SymbolKind::Variable),
425 withDetail("int"), children()))),
426 AllOf(withName("baz"), withKind(SymbolKind::Namespace),
427 withDetail(""), children()),
428 AllOf(withName("v2"), withKind(SymbolKind::Namespace),
429 withDetail(""))))}));
430}
431
432TEST(DocumentSymbols, DeclarationDefinition) {
433 TestTU TU;
434 Annotations Main(R"(
435 class Foo {
436 void $decl[[f]]();
437 };
438 void Foo::$def[[f]]() {
439 }
440 )");
441
442 TU.Code = Main.code().str();
443 EXPECT_THAT(
444 getSymbols(TU.build()),
445 ElementsAre(
446 AllOf(withName("Foo"), withKind(SymbolKind::Class),
447 withDetail("class"),
448 children(AllOf(withName("f"), withKind(SymbolKind::Method),
449 withDetail("void ()"),
450 symNameRange(Main.range("decl"))))),
451 AllOf(withName("Foo::f"), withKind(SymbolKind::Method),
452 withDetail("void ()"), symNameRange(Main.range("def")))));
453}
454
455TEST(DocumentSymbols, Concepts) {
456 TestTU TU;
457 TU.ExtraArgs = {"-std=c++20"};
458 TU.Code = "template <typename T> concept C = requires(T t) { t.foo(); };";
459
460 EXPECT_THAT(getSymbols(TU.build()),
461 ElementsAre(AllOf(withName("C"), withDetail("concept"))));
462}
463
464TEST(DocumentSymbols, ExternSymbol) {
465 TestTU TU;
466 TU.AdditionalFiles["foo.h"] = R"cpp(
467 extern int var;
468 )cpp";
469 TU.Code = R"cpp(
470 #include "foo.h"
471 )cpp";
472
473 EXPECT_THAT(getSymbols(TU.build()), IsEmpty());
474}
475
476TEST(DocumentSymbols, ExternContext) {
477 TestTU TU;
478 TU.Code = R"cpp(
479 extern "C" {
480 void foo();
481 class Foo {};
482 }
483 namespace ns {
484 extern "C" {
485 void bar();
486 class Bar {};
487 }
488 })cpp";
489
490 EXPECT_THAT(getSymbols(TU.build()),
491 ElementsAre(withName("foo"), withName("Foo"),
492 AllOf(withName("ns"),
493 children(withName("bar"), withName("Bar")))));
494}
495
496TEST(DocumentSymbols, ExportContext) {
497 TestTU TU;
498 TU.ExtraArgs = {"-std=c++20"};
499 TU.Code = R"cpp(
500 export module test;
501 export {
502 void foo();
503 class Foo {};
504 })cpp";
505
506 EXPECT_THAT(getSymbols(TU.build()),
507 ElementsAre(withName("foo"), withName("Foo")));
508}
509
510TEST(DocumentSymbols, NoLocals) {
511 TestTU TU;
512 TU.Code = R"cpp(
513 void test(int FirstParam, int SecondParam) {
514 struct LocalClass {};
515 int local_var;
516 })cpp";
517 EXPECT_THAT(getSymbols(TU.build()), ElementsAre(withName("test")));
518}
519
520TEST(DocumentSymbols, Unnamed) {
521 TestTU TU;
522 TU.Code = R"cpp(
523 struct {
524 int InUnnamed;
525 } UnnamedStruct;
526 )cpp";
527 EXPECT_THAT(
528 getSymbols(TU.build()),
529 ElementsAre(AllOf(withName("(anonymous struct)"),
530 withKind(SymbolKind::Struct), withDetail("struct"),
531 children(AllOf(withName("InUnnamed"),
532 withKind(SymbolKind::Field),
533 withDetail("int"), children()))),
534 AllOf(withName("UnnamedStruct"),
535 withKind(SymbolKind::Variable),
536 withDetail("struct (unnamed)"), children())));
537}
538
539TEST(DocumentSymbols, InHeaderFile) {
540 TestTU TU;
541 TU.AdditionalFiles["bar.h"] = R"cpp(
542 int foo() {
543 return 0;
544 }
545 )cpp";
546 TU.Code = R"cpp(
547 int i; // declaration to finish preamble
548 #include "bar.h"
549 int test() {
550 return 0;
551 }
552 )cpp";
553 EXPECT_THAT(getSymbols(TU.build()),
554 ElementsAre(withName("i"), withName("test")));
555}
556
557TEST(DocumentSymbols, Template) {
558 TestTU TU;
559 TU.Code = R"(
560 template <class T> struct Tmpl {T x = 0;};
561 template <> struct Tmpl<int> {
562 int y = 0;
563 };
564 extern template struct Tmpl<float>;
565 template struct Tmpl<double>;
566
567 template <class T, class U, class Z = float>
568 int funcTmpl(U a);
569 template <>
570 int funcTmpl<int>(double a);
571
572 template <class T, class U = double>
573 int varTmpl = T();
574 template <>
575 double varTmpl<int> = 10.0;
576 )";
577 EXPECT_THAT(
578 getSymbols(TU.build()),
579 ElementsAre(
580 AllOf(withName("Tmpl"), withKind(SymbolKind::Struct),
581 withDetail("template struct"),
582 children(AllOf(withName("x"), withKind(SymbolKind::Field),
583 withDetail("T")))),
584 AllOf(withName("Tmpl<int>"), withKind(SymbolKind::Struct),
585 withDetail("struct"),
586 children(AllOf(withName("y"), withDetail("int")))),
587 AllOf(withName("Tmpl<float>"), withKind(SymbolKind::Struct),
588 withDetail("struct"), children()),
589 AllOf(withName("Tmpl<double>"), withKind(SymbolKind::Struct),
590 withDetail("struct"), children()),
591 AllOf(withName("funcTmpl"), withDetail("template int (U)"),
592 children()),
593 AllOf(withName("funcTmpl<int>"), withDetail("int (double)"),
594 children()),
595 AllOf(withName("varTmpl"), withDetail("template int"), children()),
596 AllOf(withName("varTmpl<int>"), withDetail("double"), children())));
597}
598
599TEST(DocumentSymbols, Namespaces) {
600 TestTU TU;
601 TU.Code = R"cpp(
602 namespace ans1 {
603 int ai1;
604 namespace ans2 {
605 int ai2;
606 }
607 }
608 namespace {
609 void test() {}
610 }
611
612 namespace na {
613 inline namespace nb {
614 class Foo {};
615 }
616 }
617 namespace na {
618 // This is still inlined.
619 namespace nb {
620 class Bar {};
621 }
622 }
623 )cpp";
624 EXPECT_THAT(
625 getSymbols(TU.build()),
626 ElementsAreArray<::testing::Matcher<DocumentSymbol>>(
627 {AllOf(withName("ans1"),
628 children(AllOf(withName("ai1"), children()),
629 AllOf(withName("ans2"), children(withName("ai2"))))),
630 AllOf(withName("(anonymous namespace)"), children(withName("test"))),
631 AllOf(withName("na"),
632 children(AllOf(withName("nb"), children(withName("Foo"))))),
633 AllOf(withName("na"),
634 children(AllOf(withName("nb"), children(withName("Bar")))))}));
635}
636
637TEST(DocumentSymbols, Enums) {
638 TestTU TU;
639 TU.Code = R"(
640 enum {
641 Red
642 };
643 enum Color {
644 Green
645 };
646 enum class Color2 {
647 Yellow
648 };
649 namespace ns {
650 enum {
651 Black
652 };
653 }
654 )";
655 EXPECT_THAT(
656 getSymbols(TU.build()),
657 ElementsAre(
658 AllOf(withName("(anonymous enum)"), withDetail("enum"),
659 children(AllOf(withName("Red"), withDetail("(unnamed enum)")))),
660 AllOf(withName("Color"), withDetail("enum"),
661 children(AllOf(withName("Green"), withDetail("Color")))),
662 AllOf(withName("Color2"), withDetail("enum"),
663 children(AllOf(withName("Yellow"), withDetail("Color2")))),
664 AllOf(
665 withName("ns"),
666 children(AllOf(withName("(anonymous enum)"), withDetail("enum"),
667 children(AllOf(withName("Black"),
668 withDetail("(unnamed enum)"))))))));
669}
670
671TEST(DocumentSymbols, Macro) {
672 struct Test {
673 const char *Code;
674 testing::Matcher<DocumentSymbol> Matcher;
675 } Tests[] = {
676 {
677 R"cpp(
678 // Basic macro that generates symbols.
679 #define DEFINE_FLAG(X) bool FLAGS_##X; bool FLAGS_no##X
680 DEFINE_FLAG(pretty);
681 )cpp",
682 AllOf(withName("DEFINE_FLAG"), withDetail("(pretty)"),
683 children(withName("FLAGS_pretty"), withName("FLAGS_nopretty"))),
684 },
685 {
686 R"cpp(
687 // Hierarchy is determined by primary (name) location.
688 #define ID(X) X
689 namespace ID(ns) { int ID(y); }
690 )cpp",
691 AllOf(withName("ID"), withDetail("(ns)"),
692 children(AllOf(withName("ns"),
693 children(AllOf(withName("ID"), withDetail("(y)"),
694 children(withName("y"))))))),
695 },
696 {
697 R"cpp(
698 // More typical example where macro only generates part of a decl.
699 #define TEST(A, B) class A##_##B { void go(); }; void A##_##B::go()
700 TEST(DocumentSymbols, Macro) { }
701 )cpp",
702 AllOf(withName("TEST"), withDetail("(DocumentSymbols, Macro)"),
703 children(AllOf(withName("DocumentSymbols_Macro"),
704 children(withName("go"))),
705 withName("DocumentSymbols_Macro::go"))),
706 },
707 {
708 R"cpp(
709 // Nested macros.
710 #define NAMESPACE(NS, BODY) namespace NS { BODY }
711 NAMESPACE(a, NAMESPACE(b, int x;))
712 )cpp",
713 AllOf(
714 withName("NAMESPACE"), withDetail("(a, NAMESPACE(b, int x;))"),
715 children(AllOf(
716 withName("a"),
717 children(AllOf(withName("NAMESPACE"),
718 // FIXME: nested expansions not in TokenBuffer
719 withDetail(""),
720 children(AllOf(withName("b"),
721 children(withName("x"))))))))),
722 },
723 {
724 R"cpp(
725 // Macro invoked from body is not exposed.
726 #define INNER(X) int X
727 #define OUTER(X) INNER(X)
728 OUTER(foo);
729 )cpp",
730 AllOf(withName("OUTER"), withDetail("(foo)"),
731 children(withName("foo"))),
732 },
733 };
734 for (const Test &T : Tests) {
735 auto TU = TestTU::withCode(T.Code);
736 EXPECT_THAT(getSymbols(TU.build()), ElementsAre(T.Matcher)) << T.Code;
737 }
738}
739
740TEST(DocumentSymbols, RangeFromMacro) {
741 TestTU TU;
742 Annotations Main(R"(
743 #define FF(name) \
744 class name##_Test {};
745
746 $expansion1[[FF]](abc);
747
748 #define FF2() \
749 class Test {}
750
751 $expansion2parens[[$expansion2[[FF2]]()]];
752
753 #define FF3() \
754 void waldo()
755
756 $fullDef[[FF3() {
757 int var = 42;
758 }]]
759
760 #define FF4(name) int name = 0
761 $FooRange[[FF4($FooSelectionRange[[foo]])]];
762 )");
763 TU.Code = Main.code().str();
764 EXPECT_THAT(
765 getSymbols(TU.build()),
766 ElementsAre(
767 AllOf(withName("FF"), withDetail("(abc)"),
768 children(AllOf(withName("abc_Test"), withDetail("class"),
769 symNameRange(Main.range("expansion1"))))),
770 AllOf(withName("FF2"), withDetail("()"),
771 symNameRange(Main.range("expansion2")),
772 symRange(Main.range("expansion2parens")),
773 children(AllOf(withName("Test"), withDetail("class"),
774 symNameRange(Main.range("expansion2"))))),
775 AllOf(withName("FF3"), withDetail("()"),
776 symRange(Main.range("fullDef")),
777 children(AllOf(withName("waldo"), withDetail("void ()"),
778 symRange(Main.range("fullDef"))))),
779 AllOf(
780 withName("FF4"), withDetail("(foo)"),
781 children(AllOf(withName("foo"), symRange(Main.range("FooRange")),
782 symNameRange(Main.range("FooSelectionRange")))))));
783}
784
785TEST(DocumentSymbols, FuncTemplates) {
786 TestTU TU;
787 Annotations Source(R"cpp(
788 template <class T>
789 T foo() { return T{}; }
790
791 auto x = foo<int>();
792 auto y = foo<double>();
793 )cpp");
794 TU.Code = Source.code().str();
795 // Make sure we only see the template declaration, not instantiations.
796 EXPECT_THAT(getSymbols(TU.build()),
797 ElementsAre(AllOf(withName("foo"), withDetail("template T ()")),
798 AllOf(withName("x"), withDetail("int")),
799 AllOf(withName("y"), withDetail("double"))));
800}
801
802TEST(DocumentSymbols, UsingDirectives) {
803 TestTU TU;
804 Annotations Source(R"cpp(
805 namespace ns {
806 int foo;
807 }
808
809 namespace ns_alias = ns;
810
811 using namespace ::ns; // check we don't loose qualifiers.
812 using namespace ns_alias; // and namespace aliases.
813 )cpp");
814 TU.Code = Source.code().str();
815 EXPECT_THAT(getSymbols(TU.build()),
816 ElementsAre(withName("ns"), withName("ns_alias"),
817 withName("using namespace ::ns"),
818 withName("using namespace ns_alias")));
819}
820
821TEST(DocumentSymbols, TempSpecs) {
822 TestTU TU;
823 TU.Code = R"cpp(
824 template <typename T, typename U, int X = 5> class Foo {};
825 template <typename T> class Foo<int, T> {};
826 template <> class Foo<bool, int> {};
827 template <> class Foo<bool, int, 3> {};
828 )cpp";
829 // Foo is higher ranked because of exact name match.
830 EXPECT_THAT(getSymbols(TU.build()),
831 UnorderedElementsAre(
832 AllOf(withName("Foo"), withKind(SymbolKind::Class),
833 withDetail("template class")),
834 AllOf(withName("Foo<int, T>"), withKind(SymbolKind::Class),
835 withDetail("template class")),
836 AllOf(withName("Foo<bool, int>"), withKind(SymbolKind::Class),
837 withDetail("class")),
838 AllOf(withName("Foo<bool, int, 3>"),
839 withKind(SymbolKind::Class), withDetail("class"))));
840}
841
842TEST(DocumentSymbols, Qualifiers) {
843 TestTU TU;
844 TU.Code = R"cpp(
845 namespace foo { namespace bar {
846 struct Cls;
847
848 int func1();
849 int func2();
850 int func3();
851 int func4();
852 }}
853
854 struct foo::bar::Cls { };
855
856 int foo::bar::func1() { return 10; }
857 int ::foo::bar::func2() { return 20; }
858
859 using namespace foo;
860 int bar::func3() { return 30; }
861
862 namespace alias = foo::bar;
863 int ::alias::func4() { return 40; }
864 )cpp";
865
866 // All the qualifiers should be preserved exactly as written.
867 EXPECT_THAT(getSymbols(TU.build()),
868 UnorderedElementsAre(
869 withName("foo"), withName("foo::bar::Cls"),
870 withName("foo::bar::func1"), withName("::foo::bar::func2"),
871 withName("using namespace foo"), withName("bar::func3"),
872 withName("alias"), withName("::alias::func4")));
873}
874
875TEST(DocumentSymbols, QualifiersWithTemplateArgs) {
876 TestTU TU;
877 TU.Code = R"cpp(
878 template <typename T, typename U = double> class Foo;
879
880 template <>
881 class Foo<int, double> {
882 int method1();
883 int method2();
884 int method3();
885 };
886
887 using int_type = int;
888
889 // Typedefs should be preserved!
890 int Foo<int_type, double>::method1() { return 10; }
891
892 // Default arguments should not be shown!
893 int Foo<int>::method2() { return 20; }
894
895 using Foo_type = Foo<int>;
896 // If the whole type is aliased, this should be preserved too!
897 int Foo_type::method3() { return 30; }
898 )cpp";
899 EXPECT_THAT(getSymbols(TU.build()),
900 UnorderedElementsAre(
901 AllOf(withName("Foo"), withDetail("template class")),
902 AllOf(withName("Foo<int, double>"), withDetail("class")),
903 AllOf(withName("int_type"), withDetail("type alias")),
904 AllOf(withName("Foo<int_type, double>::method1"),
905 withDetail("int ()")),
906 AllOf(withName("Foo<int>::method2"), withDetail("int ()")),
907 AllOf(withName("Foo_type"), withDetail("type alias")),
908 AllOf(withName("Foo_type::method3"), withDetail("int ()"))));
909}
910
911TEST(DocumentSymbolsTest, Ranges) {
912 TestTU TU;
913 Annotations Main(R"(
914 $foo[[int foo(bool Argument) {
915 return 42;
916 }]]
917
918 $variable[[char GLOBAL_VARIABLE]];
919
920 $ns[[namespace ns {
921 $bar[[class Bar {
922 public:
923 $ctor[[Bar() {}]]
924 $dtor[[~Bar()]];
925
926 private:
927 $field[[unsigned Baz]];
928
929 $getbaz[[unsigned getBaz() { return Baz; }]]
930 }]];
931 }]] // namespace ns
932
933 $forwardclass[[class ForwardClassDecl]];
934
935 $struct[[struct StructDefinition {
936 $structfield[[int *Pointer = nullptr]];
937 }]];
938 $forwardstruct[[struct StructDeclaration]];
939
940 $forwardfunc[[void forwardFunctionDecl(int Something)]];
941 )");
942 TU.Code = Main.code().str();
943 EXPECT_THAT(
944 getSymbols(TU.build()),
945 UnorderedElementsAre(
946 AllOf(withName("foo"), withKind(SymbolKind::Function),
947 withDetail("int (bool)"), symRange(Main.range("foo"))),
948 AllOf(withName("GLOBAL_VARIABLE"), withKind(SymbolKind::Variable),
949 withDetail("char"), symRange(Main.range("variable"))),
950 AllOf(
951 withName("ns"), withKind(SymbolKind::Namespace),
952 symRange(Main.range("ns")),
953 children(AllOf(
954 withName("Bar"), withKind(SymbolKind::Class),
955 withDetail("class"), symRange(Main.range("bar")),
956 children(
957 AllOf(withName("Bar"), withKind(SymbolKind::Constructor),
958 withDetail("()"), symRange(Main.range("ctor"))),
959 AllOf(withName("~Bar"), withKind(SymbolKind::Constructor),
960 withDetail(""), symRange(Main.range("dtor"))),
961 AllOf(withName("Baz"), withKind(SymbolKind::Field),
962 withDetail("unsigned int"),
963 symRange(Main.range("field"))),
964 AllOf(withName("getBaz"), withKind(SymbolKind::Method),
965 withDetail("unsigned int ()"),
966 symRange(Main.range("getbaz"))))))),
967 AllOf(withName("ForwardClassDecl"), withKind(SymbolKind::Class),
968 withDetail("class"), symRange(Main.range("forwardclass"))),
969 AllOf(withName("StructDefinition"), withKind(SymbolKind::Struct),
970 withDetail("struct"), symRange(Main.range("struct")),
971 children(AllOf(withName("Pointer"), withKind(SymbolKind::Field),
972 withDetail("int *"),
973 symRange(Main.range("structfield"))))),
974 AllOf(withName("StructDeclaration"), withKind(SymbolKind::Struct),
975 withDetail("struct"), symRange(Main.range("forwardstruct"))),
976 AllOf(withName("forwardFunctionDecl"), withKind(SymbolKind::Function),
977 withDetail("void (int)"),
978 symRange(Main.range("forwardfunc")))));
979}
980
981TEST(DocumentSymbolsTest, DependentType) {
982 TestTU TU;
983 TU.Code = R"(
984 template <typename T> auto plus(T x, T y) -> decltype(x + y) { return x + y; }
985
986 template <typename Key, typename Value> class Pair {};
987
988 template <typename Key, typename Value>
989 struct Context : public Pair<Key, Value> {
990 using Pair<Key, Value>::Pair;
991 };
992 )";
993 EXPECT_THAT(
994 getSymbols(TU.build()),
995 ElementsAre(
996 AllOf(withName("plus"),
997 withDetail("template auto (T, T) -> decltype(x + y)")),
998 AllOf(withName("Pair"), withDetail("template class")),
999 AllOf(withName("Context"), withDetail("template struct"),
1000 children(AllOf(
1001 withName("Pair<type-parameter-0-0, type-parameter-0-1>"),
1002 withDetail("<dependent type>"))))));
1003}
1004
1005TEST(DocumentSymbolsTest, ObjCCategoriesAndClassExtensions) {
1006 TestTU TU;
1007 TU.ExtraArgs = {"-xobjective-c++", "-Wno-objc-root-class"};
1008 Annotations Main(R"cpp(
1009 $Cat[[@interface Cat
1010 + (id)sharedCat;
1011 @end]]
1012 $SneakyCat[[@interface Cat (Sneaky)
1013 - (id)sneak:(id)behavior;
1014 @end]]
1015
1016 $MeowCat[[@interface Cat ()
1017 - (void)meow;
1018 @end]]
1019 $PurCat[[@interface Cat ()
1020 - (void)pur;
1021 @end]]
1022 )cpp");
1023 TU.Code = Main.code().str();
1024 EXPECT_THAT(
1025 getSymbols(TU.build()),
1026 ElementsAre(
1027 AllOf(withName("Cat"), symRange(Main.range("Cat")),
1028 children(AllOf(withName("+sharedCat"),
1029 withKind(SymbolKind::Method)))),
1030 AllOf(withName("Cat(Sneaky)"), symRange(Main.range("SneakyCat")),
1031 children(
1032 AllOf(withName("-sneak:"), withKind(SymbolKind::Method)))),
1033 AllOf(
1034 withName("Cat()"), symRange(Main.range("MeowCat")),
1035 children(AllOf(withName("-meow"), withKind(SymbolKind::Method)))),
1036 AllOf(withName("Cat()"), symRange(Main.range("PurCat")),
1037 children(
1038 AllOf(withName("-pur"), withKind(SymbolKind::Method))))));
1039}
1040
1041TEST(DocumentSymbolsTest, PragmaMarkGroups) {
1042 TestTU TU;
1043 TU.ExtraArgs = {"-xobjective-c++", "-Wno-objc-root-class"};
1044 Annotations Main(R"cpp(
1045 $DogDef[[@interface Dog
1046 @end]]
1047
1048 $DogImpl[[@implementation Dog
1049
1050 + (id)sharedDoggo { return 0; }
1051
1052 #pragma $Overrides[[mark - Overrides
1053
1054 - (id)init {
1055 return self;
1056 }
1057 - (void)bark {}]]
1058
1059 #pragma $Specifics[[mark - Dog Specifics
1060
1061 - (int)isAGoodBoy {
1062 return 1;
1063 }]]
1064 @]]end // FIXME: Why doesn't this include the 'end'?
1065
1066 #pragma $End[[mark - End
1067]]
1068 )cpp");
1069 TU.Code = Main.code().str();
1070 EXPECT_THAT(
1071 getSymbols(TU.build()),
1072 UnorderedElementsAre(
1073 AllOf(withName("Dog"), symRange(Main.range("DogDef"))),
1074 AllOf(withName("Dog"), symRange(Main.range("DogImpl")),
1075 children(AllOf(withName("+sharedDoggo"),
1076 withKind(SymbolKind::Method)),
1077 AllOf(withName("Overrides"),
1078 symRange(Main.range("Overrides")),
1079 children(AllOf(withName("-init"),
1080 withKind(SymbolKind::Method)),
1081 AllOf(withName("-bark"),
1082 withKind(SymbolKind::Method)))),
1083 AllOf(withName("Dog Specifics"),
1084 symRange(Main.range("Specifics")),
1085 children(AllOf(withName("-isAGoodBoy"),
1086 withKind(SymbolKind::Method)))))),
1087 AllOf(withName("End"), symRange(Main.range("End")))));
1088}
1089
1090TEST(DocumentSymbolsTest, PragmaMarkGroupsNesting) {
1091 TestTU TU;
1092 TU.ExtraArgs = {"-xobjective-c++", "-Wno-objc-root-class"};
1093 Annotations Main(R"cpp(
1094 #pragma mark - Foo
1095 struct Foo {
1096 #pragma mark - Bar
1097 void bar() {
1098 #pragma mark - NotTopDecl
1099 }
1100 };
1101 void bar() {}
1102 )cpp");
1103 TU.Code = Main.code().str();
1104 EXPECT_THAT(
1105 getSymbols(TU.build()),
1106 UnorderedElementsAre(AllOf(
1107 withName("Foo"),
1108 children(AllOf(withName("Foo"),
1109 children(AllOf(withName("Bar"),
1110 children(AllOf(withName("bar"),
1111 children(withName(
1112 "NotTopDecl"))))))),
1113 withName("bar")))));
1114}
1115
1116TEST(DocumentSymbolsTest, PragmaMarkGroupsNoNesting) {
1117 TestTU TU;
1118 TU.ExtraArgs = {"-xobjective-c++", "-Wno-objc-root-class"};
1119 Annotations Main(R"cpp(
1120 #pragma mark Helpers
1121 void helpA(id obj) {}
1122
1123 #pragma mark -
1124 #pragma mark Core
1125
1126 void coreMethod() {}
1127 )cpp");
1128 TU.Code = Main.code().str();
1129 EXPECT_THAT(getSymbols(TU.build()),
1130 UnorderedElementsAre(withName("Helpers"), withName("helpA"),
1131 withName("(unnamed group)"),
1132 withName("Core"), withName("coreMethod")));
1133}
1134
1135} // namespace
1136} // namespace clangd
1137} // 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
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< DocumentSymbol > children
Children of this symbol, e.g. properties of a class.
Definition Protocol.h:1135
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