Code Navigation
Go to Definition
Index-based cross-TU go-to-definition
Go to definition on
#includedirectives (navigate to the included file)AST-based fallback for local/unsaved symbols
Navigate through macro wrappers to the underlying declaration
cpp#define DECLARE_HANDLER(name) void name() DECLARE_HANDLER(onReady); // go-to-def on onReady → should reach the underlying functionError recovery: navigate to variable definition even when its type is unresolved
Dependent type navigation in uninstantiated templates
cpptemplate<typename T> void process(std::vector<T>& v) { v.push_back(val); // go-to-def on push_back → vector::push_back }Template specialization → primary template (clangd#212)
cpptemplate<typename T> struct Formatter { ... }; // primary template template<> struct Formatter<std::string> { }; // go-to-def on Formatter → primary templateautokeyword → deduced type (clangd#2055)cppauto widget = getWidget(); // go-to-def on auto → Widget (the deduced type)
Implicit Code Navigation
Navigate to definitions of implicitly invoked code. In C++ many constructs generate hidden calls to constructors, operators, conversions, etc. Navigating from the syntactic construct (a brace, a keyword, an operator token) to the actual function being called is essential for understanding what code is really executing.
Implicit navigation requires an unambiguous source token — patterns where the token already has a well-defined go-to-def target (e.g., a variable name always goes to its declaration) cannot be repurposed for implicit call navigation.
Keywords
override/final→ the overridden base class virtual methodcppstruct Base { virtual void draw(); }; struct Derived : Base { void draw() override; // go-to-def on override → Base::draw };break/continue→ enclosing loop or switch head (clangd#1921). See also Document Highlight for highlighting all related control flow tokens in context.
Construction & destruction
Constructor calls — from parentheses/braces to the selected constructor
cppstruct Widget { Widget(int w, int h); }; Widget w(800, 600); // go-to-def on ( → Widget(int, int) Widget w2{800, 600}; // go-to-def on { → same auto w3 = Widget(1, 2); // go-to-def on ( → sameCopy/move construction and assignment
cppWidget a(1, 2); Widget b = a; // go-to-def on = → Widget(const Widget&) Widget c = std::move(a); // go-to-def on = → Widget(Widget&&) b = c; // go-to-def on = → operator=(const Widget&)CTAD — navigate to the selected constructor
cppstd::vector v{1, 2, 3}; // go-to-def on { → vector(initializer_list<int>)Aggregate initialization → struct definition
cppstruct Point { int x, y; }; auto p = Point{1, 2}; // go-to-def on { → Pointdeleteexpression → destructorcppdelete widget; // go-to-def on delete → Widget::~Widgetnewexpression → constructor (and customoperator newif overloaded)cppstruct Pool { static void* operator new(size_t); }; auto* p = new Pool(); // go-to-def on new → Pool() constructor // also: Pool::operator new (if overloaded) auto* arr = new Pool[10]; // go-to-def on new → Pool() default constructorMember initializer list → base class and member constructors
cppstruct Base { Base(int); }; struct Logger { Logger(std::string name); }; struct App : Base { Logger logger; App() : Base(42), logger("app") {} // go-to-def on Base → Base::Base(int) // go-to-def on logger → Logger(std::string) };Delegating constructors
cppstruct Widget { Widget(int w, int h); Widget() : Widget(0, 0) {} // go-to-def on Widget → Widget(int, int) };Inherited constructors — navigate to the base constructors brought in by
usingcppstruct Base { Base(int x); Base(int x, int y); }; struct Derived : Base { using Base::Base; // go-to-def on Base::Base → list Base's constructors };Return value implicit construction
cppWidget create() { return {800, 600}; // go-to-def on { → Widget(int, int) }Lambda init-capture → constructor
cppWidget w; auto f = [w = std::move(w)] {}; // go-to-def on = → Widget(Widget&&) auto g = [s = std::string("hi")] {}; // go-to-def on = → string(const char*)
Operators
Overloaded operators — from the operator token to its definition
cppVec a, b; auto c = a + b; // go-to-def on + → Vec::operator+ a += b; // go-to-def on += → Vec::operator+= ++it; // go-to-def on ++ → iterator::operator++ v[0]; // go-to-def on [ → vector::operator[] fn(42); // go-to-def on ( → Functor::operator() ptr->member; // go-to-def on -> → SmartPtr::operator->C++20 rewritten operators — navigate to the actual operator used by the rewrite
cppstruct S { bool operator==(const S&) const; auto operator<=>(const S&) const = default; }; S a, b; a != b; // go-to-def on != → S::operator== a > b; // go-to-def on > → S::operator<=>User-defined literals
cppusing namespace std::chrono_literals; auto d = 500ms; // go-to-def on ms → operator""ms
Conversions
Implicit conversion operators — from contexts where a conversion is invoked
cppstruct Guard { explicit operator bool() const; }; Guard g; if (g) {} // go-to-def on ( → Guard::operator bool() while (g) {} // same !g; // go-to-def on ! → Guard::operator bool() ([clangd#1931](https://github.com/clangd/clangd/issues/1931)) bool ok = bool(g); // go-to-def on bool( → Guard::operator bool()Casts invoking constructor or conversion operator
cppstruct Meters { explicit operator double() const; }; Meters m; double d = static_cast<double>(m); // go-to-def on static_cast → Meters::operator double() struct Foo { explicit Foo(int); }; auto f = static_cast<Foo>(42); // go-to-def on static_cast → Foo(int)
Range-for & structured bindings
Range-based for — navigate to
begin()/end()cppstd::vector<int> v; for (auto& x : v) {} // go-to-def on : → vector::beginStructured bindings — navigate to the underlying accessors or fields
cppstd::map<int, std::string> m; for (auto& [key, val] : m) {} // go-to-def on key → pair::first // go-to-def on val → pair::second
Coroutines
co_await/co_yield/co_return→ the corresponding awaiter/promise methodcppco_await some_awaitable; // go-to-def on co_await → operator co_await() or await_resume() co_yield value; // go-to-def on co_yield → promise::yield_value() co_return result; // go-to-def on co_return → promise::return_value()
Go to Declaration
Navigate from a symbol usage or definition to its declaration. In C++, many entities have separate declarations and definitions; go-to-declaration always targets the declaration side.
Functions — from usage or out-of-line definition to the declaration/prototype
cpp// widget.h class Widget { void draw(); // declaration }; // widget.cpp void Widget::draw() { } // out-of-line definition // go-to-decl from usage or definition → in-class declaration in widget.hForward declarations of classes and structs
cppclass Widget; // forward declaration in fwd.h class Widget { ... }; // full definition in widget.h // go-to-decl on Widget (from usage or definition) → forward declarationStatic data member → in-class declaration
cppstruct Config { static int timeout; // declaration }; int Config::timeout = 30; // out-of-class definition // go-to-decl on timeout → in-class declarationexternvariable → declarationcpp// globals.h extern int log_level; // declaration // globals.cpp int log_level = 0; // definition // go-to-decl on log_level → extern declaration in globals.hMultiple declarations — list all when an entity is declared in more than one location
Navigate even when declaration and definition signatures mismatch (e.g., parameter names differ, const qualification)
Go to Implementation
Virtual method → all override implementations
cppstruct Base { virtual void draw(); }; struct Circle : Base { void draw() override; }; struct Rect : Base { void draw() override; }; // go-to-impl on Base::draw → list Circle::draw, Rect::drawNon-virtual function declaration → out-of-line definition (go-to-impl as superset of go-to-def) (clangd#854)
cpp// widget.h class Widget { void draw(); // go-to-impl on draw → out-of-line definition in widget.cpp };Go to implementation listing all derived classes for a base class
cppstruct Base {}; struct Circle : Base {}; struct Rect : Base {}; // go-to-impl on Base → list Circle, RectTemplate duck-type navigation — when a template has known instantiations, jump to the concrete implementations of dependent member calls. Also applies to generic lambdas with known call sites.
cpptemplate<typename T> void process(T& obj) { obj.foo(); // go-to-impl on foo → list A::foo, B::foo (from all instantiations) } process(a); // T = A process(b); // T = Bcppstd::vector<std::string> names; std::ranges::for_each(names, [](const auto& s) { s.size(); // go-to-impl on size → std::string::size (from the single call site) });
Go to Type Definition
Navigate to the type definition of a symbol. Applicable to variables, parameters, fields, and any other named entity that has a type. When the type is a type alias or a pointer-like wrapper, navigation should unwrap to the underlying/pointee type.
Local variables and parameters
cppvoid process(Widget w) { auto result = w.compute(); // go-to-type-def on w → Widget // go-to-type-def on result → return type of compute() }Class/struct fields
cppstruct App { Logger logger; }; App app; // go-to-type-def on app.logger → Loggerautodeduced typescppauto it = map.begin(); // go-to-type-def on it → map::iteratorSmart pointer → pointee type (clangd#1026)
cppstd::unique_ptr<Widget> w; // go-to-type-def on w → Widget, not unique_ptrType aliases — unwrap
typedef/usingto the underlying type definition (behavior may depend on cursor position and context, e.g. whether the cursor is on the variable or on the alias name itself)cppusing Connection = detail::ConnectionImpl; Connection conn; // go-to-type-def on conn → detail::ConnectionImpl (unwraps alias)Structured binding variables
cppstd::map<int, Widget> m; auto& [id, widget] = *m.begin(); // go-to-type-def on id → int (from pair::first) // go-to-type-def on widget → Widget (from pair::second)
Find References
Index-based cross-TU find references
Include declarations option
Implicit references from range-based for loops (clangd#1081)
cppstruct Container { iterator begin(); iterator end(); }; for (auto& x : container) {} // find-refs on begin() should include this loopImplicit constructor/destructor calls
cppstruct Blob { Blob(); }; Blob b; // find-refs on Blob() should include this declarationReferences through forwarding functions — find-refs on a constructor should include calls via
std::make_unique,std::make_shared,emplace_back, etc. (clangd#716, clangd#1872)cppstruct Widget { Widget(int w, int h); }; auto p = std::make_unique<Widget>(800, 600); // find-refs on Widget(int, int) should include this vec.emplace_back(800, 600); // and thisReferences in dependent/template contexts (clangd#258, clangd#675)
cpptemplate<typename T> void process(T& obj) { obj.foo(); // find-refs on A::foo should include this (from instantiation T = A) }Read/write classification — annotate each reference as read or write access (clangd#2139)
cppint x = 0; // write int y = x + 1; // read x = y; // writeEnclosing function context — include the name of the enclosing function in each reference result for better readability (clangd#177)
Macro definition/expansion references (including uses within other macro definitions) (clangd#346)
Label → goto references
cppretry: if (failed) goto retry; // find-refs on retry label → list all gotos
Call Hierarchy
Prepare call hierarchy (functions and methods)
Incoming calls
Outgoing calls
Show function signature in
detailfieldInclude class name for member functions
// current: "draw" in file.cpp // expected: "Circle::draw" in file.cppFollow virtual dispatch (callers of
Base::drawshould include calls via derived overrides)Support non-function targets (variables, enum constants) (clangd#1308)
Calls inside lambdas
cppauto task = [&] { foo(); }; // foo() should appear in foo's incoming callsConstructor calls through forwarding functions —
make_unique,emplace_backetc. should appear in incoming calls of the constructor (clangd#2242)cppstruct Widget { Widget(int w, int h); }; auto p = std::make_unique<Widget>(800, 600); // incoming calls for Widget(int, int) should include this call site
Type Hierarchy
Prepare type hierarchy (class/struct/enum/union)
Supertypes (base classes)
Subtypes (derived classes)
Template inheritance (derived classes via template specialization)
cpptemplate<typename T> struct CRTP : Base {}; // type hierarchy on Base should show CRTP<T> as subtypeShow template arguments in type hierarchy items (clangd#31)
// current: CRTP (subtype of Base) // expected: CRTP<Foo> (subtype of Base)
Workspace Symbol
Basic workspace-wide symbol search
Fuzzy matching with word-boundary-aware scoring (camelCase, snake_case)
VecIt → matches std::vector<int>::iterator strvi → matches std::string_viewPartially qualified name search (clangd#550)
ns::Foo → matches deeply::nested::ns::FooInclude parameter types for overloaded function disambiguation (clangd#1344)
// query: "process" // results: process(int), process(std::string), process(Widget&)Enum enumerator lookup under enum scope (clangd#931)
Color::Red → matches Color::Red (even for unscoped enums)Fuzzy matching for macros (consistent with other symbol types) (clangd#914)
Prioritize underlying declarations over type aliases in results (clangd#2253)
Search by mangled (linker) name
Module Navigation
import module_name→ jump to module interface unit (clangd#2310)cppimport mylib; // go-to-def on mylib → module interface unit (export module mylib;)import :partition→ jump to partition unitcppimport :core; // go-to-def on core → partition unit (export module mylib:core;)Navigate between interface and implementation units of the same module
cpp// interface unit: export module mylib; // implementation unit: module mylib; // go-to-def on mylib → switch between interface and implementation unitsDot-separated module name — navigate each segment to its module
cppimport std.io; // go-to-def on io → std.io module interface // go-to-def on std → std module interface (if exists)
Document Highlight
Highlight all references to the symbol under cursor within the current file (textDocument/documentHighlight).
Highlight all references to the symbol under cursor in the current file
Read/Write classification for symbol highlights
Control flow token highlighting (
return,break,continue,throw,case/defaultfor switch) (clangd#1921)cppfor (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (done) break; // highlighting break → also highlights inner for if (skip) continue; // highlighting continue → also highlights inner for } }
Switch Source/Header
- Switch between source and header file
Changelog
| Date | Change | PR |
|---|---|---|
| — | Index-based go-to-definition and find references | — |
| — | Call hierarchy (incoming/outgoing) | — |
| — | Type hierarchy (supertypes/subtypes) | — |