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() { return 0; } |
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 | 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 | |
554 | TEST(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 | |
596 | TEST(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 | |
634 | TEST(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 | |
667 | TEST(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 | |
736 | TEST(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 | |
781 | TEST(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 | |
798 | TEST(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 | |
817 | TEST(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 | |
838 | TEST(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 | |
871 | TEST(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 | |
907 | TEST(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 | |
977 | TEST(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 | |
1001 | TEST(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 | |
1037 | TEST(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 | |
1086 | TEST(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 | |
1112 | TEST(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 | |