workerd C++ 代码审查清单全解:从 KJ 类型体系到并发安全的硬性规则
【免费下载链接】workerdThe JavaScript / Wasm runtime that powers Cloudflare Workers项目地址: https://gitcode.com/GitHub_Trending/wo/workerd
导读
本文基于 review-checklist.md(workerd 仓库内 C++ 代码审查的强制性检查清单),系统展开 workerd 工程在内存管理、错误处理、Promise 生命周期、类型设计与代码风格上的全部硬性规则。这份清单是 workerd(Cloudflare Workers 的 JavaScript / Wasm 运行时)在 V8 垃圾回收器与 KJ 事件循环双线程模型下长期沉淀的工程约束,读者读完可以掌握一套可直接用于 code review 的逐项检查方法,并理解每条规则背后的实现原理与仓库内对应的源码证据。
一、为什么 workerd 需要一份专门的 C++ 审查清单
workerd 是一个运行于 Linux 生产环境、同时构建于 macOS 与 Windows 的高并发运行时,其核心架构由 V8 隔离(isolate)线程与 KJ 事件循环(I/O 线程)协同驱动,还要承载 Cap'n Proto 零拷贝消息、tcmalloc 内存分配和跨平台抽象。在这种环境下,普通 C++ 项目习以为常的写法(如throw、裸指针、std::string拼接)都可能演变成内存泄漏、悬垂引用或线程竞争。
仓库中的 kj-style.md 明确要求:
detail/review-checklist.mdmustbe used when performing ANY code review of workerd C++ code
即任何一次对 workerd C++ 代码的审查都必须逐项对照本清单。同时,kj-style.md 还规定了一套“参考文件路由”机制:涉及kj::Maybe/kj::OneOf/kj::str()/KJ_DEFER/KJ_SYSCALL/kj::downcast时须读 api-patterns.md;涉及kj::Promise/.then()/.attach()/.eagerlyEvaluate()/协程/kj::MutexGuarded时须读 async-patterns.md;设计新类、审查类层次或 constness 语义时须读 type-design.md。
审查清单总计 36 条规则,每条都带Always/Never/Avoid三个级别的强制语义。下文按主题归类逐条展开,并补充对应的实现细节与仓库内证据。
二、内存管理与所有权:杜绝 STL 泄漏与裸指针
1. Always:检查 STL 类型泄漏
Alwayscheck for STL leaking in:
std::string,std::vector,std::optional,std::unique_ptr, etc.
workerd 全面使用 KJ 类型替代 C++ 标准库容器,这一映射关系在 kj-style.md 中有完整对照表:
| 不要用 | 应该用 |
|---|---|
std::string | kj::String(owned)/kj::StringPtr(borrowed) |
std::vector | kj::Array<T>(定长)/kj::Vector<T>(可增长) |
std::unique_ptr | kj::Own<T> |
std::shared_ptr | kj::Rc<T>/kj::Arc<T>(线程安全) |
std::optional | kj::Maybe<T> |
std::function | kj::Function<T> |
std::variant | kj::OneOf<T...> |
std::span/ 数组引用 | kj::ArrayPtr<T> |
std::exception | kj::Exception |
std::promise/std::future | kj::Promise<T>/kj::ForkedPromise<T> |
T*(可空) | kj::Maybe<T&> |
审查时注意:头文件中禁止包含<string>、<vector>、<memory>、<optional>、<functional>、<variant>等标准库头文件;源文件中只有在 KJ 无对应物或与第三方依赖交互时才允许使用std::。同时禁止FILE*、iostream与 C stdio,一律使用kj::str()格式化、kj/io.h做 I/O。
2. Never:禁止裸new/delete
Neverallow raw
new/delete. Should bekj::heap<T>()or similar.
KJ 的内存工厂函数族(见 kj-style.md)是唯一的堆分配入口:
kj::heap<T>(args...)→ 返回kj::Own<T>kj::heapArray<T>(size)→ 返回kj::Array<T>kj::heapArrayBuilder<T>(size)→ 逐元素构建数组kj::rc<T>(args...)→ 返回kj::Rc<T>kj::arc<T>(args...)→ 返回kj::Arc<T>
原则是:需要可移动性时用堆分配,不需要移动时优先用栈或成员变量;拷贝语义优先用显式clone()方法而非拷贝构造函数。
3. Never:禁止可空裸指针
Neveruse nullable raw pointers. Should be
kj::Maybe<T&>
裸指针与引用在 KJ 语义中代表“不拥有、仅借用”,且默认非空。可空性必须显式表达为kj::Maybe<T&>。与之配套的解包规范来自 api-patterns.md:
// 正确:KJ_IF_SOME 解包 KJ_IF_SOME(value, maybeValue) { use(value); // value 是对所含 T 的引用 } // 正确:断言式解包 auto& value = KJ_ASSERT_NONNULL(maybeValue); // 若为 none 则断言失败 auto& value = KJ_REQUIRE_NONNULL(maybeValue); // 前置条件检查 auto& value = JSG_REQUIRE_NONNULL(maybeValue, ErrorType, "message"); // 带 JS 异常类型 // 错误:直接解引用(不存在这种用法) auto& value = *maybeValue; auto& value = maybeValue.value();当从kj::Maybe中移出值时,必须用kj::mv()移出并将原 Maybe 置为kj::none,避免悬垂引用:
KJ_IF_SOME(value, maybeValue) { auto movedValue = kj::mv(value); maybeValue = kj::none; use(movedValue); }4. 所有权模型与类型设计(纵深补充)
清单第 8 条(不混合接口与实现类)与内存安全直接相关,其底层依据是 type-design.md 的两类类型划分:
- 值类型(value types):可拷贝/移动、按值比较、可序列化,无虚函数(用模板实现多态),必须有移动构造函数;
- 资源类型(resource types):不可拷贝、不可移动(用
kj::Own<T>在堆上转移所有权,用KJ_DISALLOW_COPY_AND_MOVE防止误拷贝),按身份比较,允许继承与虚函数。
此外,cpp-safety-review-checklist.md 进一步要求审查所有权与生命周期语义、kj::Own/kj::Rc/kj::Maybe的使用、RAII 与 CRTP 模式、析构函数正确性与清理顺序、缓冲区溢出与边界检查,并考虑util/weak-refs.h等弱引用模式的适用性(该文件位于 src/workerd/util/weak-refs.h)。
三、错误处理与异常规范
1. Never:禁止throw语句
Neveruse
throwstatements. Should useKJ_ASSERT/KJ_REQUIRE/KJ_FAIL_ASSERT/etc
workerd 的异常体系基于 KJ 断言宏(定义于 KJ 的kj/debug.h),由 kj-style.md 归纳为:
KJ_ASSERT(cond, msg, values...)—— 检查不变量(本代码的 bug)KJ_REQUIRE(cond, msg, values...)—— 检查前置条件(调用方的 bug)KJ_FAIL_ASSERT(msg, values...)—— 无条件失败KJ_SYSCALL(expr, msg, values...)—— 包装系统调用并检查返回值KJ_UNREACHABLE—— 标记不可达代码kj::throwFatalError(msg, values...)—— 抛出带堆栈跟踪的异常
这些宏自动捕获文件/行号、字符串化操作数并生成堆栈跟踪。KJ 的理念是异常用于容错而非控制流:异常代表“本不该发生”的事情(bug、网络故障、资源耗尽);如果调用方必须 catch 异常才能正常工作,说明接口设计有误——应改用类似openIfExists()返回kj::Maybe的替代方案。
2. Never:禁止noexcept;析构函数必须用noexcept(false)
Neveruse
noexceptAlwaysusenoexcept(false)on explicit destructors
理由在 kj-style.md 中阐述得很清楚:bug 可能在任何地方发生,abort 永远不是正确选择。显式析构函数必须声明noexcept(false),并使用kj::UnwindDetector或恢复块处理“栈展开过程中再次展开”的问题。这一点在 cpp-safety-review-checklist.md 中被特别澄清为运行时特性:workerd 遵循 KJ 的noexcept(false)析构约定,审查时不应把它当成问题标记,除非存在具体的异常安全缺陷(例如栈展开时的双重异常)。
3. Never:禁止手动 errno 检查
Neveruse manual errno checks. Should use
KJ_SYSCALL/KJ_SYSCALL_HANDLE_ERRORS
api-patterns.md 给出了标准写法:
int n; KJ_SYSCALL(n = read(fd, buf, size)); // 出错时抛异常,自动重试 EINTR KJ_SYSCALL_HANDLE_ERRORS(fd = open(path, O_RDONLY)) { case ENOENT: return nullptr; // 处理特定错误码 default: KJ_FAIL_SYSCALL("open()", error); }KJ_SYSCALL会自动处理EINTR重试并包装错误信息;KJ_SYSCALL_HANDLE_ERRORS允许对特定错误码做分支处理,这是替代手写errno分支的唯一正规途径。
四、Lambda 捕获与 Promise 生命周期:审查的重灾区
1. Never:禁止[=]全量值捕获
Neveruse
[=]lambda captures
[=]会让生命周期分析在审查时完全失效。规则细化(kj-style.md):
- Never用
[=]; - May用
[&],但仅限lambda 不会逃逸当前栈帧时——[&]表示“该 lambda 不逃逸”; - 对会逃逸的 lambda,必须逐个显式捕获变量;
- 使用
kj::coCapture(...)延长捕获变量的生命周期至 Promise 结束。
cpp-safety-review-checklist.md 把“宽泛捕获”列为 MEDIUM 级安全问题:传给.then()或延迟执行的 lambda 用[&]或[this]而只需特定成员时,应改为显式捕获并仔细考虑捕获变量的生命周期。仓库自带的 clang-tidy 检查workerd-unsafe-continuation-capture(见 tools/clang-tidy/unsafe-continuation-capture.c++)会标记传给异步接收方(kj/jsg::Promise::then/catch_、IoContext::run/addTask/awaitIo/addFunctor、kj::evalLater等)却捕获裸引用、[this]或非拥有视图的 lambda。推荐的捕获模式(来自 async-patterns.md):
| 场景 | 捕获方式 |
|---|---|
| JSG 资源 | [self = JSG_THIS] |
kj::Refcounted | [self = addRefToThis()] |
| IoContext(JS-lock 作用域内) | lambda 内auto& context = IoContext::current(); |
| IoContext(JS-lock 作用域外) | [weakRef = context.getWeakRef()]+KJ_ASSERT_NONNULL(weakRef->tryGet())或weakRef->runIfAlive(...) |
| 链会喂给不透明包装器 | IILE 协程,把this/&context作为协程参数而非 lambda 捕获 |
特别强调:不要用kj::addRef(context)强引用 IoContext 来通过检查——被 IoContext 传递拥有的链会形成引用计数环。
IoContext::WeakRef(context.getWeakRef()返回,见 src/workerd/io/io-context.h)是引用计数、非拥有的 IoContext 句柄,可在 IoContext 销毁后安全持有:tryGet()返回Maybe<IoContext&>,上下文消失后变为kj::none;runIfAlive(func)在上下文存活时执行func并返回是否执行。凡续体可能活过其起源上下文,或运行在 JS-lock 作用域之外(IoContext::current()可能取错或不存在)时,都应使用 WeakRef 模式。
2. Always:.attach()保证 Promise 存活期
Alwaysverify proper use of
.attach()on promises. Objects must stay alive for the promise duration
async-patterns.md 的经典正反例:
// 正确:stream 存活到 readAllText() 完成 return stream->readAllText().attach(kj::mv(stream)); // 错误:stream 立即销毁,promise 持悬垂引用 auto promise = stream->readAllText(); return promise;取消语义同样依赖 attach:销毁kj::Promise会立即取消它,续体不执行、只有析构函数运行;需要用.attach(kj::defer(...))保证完成与取消两条路径都会执行清理。
3. Always:后台 Promise 必须.eagerlyEvaluate()
Alwaysuse
.eagerlyEvaluate()with background promises. Lazy continuations may never execute. Co-routines are eager by default.
promise = doWork().then([]() { KJ_LOG(INFO, "done"); // 不 wait/then 的话不会运行 }).eagerlyEvaluate([](kj::Exception&& e) { KJ_LOG(ERROR, e); // 错误处理器是必需的 });若使用协程,eagerlyEvaluate()是隐式的,无需显式调用。多个后台任务用kj::TaskSet统一管理并共享错误处理器。此外kj::evalNow()可将同步代码包成“异常即拒绝 Promise”的形式:
return kj::evalNow([&]() { // 这里的 throw 变成拒绝的 Promise,而不是同步异常 return doSomethingThatMightThrow(); });4. In-Flight I/O 缓冲区与悬垂续体(纵深补充)
async-patterns.md 指出一个隐蔽陷阱:异步 I/O 会写入一个它不拥有的缓冲区(tryRead()接收裸void*),所有者必须活得比操作久,且取消必须在释放缓冲区之前拆除操作。只有 KJ 一侧能做到这一点(.attach()、.then()捕获、协程帧局部变量、三参数IoContext::awaitIo)。而传给jsg::Promise::then()的续体(含IoContext::addFunctor()方式)不行——它活在一个不透明的Wrappable里,V8 GC 可能在操作仍在写入时回收它。
正确做法是让 KJ 链持有缓冲区、只把读取的字节值传给续体:
auto promise = kj::evalNow([&]() -> kj::Promise<kj::Array<kj::byte>> { auto buffer = kj::heapArray<kj::byte>(size); auto bytes = buffer.asPtr(); return stream->tryRead(bytes.begin(), atLeast, bytes.size()) .then(buffer = kj::mv(buffer) mutable { return buffer.first(amount).attach(kj::mv(buffer)); }); });若续体必须与在途操作共享缓冲区,则用引用计数(kj::Arc)并把一个引用 attach 到 Promise 上。
五、继承与类型设计:接口即契约
1. Never:禁止混合接口类与实现类
Nevermixed interface and implementation classes. No data members in interfaces, no non-final virtuals in implementations. Intermediate subclasses are ok
type-design.md 给出的判据:一个类要么是接口(无数据成员、只有纯虚方法),要么是实现(无非 final 虚方法),混用会引发脆弱的基类问题(fragile base class)。配套规则:
- 接口不应声明析构函数;
- 允许多重继承,且鼓励(通常继承多个接口);
- 实现继承可用于组合且不产生额外堆分配;
- 优先用
kj::downcast<T>(debug 构建会断言)而非static_cast做向下转型; - 禁止
dynamic_cast做多态分发(用 if/else 长链转型派生类不可取),应扩展基类接口;dynamic_cast仅允许用于优化或诊断场景(判据:即使它永远返回 null,代码仍正确)。
2. Avoid:向下转型优先kj::downcast<T>
Avoid
static_castfor downcasting where possible. Should bekj::downcast<T>(debug-checked)
auto& derived = kj::downcast<DerivedType>(baseRef); // debug 构建断言需要无 RTTI 的优化路径时用kj::dynamicDowncastIfAvailable<T>(失败返回 null)。
3. 单例与全局状态
Neveruse singletons or mutable globals
main()或高层代码应显式构造组件并通过构造函数参数注入依赖。const方法必须线程安全(可并发调用),非const方法要求独占访问——kj::MutexGuarded<T>从类型上强制这一点:.lockShared()返回const T&,.lockExclusive()返回T&。同时禁止全局动态构造器(静态/全局变量不得有动态构造函数,全局constexpr常量没问题)。
六、代码风格与可读性:让审查更快更稳
1. Never:禁止std::to_string与+字符串拼接
Neveruse
std::to_stringor+string concatenation
统一用kj::str():
kj::String msg = kj::str("count: ", count, ", name: ", name);十六进制输出用kj::hex(n);可用KJ_STRINGIFY(MyType)或.toString()扩展;"literal"_kj后缀生成可constexpr的kj::StringPtr。
2. Never:禁止/* */块注释
Neveruse
/* */block comments. Use//line comments
文档注释放在声明之前;注释不应陈述显而易见的内容(应补充代码本身看不出的信息);TODO 格式为// TODO(type): description,type 取now/soon/someday/perf/security/cleanup/port/test,其中TODO(now)必须在合入前处理。
3. Always:命名约定
Alwaysverify naming convention conformance. TitleCase types, camelCase functions/variables, CAPS constants
kj-style.md 的完整约定:
| 类别 | 风格 |
|---|---|
| 类型(类、结构体) | TitleCase |
| 变量、函数、方法 | camelCase |
| 常量、枚举值 | CAPITAL_WITH_UNDERSCORES |
| 宏 | CAPITAL_WITH_UNDERSCORES,带项目前缀(KJ_、CAPNP_) |
| 命名空间 | oneword(保持简短),私有命名空间用_ |
| 文件 | module-name.c++、module-name.h、module-name-test.c++ |
4. Always:块必须带花括号
Alwayscheck for missing braces around blocks. Required unless entire statement is on one line
格式化细则还包括:2 空格缩进、每行最长 100 字符、续行 4 空格缩进、if (foo)等关键字后加空格、函数名后不加空格(foo(bar))、public:/private:/protected:相对类体反向缩进一个档位、命名空间内容不缩进、去除行尾空白。
七、API 设计与健壮性
1. Always:避免裸bool参数
Alwaysavoid
boolfunction parameters. Preferenum classorWD_STRONG_BOOLfor clarity at call sites. E.g.,void connect(bool secure)should bevoid connect(SecureMode mode)
workerd 为强类型布尔提供了WD_STRONG_BOOL宏,定义于 src/workerd/util/strong-bool.h,配套测试在 src/workerd/util/strong-bool-test.c++。仓库中大量使用该宏,例如 src/workerd/io/io-context.h 中的WD_STRONG_BOOL(IoContext_Runnable_Exceptional),以及 src/workerd/api/http.h、src/workerd/io/compatibility-date.h、src/workerd/io/io-channels.h 等文件。目的很直接:调用点connect(SecureMode::YES)比connect(true)的意图明确得多,且编译器能阻止隐式转换。
2. Always:错误码 / Maybe / 成功布尔用[[nodiscard]]
Alwaysuse
[[nodiscard]]with functions returning error codes,kj::Maybe, or success booleans that callers must check
cpp-safety-review-checklist.md 进一步给出 KJ 封装:返回拥有型资源、kj::Maybe、错误指示或昂贵计算结果(如kj::Promise)的函数应加KJ_WARN_UNUSED_RESULT,它可同时捕获两类问题:静默丢弃调用方必须处理的错误码/Promise,以及丢弃昂贵计算的结果。
3. Always:优先协程而非 Promise 链
Alwaysprefer coroutines over kj::Promise chains. Nested
.then()chains with complex error handling that would be more readable as a coroutine withco_await. Butalwaysavoid suggesting sweeping rewrites
注意后半个限定:审查建议应聚焦局部可读性提升,不要提议大规模重写(sweeping rewrites)——这是 workerd 审查文化的明确要求。
4. Always:检查缺失的constexpr/consteval
Alwayscheck for missing
constexpr/constevalwhere they would be appropriate
全局constexpr常量是允许的(且是唯一允许的全局对象形式)。
5. Always:先查src/workerd/util/再提新抽象
Alwaysavoid reinventing utility with custom code duplicating functionality already in
src/workerd/util/(e.g., custom ring buffer, small set, state machine, weak reference pattern).Alwayscheck the util directory before suggesting a new abstraction.
从目录列表可见 src/workerd/util/ 已沉淀了大量通用设施:环形缓冲 ring-buffer.h、状态机 state-machine.h、小型弱引用容器 small-weak-vector.h、弱引用模式 weak-refs.h、强布尔 strong-bool.h、取消器 canceler.h、批处理队列 batch-queue.h 等,且大部分带有配套-test.c++。审查者建议新抽象前必须先浏览该目录。
6. Always:检查缺失的override
Alwayscheck for missing
overrideon virtual method overrides
7. Always:标记魔法数字
Alwaysflag magic numbers (Numeric literals) without explanation or named constants
八、新文件必须携带版权头
Alwayscheck for copyright header on new files. Every new
.c++and.hfile must begin with the project copyright/license header using the current year (not copied from older files).
标准格式:
// Copyright (c) <current-year> Cloudflare, Inc. // Licensed under the Apache 2.0 license found in the LICENSE file or at: // https://opensource.org/licenses/Apache-2.0任何新文件使用过期年份(例如 2026 年创建却写2017-2022)或完全省略头文件,都必须被标记。
九、纵深:内存安全与线程安全专项审查
清单之外,workerd 还有一份配套的 cpp-safety-review-checklist.md,用于内存安全、线程安全与并发正确性专项审查。它把容易出事的模式按严重度分级:
内存安全专项
- 识别内存泄漏、use-after-free、悬垂指针/引用;审查所有权语义与生命周期管理;
- 分析智能指针使用(
kj::Own、kj::Rc、kj::Maybe);检查 RAII、CRTP 与析构顺序; - 返回指向
this(或参数)所拥有数据的非拥有视图(引用、指针、kj::ArrayPtr、kj::StringPtr)的方法必须标注KJ_LIFETIMEBOUND(展开为[[clang::lifetimebound]],让编译器在视图超过借用对象存活期时告警); - 捕获 GC 追踪引用(
jsg::Ref<T>等)的 lambda 应使用JSG_VISITABLE_LAMBDA(见 src/workerd/jsg/function.h),使 V8 GC 能穿透捕获追踪; - 具有位置语义的 RAII 作用域守卫、锁等必须用
KJ_DISALLOW_COPY_AND_MOVE防止意外移动破坏作用域不变量。
线程安全与并发专项
- 检查数据竞争、锁顺序与死锁、共享状态访问模式;
- 绝不允许 isolate 锁跨越协程挂起点:
jsg::Lock、Worker::Lock、jsg::V8StackScope等类型已标注KJ_DISALLOW_AS_COROUTINE_PARAM以在编译期强制禁止,审查新锁/作用域类型时确认其同样带此标注; - 捕获裸指针/引用的 RAII 对象不得跨越挂起点不安全使用;
- kj I/O 对象绝不能被 V8 堆对象(尤其是
jsg::Object实例)直接持有,必须经IoOwn/IoPtr(见 src/workerd/io/io-own.h)保证生命周期与线程安全; - 注意
kj::Refcounted与kj::AtomicRefcounted的选择:跨线程访问必须用原子引用计数。
关键检测模式(CRITICAL / HIGH)
- V8 回调抛出 C++ 异常:JSG 方法、属性 getter/setter 等 V8 回调若未用
liftKj(见 src/workerd/jsg/util.h)包裹而抛出 C++ 异常——V8 回调必须捕获 C++ 异常并转换为 JS 异常; - V8 堆对象直接持有 kj I/O 对象:
jsg::Object子类以kj::Own<T>/kj::Rc<T>/kj::Arc<T>存储 I/O 层对象而未用IoOwn/IoPtr; - 跨线程使用
kj::Refcounted:I/O 线程与 JS isolate 线程都可能访问的实例需要kj::AtomicRefcounted; - isolate 锁跨越
co_await:持有jsg::Lock、V8HandleScope等跨协程挂起点属于未定义行为; jsg::Object子类间引用环:两个及以上jsg::Object子类互持强引用(jsg::Ref<T>或kj::Own<T>)且未通过JSG_TRACE做 GC 追踪,会形成 V8 GC 不可见的泄漏(含 A→B→C→A 的传递环)。
MEDIUM 与运行时专项
- 异步 lambda 宽泛捕获(
[&]/[this]);热路径中的隐式 GC 触发(ArrayBuffer后备存储创建、字符串展平、v8::Object::New()); DISALLOW_KJ_IO_DESTRUCTORS_SCOPE(见 src/workerd/jsg/wrappable.h):该作用域内销毁任何 KJ 异步/I/O 对象都会令进程崩溃,强制 JS 堆对象使用IoOwn/IoPtr;IoOwn析构时创建匹配的AllowAsyncDestructorsScope允许安全销毁。引入新 GC/析构路径时须验证作用域嵌套正确(src/workerd/jsg/promise.h 与 src/workerd/io/io-context.c++ 中均有应用)。
运行时专项提醒:workerd 使用 tcmalloc(优化重点是减少分配次数而非单次分配大小,勿建议换回标准 malloc);Cap'n Proto 零拷贝消息不要“为安全起见”复制数据,应使用遍历上限(traversal limits)防资源耗尽;V8 在任何分配点都可能触发 GC,注意 V8 分配与裸指针访问的交错;生产运行于 Linux、同时构建于 macOS 与 Windows,标记缺乏可移植替代的 Linux-only 假设(如 epoll、/proc)。
十、落地:把清单融入日常审查工作流
这份清单不是悬空的原则,而是与整个工程体系咬合的强制规范:
- 审查路由:任何 workerd C++ 审查必读 review-checklist.md;涉及 KJ 特定 API 时按 kj-style.md 的路由规则加载 api-patterns.md、async-patterns.md、type-design.md;
- 工具支撑:仓库自带 clang-tidy 检查集(tools/clang-tidy/workerd-lint.c++)覆盖清单中的多条规则,如
workerd-unsafe-continuation-capture(对应 tools/clang-tidy/unsafe-continuation-capture.c++)、Promise 结果忽略检查(tools/clang-tidy/promise-ignore-result.c++)、GC 访问检查(tools/clang-tidy/visit-for-gc.c++)等,每条都有正负向测试样例; - 兜底原则:清单自身也承认“not exhaustive”——它覆盖常见模式,具体审查仍需结合代码上下文与工程判断(见 cpp-safety-review-checklist.md 开头说明)。
将这份清单逐条内化为肌肉记忆,是理解和审查 workerd 这类“V8 + KJ 双线程 + 零拷贝 + 强类型抽象”复合型运行时 C++ 代码的最短路径。
【免费下载链接】workerdThe JavaScript / Wasm runtime that powers Cloudflare Workers项目地址: https://gitcode.com/GitHub_Trending/wo/workerd
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考