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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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