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("(anonymous 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)")))),
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(withName("ns"),
665 children(AllOf(withName("(anonymous enum)"), withDetail("enum"),
666 children(AllOf(withName("Black"),
667 withDetail("(unnamed)"))))))));
668}
669
670TEST(DocumentSymbols, Macro) {
671 struct Test {
672 const char *Code;
673 testing::Matcher<DocumentSymbol> Matcher;
674 } Tests[] = {
675 {
676 R"cpp(
677 // Basic macro that generates symbols.
678 #define DEFINE_FLAG(X) bool FLAGS_##X; bool FLAGS_no##X
679 DEFINE_FLAG(pretty);
680 )cpp",
681 AllOf(withName("DEFINE_FLAG"), withDetail("(pretty)"),
682 children(withName("FLAGS_pretty"), withName("FLAGS_nopretty"))),
683 },
684 {
685 R"cpp(
686 // Hierarchy is determined by primary (name) location.
687 #define ID(X) X
688 namespace ID(ns) { int ID(y); }
689 )cpp",
690 AllOf(withName("ID"), withDetail("(ns)"),
691 children(AllOf(withName("ns"),
692 children(AllOf(withName("ID"), withDetail("(y)"),
693 children(withName("y"))))))),
694 },
695 {
696 R"cpp(
697 // More typical example where macro only generates part of a decl.
698 #define TEST(A, B) class A##_##B { void go(); }; void A##_##B::go()
699 TEST(DocumentSymbols, Macro) { }
700 )cpp",
701 AllOf(withName("TEST"), withDetail("(DocumentSymbols, Macro)"),
702 children(AllOf(withName("DocumentSymbols_Macro"),
703 children(withName("go"))),
704 withName("DocumentSymbols_Macro::go"))),
705 },
706 {
707 R"cpp(
708 // Nested macros.
709 #define NAMESPACE(NS, BODY) namespace NS { BODY }
710 NAMESPACE(a, NAMESPACE(b, int x;))
711 )cpp",
712 AllOf(
713 withName("NAMESPACE"), withDetail("(a, NAMESPACE(b, int x;))"),
714 children(AllOf(
715 withName("a"),
716 children(AllOf(withName("NAMESPACE"),
717 // FIXME: nested expansions not in TokenBuffer
718 withDetail(""),
719 children(AllOf(withName("b"),
720 children(withName("x"))))))))),
721 },
722 {
723 R"cpp(
724 // Macro invoked from body is not exposed.
725 #define INNER(X) int X
726 #define OUTER(X) INNER(X)
727 OUTER(foo);
728 )cpp",
729 AllOf(withName("OUTER"), withDetail("(foo)"),
730 children(withName("foo"))),
731 },
732 };
733 for (const Test &T : Tests) {
734 auto TU = TestTU::withCode(T.Code);
735 EXPECT_THAT(getSymbols(TU.build()), ElementsAre(T.Matcher)) << T.Code;
736 }
737}
738
739TEST(DocumentSymbols, RangeFromMacro) {
740 TestTU TU;
741 Annotations Main(R"(
742 #define FF(name) \
743 class name##_Test {};
744
745 $expansion1[[FF]](abc);
746
747 #define FF2() \
748 class Test {}
749
750 $expansion2parens[[$expansion2[[FF2]]()]];
751
752 #define FF3() \
753 void waldo()
754
755 $fullDef[[FF3() {
756 int var = 42;
757 }]]
758
759 #define FF4(name) int name = 0
760 $FooRange[[FF4($FooSelectionRange[[foo]])]];
761 )");
762 TU.Code = Main.code().str();
763 EXPECT_THAT(
764 getSymbols(TU.build()),
765 ElementsAre(
766 AllOf(withName("FF"), withDetail("(abc)"),
767 children(AllOf(withName("abc_Test"), withDetail("class"),
768 symNameRange(Main.range("expansion1"))))),
769 AllOf(withName("FF2"), withDetail("()"),
770 symNameRange(Main.range("expansion2")),
771 symRange(Main.range("expansion2parens")),
772 children(AllOf(withName("Test"), withDetail("class"),
773 symNameRange(Main.range("expansion2"))))),
774 AllOf(withName("FF3"), withDetail("()"),
775 symRange(Main.range("fullDef")),
776 children(AllOf(withName("waldo"), withDetail("void ()"),
777 symRange(Main.range("fullDef"))))),
778 AllOf(
779 withName("FF4"), withDetail("(foo)"),
780 children(AllOf(withName("foo"), symRange(Main.range("FooRange")),
781 symNameRange(Main.range("FooSelectionRange")))))));
782}
783
784TEST(DocumentSymbols, FuncTemplates) {
785 TestTU TU;
786 Annotations Source(R"cpp(
787 template <class T>
788 T foo() { return T{}; }
789
790 auto x = foo<int>();
791 auto y = foo<double>();
792 )cpp");
793 TU.Code = Source.code().str();
794 // Make sure we only see the template declaration, not instantiations.
795 EXPECT_THAT(getSymbols(TU.build()),
796 ElementsAre(AllOf(withName("foo"), withDetail("template T ()")),
797 AllOf(withName("x"), withDetail("int")),
798 AllOf(withName("y"), withDetail("double"))));
799}
800
801TEST(DocumentSymbols, UsingDirectives) {
802 TestTU TU;
803 Annotations Source(R"cpp(
804 namespace ns {
805 int foo;
806 }
807
808 namespace ns_alias = ns;
809
810 using namespace ::ns; // check we don't loose qualifiers.
811 using namespace ns_alias; // and namespace aliases.
812 )cpp");
813 TU.Code = Source.code().str();
814 EXPECT_THAT(getSymbols(TU.build()),
815 ElementsAre(withName("ns"), withName("ns_alias"),
816 withName("using namespace ::ns"),
817 withName("using namespace ns_alias")));
818}
819
820TEST(DocumentSymbols, TempSpecs) {
821 TestTU TU;
822 TU.Code = R"cpp(
823 template <typename T, typename U, int X = 5> class Foo {};
824 template <typename T> class Foo<int, T> {};
825 template <> class Foo<bool, int> {};
826 template <> class Foo<bool, int, 3> {};
827 )cpp";
828 // Foo is higher ranked because of exact name match.
829 EXPECT_THAT(getSymbols(TU.build()),
830 UnorderedElementsAre(
831 AllOf(withName("Foo"), withKind(SymbolKind::Class),
832 withDetail("template class")),
833 AllOf(withName("Foo<int, T>"), withKind(SymbolKind::Class),
834 withDetail("template class")),
835 AllOf(withName("Foo<bool, int>"), withKind(SymbolKind::Class),
836 withDetail("class")),
837 AllOf(withName("Foo<bool, int, 3>"),
838 withKind(SymbolKind::Class), withDetail("class"))));
839}
840
841TEST(DocumentSymbols, Qualifiers) {
842 TestTU TU;
843 TU.Code = R"cpp(
844 namespace foo { namespace bar {
845 struct Cls;
846
847 int func1();
848 int func2();
849 int func3();
850 int func4();
851 }}
852
853 struct foo::bar::Cls { };
854
855 int foo::bar::func1() { return 10; }
856 int ::foo::bar::func2() { return 20; }
857
858 using namespace foo;
859 int bar::func3() { return 30; }
860
861 namespace alias = foo::bar;
862 int ::alias::func4() { return 40; }
863 )cpp";
864
865 // All the qualifiers should be preserved exactly as written.
866 EXPECT_THAT(getSymbols(TU.build()),
867 UnorderedElementsAre(
868 withName("foo"), withName("foo::bar::Cls"),
869 withName("foo::bar::func1"), withName("::foo::bar::func2"),
870 withName("using namespace foo"), withName("bar::func3"),
871 withName("alias"), withName("::alias::func4")));
872}
873
874TEST(DocumentSymbols, QualifiersWithTemplateArgs) {
875 TestTU TU;
876 TU.Code = R"cpp(
877 template <typename T, typename U = double> class Foo;
878
879 template <>
880 class Foo<int, double> {
881 int method1();
882 int method2();
883 int method3();
884 };
885
886 using int_type = int;
887
888 // Typedefs should be preserved!
889 int Foo<int_type, double>::method1() { return 10; }
890
891 // Default arguments should not be shown!
892 int Foo<int>::method2() { return 20; }
893
894 using Foo_type = Foo<int>;
895 // If the whole type is aliased, this should be preserved too!
896 int Foo_type::method3() { return 30; }
897 )cpp";
898 EXPECT_THAT(getSymbols(TU.build()),
899 UnorderedElementsAre(
900 AllOf(withName("Foo"), withDetail("template class")),
901 AllOf(withName("Foo<int, double>"), withDetail("class")),
902 AllOf(withName("int_type"), withDetail("type alias")),
903 AllOf(withName("Foo<int_type, double>::method1"),
904 withDetail("int ()")),
905 AllOf(withName("Foo<int>::method2"), withDetail("int ()")),
906 AllOf(withName("Foo_type"), withDetail("type alias")),
907 AllOf(withName("Foo_type::method3"), withDetail("int ()"))));
908}
909
910TEST(DocumentSymbolsTest, Ranges) {
911 TestTU TU;
912 Annotations Main(R"(
913 $foo[[int foo(bool Argument) {
914 return 42;
915 }]]
916
917 $variable[[char GLOBAL_VARIABLE]];
918
919 $ns[[namespace ns {
920 $bar[[class Bar {
921 public:
922 $ctor[[Bar() {}]]
923 $dtor[[~Bar()]];
924
925 private:
926 $field[[unsigned Baz]];
927
928 $getbaz[[unsigned getBaz() { return Baz; }]]
929 }]];
930 }]] // namespace ns
931
932 $forwardclass[[class ForwardClassDecl]];
933
934 $struct[[struct StructDefinition {
935 $structfield[[int *Pointer = nullptr]];
936 }]];
937 $forwardstruct[[struct StructDeclaration]];
938
939 $forwardfunc[[void forwardFunctionDecl(int Something)]];
940 )");
941 TU.Code = Main.code().str();
942 EXPECT_THAT(
943 getSymbols(TU.build()),
944 UnorderedElementsAre(
945 AllOf(withName("foo"), withKind(SymbolKind::Function),
946 withDetail("int (bool)"), symRange(Main.range("foo"))),
947 AllOf(withName("GLOBAL_VARIABLE"), withKind(SymbolKind::Variable),
948 withDetail("char"), symRange(Main.range("variable"))),
949 AllOf(
950 withName("ns"), withKind(SymbolKind::Namespace),
951 symRange(Main.range("ns")),
952 children(AllOf(
953 withName("Bar"), withKind(SymbolKind::Class),
954 withDetail("class"), symRange(Main.range("bar")),
955 children(
956 AllOf(withName("Bar"), withKind(SymbolKind::Constructor),
957 withDetail("()"), symRange(Main.range("ctor"))),
958 AllOf(withName("~Bar"), withKind(SymbolKind::Constructor),
959 withDetail(""), symRange(Main.range("dtor"))),
960 AllOf(withName("Baz"), withKind(SymbolKind::Field),
961 withDetail("unsigned int"),
962 symRange(Main.range("field"))),
963 AllOf(withName("getBaz"), withKind(SymbolKind::Method),
964 withDetail("unsigned int ()"),
965 symRange(Main.range("getbaz"))))))),
966 AllOf(withName("ForwardClassDecl"), withKind(SymbolKind::Class),
967 withDetail("class"), symRange(Main.range("forwardclass"))),
968 AllOf(withName("StructDefinition"), withKind(SymbolKind::Struct),
969 withDetail("struct"), symRange(Main.range("struct")),
970 children(AllOf(withName("Pointer"), withKind(SymbolKind::Field),
971 withDetail("int *"),
972 symRange(Main.range("structfield"))))),
973 AllOf(withName("StructDeclaration"), withKind(SymbolKind::Struct),
974 withDetail("struct"), symRange(Main.range("forwardstruct"))),
975 AllOf(withName("forwardFunctionDecl"), withKind(SymbolKind::Function),
976 withDetail("void (int)"),
977 symRange(Main.range("forwardfunc")))));
978}
979
980TEST(DocumentSymbolsTest, DependentType) {
981 TestTU TU;
982 TU.Code = R"(
983 template <typename T> auto plus(T x, T y) -> decltype(x + y) { return x + y; }
984
985 template <typename Key, typename Value> class Pair {};
986
987 template <typename Key, typename Value>
988 struct Context : public Pair<Key, Value> {
989 using Pair<Key, Value>::Pair;
990 };
991 )";
992 EXPECT_THAT(
993 getSymbols(TU.build()),
994 ElementsAre(
995 AllOf(withName("plus"),
996 withDetail("template auto (T, T) -> decltype(x + y)")),
997 AllOf(withName("Pair"), withDetail("template class")),
998 AllOf(withName("Context"), withDetail("template struct"),
999 children(AllOf(
1000 withName("Pair<type-parameter-0-0, type-parameter-0-1>"),
1001 withDetail("<dependent type>"))))));
1002}
1003
1004TEST(DocumentSymbolsTest, ObjCCategoriesAndClassExtensions) {
1005 TestTU TU;
1006 TU.ExtraArgs = {"-xobjective-c++", "-Wno-objc-root-class"};
1007 Annotations Main(R"cpp(
1008 $Cat[[@interface Cat
1009 + (id)sharedCat;
1010 @end]]
1011 $SneakyCat[[@interface Cat (Sneaky)
1012 - (id)sneak:(id)behavior;
1013 @end]]
1014
1015 $MeowCat[[@interface Cat ()
1016 - (void)meow;
1017 @end]]
1018 $PurCat[[@interface Cat ()
1019 - (void)pur;
1020 @end]]
1021 )cpp");
1022 TU.Code = Main.code().str();
1023 EXPECT_THAT(
1024 getSymbols(TU.build()),
1025 ElementsAre(
1026 AllOf(withName("Cat"), symRange(Main.range("Cat")),
1027 children(AllOf(withName("+sharedCat"),
1028 withKind(SymbolKind::Method)))),
1029 AllOf(withName("Cat(Sneaky)"), symRange(Main.range("SneakyCat")),
1030 children(
1031 AllOf(withName("-sneak:"), withKind(SymbolKind::Method)))),
1032 AllOf(
1033 withName("Cat()"), symRange(Main.range("MeowCat")),
1034 children(AllOf(withName("-meow"), withKind(SymbolKind::Method)))),
1035 AllOf(withName("Cat()"), symRange(Main.range("PurCat")),
1036 children(
1037 AllOf(withName("-pur"), withKind(SymbolKind::Method))))));
1038}
1039
1040TEST(DocumentSymbolsTest, PragmaMarkGroups) {
1041 TestTU TU;
1042 TU.ExtraArgs = {"-xobjective-c++", "-Wno-objc-root-class"};
1043 Annotations Main(R"cpp(
1044 $DogDef[[@interface Dog
1045 @end]]
1046
1047 $DogImpl[[@implementation Dog
1048
1049 + (id)sharedDoggo { return 0; }
1050
1051 #pragma $Overrides[[mark - Overrides
1052
1053 - (id)init {
1054 return self;
1055 }
1056 - (void)bark {}]]
1057
1058 #pragma $Specifics[[mark - Dog Specifics
1059
1060 - (int)isAGoodBoy {
1061 return 1;
1062 }]]
1063 @]]end // FIXME: Why doesn't this include the 'end'?
1064
1065 #pragma $End[[mark - End
1066]]
1067 )cpp");
1068 TU.Code = Main.code().str();
1069 EXPECT_THAT(
1070 getSymbols(TU.build()),
1071 UnorderedElementsAre(
1072 AllOf(withName("Dog"), symRange(Main.range("DogDef"))),
1073 AllOf(withName("Dog"), symRange(Main.range("DogImpl")),
1074 children(AllOf(withName("+sharedDoggo"),
1075 withKind(SymbolKind::Method)),
1076 AllOf(withName("Overrides"),
1077 symRange(Main.range("Overrides")),
1078 children(AllOf(withName("-init"),
1079 withKind(SymbolKind::Method)),
1080 AllOf(withName("-bark"),
1081 withKind(SymbolKind::Method)))),
1082 AllOf(withName("Dog Specifics"),
1083 symRange(Main.range("Specifics")),
1084 children(AllOf(withName("-isAGoodBoy"),
1085 withKind(SymbolKind::Method)))))),
1086 AllOf(withName("End"), symRange(Main.range("End")))));
1087}
1088
1089TEST(DocumentSymbolsTest, PragmaMarkGroupsNesting) {
1090 TestTU TU;
1091 TU.ExtraArgs = {"-xobjective-c++", "-Wno-objc-root-class"};
1092 Annotations Main(R"cpp(
1093 #pragma mark - Foo
1094 struct Foo {
1095 #pragma mark - Bar
1096 void bar() {
1097 #pragma mark - NotTopDecl
1098 }
1099 };
1100 void bar() {}
1101 )cpp");
1102 TU.Code = Main.code().str();
1103 EXPECT_THAT(
1104 getSymbols(TU.build()),
1105 UnorderedElementsAre(AllOf(
1106 withName("Foo"),
1107 children(AllOf(withName("Foo"),
1108 children(AllOf(withName("Bar"),
1109 children(AllOf(withName("bar"),
1110 children(withName(
1111 "NotTopDecl"))))))),
1112 withName("bar")))));
1113}
1114
1115TEST(DocumentSymbolsTest, PragmaMarkGroupsNoNesting) {
1116 TestTU TU;
1117 TU.ExtraArgs = {"-xobjective-c++", "-Wno-objc-root-class"};
1118 Annotations Main(R"cpp(
1119 #pragma mark Helpers
1120 void helpA(id obj) {}
1121
1122 #pragma mark -
1123 #pragma mark Core
1124
1125 void coreMethod() {}
1126 )cpp");
1127 TU.Code = Main.code().str();
1128 EXPECT_THAT(getSymbols(TU.build()),
1129 UnorderedElementsAre(withName("Helpers"), withName("helpA"),
1130 withName("(unnamed group)"),
1131 withName("Core"), withName("coreMethod")));
1132}
1133
1134} // namespace
1135} // namespace clangd
1136} // 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:45
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