cargo fmt 在 workspace 里漏格式化了一个 crate

workspace 下 11 个 crate,rustfmt 1.7.0(随 rust 1.74 stable 的 toolchain),CI 跑 cargo fmt --all -- --check 一直是绿的。上个月有人改了 crates/ingest 里一个 400 多行的 match,本地手写缩进乱了,CI 依旧绿。

我先在根目录复现:

cargo fmt --all -- --check; echo $?
# 0
cargo fmt -p ingest -- --check; echo $?
# 1

差异立刻出来了:--all 没有把 ingest 算进去。根 Cargo.toml 里 ingest 是这么写的:

[workspace]
members = ["crates/*"]
exclude = ["crates/ingest"]

exclude 是为了绕开当时的一个循环依赖临时加的,后来 ingest 通过 path 依赖被别的 crate 引用,编译没问题,但 workspace 成员关系里没有它,--all 自然扫不到。改成显式 members 并去掉 exclude 后,--all 能覆盖到,第一次跑就报出 3 个文件的 diff。

为什么不用 --all 兜底就行:cargo fmt 的 --all 是遍历 workspace 的 packages 列表,不是扫磁盘。只要一个 crate 靠 exclude、或者干脆不在 members 通配里(比如放在 crates/vendor/ 下),它就不会被格式化,也不会被 --check 拦住。代价是:去掉 exclude 后每次 fmt 要全量过一遍,我们 11 个 crate 大概多 0.4 秒,可以接受;但如果 workspace 里有几十个 crate,建议在 CI 里按目录再跑一遍 find crates -name Cargo.toml 做交叉校验。

失效边界有两个。一是 cargo fmt -p 对非成员会直接报错,不是静默跳过,所以本地想验证某个 crate 是否被覆盖,用 -p 比看 --all 的输出更直接。二是 rustfmt 对宏体内的代码默认不格式化,ingest 里那段 macro_rules! 生成的 match 还是手写缩进,--check 也放过了,只能靠 rustfmt.toml 里的 format_macro_matchers 之类开关,但那个选项会改变宏定义本身的排版,我没开。

你们 workspace 里有没有那种“能编译、能跑测试、但 fmt 和 clippy 都没扫到”的 crate?我这次是靠 -p 和 --all 对拍才发现的,想看看别人是怎么在 CI 里做覆盖校验的。

0
赞同度 100% · 0 条评论 · 3 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。