# 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"}}}
---
# 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:}
}
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.
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) {
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) {
TU.ExtraArgs = {"-std=c++20"};
TU.Code = "template <typename T> 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) {
)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) {
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<int>"), WithKind(SymbolKind::Struct),
- Children(WithName("y"))),
+ WithDetail("struct"),
+ Children(AllOf(WithName("y"), WithDetail("int")))),
AllOf(WithName("Tmpl<float>"), WithKind(SymbolKind::Struct),
- Children()),
+ WithDetail("struct"), Children()),
AllOf(WithName("Tmpl<double>"), WithKind(SymbolKind::Struct),
+ WithDetail("struct"), Children()),
+ AllOf(WithName("funcTmpl"), WithDetail("template int (U)"),
+ Children()),
+ AllOf(WithName("funcTmpl<int>"), WithDetail("int (double)"),
Children()),
- AllOf(WithName("funcTmpl"), Children()),
- AllOf(WithName("funcTmpl<int>"), Children()),
- AllOf(WithName("varTmpl"), Children()),
- AllOf(WithName("varTmpl<int>"), Children())));
+ AllOf(WithName("varTmpl"), WithDetail("template int"), Children()),
+ AllOf(WithName("varTmpl<int>"), WithDetail("double"), Children())));
}
TEST(DocumentSymbols, Namespaces) {
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) {
}]]
)");
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) {
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) {
template <> class Foo<bool, int, 3> {};
)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<int, T>"), WithKind(SymbolKind::Class)),
- AllOf(WithName("Foo<bool, int>"), WithKind(SymbolKind::Class)),
- AllOf(WithName("Foo<bool, int, 3>"), WithKind(SymbolKind::Class))));
+ EXPECT_THAT(getSymbols(TU.build()),
+ UnorderedElementsAre(
+ AllOf(WithName("Foo"), WithKind(SymbolKind::Class),
+ WithDetail("template class")),
+ AllOf(WithName("Foo<int, T>"), WithKind(SymbolKind::Class),
+ WithDetail("template class")),
+ AllOf(WithName("Foo<bool, int>"), WithKind(SymbolKind::Class),
+ WithDetail("class")),
+ AllOf(WithName("Foo<bool, int, 3>"),
+ WithKind(SymbolKind::Class), WithDetail("class"))));
}
TEST(DocumentSymbols, Qualifiers) {
// 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<int, double>"),
- WithName("int_type"),
- WithName("Foo<int_type, double>::method1"),
- WithName("Foo<int>::method2"), WithName("Foo_type"),
- WithName("Foo_type::method3")));
+ EXPECT_THAT(getSymbols(TU.build()),
+ UnorderedElementsAre(
+ AllOf(WithName("Foo"), WithDetail("template class")),
+ AllOf(WithName("Foo<int, double>"), WithDetail("class")),
+ AllOf(WithName("int_type"), WithDetail("type alias")),
+ AllOf(WithName("Foo<int_type, double>::method1"),
+ WithDetail("int ()")),
+ AllOf(WithName("Foo<int>::method2"), WithDetail("int ()")),
+ AllOf(WithName("Foo_type"), WithDetail("type alias")),
+ AllOf(WithName("Foo_type::method3"), WithDetail("int ()"))));
}
TEST(DocumentSymbolsTest, Ranges) {
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 <typename T> auto plus(T x, T y) -> decltype(x + y) { return x + y; }
+
+ template <typename Key, typename Value> class Pair {};
+
+ template <typename Key, typename Value>
+ struct Context : public Pair<Key, Value> {
+ using Pair<Key, Value>::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<type-parameter-0-0, type-parameter-0-1>"),
+ WithDetail("<dependent type>"))))));
+}
+
} // namespace
} // namespace clangd
} // namespace clang