-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,
};
// impl Trait for () {
// type X = ();
// fn foo(&self) {}$0
-//
// }
// ```
// ->
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,
);
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());
+ }
}
}
}
})
}
+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};
fn baz(&self) {
todo!()
}
+
}"#,
);
}
fn foo(&self) {
${0:todo!()}
}
+
}"#,
);
}
${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){}
+
+}
"#,
)
}