diff --git a/clang-tools-extra/clangd/FindSymbols.cpp b/clang-tools-extra/clangd/FindSymbols.cpp --- a/clang-tools-extra/clangd/FindSymbols.cpp +++ b/clang-tools-extra/clangd/FindSymbols.cpp @@ -172,6 +172,38 @@ } namespace { +std::string getSymbolDetail(ASTContext &Ctx, const NamedDecl &ND) { + PrintingPolicy P(Ctx.getPrintingPolicy()); + P.SuppressScope = true; + P.SuppressUnwrittenScope = true; + P.AnonymousTagLocations = false; + P.PolishForDeclaration = true; + std::string Detail; + llvm::raw_string_ostream OS(Detail); + if (ND.getDescribedTemplateParams()) { + OS << "template "; + } + if (const auto *VD = dyn_cast(&ND)) { + // FIXME: better printing for dependent type + if (isa(VD)) { + std::string ConstructorType = VD->getType().getAsString(P); + // Print constructor type as "(int)" instead of "void (int)". + llvm::StringRef WithoutVoid = ConstructorType; + WithoutVoid.consume_front("void "); + OS << WithoutVoid; + } else if (!isa(VD)) { + VD->getType().print(OS, P); + } + } else if (const auto *TD = dyn_cast(&ND)) { + OS << TD->getKindName(); + } else if (isa(&ND)) { + OS << "type alias"; + } else if (isa(&ND)) { + OS << "concept"; + } + return std::move(OS.str()); +} + llvm::Optional declToSym(ASTContext &Ctx, const NamedDecl &ND) { auto &SM = Ctx.getSourceManager(); @@ -193,6 +225,7 @@ SI.deprecated = ND.isDeprecated(); SI.range = Range{sourceLocToPosition(SM, SymbolRange->getBegin()), sourceLocToPosition(SM, SymbolRange->getEnd())}; + SI.detail = getSymbolDetail(Ctx, ND); SourceLocation NameLoc = ND.getLocation(); SourceLocation FallbackNameLoc; diff --git a/clang-tools-extra/clangd/test/symbols.test b/clang-tools-extra/clangd/test/symbols.test --- a/clang-tools-extra/clangd/test/symbols.test +++ b/clang-tools-extra/clangd/test/symbols.test @@ -1,5 +1,5 @@ # RUN: clangd --index-file=%S/Inputs/symbols.test.yaml -lit-test < %s | FileCheck %s -{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"workspace":{"symbol":{"symbolKind":{"valueSet": [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]}}}},"trace":"off"}} +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"textDocument":{"documentSymbol":{"hierarchicalDocumentSymbolSupport":true}},"workspace":{"symbol":{"symbolKind":{"valueSet": [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]}}}},"trace":"off"}} --- {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"void foo(); int main() { foo(); }\n"}}} --- @@ -34,40 +34,54 @@ # CHECK-NEXT: "jsonrpc": "2.0", # CHECK-NEXT: "result": [ # CHECK-NEXT: { -# CHECK-NEXT: "containerName": "", +# CHECK-NEXT: "detail": "void ()", # CHECK-NEXT: "kind": 12, -# CHECK-NEXT: "location": { -# CHECK-NEXT: "range": { -# CHECK-NEXT: "end": { -# CHECK-NEXT: "character": {{.*}}, -# CHECK-NEXT: "line": {{.*}} -# CHECK-NEXT: }, -# CHECK-NEXT: "start": { -# CHECK-NEXT: "character": {{.*}}, -# CHECK-NEXT: "line": {{.*}} -# CHECK-NEXT: } +# CHECK-NEXT: "name": "foo", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": {{.*}}, +# CHECK-NEXT: "line": {{.*}} # CHECK-NEXT: }, -# CHECK-NEXT: "uri": "file://{{.*}}/main.cpp" +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": {{.*}}, +# CHECK-NEXT: "line": {{.*}} +# CHECK-NEXT: } # CHECK-NEXT: }, -# CHECK-NEXT: "name": "foo" -# CHECK-NEXT: } +# CHECK-NEXT: "selectionRange": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": {{.*}}, +# CHECK-NEXT: "line": {{.*}} +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": {{.*}}, +# CHECK-NEXT: "line": {{.*}} +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, # CHECK-NEXT: { -# CHECK-NEXT: "containerName": "", +# CHECK-NEXT: "detail": "int ()", # CHECK-NEXT: "kind": 12, -# CHECK-NEXT: "location": { -# CHECK-NEXT: "range": { -# CHECK-NEXT: "end": { -# CHECK-NEXT: "character": {{.*}}, -# CHECK-NEXT: "line": {{.*}} -# CHECK-NEXT: }, -# CHECK-NEXT: "start": { -# CHECK-NEXT: "character": {{.*}}, -# CHECK-NEXT: "line": {{.*}} -# CHECK-NEXT: } +# CHECK-NEXT: "name": "main", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": {{.*}}, +# CHECK-NEXT: "line": {{.*}} # CHECK-NEXT: }, -# CHECK-NEXT: "uri": "file://{{.*}}/main.cpp" +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": {{.*}}, +# CHECK-NEXT: "line": {{.*}} +# CHECK-NEXT: } # CHECK-NEXT: }, -# CHECK-NEXT: "name": "main" +# CHECK-NEXT: "selectionRange": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": {{.*}}, +# CHECK-NEXT: "line": {{.*}} +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": {{.*}}, +# CHECK-NEXT: "line": {{.*}} +# CHECK-NEXT: } +# CHECK-NEXT: } # CHECK-NEXT: } # CHECK-NEXT: ] # CHECK-NEXT:} diff --git a/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp b/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp --- a/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp @@ -35,6 +35,7 @@ } MATCHER_P(WithName, N, "") { return arg.name == N; } MATCHER_P(WithKind, Kind, "") { return arg.kind == Kind; } +MATCHER_P(WithDetail, Detail, "") { return arg.detail == Detail; } MATCHER_P(SymRange, Range, "") { return arg.range == Range; } // GMock helpers for matching DocumentSymbol. @@ -374,44 +375,55 @@ EXPECT_THAT( getSymbols(TU.build()), ElementsAreArray( - {AllOf(WithName("Foo"), WithKind(SymbolKind::Class), Children()), + {AllOf(WithName("Foo"), WithKind(SymbolKind::Class), + WithDetail("class"), Children()), AllOf(WithName("Foo"), WithKind(SymbolKind::Class), - Children(AllOf(WithName("Foo"), - WithKind(SymbolKind::Constructor), Children()), - AllOf(WithName("Foo"), - WithKind(SymbolKind::Constructor), Children()), - AllOf(WithName("f"), WithKind(SymbolKind::Method), - Children()), - AllOf(WithName("operator="), - WithKind(SymbolKind::Method), Children()), - AllOf(WithName("~Foo"), - WithKind(SymbolKind::Constructor), Children()), - AllOf(WithName("Nested"), WithKind(SymbolKind::Class), - Children(AllOf(WithName("f"), - WithKind(SymbolKind::Method), - Children()))))), - AllOf(WithName("Friend"), WithKind(SymbolKind::Class), Children()), - AllOf(WithName("f1"), WithKind(SymbolKind::Function), Children()), - AllOf(WithName("f2"), WithKind(SymbolKind::Function), Children()), - AllOf(WithName("KInt"), WithKind(SymbolKind::Variable), Children()), - AllOf(WithName("kStr"), WithKind(SymbolKind::Variable), Children()), - AllOf(WithName("f1"), WithKind(SymbolKind::Function), Children()), + WithDetail("class"), + Children( + AllOf(WithName("Foo"), WithKind(SymbolKind::Constructor), + WithDetail("()"), Children()), + AllOf(WithName("Foo"), WithKind(SymbolKind::Constructor), + WithDetail("(int)"), Children()), + AllOf(WithName("f"), WithKind(SymbolKind::Method), + WithDetail("void ()"), Children()), + AllOf(WithName("operator="), WithKind(SymbolKind::Method), + WithDetail("Foo &(const Foo &)"), Children()), + AllOf(WithName("~Foo"), WithKind(SymbolKind::Constructor), + WithDetail(""), Children()), + AllOf(WithName("Nested"), WithKind(SymbolKind::Class), + WithDetail("class"), + Children(AllOf( + WithName("f"), WithKind(SymbolKind::Method), + WithDetail("void ()"), Children()))))), + AllOf(WithName("Friend"), WithKind(SymbolKind::Class), + WithDetail("class"), Children()), + AllOf(WithName("f1"), WithKind(SymbolKind::Function), + WithDetail("void ()"), Children()), + AllOf(WithName("f2"), WithKind(SymbolKind::Function), + WithDetail("void ()"), Children()), + AllOf(WithName("KInt"), WithKind(SymbolKind::Variable), + WithDetail("const int"), Children()), + AllOf(WithName("kStr"), WithKind(SymbolKind::Variable), + WithDetail("const char *"), Children()), + AllOf(WithName("f1"), WithKind(SymbolKind::Function), + WithDetail("void ()"), Children()), AllOf( - WithName("foo"), WithKind(SymbolKind::Namespace), - Children( - AllOf(WithName("int32"), WithKind(SymbolKind::Class), - Children()), - AllOf(WithName("int32_t"), WithKind(SymbolKind::Class), - Children()), - AllOf(WithName("v1"), WithKind(SymbolKind::Variable), - Children()), - AllOf(WithName("bar"), WithKind(SymbolKind::Namespace), - Children(AllOf(WithName("v2"), - WithKind(SymbolKind::Variable), - Children()))), - AllOf(WithName("baz"), WithKind(SymbolKind::Namespace), - Children()), - AllOf(WithName("v2"), WithKind(SymbolKind::Namespace))))})); + WithName("foo"), WithKind(SymbolKind::Namespace), WithDetail(""), + Children(AllOf(WithName("int32"), WithKind(SymbolKind::Class), + WithDetail("type alias"), Children()), + AllOf(WithName("int32_t"), WithKind(SymbolKind::Class), + WithDetail("type alias"), Children()), + AllOf(WithName("v1"), WithKind(SymbolKind::Variable), + WithDetail("int"), Children()), + AllOf(WithName("bar"), WithKind(SymbolKind::Namespace), + WithDetail(""), + Children(AllOf(WithName("v2"), + WithKind(SymbolKind::Variable), + WithDetail("int"), Children()))), + AllOf(WithName("baz"), WithKind(SymbolKind::Namespace), + WithDetail(""), Children()), + AllOf(WithName("v2"), WithKind(SymbolKind::Namespace), + WithDetail(""))))})); } TEST(DocumentSymbols, DeclarationDefinition) { @@ -429,10 +441,12 @@ getSymbols(TU.build()), ElementsAre( AllOf(WithName("Foo"), WithKind(SymbolKind::Class), + WithDetail("class"), Children(AllOf(WithName("f"), WithKind(SymbolKind::Method), + WithDetail("void ()"), SymNameRange(Main.range("decl"))))), AllOf(WithName("Foo::f"), WithKind(SymbolKind::Method), - SymNameRange(Main.range("def"))))); + WithDetail("void ()"), SymNameRange(Main.range("def"))))); } TEST(DocumentSymbols, Concepts) { @@ -440,7 +454,8 @@ TU.ExtraArgs = {"-std=c++20"}; TU.Code = "template concept C = requires(T t) { t.foo(); };"; - EXPECT_THAT(getSymbols(TU.build()), ElementsAre(WithName("C"))); + EXPECT_THAT(getSymbols(TU.build()), + ElementsAre(AllOf(WithName("C"), WithDetail("concept")))); } TEST(DocumentSymbols, ExternSymbol) { @@ -508,12 +523,14 @@ )cpp"; EXPECT_THAT( getSymbols(TU.build()), - ElementsAre( - AllOf(WithName("(anonymous struct)"), WithKind(SymbolKind::Struct), - Children(AllOf(WithName("InUnnamed"), - WithKind(SymbolKind::Field), Children()))), - AllOf(WithName("UnnamedStruct"), WithKind(SymbolKind::Variable), - Children()))); + ElementsAre(AllOf(WithName("(anonymous struct)"), + WithKind(SymbolKind::Struct), WithDetail("struct"), + Children(AllOf(WithName("InUnnamed"), + WithKind(SymbolKind::Field), + WithDetail("int"), Children()))), + AllOf(WithName("UnnamedStruct"), + WithKind(SymbolKind::Variable), + WithDetail("struct (anonymous)"), Children()))); } TEST(DocumentSymbols, InHeaderFile) { @@ -556,17 +573,22 @@ getSymbols(TU.build()), ElementsAre( AllOf(WithName("Tmpl"), WithKind(SymbolKind::Struct), - Children(AllOf(WithName("x"), WithKind(SymbolKind::Field)))), + WithDetail("template struct"), + Children(AllOf(WithName("x"), WithKind(SymbolKind::Field), + WithDetail("T")))), AllOf(WithName("Tmpl"), WithKind(SymbolKind::Struct), - Children(WithName("y"))), + WithDetail("struct"), + Children(AllOf(WithName("y"), WithDetail("int")))), AllOf(WithName("Tmpl"), WithKind(SymbolKind::Struct), - Children()), + WithDetail("struct"), Children()), AllOf(WithName("Tmpl"), WithKind(SymbolKind::Struct), + WithDetail("struct"), Children()), + AllOf(WithName("funcTmpl"), WithDetail("template int (U)"), + Children()), + AllOf(WithName("funcTmpl"), WithDetail("int (double)"), Children()), - AllOf(WithName("funcTmpl"), Children()), - AllOf(WithName("funcTmpl"), Children()), - AllOf(WithName("varTmpl"), Children()), - AllOf(WithName("varTmpl"), Children()))); + AllOf(WithName("varTmpl"), WithDetail("template int"), Children()), + AllOf(WithName("varTmpl"), WithDetail("double"), Children()))); } TEST(DocumentSymbols, Namespaces) { @@ -628,11 +650,16 @@ EXPECT_THAT( getSymbols(TU.build()), ElementsAre( - AllOf(WithName("(anonymous enum)"), Children(WithName("Red"))), - AllOf(WithName("Color"), Children(WithName("Green"))), - AllOf(WithName("Color2"), Children(WithName("Yellow"))), - AllOf(WithName("ns"), Children(AllOf(WithName("(anonymous enum)"), - Children(WithName("Black"))))))); + AllOf(WithName("(anonymous enum)"), WithDetail("enum"), + Children(AllOf(WithName("Red"), WithDetail("(anonymous)")))), + AllOf(WithName("Color"), WithDetail("enum"), + Children(AllOf(WithName("Green"), WithDetail("Color")))), + AllOf(WithName("Color2"), WithDetail("enum"), + Children(AllOf(WithName("Yellow"), WithDetail("Color2")))), + AllOf(WithName("ns"), + Children(AllOf(WithName("(anonymous enum)"), WithDetail("enum"), + Children(AllOf(WithName("Black"), + WithDetail("(anonymous)")))))))); } TEST(DocumentSymbols, FromMacro) { @@ -656,12 +683,13 @@ }]] )"); TU.Code = Main.code().str(); - EXPECT_THAT( - getSymbols(TU.build()), - ElementsAre( - AllOf(WithName("abc_Test"), SymNameRange(Main.range("expansion1"))), - AllOf(WithName("Test"), SymNameRange(Main.range("expansion2"))), - AllOf(WithName("waldo"), SymRange(Main.range("fullDef"))))); + EXPECT_THAT(getSymbols(TU.build()), + ElementsAre(AllOf(WithName("abc_Test"), WithDetail("class"), + SymNameRange(Main.range("expansion1"))), + AllOf(WithName("Test"), WithDetail("class"), + SymNameRange(Main.range("expansion2"))), + AllOf(WithName("waldo"), WithDetail("void ()"), + SymRange(Main.range("fullDef"))))); } TEST(DocumentSymbols, FuncTemplates) { @@ -676,7 +704,9 @@ TU.Code = Source.code().str(); // Make sure we only see the template declaration, not instantiations. EXPECT_THAT(getSymbols(TU.build()), - ElementsAre(WithName("foo"), WithName("x"), WithName("y"))); + ElementsAre(AllOf(WithName("foo"), WithDetail("template T ()")), + AllOf(WithName("x"), WithDetail("int")), + AllOf(WithName("y"), WithDetail("double")))); } TEST(DocumentSymbols, UsingDirectives) { @@ -707,13 +737,16 @@ template <> class Foo {}; )cpp"; // Foo is higher ranked because of exact name match. - EXPECT_THAT( - getSymbols(TU.build()), - UnorderedElementsAre( - AllOf(WithName("Foo"), WithKind(SymbolKind::Class)), - AllOf(WithName("Foo"), WithKind(SymbolKind::Class)), - AllOf(WithName("Foo"), WithKind(SymbolKind::Class)), - AllOf(WithName("Foo"), WithKind(SymbolKind::Class)))); + EXPECT_THAT(getSymbols(TU.build()), + UnorderedElementsAre( + AllOf(WithName("Foo"), WithKind(SymbolKind::Class), + WithDetail("template class")), + AllOf(WithName("Foo"), WithKind(SymbolKind::Class), + WithDetail("template class")), + AllOf(WithName("Foo"), WithKind(SymbolKind::Class), + WithDetail("class")), + AllOf(WithName("Foo"), + WithKind(SymbolKind::Class), WithDetail("class")))); } TEST(DocumentSymbols, Qualifiers) { @@ -773,13 +806,16 @@ // If the whole type is aliased, this should be preserved too! int Foo_type::method3() { return 30; } )cpp"; - EXPECT_THAT( - getSymbols(TU.build()), - UnorderedElementsAre(WithName("Foo"), WithName("Foo"), - WithName("int_type"), - WithName("Foo::method1"), - WithName("Foo::method2"), WithName("Foo_type"), - WithName("Foo_type::method3"))); + EXPECT_THAT(getSymbols(TU.build()), + UnorderedElementsAre( + AllOf(WithName("Foo"), WithDetail("template class")), + AllOf(WithName("Foo"), WithDetail("class")), + AllOf(WithName("int_type"), WithDetail("type alias")), + AllOf(WithName("Foo::method1"), + WithDetail("int ()")), + AllOf(WithName("Foo::method2"), WithDetail("int ()")), + AllOf(WithName("Foo_type"), WithDetail("type alias")), + AllOf(WithName("Foo_type::method3"), WithDetail("int ()")))); } TEST(DocumentSymbolsTest, Ranges) { @@ -818,36 +854,64 @@ getSymbols(TU.build()), UnorderedElementsAre( AllOf(WithName("foo"), WithKind(SymbolKind::Function), - SymRange(Main.range("foo"))), + WithDetail("int (bool)"), SymRange(Main.range("foo"))), AllOf(WithName("GLOBAL_VARIABLE"), WithKind(SymbolKind::Variable), - SymRange(Main.range("variable"))), + WithDetail("char"), SymRange(Main.range("variable"))), AllOf( WithName("ns"), WithKind(SymbolKind::Namespace), SymRange(Main.range("ns")), Children(AllOf( WithName("Bar"), WithKind(SymbolKind::Class), - SymRange(Main.range("bar")), + WithDetail("class"), SymRange(Main.range("bar")), Children( AllOf(WithName("Bar"), WithKind(SymbolKind::Constructor), - SymRange(Main.range("ctor"))), + WithDetail("()"), SymRange(Main.range("ctor"))), AllOf(WithName("~Bar"), WithKind(SymbolKind::Constructor), - SymRange(Main.range("dtor"))), + WithDetail(""), SymRange(Main.range("dtor"))), AllOf(WithName("Baz"), WithKind(SymbolKind::Field), + WithDetail("unsigned int"), SymRange(Main.range("field"))), AllOf(WithName("getBaz"), WithKind(SymbolKind::Method), + WithDetail("unsigned int ()"), SymRange(Main.range("getbaz"))))))), AllOf(WithName("ForwardClassDecl"), WithKind(SymbolKind::Class), - SymRange(Main.range("forwardclass"))), + WithDetail("class"), SymRange(Main.range("forwardclass"))), AllOf(WithName("StructDefinition"), WithKind(SymbolKind::Struct), - SymRange(Main.range("struct")), + WithDetail("struct"), SymRange(Main.range("struct")), Children(AllOf(WithName("Pointer"), WithKind(SymbolKind::Field), + WithDetail("int *"), SymRange(Main.range("structfield"))))), AllOf(WithName("StructDeclaration"), WithKind(SymbolKind::Struct), - SymRange(Main.range("forwardstruct"))), + WithDetail("struct"), SymRange(Main.range("forwardstruct"))), AllOf(WithName("forwardFunctionDecl"), WithKind(SymbolKind::Function), + WithDetail("void (int)"), SymRange(Main.range("forwardfunc"))))); } +TEST(DocumentSymbolsTest, DependentType) { + TestTU TU; + TU.Code = R"( + template auto plus(T x, T y) -> decltype(x + y) { return x + y; } + + template class Pair {}; + + template + struct Context : public Pair { + using Pair::Pair; + }; + )"; + EXPECT_THAT( + getSymbols(TU.build()), + ElementsAre( + AllOf(WithName("plus"), + WithDetail("template auto (T, T) -> decltype(x + y)")), + AllOf(WithName("Pair"), WithDetail("template class")), + AllOf(WithName("Context"), WithDetail("template struct"), + Children(AllOf( + WithName("Pair"), + WithDetail("")))))); +} + } // namespace } // namespace clangd } // namespace clang