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