排查一个 Rust Analyzer 发版后出现的 bug

发现问题

前些天有用户反馈了一个 Rust Analyzer 的 issue,说的是在升级到最新版的 RA 后出现了之前没出现的 bug:当一个 trait 的 associate item 提供了默认实现时,struct impl 这个 trait 时如果不提供实现则 RA 会报错如下,而实际上这在 Rust 中是合法的。

1
2
3
4
5
trait Marker {
const FLAG: bool = false;
}
struct Foo;
impl Marker for Foo {} // <--- error: not all trait items implemented, missing: `const FLAG`

定位问题

这是一个在最新版中才出现的错误,说明是在这一版中才引入的而不是陈年老 bug。于是我在 GitHub 上 compare 这一版的 commit 和上一版本的 commit,这中间有 84 个 Files changed, 不算多。

一番浏览下来,发现了这个新增的 crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs,从名字上看就很可疑,然后仔细浏览这个文件,发现正是他的问题。这个文件所在的 PR 增加了一个新的 diagnostic,使得 RA 可以检测出用户在为 struct impl trait 的时候哪些 associate item 没有实现。

解决问题

这个 PR 是这样检测未在 impl 体中实现的 associate item 的,对于 AssocItemId::ConstId(_) => true, 的处理是直接返回 true,也就是默认为必须在 impl 体中实现而漏掉了在 trait 的定义中可能有为这个 const item 设置默认值的情况。知道了问题所在,剩下的就比较好解决了,将针对 const item 的处理修改为 Const::from(id).value(db).is_none(),也就是判断这一项有没有设置了默认值,如果有的话就不必作为 required_items 从而引发错误的 diagnostic

1
2
3
4
5
let required_items = items.iter().filter(|&(_, assoc)| match *assoc {
AssocItemId::FunctionId(it) => !db.function_data(it).has_body(),
AssocItemId::ConstId(_) => true,
AssocItemId::TypeAliasId(it) => db.type_alias_data(it).type_ref.is_none(),
});

接着再为这个小 fix 增加一个验证的 test,跑一遍 cargo test 确保所有测试都没问题之后就可以提 PR 修复了

1
2
3
4
5
6
7
8
9
10
11
12
    #[test]
fn trait_with_default_value() {
check_diagnostics(
r#"
trait Marker {
const FLAG: bool = false;
}
struct Foo;
impl Marker for Foo {}
"#,
)
}

最后

Rust Analyzer 会自动发 nightly releases,这样看来频繁发版还是有好处的,相邻版本之间的 diff 不会太大,发版后引发的 bug 才比较好定位,要是版本之间的 diff 上千个文件我可能就不会一个个去看了哈哈哈

参考