| 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 | |