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, }, 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 impls for field `{}`", field.name)), AssistId("generate_delegate_trait", ide_db::assists::AssistKind::Generate), format!("Generate delegate 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())); match delegee { Delegee::Bound(delegee) => { let bound_def = ctx.sema.source(delegee.to_owned())?.value; let bound_params = bound_def.generic_param_list(); let strukt_params = ast_strukt.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.to_string(), delegate.trait_()?.to_string() )); 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; // `old_trait_args` contains names of generic args for trait in `old_impl` let old_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 old_impl_params = old_impl.generic_param_list(); // Resolve conflicts with generic parameters 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 (renamed_strukt_params, field_ty, ty_where_clause) = if let Some(strukt_params) = resolve_conflicts_for_strukt(ast_strukt, old_impl_params.as_ref()) { let strukt_args = strukt_params.to_generic_args(); let field_ty = subst_name_in_strukt(ctx, ast_strukt, field_ty, strukt_args.clone())?; let wc = ast_strukt .where_clause() .and_then(|wc| Some(subst_name_in_strukt(ctx, ast_strukt, &wc, strukt_args)?)); (Some(strukt_params), field_ty, wc) } else { (None, field_ty.clone_for_update(), None) }; // Some generics used in `field_ty` may be instantiated, so they are no longer // `generics`. We should remove them from generics params, and use the rest params. let trait_gen_params = remove_instantiated_params(&old_impl.self_ty()?, old_impl_params, &old_trait_args); // Generate generic args that applied to current impl, this step will also remove unused params let args_for_impl = get_args_for_impl(&old_impl, &field_ty, &trait_gen_params, &old_trait_args); let mut trait_gen_args = old_impl.trait_()?.generic_arg_list(); if let Some(arg_list) = &mut trait_gen_args { *arg_list = arg_list.clone_for_update(); transform_impl(ctx, ast_strukt, &old_impl, &args_for_impl, &arg_list.syntax())?; } let mut type_gen_args = renamed_strukt_params.clone().map(|params| params.to_generic_args()); if let Some(type_args) = &mut type_gen_args { *type_args = type_args.clone_for_update(); transform_impl(ctx, ast_strukt, &old_impl, &args_for_impl, &type_args.syntax())?; } let path_type = make::ty(&trait_.name(db).to_smol_str()).clone_for_update(); transform_impl(ctx, ast_strukt, &old_impl, &args_for_impl, &path_type.syntax())?; delegate = make::impl_trait( trait_.is_unsafe(db), trait_gen_params, trait_gen_args, renamed_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.to_string(), delegate.trait_()?.to_string() )); 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 assoc = process_assoc_item( transform_assoc_item(ctx, ast_strukt, &old_impl, &args_for_impl, item)?, qualified_path_type.clone(), &field_name, )?; delegate_assoc_items.add_item(assoc); } // Remove unused where clauses if let Some(wc) = delegate.where_clause() { remove_useless_where_clauses(&delegate, wc)?; } } } Some(delegate) } fn transform_assoc_item( ctx: &AssistContext<'_>, strukt: &ast::Struct, old_impl: &ast::Impl, args: &Option, item: AssocItem, ) -> Option { let source_scope = ctx.sema.scope(&item.syntax()).unwrap(); let target_scope = ctx.sema.scope(&strukt.syntax())?; let hir_old_impl = ctx.sema.to_impl_def(old_impl)?; let item = item.clone_for_update(); 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(&item.syntax()); Some(item) } 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. .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(delegate: &ast::Impl, wc: ast::WhereClause) -> Option<()> { let trait_args = delegate.trait_()?.generic_arg_list().map(|trait_args| trait_args.generic_args()); let strukt_args = delegate.self_ty()?.generic_arg_list().map(|strukt_args| strukt_args.generic_args()); let used_generic_names = match (trait_args, strukt_args) { (None, None) => None, (None, Some(y)) => Some(y.map(|arg| arg.to_string()).collect::>()), (Some(x), None) => Some(x.map(|arg| arg.to_string()).collect::>()), (Some(x), Some(y)) => Some(x.chain(y).map(|arg| arg.to_string()).collect::>()), }; // Keep clauses that have generic clauses after substitution, and remove the rest if let Some(used_generic_names) = used_generic_names { wc.predicates() .filter(|pred| { pred.syntax() .descendants_with_tokens() .filter_map(|e| e.into_token()) .find(|e| { e.kind() == SyntaxKind::IDENT && used_generic_names.contains(&e.to_string()) }) .is_none() }) .for_each(|pred| { wc.remove_predicate(pred); }); } else { wc.predicates().for_each(|pred| wc.remove_predicate(pred)); } if wc.predicates().count() == 0 { // Remove useless whitespaces wc.syntax() .siblings_with_tokens(syntax::Direction::Prev) .skip(1) .take_while(|node_or_tok| node_or_tok.kind() == SyntaxKind::WHITESPACE) .for_each(|ws| ted::remove(ws)); wc.syntax() .siblings_with_tokens(syntax::Direction::Next) .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()); } Some(()) } fn get_args_for_impl( old_impl: &ast::Impl, field_ty: &ast::Type, trait_params: &Option, old_trait_args: &FxHashSet, ) -> Option { // Generate generic args that should be apply to current impl // // For exmaple, if we have `impl Trait for B`, and `b: B` in `S`, // then the generic `A` should be renamed to `T`. While the last two generic args // doesn't change, it renames . So we apply `` as generic arguments // to impl. let old_impl_params = old_impl.generic_param_list(); let self_ty = old_impl.self_ty(); if let (Some(old_impl_gpl), Some(self_ty)) = (old_impl_params, self_ty) { // Make pair of the arguments of `field_ty` and `old_strukt_args` to // get the list for substitution let mut arg_substs = FxHashMap::default(); match field_ty { field_ty @ ast::Type::PathType(_) => { let field_args = field_ty.generic_arg_list(); if let (Some(field_args), Some(old_impl_args)) = (field_args, self_ty.generic_arg_list()) { field_args.generic_args().zip(old_impl_args.generic_args()).for_each( |(field_arg, impl_arg)| { arg_substs.entry(impl_arg.to_string()).or_insert(field_arg); }, ) } } _ => {} } let args = old_impl_gpl .to_generic_args() .generic_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 let old_arg_name = old_arg.to_string(); if old_trait_args.contains(&old_arg_name) { // However, we should check type bounds and where clauses on old_arg, // if it has type bound, we should keep the type bound. // match trait_params.and_then(|params| params.remove_generic_arg(&old_arg)) { // Some(ast::GenericParam::TypeParam(ty)) => { // ty.type_bound_list().and_then(|bounds| ) // } // _ => {} // } if let Some(params) = trait_params { params.remove_generic_arg(&old_arg); } } replace_with.clone() }, ) }) .collect_vec(); args.is_empty().not().then(|| make::generic_arg_list(args.into_iter())) } else { None } } fn subst_name_in_strukt( 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 item_scope = ctx.sema.scope(item.syntax())?; let transform = PathTransform::adt_transformation(&item_scope, &item_scope, hir_adt, args); 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_conflicts_for_strukt( strukt: &ast::Struct, old_impl_params: Option<&ast::GenericParamList>, ) -> Option { match (strukt.generic_param_list(), 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.to_string(), path_expr_seg.to_string())) } #[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) } }"#, ) } }