X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=crates%2Fide_assists%2Fsrc%2Fhandlers%2Fadd_missing_impl_members.rs;h=a10eca10d11946c1b350b0e1189b56c2d91d2de1;hb=749eeef3e75a3acc993fdd454ebadaa7e319509a;hp=8225ae22c6a58b6929ac738b404b958ed0a9933c;hpb=cea589b3b52ff5c4e358db52dc6de150eb48a9a0;p=rust.git diff --git a/crates/ide_assists/src/handlers/add_missing_impl_members.rs b/crates/ide_assists/src/handlers/add_missing_impl_members.rs index 8225ae22c6a..a10eca10d11 100644 --- a/crates/ide_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide_assists/src/handlers/add_missing_impl_members.rs @@ -1,10 +1,12 @@ -use ide_db::traits::resolve_target_trait; -use syntax::ast::{self, AstNode}; +use hir::HasSource; +use ide_db::{helpers::insert_whitespace_into_node::insert_ws_into, traits::resolve_target_trait}; +use syntax::ast::{self, make, AstNode}; use crate::{ assist_context::{AssistContext, Assists}, utils::{ - add_trait_assoc_items_to_impl, filter_assoc_items, render_snippet, Cursor, DefaultMethods, + add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body, render_snippet, + Cursor, DefaultMethods, }, AssistId, AssistKind, }; @@ -103,7 +105,7 @@ fn add_missing_impl_members_inner( let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; let missing_items = filter_assoc_items( - ctx.db(), + &ctx.sema, &ide_db::traits::get_missing_assoc_items(&ctx.sema, &impl_def), mode, ); @@ -115,18 +117,37 @@ fn add_missing_impl_members_inner( let target = impl_def.syntax().text_range(); acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| { let target_scope = ctx.sema.scope(impl_def.syntax()); - let (new_impl_def, first_new_item) = - add_trait_assoc_items_to_impl(&ctx.sema, missing_items, trait_, impl_def, target_scope); + let missing_items = missing_items + .into_iter() + .map(|it| { + if ctx.sema.hir_file_for(it.syntax()).is_macro() { + if let Some(it) = ast::AssocItem::cast(insert_ws_into(it.syntax().clone())) { + return it; + } + } + it.clone_for_update() + }) + .collect(); + let (new_impl_def, first_new_item) = add_trait_assoc_items_to_impl( + &ctx.sema, + missing_items, + trait_, + impl_def.clone(), + target_scope, + ); match ctx.config.snippet_cap { None => builder.replace(target, new_impl_def.to_string()), Some(cap) => { let mut cursor = Cursor::Before(first_new_item.syntax()); let placeholder; if let ast::AssocItem::Fn(func) = &first_new_item { - if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) { - if m.syntax().text() == "todo!()" { - placeholder = m; - cursor = Cursor::Replace(placeholder.syntax()); + if try_gen_trait_body(ctx, func, &trait_, &impl_def).is_none() { + if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) + { + if m.syntax().text() == "todo!()" { + placeholder = m; + cursor = Cursor::Replace(placeholder.syntax()); + } } } } @@ -140,6 +161,18 @@ fn add_missing_impl_members_inner( }) } +fn try_gen_trait_body( + ctx: &AssistContext, + func: &ast::Fn, + trait_: &hir::Trait, + impl_def: &ast::Impl, +) -> Option<()> { + let trait_path = make::ext::ident_path(&trait_.name(ctx.db()).to_string()); + let hir_ty = ctx.sema.resolve_type(&impl_def.self_ty()?)?; + let adt = hir_ty.as_adt()?.source(ctx.db())?; + gen_trait_fn_body(func, &trait_path, &adt.value) +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -809,6 +842,103 @@ fn foo(&self, bar: BAR) { ${0:todo!()} } } +"#, + ) + } + + #[test] + fn does_not_requalify_self_as_crate() { + check_assist( + add_missing_default_members, + r" +struct Wrapper(T); + +trait T { + fn f(self) -> Wrapper { + Wrapper(self) + } +} + +impl T for () { + $0 +} +", + r" +struct Wrapper(T); + +trait T { + fn f(self) -> Wrapper { + Wrapper(self) + } +} + +impl T for () { + $0fn f(self) -> Wrapper { + Wrapper(self) + } +} +", + ); + } + + #[test] + fn test_default_body_generation() { + check_assist( + add_missing_impl_members, + r#" +//- minicore: default +struct Foo(usize); + +impl Default for Foo { + $0 +} +"#, + r#" +struct Foo(usize); + +impl Default for Foo { + $0fn default() -> Self { + Self(Default::default()) + } +} +"#, + ) + } + + #[test] + fn test_from_macro() { + check_assist( + add_missing_default_members, + r#" +macro_rules! foo { + () => { + trait FooB { + fn foo<'lt>(&'lt self) {} + } + } +} +foo!(); +struct Foo(usize); + +impl FooB for Foo { + $0 +} +"#, + r#" +macro_rules! foo { + () => { + trait FooB { + fn foo<'lt>(&'lt self) {} + } + } +} +foo!(); +struct Foo(usize); + +impl FooB for Foo { + $0fn foo< 'lt>(& 'lt self){} + +} "#, ) }