Improve error recovery when method-calling an assoc function

This commit is contained in:
Lukas Wirth 2025-02-16 10:51:58 +01:00
parent fb8bc313ff
commit e6ea353d94
6 changed files with 111 additions and 70 deletions

View file

@ -1,4 +1,4 @@
use hir::{db::ExpandDatabase, AssocItem, FileRange, HirDisplay, InFile};
use hir::{db::ExpandDatabase, FileRange, HirDisplay, InFile};
use ide_db::text_edit::TextEdit;
use ide_db::{
assists::{Assist, AssistId, AssistKind},
@ -112,7 +112,7 @@ fn field_fix(
}
fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -> Option<Assist> {
if let Some(assoc_item_id) = d.assoc_func_with_same_name {
if let Some(f) = d.assoc_func_with_same_name {
let db = ctx.sema.db;
let expr_ptr = &d.expr;
@ -127,30 +127,25 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -
let receiver = call.receiver()?;
let receiver_type = &ctx.sema.type_of_expr(&receiver)?.original;
let need_to_take_receiver_as_first_arg = match hir::AssocItem::from(assoc_item_id) {
AssocItem::Function(f) => {
let assoc_fn_params = f.assoc_fn_params(db);
if assoc_fn_params.is_empty() {
false
} else {
assoc_fn_params
.first()
.map(|first_arg| {
// For generic type, say `Box`, take `Box::into_raw(b: Self)` as example,
// type of `b` is `Self`, which is `Box<T, A>`, containing unspecified generics.
// However, type of `receiver` is specified, it could be `Box<i32, Global>` or something like that,
// so `first_arg.ty() == receiver_type` evaluate to `false` here.
// Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard,
// apply `.as_adt()` over `Box<T, A>` or `Box<i32, Global>` gets `Box`, so we get `true` here.
let assoc_fn_params = f.assoc_fn_params(db);
let need_to_take_receiver_as_first_arg = if assoc_fn_params.is_empty() {
false
} else {
assoc_fn_params
.first()
.map(|first_arg| {
// For generic type, say `Box`, take `Box::into_raw(b: Self)` as example,
// type of `b` is `Self`, which is `Box<T, A>`, containing unspecified generics.
// However, type of `receiver` is specified, it could be `Box<i32, Global>` or something like that,
// so `first_arg.ty() == receiver_type` evaluate to `false` here.
// Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard,
// apply `.as_adt()` over `Box<T, A>` or `Box<i32, Global>` gets `Box`, so we get `true` here.
// FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver`
first_arg.ty() == receiver_type
|| first_arg.ty().as_adt() == receiver_type.as_adt()
})
.unwrap_or(false)
}
}
_ => false,
// FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver`
first_arg.ty() == receiver_type
|| first_arg.ty().as_adt() == receiver_type.as_adt()
})
.unwrap_or(false)
};
let mut receiver_type_adt_name =