代码导航
Go to Definition
基于索引的跨翻译单元 go-to-definition
在
#include指令上 go-to-definition(导航到被包含的文件)基于 AST 的本地/未保存符号回退
穿透宏包装导航到底层声明
cpp#define DECLARE_HANDLER(name) void name() DECLARE_HANDLER(onReady); // 在 onReady 上 go-to-def → 应到达底层函数错误恢复:即使变量类型未解析也能导航到变量定义
未实例化模板中的依赖类型导航
cpptemplate<typename T> void process(std::vector<T>& v) { v.push_back(val); // 在 push_back 上 go-to-def → vector::push_back }模板特化 → 主模板(clangd#212)
cpptemplate<typename T> struct Formatter { ... }; // 主模板 template<> struct Formatter<std::string> { }; // 在 Formatter 上 go-to-def → 主模板auto关键字 → 推导出的类型(clangd#2055)cppauto widget = getWidget(); // 在 auto 上 go-to-def → Widget(推导出的类型)
隐式代码导航
导航到隐式调用的代码定义。C++ 中许多语法构造会生成对构造函数、运算符、转换等的隐式调用。从语法构造(花括号、关键字、运算符 token)导航到实际被调用的函数,对于理解代码的真实执行至关重要。
隐式导航要求有明确的源 token — 如果该 token 已有明确的 go-to-def 目标(例如变量名始终导航到其声明),则无法复用于隐式调用导航。
关键字
override/final→ 被重写的基类虚函数cppstruct Base { virtual void draw(); }; struct Derived : Base { void draw() override; // 在 override 上 go-to-def → Base::draw };break/continue→ 所在循环或 switch 的头部(clangd#1921)。另见 Document Highlight 中关于高亮上下文中相关控制流 token 的描述。
构造与析构
构造函数调用 — 从括号/花括号导航到选中的构造函数
cppstruct Widget { Widget(int w, int h); }; Widget w(800, 600); // 在 ( 上 go-to-def → Widget(int, int) Widget w2{800, 600}; // 在 { 上 go-to-def → 同上 auto w3 = Widget(1, 2); // 在 ( 上 go-to-def → 同上拷贝/移动构造与赋值
cppWidget a(1, 2); Widget b = a; // 在 = 上 go-to-def → Widget(const Widget&) Widget c = std::move(a); // 在 = 上 go-to-def → Widget(Widget&&) b = c; // 在 = 上 go-to-def → operator=(const Widget&)CTAD — 导航到选中的构造函数
cppstd::vector v{1, 2, 3}; // 在 { 上 go-to-def → vector(initializer_list<int>)聚合初始化 → 结构体定义
cppstruct Point { int x, y; }; auto p = Point{1, 2}; // 在 { 上 go-to-def → Pointdelete表达式 → 析构函数cppdelete widget; // 在 delete 上 go-to-def → Widget::~Widgetnew表达式 → 构造函数(如重载则包含自定义operator new)cppstruct Pool { static void* operator new(size_t); }; auto* p = new Pool(); // 在 new 上 go-to-def → Pool() 构造函数 // 同时:Pool::operator new(如已重载) auto* arr = new Pool[10]; // 在 new 上 go-to-def → Pool() 默认构造函数成员初始化列表 → 基类和成员的构造函数
cppstruct Base { Base(int); }; struct Logger { Logger(std::string name); }; struct App : Base { Logger logger; App() : Base(42), logger("app") {} // 在 Base 上 go-to-def → Base::Base(int) // 在 logger 上 go-to-def → Logger(std::string) };委托构造函数
cppstruct Widget { Widget(int w, int h); Widget() : Widget(0, 0) {} // 在 Widget 上 go-to-def → Widget(int, int) };继承构造函数 — 导航到通过
using引入的基类构造函数cppstruct Base { Base(int x); Base(int x, int y); }; struct Derived : Base { using Base::Base; // 在 Base::Base 上 go-to-def → 列出 Base 的构造函数 };返回值隐式构造
cppWidget create() { return {800, 600}; // 在 { 上 go-to-def → Widget(int, int) }Lambda init-capture → 构造函数
cppWidget w; auto f = [w = std::move(w)] {}; // 在 = 上 go-to-def → Widget(Widget&&) auto g = [s = std::string("hi")] {}; // 在 = 上 go-to-def → string(const char*)
运算符
重载运算符 — 从运算符 token 导航到其定义
cppVec a, b; auto c = a + b; // 在 + 上 go-to-def → Vec::operator+ a += b; // 在 += 上 go-to-def → Vec::operator+= ++it; // 在 ++ 上 go-to-def → iterator::operator++ v[0]; // 在 [ 上 go-to-def → vector::operator[] fn(42); // 在 ( 上 go-to-def → Functor::operator() ptr->member; // 在 -> 上 go-to-def → SmartPtr::operator->C++20 重写运算符 — 导航到重写使用的实际运算符
cppstruct S { bool operator==(const S&) const; auto operator<=>(const S&) const = default; }; S a, b; a != b; // 在 != 上 go-to-def → S::operator== a > b; // 在 > 上 go-to-def → S::operator<=>用户定义字面量
cppusing namespace std::chrono_literals; auto d = 500ms; // 在 ms 上 go-to-def → operator""ms
转换
隐式转换运算符 — 从转换被调用的上下文导航
cppstruct Guard { explicit operator bool() const; }; Guard g; if (g) {} // 在 ( 上 go-to-def → Guard::operator bool() while (g) {} // 同上 !g; // 在 ! 上 go-to-def → Guard::operator bool()([clangd#1931](https://github.com/clangd/clangd/issues/1931)) bool ok = bool(g); // 在 bool( 上 go-to-def → Guard::operator bool()类型转换调用构造函数或转换运算符
cppstruct Meters { explicit operator double() const; }; Meters m; double d = static_cast<double>(m); // 在 static_cast 上 go-to-def → Meters::operator double() struct Foo { explicit Foo(int); }; auto f = static_cast<Foo>(42); // 在 static_cast 上 go-to-def → Foo(int)
Range-for 与 structured bindings
Range-based for — 导航到
begin()/end()cppstd::vector<int> v; for (auto& x : v) {} // 在 : 上 go-to-def → vector::beginStructured bindings — 导航到底层访问器或字段
cppstd::map<int, std::string> m; for (auto& [key, val] : m) {} // 在 key 上 go-to-def → pair::first // 在 val 上 go-to-def → pair::second
协程
co_await/co_yield/co_return→ 对应的 awaiter/promise 方法cppco_await some_awaitable; // 在 co_await 上 go-to-def → operator co_await() 或 await_resume() co_yield value; // 在 co_yield 上 go-to-def → promise::yield_value() co_return result; // 在 co_return 上 go-to-def → promise::return_value()
Go to Declaration
从符号的使用处或定义处导航到其声明。C++ 中许多实体有独立的声明和定义;go-to-declaration 始终导航到声明一侧。
函数 — 从使用处或行外定义导航到声明/原型
cpp// widget.h class Widget { void draw(); // 声明 }; // widget.cpp void Widget::draw() { } // 行外定义 // 从使用处或定义处 go-to-decl → widget.h 中的类内声明类和结构体的前向声明
cppclass Widget; // fwd.h 中的前向声明 class Widget { ... }; // widget.h 中的完整定义 // 在 Widget 上 go-to-decl(从使用处或定义处)→ 前向声明静态数据成员 → 类内声明
cppstruct Config { static int timeout; // 声明 }; int Config::timeout = 30; // 类外定义 // 在 timeout 上 go-to-decl → 类内声明extern变量 → 声明cpp// globals.h extern int log_level; // 声明 // globals.cpp int log_level = 0; // 定义 // 在 log_level 上 go-to-decl → globals.h 中的 extern 声明多重声明 — 当实体在多个位置声明时列出所有声明
声明与定义签名不匹配时也能导航(如参数名不同、const 修饰差异)
Go to Implementation
虚函数 → 所有 override 实现
cppstruct Base { virtual void draw(); }; struct Circle : Base { void draw() override; }; struct Rect : Base { void draw() override; }; // 在 Base::draw 上 go-to-impl → 列出 Circle::draw, Rect::draw非虚函数声明 → 行外定义(go-to-impl 作为 go-to-def 的超集)(clangd#854)
cpp// widget.h class Widget { void draw(); // 在 draw 上 go-to-impl → widget.cpp 中的行外定义 };列出基类的所有派生类(go-to-implementation)
cppstruct Base {}; struct Circle : Base {}; struct Rect : Base {}; // 在 Base 上 go-to-impl → 列出 Circle、Rect模板 duck-type 导航 — 当模板有已知实例化时,跳转到依赖成员调用的具体实现。同样适用于有已知调用点的 generic lambda。
cpptemplate<typename T> void process(T& obj) { obj.foo(); // 在 foo 上 go-to-impl → 列出 A::foo, B::foo(来自所有实例化) } process(a); // T = A process(b); // T = Bcppstd::vector<std::string> names; std::ranges::for_each(names, [](const auto& s) { s.size(); // 在 size 上 go-to-impl → std::string::size(来自唯一调用点) });
Go to Type Definition
导航到符号的类型定义。适用于变量、参数、字段等任何具有类型的命名实体。当类型是类型别名或指针类包装时,应自动解包到底层/指向的类型。
局部变量和参数
cppvoid process(Widget w) { auto result = w.compute(); // 在 w 上 go-to-type-def → Widget // 在 result 上 go-to-type-def → compute() 的返回类型 }Class/struct 字段
cppstruct App { Logger logger; }; App app; // 在 app.logger 上 go-to-type-def → Loggerauto推导类型cppauto it = map.begin(); // 在 it 上 go-to-type-def → map::iterator智能指针 → 指向类型(clangd#1026)
cppstd::unique_ptr<Widget> w; // 在 w 上 go-to-type-def → Widget,而非 unique_ptr类型别名 — 解包
typedef/using到底层类型定义(具体行为可能取决于光标位置和上下文,例如光标在变量上还是在别名名称上)cppusing Connection = detail::ConnectionImpl; Connection conn; // 在 conn 上 go-to-type-def → detail::ConnectionImpl(解包别名)Structured binding 变量
cppstd::map<int, Widget> m; auto& [id, widget] = *m.begin(); // 在 id 上 go-to-type-def → int(来自 pair::first) // 在 widget 上 go-to-type-def → Widget(来自 pair::second)
Find References
基于索引的跨翻译单元 find references
包含声明选项
range-based for 循环的隐式引用(clangd#1081)
cppstruct Container { iterator begin(); iterator end(); }; for (auto& x : container) {} // find-refs on begin() 应包含此循环隐式构造函数/析构函数调用
cppstruct Blob { Blob(); }; Blob b; // find-refs on Blob() 应包含此声明转发函数穿透引用 — 对构造函数 find-refs 应包含通过
std::make_unique、std::make_shared、emplace_back等的调用(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) 应包含此处 vec.emplace_back(800, 600); // 以及此处依赖/模板上下文中的引用(clangd#258、clangd#675)
cpptemplate<typename T> void process(T& obj) { obj.foo(); // find-refs on A::foo 应包含此处(来自实例化 T = A) }读/写分类 — 标注每个引用是读操作还是写操作(clangd#2139)
cppint x = 0; // 写 int y = x + 1; // 读 x = y; // 写所在函数上下文 — 每条引用结果附带所在函数名,提升可读性(clangd#177)
宏定义/展开引用(包括在其他宏定义体内的使用)(clangd#346)
Label → goto 引用
cppretry: if (failed) goto retry; // 在 retry 标签上 find-refs → 列出所有 goto
Call Hierarchy
Prepare call hierarchy(函数和方法)
Incoming calls
Outgoing calls
在
detail字段中显示函数签名成员函数包含类名
// 当前: "draw" in file.cpp // 期望: "Circle::draw" in file.cpp跟踪虚派发(
Base::draw的调用方应包含通过派生类 override 的调用)支持非函数目标(变量、枚举常量)(clangd#1308)
lambda 内部的调用
cppauto task = [&] { foo(); }; // foo() 应出现在 foo 的 incoming calls 中通过转发函数的构造函数调用 —
make_unique、emplace_back等应出现在构造函数的 incoming calls 中(clangd#2242)cppstruct Widget { Widget(int w, int h); }; auto p = std::make_unique<Widget>(800, 600); // Widget(int, int) 的 incoming calls 应包含此调用点
Type Hierarchy
Prepare type hierarchy(class/struct/enum/union)
Supertypes(基类)
Subtypes(派生类)
模板继承(通过模板特化的派生类)
cpptemplate<typename T> struct CRTP : Base {}; // 在 Base 上查看 type hierarchy 应显示 CRTP<T> 为子类型在 type hierarchy 条目中显示模板参数(clangd#31)
// 当前: CRTP(Base 的子类型) // 期望: CRTP<Foo>(Base 的子类型)
Workspace Symbol
基本的工作区符号搜索
模糊匹配,支持词边界感知评分(camelCase、snake_case)
VecIt → 匹配 std::vector<int>::iterator strvi → 匹配 std::string_view部分限定名搜索(clangd#550)
ns::Foo → 匹配 deeply::nested::ns::Foo重载函数结果包含参数类型以便区分(clangd#1344)
// 查询:"process" // 结果:process(int)、process(std::string)、process(Widget&)枚举值在枚举作用域下查找(clangd#931)
Color::Red → 匹配 Color::Red(即使是非 scoped enum)宏的模糊匹配(与其他符号类型一致)(clangd#914)
优先显示底层声明而非类型别名(clangd#2253)
按 mangled(链接器)名称搜索
Module 导航
import module_name→ 跳转到模块接口单元(clangd#2310)cppimport mylib; // 在 mylib 上 go-to-def → 模块接口单元(export module mylib;)import :partition→ 跳转到 partition 单元cppimport :core; // 在 core 上 go-to-def → partition 单元(export module mylib:core;)同一模块的接口单元与实现单元之间互相导航
cpp// 接口单元: export module mylib; // 实现单元: module mylib; // 在 mylib 上 go-to-def → 在接口单元和实现单元之间切换点分隔模块名 — 各段导航到对应的模块
cppimport std.io; // 在 io 上 go-to-def → std.io 模块接口 // 在 std 上 go-to-def → std 模块接口(如存在)
Document Highlight
高亮当前文件中光标所在符号的所有引用(textDocument/documentHighlight)。
高亮当前文件中光标所在符号的所有引用
符号高亮的读/写分类
控制流 token 高亮(
return、break、continue、throw、case/default用于 switch)(clangd#1921)cppfor (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (done) break; // 高亮 break → 同时高亮内层 for if (skip) continue; // 高亮 continue → 同时高亮内层 for } }
Switch Source/Header
- 在源文件和头文件之间切换
变更记录
| 日期 | 变更 | PR |
|---|---|---|
| — | 基于索引的 go-to-definition 和 find references | — |
| — | Call hierarchy(incoming/outgoing) | — |
| — | Type hierarchy(supertypes/subtypes) | — |