Document Symbols
Provides the file outline and breadcrumb navigation via textDocument/documentSymbol: a nested symbol tree with ranges, selection ranges and a detail field that disambiguates overloads and shows declared types.
Symbol Hierarchy
Nested symbol tree — symbols nest by their written scope; out-of-line definitions appear at their lexical position with qualified names
Example
cppnamespace demo { struct Point { int x; int y; int manhattan() const; }; int Point::manhattan() const { return x + y; } enum class Axis { X, Y }; int origin_distance(const Point& p); namespace inner { constexpr int level = 2; } } // namespace demo // A reopened namespace gets its own outline node per written scope. namespace demo { int reopened(); } namespace demo::nested { int compact(); }Symbol ranges and selection ranges — the range spans the whole declaration; the selection range covers the full written name, including multi-token names like
~Widget,operator==andoperator boolExample
cppnamespace members { struct Widget { Widget(); explicit Widget(int size); ~Widget(); Widget& operator=(const Widget& other); bool operator==(const Widget& other) const; operator bool() const; static int instances(); int size; unsigned bits : 3; const char* name = "widget"; }; Widget::Widget(int size) : size(size), bits(0) {} int Widget::instances() { return 0; } } // namespace membersAccess specifier grouping —
public:/private:/protected:as grouping nodes for breadcrumb navigation (clangd#499)Example
cppclass Widget { public: void draw(); void resize(); private: int width; int height; };Anonymous and inline scopes — anonymous namespaces, unnamed structs and unions group their members under a placeholder name; inline namespace members stay under the inline namespace node
Example
cppnamespace { int hidden_counter = 0; } // namespace namespace misc { inline namespace v1 { int versioned(); } // namespace v1 struct Outer { struct { int anonymous_member; }; union { int as_int; float as_float; }; }; } // namespace miscUTF-16 position encoding — columns after non-ASCII text count UTF-16 code units
Example
cpp// π ≈ 3.14159, 中文注释 constexpr double 半径 = 2.0; constexpr double π值 = 3.14159; double area();
Symbol Kinds
Core symbol kinds — namespaces, classes, structs, unions, enums and their members, functions, variables, fields, structured bindings and lambdas all appear in the outline with a mapped LSP symbol kind
Example
cppnamespace kinds { union Value { int i; float f; }; enum Flags { FlagA, FlagB }; enum class Mode : unsigned char { Fast, Safe }; struct Pair { struct Meta { int tag; }; int first; int second; static int instances; }; Pair make_pair(); auto [bound_first, bound_second] = make_pair(); auto lambda = [](int x) { return x * 2; }; } // namespace kindsTemplate declarations — class, function and variable templates carry a
templatedetail prefix; concepts and abbreviated function templates (concept autoparameters) appear as wellExample
cppnamespace templates { template <typename T> struct Box { T value; void reset(); }; template <typename T> void Box<T>::reset() {} template <typename T> T zero() { return T(); } template <typename T> constexpr T pi = T(3.14159); template <typename T> concept Small = sizeof(T) <= 4; void takes_concept(Small auto x); } // namespace templatesTemplate specializations and deduction guides — explicit and partial specializations of class and variable templates appear with their template arguments in the name; members nest under their specialization; deduction guides render their deduced signature
Example
cppnamespace spec { template <typename T> struct Box { T value; }; template <> struct Box<void> {}; template <typename T> struct Box<T*> { T* pointee; }; template <typename T> T zero() { return T(); } template <> int zero<int>(); template <typename T> constexpr T pi = T(3); template <> constexpr int pi<int> = 3; template <typename T> constexpr T* pi<T*> = nullptr; template <typename T> struct Deduced { Deduced(T raw); }; template <typename T> Deduced(T*) -> Deduced<T>; // Forces the implicit instantiation Box<int>, which must not appear. Box<int> instantiated; // An explicit class instantiation gets a childless node; the instantiated // members and the function instantiation (whose location clang records at // the primary) produce no symbols. template struct Box<char>; template long zero<long>(); } // namespace specType aliases —
typedef,usingaliases and alias templates appear in the outline with atype aliasdetailExample
cppnamespace aliases { struct Widget {}; typedef Widget LegacyWidget; using ModernWidget = Widget; template <typename T> struct Box {}; template <typename T> using BoxOf = Box<T>; struct Holder { using Inner = Widget; }; } // namespace aliasesExplicit instantiation directives — the class forms appear as childless symbols; clang mislocates the function and variable forms at the pattern, so they are missing from the outline (partial) (llvm#191658)
Example
cpptemplate <typename T> struct Box { T value; }; template struct Box<int>; extern template struct Box<char>; template <typename T> void convert(T value) {} template void convert<int>(int); template <typename T> T zero = T(); template int zero<int>;
Symbol Detail
Function signatures — parameter and return types in the
detailfield disambiguate overloads; constructors drop thevoidreturn type (clangd#520, clangd#601, clangd#1232)Example
cppnamespace detail { void process(int x); void process(const char* s); struct Task { Task(); Task(int priority); int run(bool async) const; }; } // namespace detailVariable and field types — the declared type in the
detailfield; lambdas render as(lambda)Example
cppnamespace detail { int timeout = 30; const char* logger_name = "core"; struct Config { unsigned retries; double backoff; }; auto on_error = [](int code) { return code != 0; }; } // namespace detailDefault argument stripping — the signature is derived from the function type, so default parameter values never leak into the outline (clangd#221)
Example
cppnamespace detail { void open_file(const char* path, int mode = 0644); struct Server { void listen(int port = 8080, int backlog = 128); }; } // namespace detailBase classes in detail — show
: Shapeon derived class declarationsExample
cppstruct Shape {}; struct Circle : Shape { double radius; };Multiline signature ranges — the symbol range starts at the beginning of the declaration and spans the full signature, so editor sticky scroll anchors correctly (clangd#2221)
Example
cppstruct Config {}; void process_data( const Config& cfg, int flags ) {}Scoped types — a written class scope appears in the detail exactly once, for nested classes, template-ids, aliases and dependent names alike
Example
cppnamespace scoped { struct Outer { struct Inner {}; template <typename T> struct Box {}; using Alias = int; }; struct User { Outer::Inner plain; Outer::Box<int> boxed; Outer::Alias aliased; const Outer::Inner frozen; }; template <typename T> struct Holder { typename T::type value; typename T::inner::type deep; typename T::template rebind<int> bound; }; } // namespace scoped
Missing Symbols
Macro definitions — object-like and function-like macro definitions in the outline (clangd#1744)
Example
cpp#define MAX_BUFFER_SIZE 4096 #define CHECK(cond, msg) ((cond) ? 0 : (msg))Include directives —
#includeentries in the outline (clangd#2226)Example
cpp#include "config.h" int uses_config();Local symbols — variables and types declared inside function bodies nest under their function (clangd#616)
Example
cppint compute() { int local_sum = 0; struct Accumulator { int total; }; auto twice = [](int x) { return 2 * x; }; struct Pair { int a; int b; }; auto [first, second] = Pair{1, 2}; return local_sum + twice(first) + second; }Module declarations —
export module,moduleandimportdeclarations in the outlineExample
cppexport module app.core; import std; export int core_entry();#pragma marknavigation markers — editor section markers as outline entriesExample
cpp#pragma mark - Lifecycle void setup(); #pragma mark - Rendering void draw();Friend function definitions — a friend function defined inline in a class appears under that class
Example
cppstruct Owner { friend void inline_friend(Owner& o) {} friend bool operator==(const Owner& lhs, const Owner& rhs) { return &lhs == &rhs; } };
Symbol Tags
Deprecated tag — mark
[[deprecated]]symbols with the LSPdeprecatedsymbol tagExample
cpp[[deprecated("use open_v2")]] void open_v1(); void open_v2();Access and storage indicators — public / private / protected, static, virtual and abstract markers on outline entries (clangd#2123)
Example
cppclass Base { public: virtual void render() = 0; protected: static int instances(); private: int id; };
Location Correctness
Symbols from macro expansions — a symbol produced by a macro invocation is located at the invocation, not at the macro definition (clangd#475)
Example
cpp#define DEFINE_HANDLER(name) void name() DEFINE_HANDLER(on_ready); DEFINE_HANDLER(on_close); #define DECLARE_CLASS(X) class X DECLARE_CLASS(Generated) { int member; };Names spelled in macro arguments — the selection range points at the name written in the macro argument; names spelled in the macro body fall back to the invocation site (clangd#1941)
Example
cpp#define VAR(X) int X = 1; VAR(from_argument) #define COUNTER() int counter_from_body = 0; COUNTER()
Changelog
| Date | Change | PR |
|---|---|---|
| 2026-08-01 | Explicit instantiation directives pinned: class forms as childless symbols, function and variable forms missing until clang 23 | #571 |
| 2026-08-01 | Template specializations, type aliases, full-name selection ranges, macro-argument names; traversal moved onto the semantics node table | #566 |
| 2025-01-13 | Nested symbol hierarchy, basic symbol kinds | #17 |