Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs')
| -rw-r--r-- | crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs | 1595 |
1 files changed, 1595 insertions, 0 deletions
diff --git a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs new file mode 100644 index 0000000000..c25b0bbe7c --- /dev/null +++ b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -0,0 +1,1595 @@ +use crate::{ + AssistConfig, + assist_context::{AssistContext, Assists}, +}; +use hir::{HasCrate, Semantics}; +use ide_db::{ + RootDatabase, + assists::{AssistId, AssistKind, ExprFillDefaultMode}, + famous_defs::FamousDefs, + syntax_helpers::suggest_name, +}; +use syntax::{ + AstNode, + ast::{ + self, AssocItem, BlockExpr, GenericParam, HasAttrs, HasGenericParams, HasName, + HasTypeBounds, HasVisibility, edit_in_place::Indent, make, + }, + syntax_editor::Position, +}; + +// Assist: generate_blanket_trait_impl +// +// Generate blanket trait implementation. +// +// ``` +// trait $0Foo<T: Send>: ToOwned +// where +// Self::Owned: Default, +// { +// fn foo(&self) -> T; +// +// fn print_foo(&self) { +// println!("{}", self.foo()); +// } +// } +// ``` +// -> +// ``` +// trait Foo<T: Send>: ToOwned +// where +// Self::Owned: Default, +// { +// fn foo(&self) -> T; +// +// fn print_foo(&self) { +// println!("{}", self.foo()); +// } +// } +// +// impl<T: Send, T1: ToOwned + ?Sized> Foo<T> for $0T1 +// where +// Self::Owned: Default, +// { +// fn foo(&self) -> T { +// todo!() +// } +// } +// ``` +pub(crate) fn generate_blanket_trait_impl( + acc: &mut Assists, + ctx: &AssistContext<'_>, +) -> Option<()> { + let name = ctx.find_node_at_offset::<ast::Name>()?; + let traitd = ast::Trait::cast(name.syntax().parent()?)?; + + if existing_any_impl(&traitd, &ctx.sema).is_some() { + cov_mark::hit!(existing_any_impl); + return None; + } + + acc.add( + AssistId("generate_blanket_trait_impl", AssistKind::Generate, None), + "Generate blanket trait implementation", + name.syntax().text_range(), + |builder| { + let mut edit = builder.make_editor(traitd.syntax()); + let namety = make::ty_path(make::path_from_text(&name.text())); + let trait_where_clause = traitd.where_clause().map(|it| it.clone_for_update()); + let bounds = traitd.type_bound_list().and_then(exlucde_sized); + let is_unsafe = traitd.unsafe_token().is_some(); + let thisname = this_name(&traitd); + let thisty = make::ty_path(make::path_from_text(&thisname.text())); + let indent = traitd.indent_level(); + + let gendecl = make::generic_param_list([GenericParam::TypeParam(make::type_param( + thisname.clone(), + apply_sized(has_sized(&traitd, &ctx.sema), bounds), + ))]); + + let trait_gen_args = + traitd.generic_param_list().map(|param_list| param_list.to_generic_args()); + + if let Some(ref where_clause) = trait_where_clause { + where_clause.reindent_to(0.into()); + } + + let impl_ = make::impl_trait( + cfg_attrs(&traitd), + is_unsafe, + traitd.generic_param_list(), + trait_gen_args, + Some(gendecl), + None, + false, + namety, + thisty.clone(), + trait_where_clause, + None, + None, + ) + .clone_for_update(); + + if let Some(trait_assoc_list) = traitd.assoc_item_list() { + let assoc_item_list = impl_.get_or_create_assoc_item_list(); + for method in trait_assoc_list.assoc_items() { + let AssocItem::Fn(method) = method else { + continue; + }; + if method.body().is_some() { + continue; + } + let f = todo_fn(&method, ctx.config).clone_for_update(); + f.indent(1.into()); + assoc_item_list.add_item(AssocItem::Fn(f)); + } + } + + impl_.indent(indent); + + edit.insert_all( + Position::after(traitd.syntax()), + vec![ + make::tokens::whitespace(&format!("\n\n{indent}")).into(), + impl_.syntax().clone().into(), + ], + ); + + if let Some(cap) = ctx.config.snippet_cap + && let Some(self_ty) = impl_.self_ty() + { + builder.add_tabstop_before(cap, self_ty); + } + + builder.add_file_edits(ctx.vfs_file_id(), edit); + }, + ); + + Some(()) +} + +fn existing_any_impl(traitd: &ast::Trait, sema: &Semantics<'_, RootDatabase>) -> Option<hir::Impl> { + let db = sema.db; + let traitd = sema.to_def(traitd)?; + traitd + .module(db) + .impl_defs(db) + .into_iter() + .find(|impl_| impl_.trait_(db).is_some_and(|it| it == traitd)) +} + +fn has_sized(traitd: &ast::Trait, sema: &Semantics<'_, RootDatabase>) -> bool { + if let Some(sized) = find_bound("Sized", traitd.type_bound_list()) { + sized.question_mark_token().is_none() + } else if let Some(is_sized) = where_clause_sized(traitd.where_clause()) { + is_sized + } else { + contained_owned_self_method(traitd.assoc_item_list()) + || super_traits_has_sized(traitd, sema) == Some(true) + } +} + +fn super_traits_has_sized(traitd: &ast::Trait, sema: &Semantics<'_, RootDatabase>) -> Option<bool> { + let traitd = sema.to_def(traitd)?; + let sized = FamousDefs(sema, traitd.krate(sema.db)).core_marker_Sized()?; + + Some(traitd.all_supertraits(sema.db).contains(&sized)) +} + +fn contained_owned_self_method(item_list: Option<ast::AssocItemList>) -> bool { + item_list.into_iter().flat_map(|assoc_item_list| assoc_item_list.assoc_items()).any(|item| { + match item { + AssocItem::Fn(f) => { + has_owned_self(&f) && where_clause_sized(f.where_clause()).is_none() + } + _ => false, + } + }) +} + +fn has_owned_self(f: &ast::Fn) -> bool { + has_owned_self_param(f) || has_ret_owned_self(f) +} + +fn has_owned_self_param(f: &ast::Fn) -> bool { + f.param_list() + .and_then(|param_list| param_list.self_param()) + .is_some_and(|sp| sp.amp_token().is_none() && sp.colon_token().is_none()) +} + +fn has_ret_owned_self(f: &ast::Fn) -> bool { + f.ret_type() + .and_then(|ret| match ret.ty() { + Some(ast::Type::PathType(ty)) => ty.path(), + _ => None, + }) + .is_some_and(|path| { + path.segment() + .and_then(|seg| seg.name_ref()) + .is_some_and(|name| path.qualifier().is_none() && name.text() == "Self") + }) +} + +fn where_clause_sized(where_clause: Option<ast::WhereClause>) -> Option<bool> { + where_clause?.predicates().find_map(|pred| { + find_bound("Sized", pred.type_bound_list()) + .map(|bound| bound.question_mark_token().is_none()) + }) +} + +fn apply_sized(has_sized: bool, bounds: Option<ast::TypeBoundList>) -> Option<ast::TypeBoundList> { + if has_sized { + return bounds; + } + let bounds = bounds + .into_iter() + .flat_map(|bounds| bounds.bounds()) + .chain([make::type_bound_text("?Sized")]); + make::type_bound_list(bounds) +} + +fn exlucde_sized(bounds: ast::TypeBoundList) -> Option<ast::TypeBoundList> { + make::type_bound_list(bounds.bounds().filter(|bound| !ty_bound_is(bound, "Sized"))) +} + +fn this_name(traitd: &ast::Trait) -> ast::Name { + let has_iter = find_bound("Iterator", traitd.type_bound_list()).is_some(); + + let params = traitd + .generic_param_list() + .into_iter() + .flat_map(|param_list| param_list.generic_params()) + .filter_map(|param| match param { + GenericParam::LifetimeParam(_) => None, + GenericParam::ConstParam(cp) => cp.name(), + GenericParam::TypeParam(tp) => tp.name(), + }) + .map(|name| name.to_string()) + .collect::<Vec<_>>(); + + let mut name_gen = + suggest_name::NameGenerator::new_with_names(params.iter().map(String::as_str)); + + make::name(&name_gen.suggest_name(if has_iter { "I" } else { "T" })) +} + +fn find_bound(s: &str, bounds: Option<ast::TypeBoundList>) -> Option<ast::TypeBound> { + bounds.into_iter().flat_map(|bounds| bounds.bounds()).find(|bound| ty_bound_is(bound, s)) +} + +fn ty_bound_is(bound: &ast::TypeBound, s: &str) -> bool { + matches!(bound.ty(), + Some(ast::Type::PathType(ty)) if ty.path() + .and_then(|path| path.segment()) + .and_then(|segment| segment.name_ref()) + .is_some_and(|name| name.text() == s)) +} + +fn todo_fn(f: &ast::Fn, config: &AssistConfig) -> ast::Fn { + let params = f.param_list().unwrap_or_else(|| make::param_list(None, None)); + make::fn_( + cfg_attrs(f), + f.visibility(), + f.name().unwrap_or_else(|| make::name("unnamed")), + f.generic_param_list(), + f.where_clause(), + params, + default_block(config), + f.ret_type(), + f.async_token().is_some(), + f.const_token().is_some(), + f.unsafe_token().is_some(), + f.gen_token().is_some(), + ) +} + +fn default_block(config: &AssistConfig) -> BlockExpr { + let expr = match config.expr_fill_default { + ExprFillDefaultMode::Todo => make::ext::expr_todo(), + ExprFillDefaultMode::Underscore => make::ext::expr_underscore(), + ExprFillDefaultMode::Default => make::ext::expr_todo(), + }; + make::block_expr(None, Some(expr)) +} + +fn cfg_attrs(node: &impl HasAttrs) -> impl Iterator<Item = ast::Attr> { + node.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")) +} + +#[cfg(test)] +mod test { + + use super::*; + use crate::tests::{check_assist, check_assist_not_applicable}; + + #[test] + fn test_gen_blanket_works() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo<T: Send>: ToOwned +where + Self::Owned: Default, +{ + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +trait Foo<T: Send>: ToOwned +where + Self::Owned: Default, +{ + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +impl<T: Send, T1: ToOwned + ?Sized> Foo<T> for $0T1 +where + Self::Owned: Default, +{ + fn foo(&self) -> T { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_sized() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: Iterator + Sized { + fn foo(mut self) -> Self::Item { + self.next().unwrap() + } +} +"#, + r#" +trait Foo: Iterator + Sized { + fn foo(mut self) -> Self::Item { + self.next().unwrap() + } +} + +impl<I: Iterator> Foo for $0I {} +"#, + ); + + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: Iterator { + fn foo(self) -> Self::Item; +} +"#, + r#" +trait Foo: Iterator { + fn foo(self) -> Self::Item; +} + +impl<I: Iterator> Foo for $0I { + fn foo(self) -> Self::Item { + todo!() + } +} +"#, + ); + + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo { + fn foo(&self) -> Self; +} +"#, + r#" +trait Foo { + fn foo(&self) -> Self; +} + +impl<T> Foo for $0T { + fn foo(&self) -> Self { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_super_sized() { + check_assist( + generate_blanket_trait_impl, + r#" +//- minicore: default +trait $0Foo: Default { + fn foo(&self); +} +"#, + r#" +trait Foo: Default { + fn foo(&self); +} + +impl<T: Default> Foo for $0T { + fn foo(&self) { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_non_sized() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: Iterator { + fn foo(&self) -> Self::Item; +} +"#, + r#" +trait Foo: Iterator { + fn foo(&self) -> Self::Item; +} + +impl<I: Iterator + ?Sized> Foo for $0I { + fn foo(&self) -> Self::Item { + todo!() + } +} +"#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: Iterator { + fn foo(&self) -> Self::Item; + + fn each(self) where Self: Sized; +} +"#, + r#" +trait Foo: Iterator { + fn foo(&self) -> Self::Item; + + fn each(self) where Self: Sized; +} + +impl<I: Iterator + ?Sized> Foo for $0I { + fn foo(&self) -> Self::Item { + todo!() + } + + fn each(self) where Self: Sized { + todo!() + } +} +"#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: Iterator { + fn foo(&self) -> Self::Item; + + fn each(&self) -> Self where Self: Sized; +} +"#, + r#" +trait Foo: Iterator { + fn foo(&self) -> Self::Item; + + fn each(&self) -> Self where Self: Sized; +} + +impl<I: Iterator + ?Sized> Foo for $0I { + fn foo(&self) -> Self::Item { + todo!() + } + + fn each(&self) -> Self where Self: Sized { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_indent() { + check_assist( + generate_blanket_trait_impl, + r#" +mod foo { + trait $0Foo<T: Send>: ToOwned + where + Self::Owned: Default, + { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } +} + "#, + r#" +mod foo { + trait Foo<T: Send>: ToOwned + where + Self::Owned: Default, + { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + + impl<T: Send, T1: ToOwned + ?Sized> Foo<T> for $0T1 + where + Self::Owned: Default, + { + fn foo(&self) -> T { + todo!() + } + } +} + "#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +mod foo { + trait $0Foo<T: Send>: ToOwned + where + Self::Owned: Default, + Self: Send, + { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } +} + "#, + r#" +mod foo { + trait Foo<T: Send>: ToOwned + where + Self::Owned: Default, + Self: Send, + { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + + impl<T: Send, T1: ToOwned + ?Sized> Foo<T> for $0T1 + where + Self::Owned: Default, + Self: Send, + { + fn foo(&self) -> T { + todo!() + } + } +} + "#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +mod foo { + mod bar { + trait $0Foo<T: Send>: ToOwned + where + Self::Owned: Default, + Self: Send, + { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + } +} + "#, + r#" +mod foo { + mod bar { + trait Foo<T: Send>: ToOwned + where + Self::Owned: Default, + Self: Send, + { + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + + impl<T: Send, T1: ToOwned + ?Sized> Foo<T> for $0T1 + where + Self::Owned: Default, + Self: Send, + { + fn foo(&self) -> T { + todo!() + } + } + } +} + "#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +mod foo { + trait $0Foo { + fn foo(&self) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } +} + "#, + r#" +mod foo { + trait Foo { + fn foo(&self) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + + impl<T: ?Sized> Foo for $0T { + fn foo(&self) -> i32 { + todo!() + } + } +} + "#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +mod foo { + mod bar { + trait $0Foo { + fn foo(&self) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + } +} + "#, + r#" +mod foo { + mod bar { + trait Foo { + fn foo(&self) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + + impl<T: ?Sized> Foo for $0T { + fn foo(&self) -> i32 { + todo!() + } + } + } +} + "#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +mod foo { + mod bar { + #[cfg(test)] + trait $0Foo { + fn foo(&self) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + } +} + "#, + r#" +mod foo { + mod bar { + #[cfg(test)] + trait Foo { + fn foo(&self) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } + } + + #[cfg(test)] + impl<T: ?Sized> Foo for $0T { + fn foo(&self) -> i32 { + todo!() + } + } + } +} + "#, + ); + } + + #[test] + fn test_gen_blanket_remove_attribute() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo<T: Send>: ToOwned +where + Self::Owned: Default, +{ + #[doc(hidden)] + fn foo(&self) -> T; + + /// foo + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +trait Foo<T: Send>: ToOwned +where + Self::Owned: Default, +{ + #[doc(hidden)] + fn foo(&self) -> T; + + /// foo + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +impl<T: Send, T1: ToOwned + ?Sized> Foo<T> for $0T1 +where + Self::Owned: Default, +{ + fn foo(&self) -> T { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_not_gen_type_alias() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo<T: Send>: ToOwned +where + Self::Owned: Default, +{ + type X: Sync; + + fn foo(&self, x: Self::X) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +trait Foo<T: Send>: ToOwned +where + Self::Owned: Default, +{ + type X: Sync; + + fn foo(&self, x: Self::X) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +impl<T: Send, T1: ToOwned + ?Sized> Foo<T> for $0T1 +where + Self::Owned: Default, +{ + fn foo(&self, x: Self::X) -> T { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_no_quick_bound() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo<T: Send> +where + Self: ToOwned, + Self::Owned: Default, +{ + type X: Sync; + + fn foo(&self, x: Self::X) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +trait Foo<T: Send> +where + Self: ToOwned, + Self::Owned: Default, +{ + type X: Sync; + + fn foo(&self, x: Self::X) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +impl<T: Send, T1: ?Sized> Foo<T> for $0T1 +where + Self: ToOwned, + Self::Owned: Default, +{ + fn foo(&self, x: Self::X) -> T { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_no_where_clause() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo<T: Send> { + type X: Sync; + + fn foo(&self, x: Self::X) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +trait Foo<T: Send> { + type X: Sync; + + fn foo(&self, x: Self::X) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +impl<T: Send, T1: ?Sized> Foo<T> for $0T1 { + fn foo(&self, x: Self::X) -> T { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_basic() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo { + type X: Sync; + + fn foo(&self, x: Self::X) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +trait Foo { + type X: Sync; + + fn foo(&self, x: Self::X) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +impl<T: ?Sized> Foo for $0T { + fn foo(&self, x: Self::X) -> i32 { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_cfg_attrs() { + check_assist( + generate_blanket_trait_impl, + r#" +#[cfg(test)] +trait $0Foo { + fn foo(&self, x: i32) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +#[cfg(test)] +trait Foo { + fn foo(&self, x: i32) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +#[cfg(test)] +impl<T: ?Sized> Foo for $0T { + fn foo(&self, x: i32) -> i32 { + todo!() + } +} +"#, + ); + check_assist( + generate_blanket_trait_impl, + r#" +#[cfg(test)] +trait $0Foo { + /// ... + #[cfg(test)] + fn foo(&self, x: i32) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + r#" +#[cfg(test)] +trait Foo { + /// ... + #[cfg(test)] + fn foo(&self, x: i32) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +#[cfg(test)] +impl<T: ?Sized> Foo for $0T { + #[cfg(test)] + fn foo(&self, x: i32) -> i32 { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo {} +"#, + r#" +trait Foo {} + +impl<T: ?Sized> Foo for $0T {} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_quick_bounds() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: Copy {} +"#, + r#" +trait Foo: Copy {} + +impl<T: Copy + ?Sized> Foo for $0T {} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_where_clause() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo where Self: Copy {} +"#, + r#" +trait Foo where Self: Copy {} + +impl<T: ?Sized> Foo for $0T +where Self: Copy +{ +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_where_clause_comma() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo where Self: Copy, {} +"#, + r#" +trait Foo where Self: Copy, {} + +impl<T: ?Sized> Foo for $0T +where Self: Copy, +{ +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_where_clause_newline() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo +where Self: Copy +{} +"#, + r#" +trait Foo +where Self: Copy +{} + +impl<T: ?Sized> Foo for $0T +where Self: Copy +{ +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_where_clause_newline_newline() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo +where + Self: Copy +{} +"#, + r#" +trait Foo +where + Self: Copy +{} + +impl<T: ?Sized> Foo for $0T +where + Self: Copy +{ +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_where_clause_newline_newline_comma() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo +where + Self: Copy, +{} +"#, + r#" +trait Foo +where + Self: Copy, +{} + +impl<T: ?Sized> Foo for $0T +where + Self: Copy, +{ +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_multiple_where_clause() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo +where + Self: Copy, + (): Into<Self>, +{} +"#, + r#" +trait Foo +where + Self: Copy, + (): Into<Self>, +{} + +impl<T: ?Sized> Foo for $0T +where + Self: Copy, + (): Into<Self>, +{ +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_trait_with_multiple_bounds_where_clause() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo +where + Self: Copy + Sync, + (): Into<Self>, +{} +"#, + r#" +trait Foo +where + Self: Copy + Sync, + (): Into<Self>, +{} + +impl<T: ?Sized> Foo for $0T +where + Self: Copy + Sync, + (): Into<Self>, +{ +} +"#, + ); + } + + #[test] + fn test_gen_blanket_empty_generate() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo { + fn foo(&self) {} +} +"#, + r#" +trait Foo { + fn foo(&self) {} +} + +impl<T: ?Sized> Foo for $0T {} +"#, + ); + } + + #[test] + fn test_gen_blanket_trait_with_doc() { + check_assist( + generate_blanket_trait_impl, + r#" +/// some docs +trait $0Foo {} +"#, + r#" +/// some docs +trait Foo {} + +impl<T: ?Sized> Foo for $0T {} +"#, + ); + } + + #[test] + fn test_gen_blanket_multiple_method() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo { + fn foo(&self); + fn bar(&self); +} +"#, + r#" +trait Foo { + fn foo(&self); + fn bar(&self); +} + +impl<T: ?Sized> Foo for $0T { + fn foo(&self) { + todo!() + } + + fn bar(&self) { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_method_with_generic() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo { + fn foo<T>(&self, value: i32) -> i32; + fn bar<T>(&self, value: i32) -> i32 { todo!() } +} +"#, + r#" +trait Foo { + fn foo<T>(&self, value: i32) -> i32; + fn bar<T>(&self, value: i32) -> i32 { todo!() } +} + +impl<T: ?Sized> Foo for $0T { + fn foo<T>(&self, value: i32) -> i32 { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_method_with_lifetimes() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo<'a> { + fn foo(&self) -> &'a str; +} +"#, + r#" +trait Foo<'a> { + fn foo(&self) -> &'a str; +} + +impl<'a, T: ?Sized> Foo<'a> for $0T { + fn foo(&self) -> &'a str { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_method_with_lifetime_bounds() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo<'a: 'static> { + fn foo(&self) -> &'a str; +} +"#, + r#" +trait Foo<'a: 'static> { + fn foo(&self) -> &'a str; +} + +impl<'a: 'static, T: ?Sized> Foo<'a> for $0T { + fn foo(&self) -> &'a str { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_method_with_lifetime_quick_bounds() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo<'a>: 'a { + fn foo(&self) -> &'a str; +} +"#, + r#" +trait Foo<'a>: 'a { + fn foo(&self) -> &'a str; +} + +impl<'a, T: 'a + ?Sized> Foo<'a> for $0T { + fn foo(&self) -> &'a str { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_method_with_multiple_lifetimes() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo<'a, 'b> { + fn foo(&self) -> &'a &'b str; +} +"#, + r#" +trait Foo<'a, 'b> { + fn foo(&self) -> &'a &'b str; +} + +impl<'a, 'b, T: ?Sized> Foo<'a, 'b> for $0T { + fn foo(&self) -> &'a &'b str { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_method_with_lifetime_bounds_at_where_clause() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo<'a> +where 'a: 'static, +{ + fn foo(&self) -> &'a str; +} +"#, + r#" +trait Foo<'a> +where 'a: 'static, +{ + fn foo(&self) -> &'a str; +} + +impl<'a, T: ?Sized> Foo<'a> for $0T +where 'a: 'static, +{ + fn foo(&self) -> &'a str { + todo!() + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_not_on_name() { + check_assist_not_applicable( + generate_blanket_trait_impl, + r#" +trait Foo<T: Send>: $0ToOwned +where + Self::Owned: Default, +{ + fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + ); + + check_assist_not_applicable( + generate_blanket_trait_impl, + r#" +trait Foo<T: Send>: ToOwned +where + Self::Owned: Default, +{ + $0fn foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + ); + + check_assist_not_applicable( + generate_blanket_trait_impl, + r#" +trait Foo<T: Send>: ToOwned +where + Self::Owned: Default, +{ + fn $0foo(&self) -> T; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} +"#, + ); + } + + #[test] + fn test_gen_blanket_existing_impl() { + cov_mark::check!(existing_any_impl); + check_assist_not_applicable( + generate_blanket_trait_impl, + r#" +trait $0Foo: Default { + fn foo(&self) -> Self; +} +impl Foo for () {} +"#, + ); + } + + #[test] + fn test_gen_blanket_existing_other_impl() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo: Default { + fn foo(&self) -> Self; +} +trait Bar: Default { + fn bar(&self) -> Self; +} +impl Bar for () {} +"#, + r#" +trait Foo: Default { + fn foo(&self) -> Self; +} + +impl<T: Default> Foo for $0T { + fn foo(&self) -> Self { + todo!() + } +} +trait Bar: Default { + fn bar(&self) -> Self; +} +impl Bar for () {} +"#, + ); + } + + #[test] + fn test_gen_blanket_apply_on_other_impl_block() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo { + fn foo(&self) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +trait Bar {} +impl Bar for i32 {} +"#, + r#" +trait Foo { + fn foo(&self) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +impl<T: ?Sized> Foo for $0T { + fn foo(&self) -> i32 { + todo!() + } +} + +trait Bar {} +impl Bar for i32 {} +"#, + ); + } + + #[test] + fn test_gen_blanket_apply_on_other_blanket_impl_block() { + check_assist( + generate_blanket_trait_impl, + r#" +trait $0Foo { + fn foo(&self) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +trait Bar {} +impl<T> Bar for T {} +"#, + r#" +trait Foo { + fn foo(&self) -> i32; + + fn print_foo(&self) { + println!("{}", self.foo()); + } +} + +impl<T: ?Sized> Foo for $0T { + fn foo(&self) -> i32 { + todo!() + } +} + +trait Bar {} +impl<T> Bar for T {} +"#, + ); + } +} |