use ast::make; use hir::next_solver::{DbInterner, TypingMode}; use hir::{HasCrate, ModuleDef, Semantics}; use ide_db::{ RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast, imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor, }; use syntax::syntax_editor::{Element, Position}; use syntax::{ TokenText, ast::{self, AstNode, HasAttrs, HasGenericParams, HasName, edit::AstNodeEdit}, }; use crate::{ AssistId, assist_context::{AssistContext, Assists}, }; // Assist: generate_single_field_struct_from // // Implement From for a single field structure, ignore trivial types. // // ``` // # //- minicore: from, phantom_data // use core::marker::PhantomData; // struct $0Foo { // id: i32, // _phantom_data: PhantomData, // } // ``` // -> // ``` // use core::marker::PhantomData; // struct Foo { // id: i32, // _phantom_data: PhantomData, // } // // impl From for Foo { // fn from(id: i32) -> Self { // Self { id, _phantom_data: PhantomData } // } // } // ``` pub(crate) fn generate_single_field_struct_from( acc: &mut Assists, ctx: &AssistContext<'_>, ) -> Option<()> { let strukt_name = ctx.find_node_at_offset::()?; let adt = ast::Adt::cast(strukt_name.syntax().parent()?)?; let ast::Adt::Struct(strukt) = adt else { tracing::debug!(?adt); return None; }; let sema = &ctx.sema; let (names, types) = get_fields(&strukt)?; let module = sema.scope(strukt.syntax())?.module(); let constructors = make_constructors(ctx, module, &types); if constructors.iter().filter(|expr| expr.is_none()).count() != 1 { tracing::debug!(?constructors); return None; } let main_field_i = constructors.iter().position(Option::is_none)?; if from_impl_exists(&strukt, main_field_i, &ctx.sema).is_some() { tracing::debug!(?strukt, ?main_field_i); return None; } let main_field_name = names.as_ref().map_or(TokenText::borrowed("value"), |names| names[main_field_i].text()); let main_field_ty = types[main_field_i].clone(); acc.add( AssistId::generate("generate_single_field_struct_from"), "Generate single field `From`", strukt.syntax().text_range(), |builder| { let indent = strukt.indent_level(); let ty_where_clause = strukt.where_clause(); let type_gen_params = strukt.generic_param_list(); let type_gen_args = type_gen_params.as_ref().map(|params| params.to_generic_args()); let trait_gen_args = Some(make::generic_arg_list([ast::GenericArg::TypeArg( make::type_arg(main_field_ty.clone()), )])); let ty = make::ty(&strukt_name.text()); let constructor = make_adt_constructor(names.as_deref(), constructors, &main_field_name); let body = make::block_expr([], Some(constructor)); let fn_ = make::fn_( None, None, make::name("from"), None, None, make::param_list( None, [make::param( make::path_pat(make::path_from_text(&main_field_name)), main_field_ty, )], ), body, Some(make::ret_type(make::ty("Self"))), false, false, false, false, ) .indent(1.into()); let cfg_attrs = strukt .attrs() .filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")); let impl_ = make::impl_trait( cfg_attrs, false, None, trait_gen_args, type_gen_params, type_gen_args, false, make::ty("From"), ty.clone(), None, ty_where_clause.map(|wc| wc.reset_indent()), None, ) .clone_for_update(); impl_.get_or_create_assoc_item_list().add_item(fn_.into()); let impl_ = impl_.indent(indent); let mut edit = builder.make_editor(strukt.syntax()); edit.insert_all( Position::after(strukt.syntax()), vec![ make::tokens::whitespace(&format!("\n\n{indent}")).syntax_element(), impl_.syntax().syntax_element(), ], ); builder.add_file_edits(ctx.vfs_file_id(), edit); }, ) } fn make_adt_constructor( names: Option<&[ast::Name]>, constructors: Vec>, main_field_name: &TokenText<'_>, ) -> ast::Expr { if let Some(names) = names { let fields = make::record_expr_field_list(names.iter().zip(constructors).map( |(name, initializer)| { make::record_expr_field(make::name_ref(&name.text()), initializer) }, )); make::record_expr(make::path_from_text("Self"), fields).into() } else { let arg_list = make::arg_list(constructors.into_iter().map(|expr| { expr.unwrap_or_else(|| make::expr_path(make::path_from_text(main_field_name))) })); make::expr_call(make::expr_path(make::path_from_text("Self")), arg_list).into() } } fn make_constructors( ctx: &AssistContext<'_>, module: hir::Module, types: &[ast::Type], ) -> Vec> { let (db, sema) = (ctx.db(), &ctx.sema); let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate(ctx.sema.db))); types .iter() .map(|ty| { let ty = sema.resolve_type(ty)?; if ty.is_unit() { return Some(make::expr_tuple([]).into()); } let item_in_ns = ModuleDef::Adt(ty.as_adt()?).into(); let edition = module.krate(db).edition(db); let ty_path = module.find_path(db, item_for_path_search(db, item_in_ns)?, cfg)?; use_trivial_constructor(db, mod_path_to_ast(&ty_path, edition), &ty, edition) }) .collect() } fn get_fields(strukt: &ast::Struct) -> Option<(Option>, Vec)> { Some(match strukt.kind() { ast::StructKind::Unit => return None, ast::StructKind::Record(fields) => { let names = fields.fields().map(|field| field.name()).collect::>()?; let types = fields.fields().map(|field| field.ty()).collect::>()?; (Some(names), types) } ast::StructKind::Tuple(fields) => { (None, fields.fields().map(|field| field.ty()).collect::>()?) } }) } #[tracing::instrument(ret)] fn from_impl_exists( strukt: &ast::Struct, main_field_i: usize, sema: &Semantics<'_, RootDatabase>, ) -> Option<()> { let db = sema.db; let strukt = sema.to_def(strukt)?; let krate = strukt.krate(db); let from_trait = FamousDefs(sema, krate).core_convert_From()?; let interner = DbInterner::new_with(db, krate.base()); use hir::next_solver::infer::DbInternerInferExt; let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); let strukt = strukt.instantiate_infer(&infcx); let field_ty = strukt.fields(db).get(main_field_i)?.ty(db); let struct_ty = strukt.ty(db); tracing::debug!(?strukt, ?field_ty, ?struct_ty); struct_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(()) } #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; use super::generate_single_field_struct_from; #[test] fn works() { check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo { foo: i32, } "#, r#" struct Foo { foo: i32, } impl From for Foo { fn from(foo: i32) -> Self { Self { foo } } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from, phantom_data struct $0Foo { b1: (), b2: core::marker::PhantomData, foo: i32, a1: (), a2: core::marker::PhantomData, } "#, r#" struct Foo { b1: (), b2: core::marker::PhantomData, foo: i32, a1: (), a2: core::marker::PhantomData, } impl From for Foo { fn from(foo: i32) -> Self { Self { b1: (), b2: core::marker::PhantomData, foo, a1: (), a2: core::marker::PhantomData } } } "#, ); } #[test] fn cfgs() { check_assist( generate_single_field_struct_from, r#" //- minicore: from #[cfg(feature = "foo")] #[cfg(test)] struct $0Foo { foo: i32, } "#, r#" #[cfg(feature = "foo")] #[cfg(test)] struct Foo { foo: i32, } #[cfg(feature = "foo")] #[cfg(test)] impl From for Foo { fn from(foo: i32) -> Self { Self { foo } } } "#, ); } #[test] fn indent() { check_assist( generate_single_field_struct_from, r#" //- minicore: from mod foo { struct $0Foo { foo: i32, } } "#, r#" mod foo { struct Foo { foo: i32, } impl From for Foo { fn from(foo: i32) -> Self { Self { foo } } } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from mod foo { mod bar { struct $0Foo { foo: i32, } } } "#, r#" mod foo { mod bar { struct Foo { foo: i32, } impl From for Foo { fn from(foo: i32) -> Self { Self { foo } } } } } "#, ); } #[test] fn where_clause_indent() { check_assist( generate_single_field_struct_from, r#" //- minicore: from mod foo { mod bar { trait Trait {} struct $0Foo where T: Trait, { foo: T, } } } "#, r#" mod foo { mod bar { trait Trait {} struct Foo where T: Trait, { foo: T, } impl From for Foo where T: Trait, { fn from(foo: T) -> Self { Self { foo } } } } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from mod foo { mod bar { trait Trait {} struct $0Foo where T: Trait<{ true }> { foo: T, } } } "#, r#" mod foo { mod bar { trait Trait {} struct Foo where T: Trait<{ true }> { foo: T, } impl From for Foo where T: Trait<{ true }> { fn from(foo: T) -> Self { Self { foo } } } } } "#, ); } #[test] fn generics() { check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo { foo: T, } "#, r#" struct Foo { foo: T, } impl From for Foo { fn from(foo: T) -> Self { Self { foo } } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo { foo: T, } "#, r#" struct Foo { foo: T, } impl From for Foo { fn from(foo: T) -> Self { Self { foo } } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo where T: Sync,{ foo: T, } "#, r#" struct Foo where T: Sync,{ foo: T, } impl From for Foo where T: Sync, { fn from(foo: T) -> Self { Self { foo } } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo where T: Sync { foo: T, } "#, r#" struct Foo where T: Sync { foo: T, } impl From for Foo where T: Sync { fn from(foo: T) -> Self { Self { foo } } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo where T: Sync, Self: Send { foo: T, } "#, r#" struct Foo where T: Sync, Self: Send { foo: T, } impl From for Foo where T: Sync, Self: Send { fn from(foo: T) -> Self { Self { foo } } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo where T: Sync, Self: Send { foo: T, } "#, r#" struct Foo where T: Sync, Self: Send { foo: T, } impl From for Foo where T: Sync, Self: Send { fn from(foo: T) -> Self { Self { foo } } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo where T: Sync, Self: Send, { foo: T, } "#, r#" struct Foo where T: Sync, Self: Send, { foo: T, } impl From for Foo where T: Sync, Self: Send, { fn from(foo: T) -> Self { Self { foo } } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo where T: Sync, Self: Send, { foo: T, } "#, r#" struct Foo where T: Sync, Self: Send, { foo: T, } impl From for Foo where T: Sync, Self: Send, { fn from(foo: T) -> Self { Self { foo } } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo where T: Sync, Self: Send, { foo: T, } "#, r#" struct Foo where T: Sync, Self: Send, { foo: T, } impl From for Foo where T: Sync, Self: Send, { fn from(foo: T) -> Self { Self { foo } } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo where T: Sync, Self: Send, { foo: T, } "#, r#" struct Foo where T: Sync, Self: Send, { foo: T, } impl From for Foo where T: Sync, Self: Send, { fn from(foo: T) -> Self { Self { foo } } } "#, ); } #[test] fn tuple() { check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo(i32); "#, r#" struct Foo(i32); impl From for Foo { fn from(value: i32) -> Self { Self(value) } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo(T); "#, r#" struct Foo(T); impl From for Foo { fn from(value: T) -> Self { Self(value) } } "#, ); } #[test] fn trivial() { check_assist( generate_single_field_struct_from, r#" //- minicore: from, phantom_data use core::marker::PhantomData; struct $0Foo(i32, PhantomData); "#, r#" use core::marker::PhantomData; struct Foo(i32, PhantomData); impl From for Foo { fn from(value: i32) -> Self { Self(value, PhantomData) } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from, phantom_data use core::marker::PhantomData; struct $0Foo(i32, PhantomData<()>); "#, r#" use core::marker::PhantomData; struct Foo(i32, PhantomData<()>); impl From for Foo { fn from(value: i32) -> Self { Self(value, PhantomData) } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from, phantom_data use core::marker::PhantomData; struct $0Foo(PhantomData<()>, i32, PhantomData<()>); "#, r#" use core::marker::PhantomData; struct Foo(PhantomData<()>, i32, PhantomData<()>); impl From for Foo { fn from(value: i32) -> Self { Self(PhantomData, value, PhantomData) } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from, phantom_data use core::marker::PhantomData; struct $0Foo(PhantomData, i32, PhantomData<()>); "#, r#" use core::marker::PhantomData; struct Foo(PhantomData, i32, PhantomData<()>); impl From for Foo { fn from(value: i32) -> Self { Self(PhantomData, value, PhantomData) } } "#, ); } #[test] fn unit() { check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo(i32, ()); "#, r#" struct Foo(i32, ()); impl From for Foo { fn from(value: i32) -> Self { Self(value, ()) } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo((), i32, ()); "#, r#" struct Foo((), i32, ()); impl From for Foo { fn from(value: i32) -> Self { Self((), value, ()) } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo((), (), i32, ()); "#, r#" struct Foo((), (), i32, ()); impl From for Foo { fn from(value: i32) -> Self { Self((), (), value, ()) } } "#, ); } #[test] fn invalid_multiple_main_field() { check_assist_not_applicable( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo(i32, i32); "#, ); check_assist_not_applicable( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo(i32, T); "#, ); check_assist_not_applicable( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo(T, T); "#, ); check_assist_not_applicable( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo { foo: T, bar: i32 } "#, ); check_assist_not_applicable( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo { foo: i32, bar: i64 } "#, ); } #[test] fn exists_other_from() { check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo(i32); impl From<&i32> for Foo { fn from(value: &i32) -> Self { todo!() } } "#, r#" struct Foo(i32); impl From for Foo { fn from(value: i32) -> Self { Self(value) } } impl From<&i32> for Foo { fn from(value: &i32) -> Self { todo!() } } "#, ); check_assist( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo(i32); type X = i32; impl From<&X> for Foo { fn from(value: &X) -> Self { todo!() } } "#, r#" struct Foo(i32); impl From for Foo { fn from(value: i32) -> Self { Self(value) } } type X = i32; impl From<&X> for Foo { fn from(value: &X) -> Self { todo!() } } "#, ); } #[test] fn exists_from() { check_assist_not_applicable( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo(i32); impl From for Foo { fn from(_: i32) -> Self { todo!() } } "#, ); check_assist_not_applicable( generate_single_field_struct_from, r#" //- minicore: from struct $0Foo(i32); type X = i32; impl From for Foo { fn from(_: X) -> Self { todo!() } } "#, ); } }