mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Merge #9825
9825: Generate default impl when converting #[derive(Default)] to manual impl r=Veykril a=yoshuawuyts Similar to https://github.com/rust-analyzer/rust-analyzer/pull/9814, but for `#[derive(Default)]`. Thanks! ## Follow-up steps I've added the tests inside `handlers/replace_derive_with_manual_impl.rs` again, but I'm planning a follow-up PR to extract these to `utils/` so we can share them between assists - and maybe even add another assist just for the purpose of testing these impls (e.g. `generate_default_trait_body`). The step after _that_ is likely to fill out the remaining traits, so we can make it so whenever RA auto-completes a trait which also can be derived, we provide a default function body. Co-authored-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
This commit is contained in:
commit
63427afb69
1 changed files with 113 additions and 4 deletions
|
@ -169,7 +169,7 @@ fn impl_def_from_trait(
|
|||
|
||||
// Generate a default `impl` function body for the derived trait.
|
||||
if let ast::AssocItem::Fn(ref func) = first_assoc_item {
|
||||
let _ = gen_default_impl(func, trait_path, adt, annotated_name);
|
||||
let _ = gen_trait_body_impl(func, trait_path, adt, annotated_name);
|
||||
};
|
||||
|
||||
Some((impl_def, first_assoc_item))
|
||||
|
@ -180,7 +180,7 @@ fn impl_def_from_trait(
|
|||
/// Returns `Option` so that we can use `?` rather than `if let Some`. Returning
|
||||
/// `None` means that generating a custom trait body failed, and the body will remain
|
||||
/// as `todo!` instead.
|
||||
fn gen_default_impl(
|
||||
fn gen_trait_body_impl(
|
||||
func: &ast::Fn,
|
||||
trait_path: &ast::Path,
|
||||
adt: &ast::Adt,
|
||||
|
@ -188,7 +188,8 @@ fn gen_default_impl(
|
|||
) -> Option<()> {
|
||||
match trait_path.segment()?.name_ref()?.text().as_str() {
|
||||
"Debug" => gen_debug_impl(adt, func, annotated_name),
|
||||
_ => Some(()),
|
||||
"Default" => gen_default_impl(adt, func),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,7 +197,7 @@ fn gen_default_impl(
|
|||
fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) -> Option<()> {
|
||||
match adt {
|
||||
// `Debug` cannot be derived for unions, so no default impl can be provided.
|
||||
ast::Adt::Union(_) => Some(()),
|
||||
ast::Adt::Union(_) => None,
|
||||
|
||||
// => match self { Self::Variant => write!(f, "Variant") }
|
||||
ast::Adt::Enum(enum_) => {
|
||||
|
@ -276,6 +277,50 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) ->
|
|||
}
|
||||
}
|
||||
|
||||
/// Generate a `Debug` impl based on the fields and members of the target type.
|
||||
fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
|
||||
fn gen_default_call() -> ast::Expr {
|
||||
let trait_name = make::ext::ident_path("Default");
|
||||
let method_name = make::ext::ident_path("default");
|
||||
let fn_name = make::expr_path(make::path_concat(trait_name, method_name));
|
||||
make::expr_call(fn_name, make::arg_list(None))
|
||||
}
|
||||
match adt {
|
||||
// `Debug` cannot be derived for unions, so no default impl can be provided.
|
||||
ast::Adt::Union(_) => None,
|
||||
// Deriving `Debug` for enums is not stable yet.
|
||||
ast::Adt::Enum(_) => None,
|
||||
ast::Adt::Struct(strukt) => {
|
||||
let expr = match strukt.field_list() {
|
||||
Some(ast::FieldList::RecordFieldList(field_list)) => {
|
||||
let mut fields = vec![];
|
||||
for field in field_list.fields() {
|
||||
let method_call = gen_default_call();
|
||||
let name_ref = make::name_ref(&field.name()?.to_string());
|
||||
let field = make::record_expr_field(name_ref, Some(method_call));
|
||||
fields.push(field);
|
||||
}
|
||||
let struct_name = make::ext::ident_path("Self");
|
||||
let fields = make::record_expr_field_list(fields);
|
||||
make::record_expr(struct_name, fields).into()
|
||||
}
|
||||
Some(ast::FieldList::TupleFieldList(field_list)) => {
|
||||
let struct_name = make::expr_path(make::ext::ident_path("Self"));
|
||||
let fields = field_list.fields().map(|_| gen_default_call());
|
||||
make::expr_call(struct_name, make::arg_list(fields))
|
||||
}
|
||||
None => {
|
||||
let struct_name = make::ext::ident_path("Self");
|
||||
let fields = make::record_expr_field_list(None);
|
||||
make::record_expr(struct_name, fields).into()
|
||||
}
|
||||
};
|
||||
let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
|
||||
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
}
|
||||
fn update_attribute(
|
||||
builder: &mut AssistBuilder,
|
||||
input: &ast::TokenTree,
|
||||
|
@ -405,6 +450,70 @@ impl core::fmt::Debug for Foo {
|
|||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
#[test]
|
||||
fn add_custom_impl_default_record_struct() {
|
||||
check_assist(
|
||||
replace_derive_with_manual_impl,
|
||||
r#"
|
||||
//- minicore: default
|
||||
#[derive(Defau$0lt)]
|
||||
struct Foo {
|
||||
foo: usize,
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct Foo {
|
||||
foo: usize,
|
||||
}
|
||||
|
||||
impl Default for Foo {
|
||||
$0fn default() -> Self {
|
||||
Self { foo: Default::default() }
|
||||
}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
#[test]
|
||||
fn add_custom_impl_default_tuple_struct() {
|
||||
check_assist(
|
||||
replace_derive_with_manual_impl,
|
||||
r#"
|
||||
//- minicore: default
|
||||
#[derive(Defau$0lt)]
|
||||
struct Foo(usize);
|
||||
"#,
|
||||
r#"
|
||||
struct Foo(usize);
|
||||
|
||||
impl Default for Foo {
|
||||
$0fn default() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
#[test]
|
||||
fn add_custom_impl_default_empty_struct() {
|
||||
check_assist(
|
||||
replace_derive_with_manual_impl,
|
||||
r#"
|
||||
//- minicore: default
|
||||
#[derive(Defau$0lt)]
|
||||
struct Foo;
|
||||
"#,
|
||||
r#"
|
||||
struct Foo;
|
||||
|
||||
impl Default for Foo {
|
||||
$0fn default() -> Self {
|
||||
Self { }
|
||||
}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue