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