]> git.lizzy.rs Git - rust.git/blobdiff - crates/ide_assists/src/handlers/add_missing_impl_members.rs
fix: insert whitespaces into assoc items for assist when macro generated
[rust.git] / crates / ide_assists / src / handlers / add_missing_impl_members.rs
index 0148635f9eba29eb69a509966976a7132df8362d..a10eca10d11946c1b350b0e1189b56c2d91d2de1 100644 (file)
@@ -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,
 };
@@ -64,7 +66,6 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -
 // impl Trait for () {
 //     type X = ();
 //     fn foo(&self) {}$0
-//
 // }
 // ```
 // ->
@@ -104,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,
     );
@@ -116,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());
+                            }
                         }
                     }
                 }
@@ -141,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};
@@ -195,6 +227,7 @@ fn foo(&self) {
     fn baz(&self) {
         todo!()
     }
+
 }"#,
         );
     }
@@ -231,6 +264,7 @@ fn bar(&self) {}
     fn foo(&self) {
         ${0:todo!()}
     }
+
 }"#,
         );
     }
@@ -808,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>(T);
+
+trait T {
+    fn f(self) -> Wrapper<Self> {
+        Wrapper(self)
+    }
+}
+
+impl T for () {
+    $0
+}
+",
+            r"
+struct Wrapper<T>(T);
+
+trait T {
+    fn f(self) -> Wrapper<Self> {
+        Wrapper(self)
+    }
+}
+
+impl T for () {
+    $0fn f(self) -> Wrapper<Self> {
+        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){}
+
+}
 "#,
         )
     }