use std::ops::Not; use crate::{ assist_context::{AssistContext, Assists}, utils::{convert_param_list_to_arg_list, suggest_name}, }; use either::Either; use hir::{db::HirDatabase, HasVisibility}; use ide_db::{ assists::{AssistId, GroupLabel}, path_transform::PathTransform, FxHashMap, FxHashSet, }; use itertools::Itertools; use syntax::{ ast::{ self, edit::{self, AstNodeEdit}, make, AssocItem, GenericArgList, GenericParamList, HasGenericParams, HasName, HasTypeBounds, HasVisibility as astHasVisibility, Path, WherePred, }, ted::{self, Position}, AstNode, NodeOrToken, SmolStr, SyntaxKind, }; // Assist: generate_delegate_trait // // Generate delegate trait implementation for `StructField`s. // // ``` // trait SomeTrait { // type T; // fn fn_(arg: u32) -> u32; // fn method_(&mut self) -> bool; // } // struct A; // impl SomeTrait for A { // type T = u32; // // fn fn_(arg: u32) -> u32 { // 42 // } // // fn method_(&mut self) -> bool { // false // } // } // struct B { // a$0: A, // } // ``` // -> // ``` // trait SomeTrait { // type T; // fn fn_(arg: u32) -> u32; // fn method_(&mut self) -> bool; // } // struct A; // impl SomeTrait for A { // type T = u32; // // fn fn_(arg: u32) -> u32 { // 42 // } // // fn method_(&mut self) -> bool { // false // } // } // struct B { // a: A, // } // // impl SomeTrait for B { // type T = ::T; // // fn fn_(arg: u32) -> u32 { // ::fn_(arg) // } // // fn method_(&mut self) -> bool { // ::method_(&mut self.a) // } // } // ``` pub(crate) fn generate_delegate_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let strukt = Struct::new(ctx.find_node_at_offset::()?)?; let field: Field = match ctx.find_node_at_offset::() { Some(field) => Field::new(&ctx, Either::Left(field))?, None => { let field = ctx.find_node_at_offset::()?; let field_list = ctx.find_node_at_offset::()?; Field::new(&ctx, either::Right((field, field_list)))? } }; strukt.delegate(field, acc, ctx); Some(()) } /// A utility object that represents a struct's field. #[derive(Debug)] struct Field { name: String, ty: ast::Type, range: syntax::TextRange, impls: Vec, } impl Field { pub(crate) fn new( ctx: &AssistContext<'_>, f: Either, ) -> Option { let db = ctx.sema.db; let module = ctx.sema.to_module_def(ctx.file_id())?; let (name, range, ty) = match f { Either::Left(f) => { let name = f.name()?.to_string(); (name, f.syntax().text_range(), f.ty()?) } Either::Right((f, l)) => { let name = l.fields().position(|it| it == f)?.to_string(); (name, f.syntax().text_range(), f.ty()?) } }; let hir_ty = ctx.sema.resolve_type(&ty)?; let type_impls = hir::Impl::all_for_type(db, hir_ty.clone()); let mut impls = Vec::with_capacity(type_impls.len()); if let Some(tp) = hir_ty.as_type_param(db) { for tb in tp.trait_bounds(db) { impls.push(Delegee::Bound(tb)); } }; for imp in type_impls { if let Some(tr) = imp.trait_(db).filter(|tr| tr.is_visible_from(db, module)) { impls.push(Delegee::Impls(tr, imp)) } } Some(Field { name, ty, range, impls }) } } /// A field that we want to delegate can offer the enclosing struct /// trait to implement in two ways. The first way is when the field /// actually implements the trait and the second way is when the field /// has a bound type parameter. We handle these cases in different ways /// hence the enum. #[derive(Debug)] enum Delegee { Bound(hir::Trait), Impls(hir::Trait, hir::Impl), } impl Delegee { fn signature(&self, db: &dyn HirDatabase) -> String { let mut s = String::new(); let (Delegee::Bound(it) | Delegee::Impls(it, _)) = self; for m in it.module(db).path_to_root(db).iter().rev() { if let Some(name) = m.name(db) { s.push_str(&format!("{}::", name.to_smol_str())); } } s.push_str(&it.name(db).to_smol_str()); s } } /// A utility struct that is used for the enclosing struct. struct Struct { strukt: ast::Struct, name: ast::Name, } impl Struct { pub(crate) fn new(s: ast::Struct) -> Option { let name = s.name()?; Some(Struct { name, strukt: s }) } pub(crate) fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_>) { let db = ctx.db(); for delegee in &field.impls { let trait_ = match delegee { Delegee::Bound(b) => b, Delegee::Impls(i, _) => i, }; // Skip trait that has `Self` type, which cannot be delegated // // See [`test_self_ty`] if has_self_type(*trait_, ctx).is_some() { continue; } // FIXME : We can omit already implemented impl_traits // But we don't know what the &[hir::Type] argument should look like. // if self.hir_ty.impls_trait(db, trait_, &[]) { // continue; // } let signature = delegee.signature(db); let Some(delegate) = generate_impl(ctx, self, &field.ty, &field.name, delegee) else { continue; }; acc.add_group( &GroupLabel(format!("Generate delegate trait impls for field `{}`", field.name)), AssistId("generate_delegate_trait", ide_db::assists::AssistKind::Generate), format!("Generate delegate trait impl `{}` for `{}`", signature, field.name), field.range, |builder| { builder.insert( self.strukt.syntax().text_range().end(), format!("\n\n{}", delegate.syntax()), ); }, ); } } } fn generate_impl( ctx: &AssistContext<'_>, strukt: &Struct, field_ty: &ast::Type, field_name: &String, delegee: &Delegee, ) -> Option { let delegate: ast::Impl; let db = ctx.db(); let ast_strukt = &strukt.strukt; let strukt_ty = make::ty_path(make::ext::ident_path(&strukt.name.to_string())); let strukt_params = ast_strukt.generic_param_list(); match delegee { Delegee::Bound(delegee) => { let bound_def = ctx.sema.source(delegee.to_owned())?.value; let bound_params = bound_def.generic_param_list(); delegate = make::impl_trait( delegee.is_unsafe(db), bound_params.clone(), bound_params.map(|params| params.to_generic_args()), strukt_params.clone(), strukt_params.map(|params| params.to_generic_args()), delegee.is_auto(db), make::ty(&delegee.name(db).to_smol_str()), strukt_ty, bound_def.where_clause(), ast_strukt.where_clause(), None, ) .clone_for_update(); // Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths let qualified_path_type = make::path_from_text(&format!("<{} as {}>", field_ty, delegate.trait_()?)); let delegate_assoc_items = delegate.get_or_create_assoc_item_list(); match bound_def.assoc_item_list() { Some(ai) => { ai.assoc_items() .filter(|item| matches!(item, AssocItem::MacroCall(_)).not()) .for_each(|item| { let assoc = process_assoc_item(item, qualified_path_type.clone(), &field_name); if let Some(assoc) = assoc { delegate_assoc_items.add_item(assoc); } }); } None => {} }; let target_scope = ctx.sema.scope(strukt.strukt.syntax())?; let source_scope = ctx.sema.scope(bound_def.syntax())?; let transform = PathTransform::generic_transformation(&target_scope, &source_scope); transform.apply(&delegate.syntax()); } Delegee::Impls(trait_, old_impl) => { let old_impl = ctx.sema.source(old_impl.to_owned())?.value; let old_impl_params = old_impl.generic_param_list(); // 1) Resolve conflicts between generic parameters in old_impl and // those in strukt. // // These generics parameters will also be used in `field_ty` and // `where_clauses`, so we should substitude arguments in them as well. let strukt_params = resolve_name_conflicts(strukt_params, &old_impl_params); let (field_ty, ty_where_clause) = match &strukt_params { Some(strukt_params) => { let args = strukt_params.to_generic_args(); let field_ty = rename_strukt_args(ctx, ast_strukt, field_ty, &args)?; let where_clause = ast_strukt .where_clause() .and_then(|wc| Some(rename_strukt_args(ctx, ast_strukt, &wc, &args)?)); (field_ty, where_clause) } None => (field_ty.clone_for_update(), None), }; // 2) Handle instantiated generics in `field_ty`. // 2.1) Some generics used in `self_ty` may be instantiated, so they // are no longer generics, we should remove and instantiate those // generics in advance. // `old_trait_args` contains names of generic args for trait in `old_impl` let old_impl_trait_args = old_impl .trait_()? .generic_arg_list() .map(|l| l.generic_args().map(|arg| arg.to_string())) .map_or_else(|| FxHashSet::default(), |it| it.collect()); let trait_gen_params = remove_instantiated_params( &old_impl.self_ty()?, old_impl_params.clone(), &old_impl_trait_args, ); // 2.2) Generate generic args applied on impl. let transform_args = generate_args_for_impl( old_impl_params, &old_impl.self_ty()?, &field_ty, &trait_gen_params, &old_impl_trait_args, ); // 2.3) Instantiate generics with `transform_impl`, this step also // remove unused params. let mut trait_gen_args = old_impl.trait_()?.generic_arg_list(); if let Some(trait_args) = &mut trait_gen_args { *trait_args = trait_args.clone_for_update(); transform_impl(ctx, ast_strukt, &old_impl, &transform_args, &trait_args.syntax())?; } let type_gen_args = strukt_params.clone().map(|params| params.to_generic_args()); let path_type = make::ty(&trait_.name(db).to_smol_str()).clone_for_update(); transform_impl(ctx, ast_strukt, &old_impl, &transform_args, &path_type.syntax())?; // 3) Generate delegate trait impl delegate = make::impl_trait( trait_.is_unsafe(db), trait_gen_params, trait_gen_args, strukt_params, type_gen_args, trait_.is_auto(db), path_type, strukt_ty, old_impl.where_clause().map(|wc| wc.clone_for_update()), ty_where_clause, None, ) .clone_for_update(); // Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths let qualified_path_type = make::path_from_text(&format!("<{} as {}>", field_ty, delegate.trait_()?)); // 4) Transform associated items in delegte trait impl let delegate_assoc_items = delegate.get_or_create_assoc_item_list(); for item in old_impl .get_or_create_assoc_item_list() .assoc_items() .filter(|item| matches!(item, AssocItem::MacroCall(_)).not()) { let item = item.clone_for_update(); transform_impl(ctx, ast_strukt, &old_impl, &transform_args, item.syntax())?; let assoc = process_assoc_item(item, qualified_path_type.clone(), &field_name)?; delegate_assoc_items.add_item(assoc); } // 5) Remove useless where clauses if let Some(wc) = delegate.where_clause() { remove_useless_where_clauses(&delegate.trait_()?, &delegate.self_ty()?, wc); } } } Some(delegate) } fn transform_impl( ctx: &AssistContext<'_>, strukt: &ast::Struct, old_impl: &ast::Impl, args: &Option, syntax: &syntax::SyntaxNode, ) -> Option<()> { let source_scope = ctx.sema.scope(&old_impl.self_ty()?.syntax())?; let target_scope = ctx.sema.scope(&strukt.syntax())?; let hir_old_impl = ctx.sema.to_impl_def(old_impl)?; let transform = args.as_ref().map_or_else( || PathTransform::generic_transformation(&target_scope, &source_scope), |args| { PathTransform::impl_transformation( &target_scope, &source_scope, hir_old_impl, args.clone(), ) }, ); transform.apply(&syntax); Some(()) } fn remove_instantiated_params( self_ty: &ast::Type, old_impl_params: Option, old_trait_args: &FxHashSet, ) -> Option { match self_ty { ast::Type::PathType(path_type) => { old_impl_params.and_then(|gpl| { // Remove generic parameters in field_ty (which is instantiated). let new_gpl = gpl.clone_for_update(); path_type .path()? .segments() .filter_map(|seg| seg.generic_arg_list()) .flat_map(|it| it.generic_args()) // However, if the param is also used in the trait arguments, // it shouldn't be removed now, which will be instantiated in // later `path_transform` .filter(|arg| !old_trait_args.contains(&arg.to_string())) .for_each(|arg| new_gpl.remove_generic_arg(&arg)); (new_gpl.generic_params().count() > 0).then_some(new_gpl) }) } _ => old_impl_params, } } fn remove_useless_where_clauses(trait_ty: &ast::Type, self_ty: &ast::Type, wc: ast::WhereClause) { let live_generics = [trait_ty, self_ty] .into_iter() .flat_map(|ty| ty.generic_arg_list()) .flat_map(|gal| gal.generic_args()) .map(|x| x.to_string()) .collect::>(); // Keep where-clauses that have generics after substitution, and remove the // rest. let has_live_generics = |pred: &WherePred| { pred.syntax() .descendants_with_tokens() .filter_map(|e| e.into_token()) .any(|e| e.kind() == SyntaxKind::IDENT && live_generics.contains(&e.to_string())) .not() }; wc.predicates().filter(has_live_generics).for_each(|pred| wc.remove_predicate(pred)); if wc.predicates().count() == 0 { // Remove useless whitespaces [syntax::Direction::Prev, syntax::Direction::Next] .into_iter() .flat_map(|dir| { wc.syntax() .siblings_with_tokens(dir) .skip(1) .take_while(|node_or_tok| node_or_tok.kind() == SyntaxKind::WHITESPACE) }) .for_each(|ws| ted::remove(ws)); ted::insert( ted::Position::after(wc.syntax()), NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)), ); // Remove where clause ted::remove(wc.syntax()); } } // Generate generic args that should be apply to current impl. // // For exmaple, say we have implementation `impl Trait for B`, // and `b: B` in struct `S`. Then the `A` should be instantiated to `T`. // While the last two generic args `B` and `C` doesn't change, it remains // ``. So we apply `` as generic arguments to impl. fn generate_args_for_impl( old_impl_gpl: Option, self_ty: &ast::Type, field_ty: &ast::Type, trait_params: &Option, old_trait_args: &FxHashSet, ) -> Option { let Some(old_impl_args) = old_impl_gpl.map(|gpl| gpl.to_generic_args().generic_args()) else { return None; }; // Create pairs of the args of `self_ty` and corresponding `field_ty` to // form the substitution list let mut arg_substs = FxHashMap::default(); match field_ty { field_ty @ ast::Type::PathType(_) => { let field_args = field_ty.generic_arg_list().map(|gal| gal.generic_args()); let self_ty_args = self_ty.generic_arg_list().map(|gal| gal.generic_args()); if let (Some(field_args), Some(self_ty_args)) = (field_args, self_ty_args) { self_ty_args.zip(field_args).for_each(|(self_ty_arg, field_arg)| { arg_substs.entry(self_ty_arg.to_string()).or_insert(field_arg); }) } } _ => {} } let args = old_impl_args .map(|old_arg| { arg_substs.get(&old_arg.to_string()).map_or_else( || old_arg.clone(), |replace_with| { // The old_arg will be replaced, so it becomes redundant if trait_params.is_some() && old_trait_args.contains(&old_arg.to_string()) { trait_params.as_ref().unwrap().remove_generic_arg(&old_arg) } replace_with.clone() }, ) }) .collect_vec(); args.is_empty().not().then(|| make::generic_arg_list(args.into_iter())) } fn rename_strukt_args( ctx: &AssistContext<'_>, strukt: &ast::Struct, item: &N, args: &GenericArgList, ) -> Option where N: ast::AstNode, { let hir_strukt = ctx.sema.to_struct_def(strukt)?; let hir_adt = hir::Adt::from(hir_strukt); let item = item.clone_for_update(); let scope = ctx.sema.scope(item.syntax())?; let transform = PathTransform::adt_transformation(&scope, &scope, hir_adt, args.clone()); transform.apply(&item.syntax()); Some(item) } fn has_self_type(trait_: hir::Trait, ctx: &AssistContext<'_>) -> Option<()> { let trait_source = ctx.sema.source(trait_)?.value; trait_source .syntax() .descendants_with_tokens() .filter_map(|e| e.into_token()) .find(|e| e.kind() == SyntaxKind::SELF_TYPE_KW) .map(|_| ()) } fn resolve_name_conflicts( strukt_params: Option, old_impl_params: &Option, ) -> Option { match (strukt_params, old_impl_params) { (Some(old_strukt_params), Some(old_impl_params)) => { let params = make::generic_param_list(std::iter::empty()).clone_for_update(); for old_strukt_param in old_strukt_params.generic_params() { // Get old name from `strukt` let mut name = SmolStr::from(match &old_strukt_param { ast::GenericParam::ConstParam(c) => c.name()?.to_string(), ast::GenericParam::LifetimeParam(l) => { l.lifetime()?.lifetime_ident_token()?.to_string() } ast::GenericParam::TypeParam(t) => t.name()?.to_string(), }); // The new name cannot be conflicted with generics in trait, and the renamed names. name = suggest_name::for_unique_generic_name(&name, old_impl_params); name = suggest_name::for_unique_generic_name(&name, ¶ms); match old_strukt_param { ast::GenericParam::ConstParam(c) => { if let Some(const_ty) = c.ty() { let const_param = make::const_param(make::name(&name), const_ty); params.add_generic_param(ast::GenericParam::ConstParam( const_param.clone_for_update(), )); } } p @ ast::GenericParam::LifetimeParam(_) => { params.add_generic_param(p.clone_for_update()); } ast::GenericParam::TypeParam(t) => { let type_bounds = t.type_bound_list(); let type_param = make::type_param(make::name(&name), type_bounds); params.add_generic_param(ast::GenericParam::TypeParam( type_param.clone_for_update(), )); } } } Some(params) } (Some(old_strukt_gpl), None) => Some(old_strukt_gpl), _ => None, } } fn process_assoc_item( item: syntax::ast::AssocItem, qual_path_ty: ast::Path, base_name: &str, ) -> Option { match item { AssocItem::Const(c) => const_assoc_item(c, qual_path_ty), AssocItem::Fn(f) => func_assoc_item(f, qual_path_ty, base_name), AssocItem::MacroCall(_) => { // FIXME : Handle MacroCall case. // macro_assoc_item(mac, qual_path_ty) None } AssocItem::TypeAlias(ta) => ty_assoc_item(ta, qual_path_ty), } } fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option { let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str()); // We want rhs of the const assignment to be a qualified path // The general case for const assigment can be found [here](`https://doc.rust-lang.org/reference/items/constant-items.html`) // The qualified will have the following generic syntax : // >::ConstName; // FIXME : We can't rely on `make::path_qualified` for now but it would be nice to replace the following with it. // make::path_qualified(qual_path_ty, path_expr_segment.as_single_segment().unwrap()); let qualified_path = qualified_path(qual_path_ty, path_expr_segment); let inner = make::item_const( item.visibility(), item.name()?, item.ty()?, make::expr_path(qualified_path), ) .clone_for_update(); Some(AssocItem::Const(inner)) } fn func_assoc_item( item: syntax::ast::Fn, qual_path_ty: Path, base_name: &str, ) -> Option { let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str()); let qualified_path = qualified_path(qual_path_ty, path_expr_segment); let call = match item.param_list() { // Methods and funcs should be handled separately. // We ask if the func has a `self` param. Some(l) => match l.self_param() { Some(slf) => { let mut self_kw = make::expr_path(make::path_from_text("self")); self_kw = make::expr_field(self_kw, base_name); let tail_expr_self = match slf.kind() { ast::SelfParamKind::Owned => self_kw, ast::SelfParamKind::Ref => make::expr_ref(self_kw, false), ast::SelfParamKind::MutRef => make::expr_ref(self_kw, true), }; let param_count = l.params().count(); let args = convert_param_list_to_arg_list(l).clone_for_update(); let pos_after_l_paren = Position::after(args.l_paren_token()?); if param_count > 0 { // Add SelfParam and a TOKEN::COMMA ted::insert_all_raw( pos_after_l_paren, vec![ NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()), NodeOrToken::Token(make::token(SyntaxKind::COMMA)), NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)), ], ); } else { // Add SelfParam only ted::insert_raw( pos_after_l_paren, NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()), ); } make::expr_call(make::expr_path(qualified_path), args) } None => { make::expr_call(make::expr_path(qualified_path), convert_param_list_to_arg_list(l)) } }, None => make::expr_call( make::expr_path(qualified_path), convert_param_list_to_arg_list(make::param_list(None, Vec::new())), ), } .clone_for_update(); let body = make::block_expr(vec![], Some(call)).clone_for_update(); let func = make::fn_( item.visibility(), item.name()?, item.generic_param_list(), item.where_clause(), item.param_list()?, body, item.ret_type(), item.async_token().is_some(), item.const_token().is_some(), item.unsafe_token().is_some(), ) .clone_for_update(); Some(AssocItem::Fn(func.indent(edit::IndentLevel(1)).clone_for_update())) } fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> Option { let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str()); let qualified_path = qualified_path(qual_path_ty, path_expr_segment); let ty = make::ty_path(qualified_path); let ident = item.name()?.to_string(); let alias = make::ty_alias( ident.as_str(), item.generic_param_list(), None, item.where_clause(), Some((ty, None)), ) .clone_for_update(); Some(AssocItem::TypeAlias(alias)) } fn qualified_path(qual_path_ty: ast::Path, path_expr_seg: ast::Path) -> ast::Path { make::path_from_text(&format!("{}::{}", qual_path_ty, path_expr_seg)) } #[cfg(test)] mod test { use super::*; use crate::tests::{check_assist, check_assist_not_applicable}; #[test] fn test_tuple_struct_basic() { check_assist( generate_delegate_trait, r#" struct Base; struct S(B$0ase); trait Trait {} impl Trait for Base {} "#, r#" struct Base; struct S(Base); impl Trait for S {} trait Trait {} impl Trait for Base {} "#, ); } #[test] fn test_self_ty() { // trait whith `Self` type cannot be delegated // // See the function `fn f() -> Self`. // It should be `fn f() -> Base` in `Base`, and `fn f() -> S` in `S` check_assist_not_applicable( generate_delegate_trait, r#" struct Base(()); struct S(B$0ase); trait Trait { fn f() -> Self; } impl Trait for Base { fn f() -> Base { Base(()) } } "#, ); } #[test] fn test_struct_struct_basic() { check_assist( generate_delegate_trait, r#" struct Base; struct S { ba$0se : Base } trait Trait {} impl Trait for Base {} "#, r#" struct Base; struct S { base : Base } impl Trait for S {} trait Trait {} impl Trait for Base {} "#, ) } // Structs need to be by def populated with fields // However user can invoke this assist while still editing // We therefore assert its non-applicability #[test] fn test_yet_empty_struct() { check_assist_not_applicable( generate_delegate_trait, r#" struct Base; struct S { $0 } impl Trait for S {} trait Trait {} impl Trait for Base {} "#, ) } #[test] fn test_yet_unspecified_field_type() { check_assist_not_applicable( generate_delegate_trait, r#" struct Base; struct S { ab$0c } impl Trait for S {} trait Trait {} impl Trait for Base {} "#, ); } #[test] fn test_unsafe_trait() { check_assist( generate_delegate_trait, r#" struct Base; struct S { ba$0se : Base } unsafe trait Trait {} unsafe impl Trait for Base {} "#, r#" struct Base; struct S { base : Base } unsafe impl Trait for S {} unsafe trait Trait {} unsafe impl Trait for Base {} "#, ); } #[test] fn test_unsafe_trait_with_unsafe_fn() { check_assist( generate_delegate_trait, r#" struct Base; struct S { ba$0se: Base, } unsafe trait Trait { unsafe fn a_func(); unsafe fn a_method(&self); } unsafe impl Trait for Base { unsafe fn a_func() {} unsafe fn a_method(&self) {} } "#, r#" struct Base; struct S { base: Base, } unsafe impl Trait for S { unsafe fn a_func() { ::a_func() } unsafe fn a_method(&self) { ::a_method(&self.base) } } unsafe trait Trait { unsafe fn a_func(); unsafe fn a_method(&self); } unsafe impl Trait for Base { unsafe fn a_func() {} unsafe fn a_method(&self) {} } "#, ); } #[test] fn test_struct_with_where_clause() { check_assist( generate_delegate_trait, r#" trait AnotherTrait {} struct S where T: AnotherTrait, { b$0 : T, }"#, r#" trait AnotherTrait {} struct S where T: AnotherTrait, { b : T, } impl AnotherTrait for S where T: AnotherTrait, {}"#, ); } #[test] fn test_fields_with_generics() { check_assist( generate_delegate_trait, r#" struct B { a: T } trait Trait { fn f(&self, a: T) -> T; } impl Trait for B { fn f(&self, a: T1) -> T1 { a } } struct A {} struct S { b :$0 B, } "#, r#" struct B { a: T } trait Trait { fn f(&self, a: T) -> T; } impl Trait for B { fn f(&self, a: T1) -> T1 { a } } struct A {} struct S { b : B, } impl Trait for S { fn f(&self, a: T1) -> T1 { as Trait>::f(&self.b, a) } } "#, ); } #[test] fn test_generics_with_conflict_names() { check_assist( generate_delegate_trait, r#" struct B { a: T } trait Trait { fn f(&self, a: T) -> T; } impl Trait for B { fn f(&self, a: T) -> T { a } } struct S { b : $0B, } "#, r#" struct B { a: T } trait Trait { fn f(&self, a: T) -> T; } impl Trait for B { fn f(&self, a: T) -> T { a } } struct S { b : B, } impl Trait for S { fn f(&self, a: T) -> T { as Trait>::f(&self.b, a) } } "#, ); } #[test] fn test_lifetime_with_conflict_names() { check_assist( generate_delegate_trait, r#" struct B<'a, T> { a: &'a T } trait Trait { fn f(&self, a: T) -> T; } impl<'a, T, T0> Trait for B<'a, T0> { fn f(&self, a: T) -> T { a } } struct S<'a, T> { b : $0B<'a, T>, } "#, r#" struct B<'a, T> { a: &'a T } trait Trait { fn f(&self, a: T) -> T; } impl<'a, T, T0> Trait for B<'a, T0> { fn f(&self, a: T) -> T { a } } struct S<'a, T> { b : B<'a, T>, } impl<'a, T, T1> Trait for S<'a, T1> { fn f(&self, a: T) -> T { as Trait>::f(&self.b, a) } } "#, ); } #[test] fn test_multiple_generics() { check_assist( generate_delegate_trait, r#" struct B { a: T1, b: T2 } trait Trait { fn f(&self, a: T) -> T; } impl Trait for B { fn f(&self, a: T) -> T { a } } struct S { b :$0 B, } "#, r#" struct B { a: T1, b: T2 } trait Trait { fn f(&self, a: T) -> T; } impl Trait for B { fn f(&self, a: T) -> T { a } } struct S { b : B, } impl Trait for S { fn f(&self, a: i32) -> i32 { as Trait>::f(&self.b, a) } } "#, ); } #[test] fn test_generics_multiplex() { check_assist( generate_delegate_trait, r#" struct B { a: T } trait Trait { fn f(&self, a: T) -> T; } impl Trait for B { fn f(&self, a: T) -> T { a } } struct S { b : $0B, } "#, r#" struct B { a: T } trait Trait { fn f(&self, a: T) -> T; } impl Trait for B { fn f(&self, a: T) -> T { a } } struct S { b : B, } impl Trait for S { fn f(&self, a: T0) -> T0 { as Trait>::f(&self.b, a) } } "#, ); } #[test] fn test_complex_without_where() { check_assist( generate_delegate_trait, r#" trait Trait<'a, T, const C: usize> { type AssocType; const AssocConst: usize; fn assoc_fn(p: ()); fn assoc_method(&self, p: ()); } struct Base; struct S { field$0: Base } impl<'a, T, const C: usize> Trait<'a, T, C> for Base { type AssocType = (); const AssocConst: usize = 0; fn assoc_fn(p: ()) {} fn assoc_method(&self, p: ()) {} } "#, r#" trait Trait<'a, T, const C: usize> { type AssocType; const AssocConst: usize; fn assoc_fn(p: ()); fn assoc_method(&self, p: ()); } struct Base; struct S { field: Base } impl<'a, T, const C: usize> Trait<'a, T, C> for S { type AssocType = >::AssocType; const AssocConst: usize = >::AssocConst; fn assoc_fn(p: ()) { >::assoc_fn(p) } fn assoc_method(&self, p: ()) { >::assoc_method(&self.field, p) } } impl<'a, T, const C: usize> Trait<'a, T, C> for Base { type AssocType = (); const AssocConst: usize = 0; fn assoc_fn(p: ()) {} fn assoc_method(&self, p: ()) {} } "#, ); } #[test] fn test_complex_two() { check_assist( generate_delegate_trait, r" trait AnotherTrait {} trait Trait<'a, T, const C: usize> { type AssocType; const AssocConst: usize; fn assoc_fn(p: ()); fn assoc_method(&self, p: ()); } struct Base; struct S { fi$0eld: Base, } impl<'b, C, const D: usize> Trait<'b, C, D> for Base where C: AnotherTrait, { type AssocType = (); const AssocConst: usize = 0; fn assoc_fn(p: ()) {} fn assoc_method(&self, p: ()) {} }", r#" trait AnotherTrait {} trait Trait<'a, T, const C: usize> { type AssocType; const AssocConst: usize; fn assoc_fn(p: ()); fn assoc_method(&self, p: ()); } struct Base; struct S { field: Base, } impl<'b, C, const D: usize> Trait<'b, C, D> for S where C: AnotherTrait, { type AssocType = >::AssocType; const AssocConst: usize = >::AssocConst; fn assoc_fn(p: ()) { >::assoc_fn(p) } fn assoc_method(&self, p: ()) { >::assoc_method(&self.field, p) } } impl<'b, C, const D: usize> Trait<'b, C, D> for Base where C: AnotherTrait, { type AssocType = (); const AssocConst: usize = 0; fn assoc_fn(p: ()) {} fn assoc_method(&self, p: ()) {} }"#, ) } #[test] fn test_complex_three() { check_assist( generate_delegate_trait, r#" trait AnotherTrait {} trait YetAnotherTrait {} struct StructImplsAll(); impl AnotherTrait for StructImplsAll {} impl YetAnotherTrait for StructImplsAll {} trait Trait<'a, T, const C: usize> { type A; const ASSOC_CONST: usize = C; fn assoc_fn(p: ()); fn assoc_method(&self, p: ()); } struct Base; struct S { fi$0eld: Base, } impl<'b, A: AnotherTrait + YetAnotherTrait, const B: usize> Trait<'b, A, B> for Base where A: AnotherTrait, { type A = i32; const ASSOC_CONST: usize = B; fn assoc_fn(p: ()) {} fn assoc_method(&self, p: ()) {} } "#, r#" trait AnotherTrait {} trait YetAnotherTrait {} struct StructImplsAll(); impl AnotherTrait for StructImplsAll {} impl YetAnotherTrait for StructImplsAll {} trait Trait<'a, T, const C: usize> { type A; const ASSOC_CONST: usize = C; fn assoc_fn(p: ()); fn assoc_method(&self, p: ()); } struct Base; struct S { field: Base, } impl<'b, A: AnotherTrait + YetAnotherTrait, const B: usize> Trait<'b, A, B> for S where A: AnotherTrait, { type A = >::A; const ASSOC_CONST: usize = >::ASSOC_CONST; fn assoc_fn(p: ()) { >::assoc_fn(p) } fn assoc_method(&self, p: ()) { >::assoc_method(&self.field, p) } } impl<'b, A: AnotherTrait + YetAnotherTrait, const B: usize> Trait<'b, A, B> for Base where A: AnotherTrait, { type A = i32; const ASSOC_CONST: usize = B; fn assoc_fn(p: ()) {} fn assoc_method(&self, p: ()) {} } "#, ) } #[test] fn test_type_bound() { check_assist( generate_delegate_trait, r#" trait AnotherTrait {} struct S where T: AnotherTrait, { b$0: T, }"#, r#" trait AnotherTrait {} struct S where T: AnotherTrait, { b: T, } impl AnotherTrait for S where T: AnotherTrait, {}"#, ); } #[test] fn test_type_bound_with_generics_1() { check_assist( generate_delegate_trait, r#" trait AnotherTrait {} struct B where T1: AnotherTrait { a: T, b: T1 } trait Trait { fn f(&self, a: T) -> T; } impl Trait for B { fn f(&self, a: T) -> T { a } } struct S where T1: AnotherTrait { b : $0B, }"#, r#" trait AnotherTrait {} struct B where T1: AnotherTrait { a: T, b: T1 } trait Trait { fn f(&self, a: T) -> T; } impl Trait for B { fn f(&self, a: T) -> T { a } } struct S where T1: AnotherTrait { b : B, } impl Trait for S where T10: AnotherTrait { fn f(&self, a: T) -> T { as Trait>::f(&self.b, a) } }"#, ); } #[test] fn test_type_bound_with_generics_2() { check_assist( generate_delegate_trait, r#" trait AnotherTrait {} struct B where T1: AnotherTrait { b: T1 } trait Trait { fn f(&self, a: T1) -> T1; } impl Trait for B { fn f(&self, a: T) -> T { a } } struct S where T: AnotherTrait { b : $0B, }"#, r#" trait AnotherTrait {} struct B where T1: AnotherTrait { b: T1 } trait Trait { fn f(&self, a: T1) -> T1; } impl Trait for B { fn f(&self, a: T) -> T { a } } struct S where T: AnotherTrait { b : B, } impl Trait for S where T0: AnotherTrait { fn f(&self, a: T) -> T { as Trait>::f(&self.b, a) } }"#, ); } #[test] fn test_docstring_example() { check_assist( generate_delegate_trait, r#" trait SomeTrait { type T; fn fn_(arg: u32) -> u32; fn method_(&mut self) -> bool; } struct A; impl SomeTrait for A { type T = u32; fn fn_(arg: u32) -> u32 { 42 } fn method_(&mut self) -> bool { false } } struct B { a$0: A, } "#, r#" trait SomeTrait { type T; fn fn_(arg: u32) -> u32; fn method_(&mut self) -> bool; } struct A; impl SomeTrait for A { type T = u32; fn fn_(arg: u32) -> u32 { 42 } fn method_(&mut self) -> bool { false } } struct B { a: A, } impl SomeTrait for B { type T = ::T; fn fn_(arg: u32) -> u32 { ::fn_(arg) } fn method_(&mut self) -> bool { ::method_(&mut self.a) } } "#, ); } #[test] fn import_from_other_mod() { check_assist( generate_delegate_trait, r#" mod some_module { pub trait SomeTrait { type T; fn fn_(arg: u32) -> u32; fn method_(&mut self) -> bool; } pub struct A; impl SomeTrait for A { type T = u32; fn fn_(arg: u32) -> u32 { 42 } fn method_(&mut self) -> bool { false } } } struct B { a$0: some_module::A, }"#, r#" mod some_module { pub trait SomeTrait { type T; fn fn_(arg: u32) -> u32; fn method_(&mut self) -> bool; } pub struct A; impl SomeTrait for A { type T = u32; fn fn_(arg: u32) -> u32 { 42 } fn method_(&mut self) -> bool { false } } } struct B { a: some_module::A, } impl some_module::SomeTrait for B { type T = ::T; fn fn_(arg: u32) -> u32 { ::fn_(arg) } fn method_(&mut self) -> bool { ::method_(&mut self.a) } }"#, ) } }