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 | |
16 | namespace clang { |
17 | namespace clangd { |
18 | |
19 | namespace { |
20 | |
21 | using ::testing::AllOf; |
22 | using ::testing::ElementsAre; |
23 | using ::testing::ElementsAreArray; |
24 | using ::testing::Field; |
25 | using ::testing::IsEmpty; |
26 | using ::testing::UnorderedElementsAre; |
27 | |
28 | // GMock helpers for matching SymbolInfos items. |
29 | MATCHER_P(qName, Name, "" ) { |
30 | if (arg.containerName.empty()) |
31 | return arg.name == Name; |
32 | return (arg.containerName + "::" + arg.name) == Name; |
33 | } |
34 | MATCHER_P(withName, N, "" ) { return arg.name == N; } |
35 | MATCHER_P(withKind, Kind, "" ) { return arg.kind == Kind; } |
36 | MATCHER_P(withDetail, Detail, "" ) { return arg.detail == Detail; } |
37 | MATCHER_P(symRange, Range, "" ) { return arg.range == Range; } |
38 | |
39 | // GMock helpers for matching DocumentSymbol. |
40 | MATCHER_P(symNameRange, Range, "" ) { return arg.selectionRange == Range; } |
41 | template <class... ChildMatchers> |
42 | ::testing::Matcher<DocumentSymbol> children(ChildMatchers... ChildrenM) { |
43 | return Field(&DocumentSymbol::children, UnorderedElementsAre(ChildrenM...)); |
44 | } |
45 | |
46 | std::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 | |
54 | TEST(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 | |
67 | TEST(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 | |
78 | TEST(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 | |
96 | TEST(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 | |
113 | TEST(WorkspaceSymbols, InMainFile) { |
114 | TestTU TU; |
115 | TU.Code = R"cpp( |
116 | int test() {} |
117 | static void test2() {} |
118 | )cpp" ; |
119 | EXPECT_THAT(getSymbols(TU, "test" ), |
120 | ElementsAre(qName("test" ), qName("test2" ))); |
121 | } |
122 | |
123 | TEST(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 | |
167 | TEST(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 | |
177 | TEST(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 | |
195 | TEST(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 | |
222 | TEST(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 | |
263 | TEST(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 | |
275 | TEST(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 | |
286 | TEST(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 | |
304 | TEST(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 | |
323 | std::vector<DocumentSymbol> getSymbols(ParsedAST AST) { |
324 | auto SymbolInfos = getDocumentSymbols(AST); |
325 | EXPECT_TRUE(bool(SymbolInfos)) << "documentSymbols returned an error" ; |
326 | return *SymbolInfos; |
327 | } |
328 | |
329 | TEST(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 | |
429 | TEST(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 | |
452 | TEST(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 | |
461 | TEST(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 | |
473 | TEST(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 | |
493 | TEST(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 | |
507 | TEST(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 | |
517 | TEST(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 | |
536 | TEST(DocumentSymbols, InHeaderFile) { |
537 | TestTU TU; |
538 | TU.AdditionalFiles["bar.h" ] = R"cpp( |
539 | int foo() { |
540 | } |
541 | )cpp" ; |
542 | TU.Code = R"cpp( |
543 | int i; // declaration to finish preamble |
544 | #include "bar.h" |
545 | int test() { |
546 | } |
547 | )cpp" ; |
548 | EXPECT_THAT(getSymbols(TU.build()), |
549 | ElementsAre(withName("i" ), withName("test" ))); |
550 | } |
551 | |
552 | TEST(DocumentSymbols, Template) { |
553 | TestTU TU; |
554 | TU.Code = R"( |
555 | template <class T> struct Tmpl {T x = 0;}; |
556 | template <> struct Tmpl<int> { |
557 | int y = 0; |
558 | }; |
559 | extern template struct Tmpl<float>; |
560 | template struct Tmpl<double>; |
561 | |
562 | template <class T, class U, class Z = float> |
563 | int funcTmpl(U a); |
564 | template <> |
565 | int funcTmpl<int>(double a); |
566 | |
567 | template <class T, class U = double> |
568 | int varTmpl = T(); |
569 | template <> |
570 | double varTmpl<int> = 10.0; |
571 | )" ; |
572 | EXPECT_THAT( |
573 | getSymbols(TU.build()), |
574 | ElementsAre( |
575 | AllOf(withName("Tmpl" ), withKind(SymbolKind::Struct), |
576 | withDetail("template struct" ), |
577 | children(AllOf(withName("x" ), withKind(SymbolKind::Field), |
578 | withDetail("T" )))), |
579 | AllOf(withName("Tmpl<int>" ), withKind(SymbolKind::Struct), |
580 | withDetail("struct" ), |
581 | children(AllOf(withName("y" ), withDetail("int" )))), |
582 | AllOf(withName("Tmpl<float>" ), withKind(SymbolKind::Struct), |
583 | withDetail("struct" ), children()), |
584 | AllOf(withName("Tmpl<double>" ), withKind(SymbolKind::Struct), |
585 | withDetail("struct" ), children()), |
586 | AllOf(withName("funcTmpl" ), withDetail("template int (U)" ), |
587 | children()), |
588 | AllOf(withName("funcTmpl<int>" ), withDetail("int (double)" ), |
589 | children()), |
590 | AllOf(withName("varTmpl" ), withDetail("template int" ), children()), |
591 | AllOf(withName("varTmpl<int>" ), withDetail("double" ), children()))); |
592 | } |
593 | |
594 | TEST(DocumentSymbols, Namespaces) { |
595 | TestTU TU; |
596 | TU.Code = R"cpp( |
597 | namespace ans1 { |
598 | int ai1; |
599 | namespace ans2 { |
600 | int ai2; |
601 | } |
602 | } |
603 | namespace { |
604 | void test() {} |
605 | } |
606 | |
607 | namespace na { |
608 | inline namespace nb { |
609 | class Foo {}; |
610 | } |
611 | } |
612 | namespace na { |
613 | // This is still inlined. |
614 | namespace nb { |
615 | class Bar {}; |
616 | } |
617 | } |
618 | )cpp" ; |
619 | EXPECT_THAT( |
620 | getSymbols(TU.build()), |
621 | ElementsAreArray<::testing::Matcher<DocumentSymbol>>( |
622 | {AllOf(withName("ans1" ), |
623 | children(AllOf(withName("ai1" ), children()), |
624 | AllOf(withName("ans2" ), children(withName("ai2" ))))), |
625 | AllOf(withName("(anonymous namespace)" ), children(withName("test" ))), |
626 | AllOf(withName("na" ), |
627 | children(AllOf(withName("nb" ), children(withName("Foo" ))))), |
628 | AllOf(withName("na" ), |
629 | children(AllOf(withName("nb" ), children(withName("Bar" )))))})); |
630 | } |
631 | |
632 | TEST(DocumentSymbols, Enums) { |
633 | TestTU TU; |
634 | TU.Code = R"( |
635 | enum { |
636 | Red |
637 | }; |
638 | enum Color { |
639 | Green |
640 | }; |
641 | enum class Color2 { |
642 | Yellow |
643 | }; |
644 | namespace ns { |
645 | enum { |
646 | Black |
647 | }; |
648 | } |
649 | )" ; |
650 | EXPECT_THAT( |
651 | getSymbols(TU.build()), |
652 | ElementsAre( |
653 | AllOf(withName("(anonymous enum)" ), withDetail("enum" ), |
654 | children(AllOf(withName("Red" ), withDetail("(unnamed)" )))), |
655 | AllOf(withName("Color" ), withDetail("enum" ), |
656 | children(AllOf(withName("Green" ), withDetail("Color" )))), |
657 | AllOf(withName("Color2" ), withDetail("enum" ), |
658 | children(AllOf(withName("Yellow" ), withDetail("Color2" )))), |
659 | AllOf(withName("ns" ), |
660 | children(AllOf(withName("(anonymous enum)" ), withDetail("enum" ), |
661 | children(AllOf(withName("Black" ), |
662 | withDetail("(unnamed)" )))))))); |
663 | } |
664 | |
665 | TEST(DocumentSymbols, Macro) { |
666 | struct Test { |
667 | const char *Code; |
668 | testing::Matcher<DocumentSymbol> Matcher; |
669 | } Tests[] = { |
670 | { |
671 | .Code: R"cpp( |
672 | // Basic macro that generates symbols. |
673 | #define DEFINE_FLAG(X) bool FLAGS_##X; bool FLAGS_no##X |
674 | DEFINE_FLAG(pretty); |
675 | )cpp" , |
676 | .Matcher: AllOf(matchers: withName(gmock_p0: "DEFINE_FLAG" ), matchers: withDetail(gmock_p0: "(pretty)" ), |
677 | matchers: children(ChildrenM: withName(gmock_p0: "FLAGS_pretty" ), ChildrenM: withName(gmock_p0: "FLAGS_nopretty" ))), |
678 | }, |
679 | { |
680 | .Code: R"cpp( |
681 | // Hierarchy is determined by primary (name) location. |
682 | #define ID(X) X |
683 | namespace ID(ns) { int ID(y); } |
684 | )cpp" , |
685 | .Matcher: AllOf(matchers: withName(gmock_p0: "ID" ), matchers: withDetail(gmock_p0: "(ns)" ), |
686 | matchers: children(ChildrenM: AllOf(matchers: withName(gmock_p0: "ns" ), |
687 | matchers: children(ChildrenM: AllOf(matchers: withName(gmock_p0: "ID" ), matchers: withDetail(gmock_p0: "(y)" ), |
688 | matchers: children(ChildrenM: withName(gmock_p0: "y" ))))))), |
689 | }, |
690 | { |
691 | .Code: R"cpp( |
692 | // More typical example where macro only generates part of a decl. |
693 | #define TEST(A, B) class A##_##B { void go(); }; void A##_##B::go() |
694 | TEST(DocumentSymbols, Macro) { } |
695 | )cpp" , |
696 | .Matcher: AllOf(matchers: withName(gmock_p0: "TEST" ), matchers: withDetail(gmock_p0: "(DocumentSymbols, Macro)" ), |
697 | matchers: children(ChildrenM: AllOf(matchers: withName(gmock_p0: "DocumentSymbols_Macro" ), |
698 | matchers: children(ChildrenM: withName(gmock_p0: "go" ))), |
699 | ChildrenM: withName(gmock_p0: "DocumentSymbols_Macro::go" ))), |
700 | }, |
701 | { |
702 | .Code: R"cpp( |
703 | // Nested macros. |
704 | #define NAMESPACE(NS, BODY) namespace NS { BODY } |
705 | NAMESPACE(a, NAMESPACE(b, int x;)) |
706 | )cpp" , |
707 | .Matcher: AllOf( |
708 | matchers: withName(gmock_p0: "NAMESPACE" ), matchers: withDetail(gmock_p0: "(a, NAMESPACE(b, int x;))" ), |
709 | matchers: children(ChildrenM: AllOf( |
710 | matchers: withName(gmock_p0: "a" ), |
711 | matchers: children(ChildrenM: AllOf(matchers: withName(gmock_p0: "NAMESPACE" ), |
712 | // FIXME: nested expansions not in TokenBuffer |
713 | matchers: withDetail(gmock_p0: "" ), |
714 | matchers: children(ChildrenM: AllOf(matchers: withName(gmock_p0: "b" ), |
715 | matchers: children(ChildrenM: withName(gmock_p0: "x" ))))))))), |
716 | }, |
717 | { |
718 | .Code: R"cpp( |
719 | // Macro invoked from body is not exposed. |
720 | #define INNER(X) int X |
721 | #define OUTER(X) INNER(X) |
722 | OUTER(foo); |
723 | )cpp" , |
724 | .Matcher: AllOf(matchers: withName(gmock_p0: "OUTER" ), matchers: withDetail(gmock_p0: "(foo)" ), |
725 | matchers: children(ChildrenM: withName(gmock_p0: "foo" ))), |
726 | }, |
727 | }; |
728 | for (const Test &T : Tests) { |
729 | auto TU = TestTU::withCode(Code: T.Code); |
730 | EXPECT_THAT(getSymbols(TU.build()), ElementsAre(T.Matcher)) << T.Code; |
731 | } |
732 | } |
733 | |
734 | TEST(DocumentSymbols, RangeFromMacro) { |
735 | TestTU TU; |
736 | Annotations Main(R"( |
737 | #define FF(name) \ |
738 | class name##_Test {}; |
739 | |
740 | $expansion1[[FF]](abc); |
741 | |
742 | #define FF2() \ |
743 | class Test {} |
744 | |
745 | $expansion2parens[[$expansion2[[FF2]]()]]; |
746 | |
747 | #define FF3() \ |
748 | void waldo() |
749 | |
750 | $fullDef[[FF3() { |
751 | int var = 42; |
752 | }]] |
753 | |
754 | #define FF4(name) int name = 0 |
755 | $FooRange[[FF4($FooSelectionRange[[foo]])]]; |
756 | )" ); |
757 | TU.Code = Main.code().str(); |
758 | EXPECT_THAT( |
759 | getSymbols(TU.build()), |
760 | ElementsAre( |
761 | AllOf(withName("FF" ), withDetail("(abc)" ), |
762 | children(AllOf(withName("abc_Test" ), withDetail("class" ), |
763 | symNameRange(Main.range("expansion1" ))))), |
764 | AllOf(withName("FF2" ), withDetail("()" ), |
765 | symNameRange(Main.range("expansion2" )), |
766 | symRange(Main.range("expansion2parens" )), |
767 | children(AllOf(withName("Test" ), withDetail("class" ), |
768 | symNameRange(Main.range("expansion2" ))))), |
769 | AllOf(withName("FF3" ), withDetail("()" ), |
770 | symRange(Main.range("fullDef" )), |
771 | children(AllOf(withName("waldo" ), withDetail("void ()" ), |
772 | symRange(Main.range("fullDef" ))))), |
773 | AllOf( |
774 | withName("FF4" ), withDetail("(foo)" ), |
775 | children(AllOf(withName("foo" ), symRange(Main.range("FooRange" )), |
776 | symNameRange(Main.range("FooSelectionRange" ))))))); |
777 | } |
778 | |
779 | TEST(DocumentSymbols, FuncTemplates) { |
780 | TestTU TU; |
781 | Annotations Source(R"cpp( |
782 | template <class T> |
783 | T foo() {} |
784 | |
785 | auto x = foo<int>(); |
786 | auto y = foo<double>(); |
787 | )cpp" ); |
788 | TU.Code = Source.code().str(); |
789 | // Make sure we only see the template declaration, not instantiations. |
790 | EXPECT_THAT(getSymbols(TU.build()), |
791 | ElementsAre(AllOf(withName("foo" ), withDetail("template T ()" )), |
792 | AllOf(withName("x" ), withDetail("int" )), |
793 | AllOf(withName("y" ), withDetail("double" )))); |
794 | } |
795 | |
796 | TEST(DocumentSymbols, UsingDirectives) { |
797 | TestTU TU; |
798 | Annotations Source(R"cpp( |
799 | namespace ns { |
800 | int foo; |
801 | } |
802 | |
803 | namespace ns_alias = ns; |
804 | |
805 | using namespace ::ns; // check we don't loose qualifiers. |
806 | using namespace ns_alias; // and namespace aliases. |
807 | )cpp" ); |
808 | TU.Code = Source.code().str(); |
809 | EXPECT_THAT(getSymbols(TU.build()), |
810 | ElementsAre(withName("ns" ), withName("ns_alias" ), |
811 | withName("using namespace ::ns" ), |
812 | withName("using namespace ns_alias" ))); |
813 | } |
814 | |
815 | TEST(DocumentSymbols, TempSpecs) { |
816 | TestTU TU; |
817 | TU.Code = R"cpp( |
818 | template <typename T, typename U, int X = 5> class Foo {}; |
819 | template <typename T> class Foo<int, T> {}; |
820 | template <> class Foo<bool, int> {}; |
821 | template <> class Foo<bool, int, 3> {}; |
822 | )cpp" ; |
823 | // Foo is higher ranked because of exact name match. |
824 | EXPECT_THAT(getSymbols(TU.build()), |
825 | UnorderedElementsAre( |
826 | AllOf(withName("Foo" ), withKind(SymbolKind::Class), |
827 | withDetail("template class" )), |
828 | AllOf(withName("Foo<int, T>" ), withKind(SymbolKind::Class), |
829 | withDetail("template class" )), |
830 | AllOf(withName("Foo<bool, int>" ), withKind(SymbolKind::Class), |
831 | withDetail("class" )), |
832 | AllOf(withName("Foo<bool, int, 3>" ), |
833 | withKind(SymbolKind::Class), withDetail("class" )))); |
834 | } |
835 | |
836 | TEST(DocumentSymbols, Qualifiers) { |
837 | TestTU TU; |
838 | TU.Code = R"cpp( |
839 | namespace foo { namespace bar { |
840 | struct Cls; |
841 | |
842 | int func1(); |
843 | int func2(); |
844 | int func3(); |
845 | int func4(); |
846 | }} |
847 | |
848 | struct foo::bar::Cls { }; |
849 | |
850 | int foo::bar::func1() { return 10; } |
851 | int ::foo::bar::func2() { return 20; } |
852 | |
853 | using namespace foo; |
854 | int bar::func3() { return 30; } |
855 | |
856 | namespace alias = foo::bar; |
857 | int ::alias::func4() { return 40; } |
858 | )cpp" ; |
859 | |
860 | // All the qualifiers should be preserved exactly as written. |
861 | EXPECT_THAT(getSymbols(TU.build()), |
862 | UnorderedElementsAre( |
863 | withName("foo" ), withName("foo::bar::Cls" ), |
864 | withName("foo::bar::func1" ), withName("::foo::bar::func2" ), |
865 | withName("using namespace foo" ), withName("bar::func3" ), |
866 | withName("alias" ), withName("::alias::func4" ))); |
867 | } |
868 | |
869 | TEST(DocumentSymbols, QualifiersWithTemplateArgs) { |
870 | TestTU TU; |
871 | TU.Code = R"cpp( |
872 | template <typename T, typename U = double> class Foo; |
873 | |
874 | template <> |
875 | class Foo<int, double> { |
876 | int method1(); |
877 | int method2(); |
878 | int method3(); |
879 | }; |
880 | |
881 | using int_type = int; |
882 | |
883 | // Typedefs should be preserved! |
884 | int Foo<int_type, double>::method1() { return 10; } |
885 | |
886 | // Default arguments should not be shown! |
887 | int Foo<int>::method2() { return 20; } |
888 | |
889 | using Foo_type = Foo<int>; |
890 | // If the whole type is aliased, this should be preserved too! |
891 | int Foo_type::method3() { return 30; } |
892 | )cpp" ; |
893 | EXPECT_THAT(getSymbols(TU.build()), |
894 | UnorderedElementsAre( |
895 | AllOf(withName("Foo" ), withDetail("template class" )), |
896 | AllOf(withName("Foo<int, double>" ), withDetail("class" )), |
897 | AllOf(withName("int_type" ), withDetail("type alias" )), |
898 | AllOf(withName("Foo<int_type, double>::method1" ), |
899 | withDetail("int ()" )), |
900 | AllOf(withName("Foo<int>::method2" ), withDetail("int ()" )), |
901 | AllOf(withName("Foo_type" ), withDetail("type alias" )), |
902 | AllOf(withName("Foo_type::method3" ), withDetail("int ()" )))); |
903 | } |
904 | |
905 | TEST(DocumentSymbolsTest, Ranges) { |
906 | TestTU TU; |
907 | Annotations Main(R"( |
908 | $foo[[int foo(bool Argument) { |
909 | return 42; |
910 | }]] |
911 | |
912 | $variable[[char GLOBAL_VARIABLE]]; |
913 | |
914 | $ns[[namespace ns { |
915 | $bar[[class Bar { |
916 | public: |
917 | $ctor[[Bar() {}]] |
918 | $dtor[[~Bar()]]; |
919 | |
920 | private: |
921 | $field[[unsigned Baz]]; |
922 | |
923 | $getbaz[[unsigned getBaz() { return Baz; }]] |
924 | }]]; |
925 | }]] // namespace ns |
926 | |
927 | $forwardclass[[class ForwardClassDecl]]; |
928 | |
929 | $struct[[struct StructDefinition { |
930 | $structfield[[int *Pointer = nullptr]]; |
931 | }]]; |
932 | $forwardstruct[[struct StructDeclaration]]; |
933 | |
934 | $forwardfunc[[void forwardFunctionDecl(int Something)]]; |
935 | )" ); |
936 | TU.Code = Main.code().str(); |
937 | EXPECT_THAT( |
938 | getSymbols(TU.build()), |
939 | UnorderedElementsAre( |
940 | AllOf(withName("foo" ), withKind(SymbolKind::Function), |
941 | withDetail("int (bool)" ), symRange(Main.range("foo" ))), |
942 | AllOf(withName("GLOBAL_VARIABLE" ), withKind(SymbolKind::Variable), |
943 | withDetail("char" ), symRange(Main.range("variable" ))), |
944 | AllOf( |
945 | withName("ns" ), withKind(SymbolKind::Namespace), |
946 | symRange(Main.range("ns" )), |
947 | children(AllOf( |
948 | withName("Bar" ), withKind(SymbolKind::Class), |
949 | withDetail("class" ), symRange(Main.range("bar" )), |
950 | children( |
951 | AllOf(withName("Bar" ), withKind(SymbolKind::Constructor), |
952 | withDetail("()" ), symRange(Main.range("ctor" ))), |
953 | AllOf(withName("~Bar" ), withKind(SymbolKind::Constructor), |
954 | withDetail("" ), symRange(Main.range("dtor" ))), |
955 | AllOf(withName("Baz" ), withKind(SymbolKind::Field), |
956 | withDetail("unsigned int" ), |
957 | symRange(Main.range("field" ))), |
958 | AllOf(withName("getBaz" ), withKind(SymbolKind::Method), |
959 | withDetail("unsigned int ()" ), |
960 | symRange(Main.range("getbaz" ))))))), |
961 | AllOf(withName("ForwardClassDecl" ), withKind(SymbolKind::Class), |
962 | withDetail("class" ), symRange(Main.range("forwardclass" ))), |
963 | AllOf(withName("StructDefinition" ), withKind(SymbolKind::Struct), |
964 | withDetail("struct" ), symRange(Main.range("struct" )), |
965 | children(AllOf(withName("Pointer" ), withKind(SymbolKind::Field), |
966 | withDetail("int *" ), |
967 | symRange(Main.range("structfield" ))))), |
968 | AllOf(withName("StructDeclaration" ), withKind(SymbolKind::Struct), |
969 | withDetail("struct" ), symRange(Main.range("forwardstruct" ))), |
970 | AllOf(withName("forwardFunctionDecl" ), withKind(SymbolKind::Function), |
971 | withDetail("void (int)" ), |
972 | symRange(Main.range("forwardfunc" ))))); |
973 | } |
974 | |
975 | TEST(DocumentSymbolsTest, DependentType) { |
976 | TestTU TU; |
977 | TU.Code = R"( |
978 | template <typename T> auto plus(T x, T y) -> decltype(x + y) { return x + y; } |
979 | |
980 | template <typename Key, typename Value> class Pair {}; |
981 | |
982 | template <typename Key, typename Value> |
983 | struct Context : public Pair<Key, Value> { |
984 | using Pair<Key, Value>::Pair; |
985 | }; |
986 | )" ; |
987 | EXPECT_THAT( |
988 | getSymbols(TU.build()), |
989 | ElementsAre( |
990 | AllOf(withName("plus" ), |
991 | withDetail("template auto (T, T) -> decltype(x + y)" )), |
992 | AllOf(withName("Pair" ), withDetail("template class" )), |
993 | AllOf(withName("Context" ), withDetail("template struct" ), |
994 | children(AllOf( |
995 | withName("Pair<type-parameter-0-0, type-parameter-0-1>" ), |
996 | withDetail("<dependent type>" )))))); |
997 | } |
998 | |
999 | TEST(DocumentSymbolsTest, ObjCCategoriesAndClassExtensions) { |
1000 | TestTU TU; |
1001 | TU.ExtraArgs = {"-xobjective-c++" , "-Wno-objc-root-class" }; |
1002 | Annotations Main(R"cpp( |
1003 | $Cat[[@interface Cat |
1004 | + (id)sharedCat; |
1005 | @end]] |
1006 | $SneakyCat[[@interface Cat (Sneaky) |
1007 | - (id)sneak:(id)behavior; |
1008 | @end]] |
1009 | |
1010 | $MeowCat[[@interface Cat () |
1011 | - (void)meow; |
1012 | @end]] |
1013 | $PurCat[[@interface Cat () |
1014 | - (void)pur; |
1015 | @end]] |
1016 | )cpp" ); |
1017 | TU.Code = Main.code().str(); |
1018 | EXPECT_THAT( |
1019 | getSymbols(TU.build()), |
1020 | ElementsAre( |
1021 | AllOf(withName("Cat" ), symRange(Main.range("Cat" )), |
1022 | children(AllOf(withName("+sharedCat" ), |
1023 | withKind(SymbolKind::Method)))), |
1024 | AllOf(withName("Cat(Sneaky)" ), symRange(Main.range("SneakyCat" )), |
1025 | children( |
1026 | AllOf(withName("-sneak:" ), withKind(SymbolKind::Method)))), |
1027 | AllOf( |
1028 | withName("Cat()" ), symRange(Main.range("MeowCat" )), |
1029 | children(AllOf(withName("-meow" ), withKind(SymbolKind::Method)))), |
1030 | AllOf(withName("Cat()" ), symRange(Main.range("PurCat" )), |
1031 | children( |
1032 | AllOf(withName("-pur" ), withKind(SymbolKind::Method)))))); |
1033 | } |
1034 | |
1035 | TEST(DocumentSymbolsTest, PragmaMarkGroups) { |
1036 | TestTU TU; |
1037 | TU.ExtraArgs = {"-xobjective-c++" , "-Wno-objc-root-class" }; |
1038 | Annotations Main(R"cpp( |
1039 | $DogDef[[@interface Dog |
1040 | @end]] |
1041 | |
1042 | $DogImpl[[@implementation Dog |
1043 | |
1044 | + (id)sharedDoggo { return 0; } |
1045 | |
1046 | #pragma $Overrides[[mark - Overrides |
1047 | |
1048 | - (id)init { |
1049 | return self; |
1050 | } |
1051 | - (void)bark {}]] |
1052 | |
1053 | #pragma $Specifics[[mark - Dog Specifics |
1054 | |
1055 | - (int)isAGoodBoy { |
1056 | return 1; |
1057 | }]] |
1058 | @]]end // FIXME: Why doesn't this include the 'end'? |
1059 | |
1060 | #pragma $End[[mark - End |
1061 | ]] |
1062 | )cpp" ); |
1063 | TU.Code = Main.code().str(); |
1064 | EXPECT_THAT( |
1065 | getSymbols(TU.build()), |
1066 | UnorderedElementsAre( |
1067 | AllOf(withName("Dog" ), symRange(Main.range("DogDef" ))), |
1068 | AllOf(withName("Dog" ), symRange(Main.range("DogImpl" )), |
1069 | children(AllOf(withName("+sharedDoggo" ), |
1070 | withKind(SymbolKind::Method)), |
1071 | AllOf(withName("Overrides" ), |
1072 | symRange(Main.range("Overrides" )), |
1073 | children(AllOf(withName("-init" ), |
1074 | withKind(SymbolKind::Method)), |
1075 | AllOf(withName("-bark" ), |
1076 | withKind(SymbolKind::Method)))), |
1077 | AllOf(withName("Dog Specifics" ), |
1078 | symRange(Main.range("Specifics" )), |
1079 | children(AllOf(withName("-isAGoodBoy" ), |
1080 | withKind(SymbolKind::Method)))))), |
1081 | AllOf(withName("End" ), symRange(Main.range("End" ))))); |
1082 | } |
1083 | |
1084 | TEST(DocumentSymbolsTest, PragmaMarkGroupsNesting) { |
1085 | TestTU TU; |
1086 | TU.ExtraArgs = {"-xobjective-c++" , "-Wno-objc-root-class" }; |
1087 | Annotations Main(R"cpp( |
1088 | #pragma mark - Foo |
1089 | struct Foo { |
1090 | #pragma mark - Bar |
1091 | void bar() { |
1092 | #pragma mark - NotTopDecl |
1093 | } |
1094 | }; |
1095 | void bar() {} |
1096 | )cpp" ); |
1097 | TU.Code = Main.code().str(); |
1098 | EXPECT_THAT( |
1099 | getSymbols(TU.build()), |
1100 | UnorderedElementsAre(AllOf( |
1101 | withName("Foo" ), |
1102 | children(AllOf(withName("Foo" ), |
1103 | children(AllOf(withName("Bar" ), |
1104 | children(AllOf(withName("bar" ), |
1105 | children(withName( |
1106 | "NotTopDecl" ))))))), |
1107 | withName("bar" ))))); |
1108 | } |
1109 | |
1110 | TEST(DocumentSymbolsTest, PragmaMarkGroupsNoNesting) { |
1111 | TestTU TU; |
1112 | TU.ExtraArgs = {"-xobjective-c++" , "-Wno-objc-root-class" }; |
1113 | Annotations Main(R"cpp( |
1114 | #pragma mark Helpers |
1115 | void helpA(id obj) {} |
1116 | |
1117 | #pragma mark - |
1118 | #pragma mark Core |
1119 | |
1120 | void coreMethod() {} |
1121 | )cpp" ); |
1122 | TU.Code = Main.code().str(); |
1123 | EXPECT_THAT(getSymbols(TU.build()), |
1124 | UnorderedElementsAre(withName("Helpers" ), withName("helpA" ), |
1125 | withName("(unnamed group)" ), |
1126 | withName("Core" ), withName("coreMethod" ))); |
1127 | } |
1128 | |
1129 | } // namespace |
1130 | } // namespace clangd |
1131 | } // namespace clang |
1132 | |