Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
let is_unsafe = traitd.unsafe_token().is_some();
1use crate::{2 AssistConfig,3 assist_context::{AssistContext, Assists},4};5use hir::{HasCrate, Semantics};6use ide_db::{7 RootDatabase,8 assists::{AssistId, AssistKind},9 famous_defs::FamousDefs,10 syntax_helpers::suggest_name,11};12use syntax::{13 AstNode,14 ast::{15 self, AssocItem, GenericParam, HasAttrs, HasGenericParams, HasName, HasTypeBounds,16 HasVisibility, edit::AstNodeEdit, syntax_factory::SyntaxFactory,17 },18 syntax_editor::Position,19};2021// Assist: generate_blanket_trait_impl22//23// Generate blanket trait implementation.24//25// ```26// trait $0Foo<T: Send>: ToOwned27// where28// Self::Owned: Default,29// {30// fn foo(&self) -> T;31//32// fn print_foo(&self) {33// println!("{}", self.foo());34// }35// }36// ```37// ->38// ```39// trait Foo<T: Send>: ToOwned40// where41// Self::Owned: Default,42// {43// fn foo(&self) -> T;44//45// fn print_foo(&self) {46// println!("{}", self.foo());47// }48// }49//50// impl<T: Send, T1: ToOwned + ?Sized> Foo<T> for $0T151// where52// Self::Owned: Default,53// {54// fn foo(&self) -> T {55// todo!()56// }57// }58// ```59pub(crate) fn generate_blanket_trait_impl(60 acc: &mut Assists,61 ctx: &AssistContext<'_>,62) -> Option<()> {63 let name = ctx.find_node_at_offset::<ast::Name>()?;64 let traitd = ast::Trait::cast(name.syntax().parent()?)?;6566 if existing_any_impl(&traitd, &ctx.sema).is_some() {67 cov_mark::hit!(existing_any_impl);68 return None;69 }7071 acc.add(72 AssistId("generate_blanket_trait_impl", AssistKind::Generate, None),73 "Generate blanket trait implementation",74 name.syntax().text_range(),75 |builder| {76 let editor = builder.make_editor(traitd.syntax());77 let make = editor.make();78 let namety = make.ty_path(make.path_from_text(&name.text()));79 let trait_where_clause = traitd.where_clause().map(|it| it.reset_indent());80 let bounds = traitd.type_bound_list().and_then(|list| exclude_sized(make, list));81 let is_unsafe = traitd.unsafe_token().is_some();82 let thisname = this_name(make, &traitd);83 let thisty = make.ty_path(make.path_from_text(&thisname.text()));84 let indent = traitd.indent_level();8586 let gendecl = make.generic_param_list([GenericParam::TypeParam(make.type_param(87 thisname.clone(),88 apply_sized(make, has_sized(&traitd, &ctx.sema), bounds),89 ))]);9091 let trait_gen_args =92 traitd.generic_param_list().map(|param_list| param_list.to_generic_args());9394 let impl_ = make.impl_trait(95 cfg_attrs(&traitd),96 is_unsafe,97 traitd.generic_param_list(),98 trait_gen_args,99 Some(gendecl),100 None,101 false,102 namety.into(),103 thisty.into(),104 trait_where_clause,105 None,106 None,107 );108109 if let Some(trait_assoc_list) = traitd.assoc_item_list() {110 let assoc_item_list = impl_.get_or_create_assoc_item_list_with_editor(&editor);111 for item in trait_assoc_list.assoc_items() {112 let item = match item {113 ast::AssocItem::Fn(method) if method.body().is_none() => {114 todo_fn(make, &method, ctx.config).into()115 }116 ast::AssocItem::Const(_) | ast::AssocItem::TypeAlias(_) => item,117 _ => continue,118 };119 assoc_item_list.add_item(item.reset_indent().indent(1.into()));120 }121 }122123 let impl_ = impl_.indent(indent);124125 editor.insert_all(126 Position::after(traitd.syntax()),127 vec![128 make.whitespace(&format!("\n\n{indent}")).into(),129 impl_.syntax().clone().into(),130 ],131 );132133 if let Some(cap) = ctx.config.snippet_cap134 && let Some(self_ty) = impl_.self_ty()135 {136 builder.add_tabstop_before(cap, self_ty);137 }138 builder.add_file_edits(ctx.vfs_file_id(), editor);139 },140 );141142 Some(())143}144145fn existing_any_impl(traitd: &ast::Trait, sema: &Semantics<'_, RootDatabase>) -> Option<hir::Impl> {146 let db = sema.db;147 let traitd = sema.to_def(traitd)?;148 traitd149 .module(db)150 .impl_defs(db)151 .into_iter()152 .find(|impl_| impl_.trait_(db).is_some_and(|it| it == traitd))153}154155fn has_sized(traitd: &ast::Trait, sema: &Semantics<'_, RootDatabase>) -> bool {156 if let Some(sized) = find_bound("Sized", traitd.type_bound_list()) {157 sized.question_mark_token().is_none()158 } else if let Some(is_sized) = where_clause_sized(traitd.where_clause()) {159 is_sized160 } else {161 contained_owned_self_method(traitd.assoc_item_list())162 || super_traits_has_sized(traitd, sema) == Some(true)163 }164}165166fn super_traits_has_sized(traitd: &ast::Trait, sema: &Semantics<'_, RootDatabase>) -> Option<bool> {167 let traitd = sema.to_def(traitd)?;168 let sized = FamousDefs(sema, traitd.krate(sema.db)).core_marker_Sized()?;169170 Some(traitd.all_supertraits(sema.db).contains(&sized))171}172173fn contained_owned_self_method(item_list: Option<ast::AssocItemList>) -> bool {174 item_list.into_iter().flat_map(|assoc_item_list| assoc_item_list.assoc_items()).any(|item| {175 match item {176 AssocItem::Fn(f) => {177 has_owned_self(&f) && where_clause_sized(f.where_clause()).is_none()178 }179 _ => false,180 }181 })182}183184fn has_owned_self(f: &ast::Fn) -> bool {185 has_owned_self_param(f) || has_ret_owned_self(f)186}187188fn has_owned_self_param(f: &ast::Fn) -> bool {189 f.param_list()190 .and_then(|param_list| param_list.self_param())191 .is_some_and(|sp| sp.amp_token().is_none() && sp.colon_token().is_none())192}193194fn has_ret_owned_self(f: &ast::Fn) -> bool {195 f.ret_type()196 .and_then(|ret| match ret.ty() {197 Some(ast::Type::PathType(ty)) => ty.path(),198 _ => None,199 })200 .is_some_and(|path| {201 path.segment()202 .and_then(|seg| seg.name_ref())203 .is_some_and(|name| path.qualifier().is_none() && name.text() == "Self")204 })205}206207fn where_clause_sized(where_clause: Option<ast::WhereClause>) -> Option<bool> {208 where_clause?.predicates().find_map(|pred| {209 find_bound("Sized", pred.type_bound_list())210 .map(|bound| bound.question_mark_token().is_none())211 })212}213214fn apply_sized(215 make: &SyntaxFactory,216 has_sized: bool,217 bounds: Option<ast::TypeBoundList>,218) -> Option<ast::TypeBoundList> {219 if has_sized {220 return bounds;221 }222 let bounds = bounds223 .into_iter()224 .flat_map(|bounds| bounds.bounds())225 .chain([make.type_bound_text("?Sized")]);226 make.type_bound_list(bounds)227}228229fn exclude_sized(make: &SyntaxFactory, bounds: ast::TypeBoundList) -> Option<ast::TypeBoundList> {230 make.type_bound_list(bounds.bounds().filter(|bound| !ty_bound_is(bound, "Sized")))231}232233fn this_name(make: &SyntaxFactory, traitd: &ast::Trait) -> ast::Name {234 let has_iter = find_bound("Iterator", traitd.type_bound_list()).is_some();235236 let params = traitd237 .generic_param_list()238 .into_iter()239 .flat_map(|param_list| param_list.generic_params())240 .filter_map(|param| match param {241 GenericParam::LifetimeParam(_) => None,242 GenericParam::ConstParam(cp) => cp.name(),243 GenericParam::TypeParam(tp) => tp.name(),244 })245 .map(|name| name.to_string())246 .collect::<Vec<_>>();247248 let mut name_gen =249 suggest_name::NameGenerator::new_with_names(params.iter().map(String::as_str));250251 make.name(&name_gen.suggest_name(if has_iter { "I" } else { "T" }))252}253254fn find_bound(s: &str, bounds: Option<ast::TypeBoundList>) -> Option<ast::TypeBound> {255 bounds.into_iter().flat_map(|bounds| bounds.bounds()).find(|bound| ty_bound_is(bound, s))256}257258fn ty_bound_is(bound: &ast::TypeBound, s: &str) -> bool {259 matches!(bound.ty(),260 Some(ast::Type::PathType(ty)) if ty.path()261 .and_then(|path| path.segment())262 .and_then(|segment| segment.name_ref())263 .is_some_and(|name| name.text() == s))264}265266fn todo_fn(make: &SyntaxFactory, f: &ast::Fn, config: &AssistConfig) -> ast::Fn {267 let params = f.param_list().unwrap_or_else(|| make.param_list(None, None));268 make.fn_(269 cfg_attrs(f),270 f.visibility(),271 f.name().unwrap_or_else(|| make.name("unnamed")),272 f.generic_param_list(),273 f.where_clause(),274 params,275 make.block_expr(None, Some(crate::utils::expr_fill_default(config))),276 f.ret_type(),277 f.async_token().is_some(),278 f.const_token().is_some(),279 f.unsafe_token().is_some(),280 f.gen_token().is_some(),281 )282}283284fn cfg_attrs(node: &impl HasAttrs) -> impl Iterator<Item = ast::Attr> {285 node.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))))286}287288#[cfg(test)]289mod test {290291 use super::*;292 use crate::tests::{check_assist, check_assist_not_applicable};293294 #[test]295 fn test_gen_blanket_works() {296 check_assist(297 generate_blanket_trait_impl,298 r#"299trait $0Foo<T: Send>: ToOwned300where301 Self::Owned: Default,302{303 fn foo(&self) -> T;304305 fn print_foo(&self) {306 println!("{}", self.foo());307 }308}309"#,310 r#"311trait Foo<T: Send>: ToOwned312where313 Self::Owned: Default,314{315 fn foo(&self) -> T;316317 fn print_foo(&self) {318 println!("{}", self.foo());319 }320}321322impl<T: Send, T1: ToOwned + ?Sized> Foo<T> for $0T1323where324 Self::Owned: Default,325{326 fn foo(&self) -> T {327 todo!()328 }329}330"#,331 );332 }333334 #[test]335 fn test_gen_blanket_sized() {336 check_assist(337 generate_blanket_trait_impl,338 r#"339trait $0Foo: Iterator + Sized {340 fn foo(mut self) -> Self::Item {341 self.next().unwrap()342 }343}344"#,345 r#"346trait Foo: Iterator + Sized {347 fn foo(mut self) -> Self::Item {348 self.next().unwrap()349 }350}351352impl<I: Iterator> Foo for $0I {}353"#,354 );355356 check_assist(357 generate_blanket_trait_impl,358 r#"359trait $0Foo: Iterator {360 fn foo(self) -> Self::Item;361}362"#,363 r#"364trait Foo: Iterator {365 fn foo(self) -> Self::Item;366}367368impl<I: Iterator> Foo for $0I {369 fn foo(self) -> Self::Item {370 todo!()371 }372}373"#,374 );375376 check_assist(377 generate_blanket_trait_impl,378 r#"379trait $0Foo {380 fn foo(&self) -> Self;381}382"#,383 r#"384trait Foo {385 fn foo(&self) -> Self;386}387388impl<T> Foo for $0T {389 fn foo(&self) -> Self {390 todo!()391 }392}393"#,394 );395 }396397 #[test]398 fn test_gen_blanket_super_sized() {399 check_assist(400 generate_blanket_trait_impl,401 r#"402//- minicore: default403trait $0Foo: Default {404 fn foo(&self);405}406"#,407 r#"408trait Foo: Default {409 fn foo(&self);410}411412impl<T: Default> Foo for $0T {413 fn foo(&self) {414 todo!()415 }416}417"#,418 );419 }420421 #[test]422 fn test_gen_blanket_non_sized() {423 check_assist(424 generate_blanket_trait_impl,425 r#"426trait $0Foo: Iterator {427 fn foo(&self) -> Self::Item;428}429"#,430 r#"431trait Foo: Iterator {432 fn foo(&self) -> Self::Item;433}434435impl<I: Iterator + ?Sized> Foo for $0I {436 fn foo(&self) -> Self::Item {437 todo!()438 }439}440"#,441 );442 check_assist(443 generate_blanket_trait_impl,444 r#"445trait $0Foo: Iterator {446 fn foo(&self) -> Self::Item;447448 fn each(self) where Self: Sized;449}450"#,451 r#"452trait Foo: Iterator {453 fn foo(&self) -> Self::Item;454455 fn each(self) where Self: Sized;456}457458impl<I: Iterator + ?Sized> Foo for $0I {459 fn foo(&self) -> Self::Item {460 todo!()461 }462463 fn each(self) where Self: Sized {464 todo!()465 }466}467"#,468 );469 check_assist(470 generate_blanket_trait_impl,471 r#"472trait $0Foo: Iterator {473 fn foo(&self) -> Self::Item;474475 fn each(&self) -> Self where Self: Sized;476}477"#,478 r#"479trait Foo: Iterator {480 fn foo(&self) -> Self::Item;481482 fn each(&self) -> Self where Self: Sized;483}484485impl<I: Iterator + ?Sized> Foo for $0I {486 fn foo(&self) -> Self::Item {487 todo!()488 }489490 fn each(&self) -> Self where Self: Sized {491 todo!()492 }493}494"#,495 );496 }497498 #[test]499 fn test_gen_blanket_other_assoc_items() {500 check_assist(501 generate_blanket_trait_impl,502 r#"503trait $0Foo {504 type Item;505506 const N: usize;507508 fn foo(&self);509}510"#,511 r#"512trait Foo {513 type Item;514515 const N: usize;516517 fn foo(&self);518}519520impl<T: ?Sized> Foo for $0T {521 type Item;522523 const N: usize;524525 fn foo(&self) {526 todo!()527 }528}529"#,530 );531 }532533 #[test]534 fn test_gen_blanket_indent() {535 check_assist(536 generate_blanket_trait_impl,537 r#"538mod foo {539 trait $0Foo<T: Send>: ToOwned540 where541 Self::Owned: Default,542 {543 fn foo(&self) -> T;544545 fn print_foo(&self) {546 println!("{}", self.foo());547 }548 }549}550 "#,551 r#"552mod foo {553 trait Foo<T: Send>: ToOwned554 where555 Self::Owned: Default,556 {557 fn foo(&self) -> T;558559 fn print_foo(&self) {560 println!("{}", self.foo());561 }562 }563564 impl<T: Send, T1: ToOwned + ?Sized> Foo<T> for $0T1565 where566 Self::Owned: Default,567 {568 fn foo(&self) -> T {569 todo!()570 }571 }572}573 "#,574 );575 check_assist(576 generate_blanket_trait_impl,577 r#"578mod foo {579 trait $0Foo<T: Send>: ToOwned580 where581 Self::Owned: Default,582 Self: Send,583 {584 fn foo(&self) -> T;585586 fn print_foo(&self) {587 println!("{}", self.foo());588 }589 }590}591 "#,592 r#"593mod foo {594 trait Foo<T: Send>: ToOwned595 where596 Self::Owned: Default,597 Self: Send,598 {599 fn foo(&self) -> T;600601 fn print_foo(&self) {602 println!("{}", self.foo());603 }604 }605606 impl<T: Send, T1: ToOwned + ?Sized> Foo<T> for $0T1607 where608 Self::Owned: Default,609 Self: Send,610 {611 fn foo(&self) -> T {612 todo!()613 }614 }615}616 "#,617 );618 check_assist(619 generate_blanket_trait_impl,620 r#"621mod foo {622 mod bar {623 trait $0Foo<T: Send>: ToOwned624 where625 Self::Owned: Default,626 Self: Send,627 {628 fn foo(&self) -> T;629630 fn print_foo(&self) {631 println!("{}", self.foo());632 }633 }634 }635}636 "#,637 r#"638mod foo {639 mod bar {640 trait Foo<T: Send>: ToOwned641 where642 Self::Owned: Default,643 Self: Send,644 {645 fn foo(&self) -> T;646647 fn print_foo(&self) {648 println!("{}", self.foo());649 }650 }651652 impl<T: Send, T1: ToOwned + ?Sized> Foo<T> for $0T1653 where654 Self::Owned: Default,655 Self: Send,656 {657 fn foo(&self) -> T {658 todo!()659 }660 }661 }662}663 "#,664 );665 check_assist(666 generate_blanket_trait_impl,667 r#"668mod foo {669 trait $0Foo {670 fn foo(&self) -> i32;671672 fn print_foo(&self) {673 println!("{}", self.foo());674 }675 }676}677 "#,678 r#"679mod foo {680 trait Foo {681 fn foo(&self) -> i32;682683 fn print_foo(&self) {684 println!("{}", self.foo());685 }686 }687688 impl<T: ?Sized> Foo for $0T {689 fn foo(&self) -> i32 {690 todo!()691 }692 }693}694 "#,695 );696 check_assist(697 generate_blanket_trait_impl,698 r#"699mod foo {700 mod bar {701 trait $0Foo {702 fn foo(&self) -> i32;703704 fn print_foo(&self) {705 println!("{}", self.foo());706 }707 }708 }709}710 "#,711 r#"712mod foo {713 mod bar {714 trait Foo {715 fn foo(&self) -> i32;716717 fn print_foo(&self) {718 println!("{}", self.foo());719 }720 }721722 impl<T: ?Sized> Foo for $0T {723 fn foo(&self) -> i32 {724 todo!()725 }726 }727 }728}729 "#,730 );731 check_assist(732 generate_blanket_trait_impl,733 r#"734mod foo {735 mod bar {736 #[cfg(test)]737 trait $0Foo {738 fn foo(&self) -> i32;739740 fn print_foo(&self) {741 println!("{}", self.foo());742 }743 }744 }745}746 "#,747 r#"748mod foo {749 mod bar {750 #[cfg(test)]751 trait Foo {752 fn foo(&self) -> i32;753754 fn print_foo(&self) {755 println!("{}", self.foo());756 }757 }758759 #[cfg(test)]760 impl<T: ?Sized> Foo for $0T {761 fn foo(&self) -> i32 {762 todo!()763 }764 }765 }766}767 "#,768 );769 check_assist(770 generate_blanket_trait_impl,771 r#"772mod foo {773 mod bar {774 trait $0Foo {775 type Item: Bar<776 Self,777 >;778779 const N: Baz<780 Self,781 >;782 }783 }784}785 "#,786 r#"787mod foo {788 mod bar {789 trait Foo {790 type Item: Bar<791 Self,792 >;793794 const N: Baz<795 Self,796 >;797 }798799 impl<T: ?Sized> Foo for $0T {800 type Item: Bar<801 Self,802 >;803804 const N: Baz<805 Self,806 >;807 }808 }809}810 "#,811 );812 }813814 #[test]815 fn test_gen_blanket_remove_attribute() {816 check_assist(817 generate_blanket_trait_impl,818 r#"819trait $0Foo<T: Send>: ToOwned820where821 Self::Owned: Default,822{823 #[doc(hidden)]824 fn foo(&self) -> T;825826 /// foo827 fn print_foo(&self) {828 println!("{}", self.foo());829 }830}831"#,832 r#"833trait Foo<T: Send>: ToOwned834where835 Self::Owned: Default,836{837 #[doc(hidden)]838 fn foo(&self) -> T;839840 /// foo841 fn print_foo(&self) {842 println!("{}", self.foo());843 }844}845846impl<T: Send, T1: ToOwned + ?Sized> Foo<T> for $0T1847where848 Self::Owned: Default,849{850 fn foo(&self) -> T {851 todo!()852 }853}854"#,855 );856 }857858 #[test]859 fn test_gen_blanket_not_gen_type_alias() {860 check_assist(861 generate_blanket_trait_impl,862 r#"863trait $0Foo<T: Send>: ToOwned864where865 Self::Owned: Default,866{867 type X: Sync;868869 fn foo(&self, x: Self::X) -> T;870871 fn print_foo(&self) {872 println!("{}", self.foo());873 }874}875"#,876 r#"877trait Foo<T: Send>: ToOwned878where879 Self::Owned: Default,880{881 type X: Sync;882883 fn foo(&self, x: Self::X) -> T;884885 fn print_foo(&self) {886 println!("{}", self.foo());887 }888}889890impl<T: Send, T1: ToOwned + ?Sized> Foo<T> for $0T1891where892 Self::Owned: Default,893{894 type X: Sync;895896 fn foo(&self, x: Self::X) -> T {897 todo!()898 }899}900"#,901 );902 }903904 #[test]905 fn test_gen_blanket_no_quick_bound() {906 check_assist(907 generate_blanket_trait_impl,908 r#"909trait $0Foo<T: Send>910where911 Self: ToOwned,912 Self::Owned: Default,913{914 type X: Sync;915916 fn foo(&self, x: Self::X) -> T;917918 fn print_foo(&self) {919 println!("{}", self.foo());920 }921}922"#,923 r#"924trait Foo<T: Send>925where926 Self: ToOwned,927 Self::Owned: Default,928{929 type X: Sync;930931 fn foo(&self, x: Self::X) -> T;932933 fn print_foo(&self) {934 println!("{}", self.foo());935 }936}937938impl<T: Send, T1: ?Sized> Foo<T> for $0T1939where940 Self: ToOwned,941 Self::Owned: Default,942{943 type X: Sync;944945 fn foo(&self, x: Self::X) -> T {946 todo!()947 }948}949"#,950 );951 }952953 #[test]954 fn test_gen_blanket_no_where_clause() {955 check_assist(956 generate_blanket_trait_impl,957 r#"958trait $0Foo<T: Send> {959 type X: Sync;960961 fn foo(&self, x: Self::X) -> T;962963 fn print_foo(&self) {964 println!("{}", self.foo());965 }966}967"#,968 r#"969trait Foo<T: Send> {970 type X: Sync;971972 fn foo(&self, x: Self::X) -> T;973974 fn print_foo(&self) {975 println!("{}", self.foo());976 }977}978979impl<T: Send, T1: ?Sized> Foo<T> for $0T1 {980 type X: Sync;981982 fn foo(&self, x: Self::X) -> T {983 todo!()984 }985}986"#,987 );988 }989990 #[test]991 fn test_gen_blanket_basic() {992 check_assist(993 generate_blanket_trait_impl,994 r#"995trait $0Foo {996 type X: Sync;997998 fn foo(&self, x: Self::X) -> i32;9991000 fn print_foo(&self) {1001 println!("{}", self.foo());1002 }1003}1004"#,1005 r#"1006trait Foo {1007 type X: Sync;10081009 fn foo(&self, x: Self::X) -> i32;10101011 fn print_foo(&self) {1012 println!("{}", self.foo());1013 }1014}10151016impl<T: ?Sized> Foo for $0T {1017 type X: Sync;10181019 fn foo(&self, x: Self::X) -> i32 {1020 todo!()1021 }1022}1023"#,1024 );1025 }10261027 #[test]1028 fn test_gen_blanket_cfg_attrs() {1029 check_assist(1030 generate_blanket_trait_impl,1031 r#"1032#[cfg(test)]1033trait $0Foo {1034 fn foo(&self, x: i32) -> i32;10351036 fn print_foo(&self) {1037 println!("{}", self.foo());1038 }1039}1040"#,1041 r#"1042#[cfg(test)]1043trait Foo {1044 fn foo(&self, x: i32) -> i32;10451046 fn print_foo(&self) {1047 println!("{}", self.foo());1048 }1049}10501051#[cfg(test)]1052impl<T: ?Sized> Foo for $0T {1053 fn foo(&self, x: i32) -> i32 {1054 todo!()1055 }1056}1057"#,1058 );1059 check_assist(1060 generate_blanket_trait_impl,1061 r#"1062#[cfg(test)]1063trait $0Foo {1064 /// ...1065 #[cfg(test)]1066 fn foo(&self, x: i32) -> i32;10671068 fn print_foo(&self) {1069 println!("{}", self.foo());1070 }1071}1072"#,1073 r#"1074#[cfg(test)]1075trait Foo {1076 /// ...1077 #[cfg(test)]1078 fn foo(&self, x: i32) -> i32;10791080 fn print_foo(&self) {1081 println!("{}", self.foo());1082 }1083}10841085#[cfg(test)]1086impl<T: ?Sized> Foo for $0T {1087 #[cfg(test)]1088 fn foo(&self, x: i32) -> i32 {1089 todo!()1090 }1091}1092"#,1093 );1094 }10951096 #[test]1097 fn test_gen_blanket_empty_trait() {1098 check_assist(1099 generate_blanket_trait_impl,1100 r#"1101trait $0Foo {}1102"#,1103 r#"1104trait Foo {}11051106impl<T: ?Sized> Foo for $0T {}1107"#,1108 );1109 }11101111 #[test]1112 fn test_gen_blanket_empty_trait_with_quick_bounds() {1113 check_assist(1114 generate_blanket_trait_impl,1115 r#"1116trait $0Foo: Copy {}1117"#,1118 r#"1119trait Foo: Copy {}11201121impl<T: Copy + ?Sized> Foo for $0T {}1122"#,1123 );1124 }11251126 #[test]1127 fn test_gen_blanket_empty_trait_with_where_clause() {1128 check_assist(1129 generate_blanket_trait_impl,1130 r#"1131trait $0Foo where Self: Copy {}1132"#,1133 r#"1134trait Foo where Self: Copy {}11351136impl<T: ?Sized> Foo for $0T1137where Self: Copy1138{1139}1140"#,1141 );1142 }11431144 #[test]1145 fn test_gen_blanket_empty_trait_with_where_clause_comma() {1146 check_assist(1147 generate_blanket_trait_impl,1148 r#"1149trait $0Foo where Self: Copy, {}1150"#,1151 r#"1152trait Foo where Self: Copy, {}11531154impl<T: ?Sized> Foo for $0T1155where Self: Copy,1156{1157}1158"#,1159 );1160 }11611162 #[test]1163 fn test_gen_blanket_empty_trait_with_where_clause_newline() {1164 check_assist(1165 generate_blanket_trait_impl,1166 r#"1167trait $0Foo1168where Self: Copy1169{}1170"#,1171 r#"1172trait Foo1173where Self: Copy1174{}11751176impl<T: ?Sized> Foo for $0T1177where Self: Copy1178{1179}1180"#,1181 );1182 }11831184 #[test]1185 fn test_gen_blanket_empty_trait_with_where_clause_newline_newline() {1186 check_assist(1187 generate_blanket_trait_impl,1188 r#"1189trait $0Foo1190where1191 Self: Copy1192{}1193"#,1194 r#"1195trait Foo1196where1197 Self: Copy1198{}11991200impl<T: ?Sized> Foo for $0T1201where1202 Self: Copy1203{1204}1205"#,1206 );1207 }12081209 #[test]1210 fn test_gen_blanket_empty_trait_with_where_clause_newline_newline_comma() {1211 check_assist(1212 generate_blanket_trait_impl,1213 r#"1214trait $0Foo1215where1216 Self: Copy,1217{}1218"#,1219 r#"1220trait Foo1221where1222 Self: Copy,1223{}12241225impl<T: ?Sized> Foo for $0T1226where1227 Self: Copy,1228{1229}1230"#,1231 );1232 }12331234 #[test]1235 fn test_gen_blanket_empty_trait_with_multiple_where_clause() {1236 check_assist(1237 generate_blanket_trait_impl,1238 r#"1239trait $0Foo1240where1241 Self: Copy,1242 (): Into<Self>,1243{}1244"#,1245 r#"1246trait Foo1247where1248 Self: Copy,1249 (): Into<Self>,1250{}12511252impl<T: ?Sized> Foo for $0T1253where1254 Self: Copy,1255 (): Into<Self>,1256{1257}1258"#,1259 );1260 }12611262 #[test]1263 fn test_gen_blanket_empty_trait_with_multiple_bounds_where_clause() {1264 check_assist(1265 generate_blanket_trait_impl,1266 r#"1267trait $0Foo1268where1269 Self: Copy + Sync,1270 (): Into<Self>,1271{}1272"#,1273 r#"1274trait Foo1275where1276 Self: Copy + Sync,1277 (): Into<Self>,1278{}12791280impl<T: ?Sized> Foo for $0T1281where1282 Self: Copy + Sync,1283 (): Into<Self>,1284{1285}1286"#,1287 );1288 }12891290 #[test]1291 fn test_gen_blanket_empty_generate() {1292 check_assist(1293 generate_blanket_trait_impl,1294 r#"1295trait $0Foo {1296 fn foo(&self) {}1297}1298"#,1299 r#"1300trait Foo {1301 fn foo(&self) {}1302}13031304impl<T: ?Sized> Foo for $0T {}1305"#,1306 );1307 }13081309 #[test]1310 fn test_gen_blanket_trait_with_doc() {1311 check_assist(1312 generate_blanket_trait_impl,1313 r#"1314/// some docs1315trait $0Foo {}1316"#,1317 r#"1318/// some docs1319trait Foo {}13201321impl<T: ?Sized> Foo for $0T {}1322"#,1323 );1324 }13251326 #[test]1327 fn test_gen_blanket_multiple_method() {1328 check_assist(1329 generate_blanket_trait_impl,1330 r#"1331trait $0Foo {1332 fn foo(&self);1333 fn bar(&self);1334}1335"#,1336 r#"1337trait Foo {1338 fn foo(&self);1339 fn bar(&self);1340}13411342impl<T: ?Sized> Foo for $0T {1343 fn foo(&self) {1344 todo!()1345 }13461347 fn bar(&self) {1348 todo!()1349 }1350}1351"#,1352 );1353 }13541355 #[test]1356 fn test_gen_blanket_method_with_generic() {1357 check_assist(1358 generate_blanket_trait_impl,1359 r#"1360trait $0Foo {1361 fn foo<T>(&self, value: i32) -> i32;1362 fn bar<T>(&self, value: i32) -> i32 { todo!() }1363}1364"#,1365 r#"1366trait Foo {1367 fn foo<T>(&self, value: i32) -> i32;1368 fn bar<T>(&self, value: i32) -> i32 { todo!() }1369}13701371impl<T: ?Sized> Foo for $0T {1372 fn foo<T>(&self, value: i32) -> i32 {1373 todo!()1374 }1375}1376"#,1377 );1378 }13791380 #[test]1381 fn test_gen_blanket_method_with_lifetimes() {1382 check_assist(1383 generate_blanket_trait_impl,1384 r#"1385trait $0Foo<'a> {1386 fn foo(&self) -> &'a str;1387}1388"#,1389 r#"1390trait Foo<'a> {1391 fn foo(&self) -> &'a str;1392}13931394impl<'a, T: ?Sized> Foo<'a> for $0T {1395 fn foo(&self) -> &'a str {1396 todo!()1397 }1398}1399"#,1400 );1401 }14021403 #[test]1404 fn test_gen_blanket_method_with_lifetime_bounds() {1405 check_assist(1406 generate_blanket_trait_impl,1407 r#"1408trait $0Foo<'a: 'static> {1409 fn foo(&self) -> &'a str;1410}1411"#,1412 r#"1413trait Foo<'a: 'static> {1414 fn foo(&self) -> &'a str;1415}14161417impl<'a: 'static, T: ?Sized> Foo<'a> for $0T {1418 fn foo(&self) -> &'a str {1419 todo!()1420 }1421}1422"#,1423 );1424 }14251426 #[test]1427 fn test_gen_blanket_method_with_lifetime_quick_bounds() {1428 check_assist(1429 generate_blanket_trait_impl,1430 r#"1431trait $0Foo<'a>: 'a {1432 fn foo(&self) -> &'a str;1433}1434"#,1435 r#"1436trait Foo<'a>: 'a {1437 fn foo(&self) -> &'a str;1438}14391440impl<'a, T: 'a + ?Sized> Foo<'a> for $0T {1441 fn foo(&self) -> &'a str {1442 todo!()1443 }1444}1445"#,1446 );1447 }14481449 #[test]1450 fn test_gen_blanket_method_with_multiple_lifetimes() {1451 check_assist(1452 generate_blanket_trait_impl,1453 r#"1454trait $0Foo<'a, 'b> {1455 fn foo(&self) -> &'a &'b str;1456}1457"#,1458 r#"1459trait Foo<'a, 'b> {1460 fn foo(&self) -> &'a &'b str;1461}14621463impl<'a, 'b, T: ?Sized> Foo<'a, 'b> for $0T {1464 fn foo(&self) -> &'a &'b str {1465 todo!()1466 }1467}1468"#,1469 );1470 }14711472 #[test]1473 fn test_gen_blanket_method_with_lifetime_bounds_at_where_clause() {1474 check_assist(1475 generate_blanket_trait_impl,1476 r#"1477trait $0Foo<'a>1478where 'a: 'static,1479{1480 fn foo(&self) -> &'a str;1481}1482"#,1483 r#"1484trait Foo<'a>1485where 'a: 'static,1486{1487 fn foo(&self) -> &'a str;1488}14891490impl<'a, T: ?Sized> Foo<'a> for $0T1491where 'a: 'static,1492{1493 fn foo(&self) -> &'a str {1494 todo!()1495 }1496}1497"#,1498 );1499 }15001501 #[test]1502 fn test_gen_blanket_not_on_name() {1503 check_assist_not_applicable(1504 generate_blanket_trait_impl,1505 r#"1506trait Foo<T: Send>: $0ToOwned1507where1508 Self::Owned: Default,1509{1510 fn foo(&self) -> T;15111512 fn print_foo(&self) {1513 println!("{}", self.foo());1514 }1515}1516"#,1517 );15181519 check_assist_not_applicable(1520 generate_blanket_trait_impl,1521 r#"1522trait Foo<T: Send>: ToOwned1523where1524 Self::Owned: Default,1525{1526 $0fn foo(&self) -> T;15271528 fn print_foo(&self) {1529 println!("{}", self.foo());1530 }1531}1532"#,1533 );15341535 check_assist_not_applicable(1536 generate_blanket_trait_impl,1537 r#"1538trait Foo<T: Send>: ToOwned1539where1540 Self::Owned: Default,1541{1542 fn $0foo(&self) -> T;15431544 fn print_foo(&self) {1545 println!("{}", self.foo());1546 }1547}1548"#,1549 );1550 }15511552 #[test]1553 fn test_gen_blanket_existing_impl() {1554 cov_mark::check!(existing_any_impl);1555 check_assist_not_applicable(1556 generate_blanket_trait_impl,1557 r#"1558trait $0Foo: Default {1559 fn foo(&self) -> Self;1560}1561impl Foo for () {}1562"#,1563 );1564 }15651566 #[test]1567 fn test_gen_blanket_existing_other_impl() {1568 check_assist(1569 generate_blanket_trait_impl,1570 r#"1571trait $0Foo: Default {1572 fn foo(&self) -> Self;1573}1574trait Bar: Default {1575 fn bar(&self) -> Self;1576}1577impl Bar for () {}1578"#,1579 r#"1580trait Foo: Default {1581 fn foo(&self) -> Self;1582}15831584impl<T: Default> Foo for $0T {1585 fn foo(&self) -> Self {1586 todo!()1587 }1588}1589trait Bar: Default {1590 fn bar(&self) -> Self;1591}1592impl Bar for () {}1593"#,1594 );1595 }15961597 #[test]1598 fn test_gen_blanket_apply_on_other_impl_block() {1599 check_assist(1600 generate_blanket_trait_impl,1601 r#"1602trait $0Foo {1603 fn foo(&self) -> i32;16041605 fn print_foo(&self) {1606 println!("{}", self.foo());1607 }1608}16091610trait Bar {}1611impl Bar for i32 {}1612"#,1613 r#"1614trait Foo {1615 fn foo(&self) -> i32;16161617 fn print_foo(&self) {1618 println!("{}", self.foo());1619 }1620}16211622impl<T: ?Sized> Foo for $0T {1623 fn foo(&self) -> i32 {1624 todo!()1625 }1626}16271628trait Bar {}1629impl Bar for i32 {}1630"#,1631 );1632 }16331634 #[test]1635 fn test_gen_blanket_apply_on_other_blanket_impl_block() {1636 check_assist(1637 generate_blanket_trait_impl,1638 r#"1639trait $0Foo {1640 fn foo(&self) -> i32;16411642 fn print_foo(&self) {1643 println!("{}", self.foo());1644 }1645}16461647trait Bar {}1648impl<T> Bar for T {}1649"#,1650 r#"1651trait Foo {1652 fn foo(&self) -> i32;16531654 fn print_foo(&self) {1655 println!("{}", self.foo());1656 }1657}16581659impl<T: ?Sized> Foo for $0T {1660 fn foo(&self) -> i32 {1661 todo!()1662 }1663}16641665trait Bar {}1666impl<T> Bar for T {}1667"#,1668 );1669 }1670}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.