use crate::{ assist_context::{AssistContext, Assists}, AssistId, }; use ide_db::helpers::FamousDefs; use syntax::{ ast::{self, Impl, NameOwner}, AstNode, }; // Assist: generate_default_from_new // // Generates default implementation from new method. // // ``` // struct Example { _inner: () } // // impl Example { // pub fn n$0ew() -> Self { // Self { _inner: () } // } // } // ``` // -> // ``` // struct Example { _inner: () } // // impl Example { // pub fn new() -> Self { // Self { _inner: () } // } // } // // impl Default for Example { // fn default() -> Self { // Self::new() // } // } // ``` pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let fn_node = ctx.find_node_at_offset::()?; let fn_name = fn_node.name()?; if fn_name.text() != "new" { cov_mark::hit!(other_function_than_new); return None; } if fn_node.param_list()?.params().next().is_some() { cov_mark::hit!(new_function_with_parameters); return None; } let impl_ = fn_node.syntax().ancestors().into_iter().find_map(ast::Impl::cast)?; if is_default_implemented(ctx, &impl_) { cov_mark::hit!(default_block_is_already_present); cov_mark::hit!(struct_in_module_with_default); return None; } let insert_location = impl_.syntax().text_range(); acc.add( AssistId("generate_default_from_new", crate::AssistKind::Generate), "Generate a Default impl from a new fn", insert_location, move |builder| { let code = default_fn_node_for_new(impl_); builder.insert(insert_location.end(), code); }, ) } fn default_fn_node_for_new(impl_: Impl) -> String { format!( " impl Default for {} {{ fn default() -> Self {{ Self::new() }} }}", impl_.self_ty().unwrap().syntax().text() ) } fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool { let db = ctx.sema.db; let impl_ = ctx.sema.to_def(impl_); let impl_def = match impl_ { Some(value) => value, None => return false, }; let ty = impl_def.target_ty(db); let krate = impl_def.module(db).krate(); let default = FamousDefs(&ctx.sema, Some(krate)).core_default_Default(); let default_trait = match default { Some(value) => value, None => return false, }; ty.impls_trait(db, default_trait, &[]) } #[cfg(test)] mod tests { use ide_db::helpers::FamousDefs; use crate::tests::{check_assist, check_assist_not_applicable}; use super::*; #[test] fn generate_default() { check_pass( r#" struct Example { _inner: () } impl Example { pub fn ne$0w() -> Self { Self { _inner: () } } } fn main() {} "#, r#" struct Example { _inner: () } impl Example { pub fn new() -> Self { Self { _inner: () } } } impl Default for Example { fn default() -> Self { Self::new() } } fn main() {} "#, ); } #[test] fn generate_default2() { check_pass( r#" struct Test { value: u32 } impl Test { pub fn ne$0w() -> Self { Self { value: 0 } } } "#, r#" struct Test { value: u32 } impl Test { pub fn new() -> Self { Self { value: 0 } } } impl Default for Test { fn default() -> Self { Self::new() } } "#, ); } #[test] fn new_function_with_parameters() { cov_mark::check!(new_function_with_parameters); check_not_applicable( r#" struct Example { _inner: () } impl Example { pub fn $0new(value: ()) -> Self { Self { _inner: value } } } "#, ); } #[test] fn other_function_than_new() { cov_mark::check!(other_function_than_new); check_not_applicable( r#" struct Example { _inner: () } impl Example { pub fn a$0dd() -> Self { Self { _inner: () } } } "#, ); } #[test] fn default_block_is_already_present() { cov_mark::check!(default_block_is_already_present); check_not_applicable( r#" struct Example { _inner: () } impl Example { pub fn n$0ew() -> Self { Self { _inner: () } } } impl Default for Example { fn default() -> Self { Self::new() } } "#, ); } #[test] fn standalone_new_function() { check_not_applicable( r#" fn n$0ew() -> u32 { 0 } "#, ); } #[test] fn multiple_struct_blocks() { check_pass( r#" struct Example { _inner: () } struct Test { value: u32 } impl Example { pub fn new$0() -> Self { Self { _inner: () } } } "#, r#" struct Example { _inner: () } struct Test { value: u32 } impl Example { pub fn new() -> Self { Self { _inner: () } } } impl Default for Example { fn default() -> Self { Self::new() } } "#, ); } #[test] fn when_struct_is_after_impl() { check_pass( r#" impl Example { pub fn $0new() -> Self { Self { _inner: () } } } struct Example { _inner: () } "#, r#" impl Example { pub fn new() -> Self { Self { _inner: () } } } impl Default for Example { fn default() -> Self { Self::new() } } struct Example { _inner: () } "#, ); } #[test] fn struct_in_module() { check_pass( r#" mod test { struct Example { _inner: () } impl Example { pub fn n$0ew() -> Self { Self { _inner: () } } } } "#, r#" mod test { struct Example { _inner: () } impl Example { pub fn new() -> Self { Self { _inner: () } } } impl Default for Example { fn default() -> Self { Self::new() } } } "#, ); } #[test] fn struct_in_module_with_default() { cov_mark::check!(struct_in_module_with_default); check_not_applicable( r#" mod test { struct Example { _inner: () } impl Example { pub fn n$0ew() -> Self { Self { _inner: () } } } impl Default for Example { fn default() -> Self { Self::new() } } } "#, ); } fn check_pass(before: &str, after: &str) { let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE); check_assist(generate_default_from_new, before, after); } fn check_not_applicable(before: &str) { let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE); check_assist_not_applicable(generate_default_from_new, before); } }