Auto merge of #12539 - soruh:instanciate_empty_structs, r=Veykril

Automatically instaciate trivially instaciable structs in "Generate new" and "Fill struct fields"

As proposed in #12535 this PR changes the "Generate new" and "Fill struct fields" assist/diagnostic to instanciate structs with no fields and enums with a single empty variant.

For example:
```rust
pub enum Bar {
    Bar {},
}
struct Foo<T> {
    a: usize,
    bar: Bar,
    _phantom: std::marker::PhantomData<T>,
}
impl<T> Foo<T> {
    /* generate new */

    fn random() -> Self {
        Self { /* Fill struct fields */ }
    }
}
```

was previously:
```rust
impl<T> Foo<T> {
    fn new(a: usize, bar: Bar, _phantom: std::marker::PhantomData<T>) -> Self {
        Self { a, bar, _phantom }
    }

    fn random() -> Self {
        Self {
            a: todo!(),
            bar: todo!(),
            _phantom: todo!(),
        }
    }
}
```

and is now:
```rust
impl<T> Foo<T> {
  fn new(a: usize) -> Self {
      Self {
          a,
          bar: Bar::Bar {},
          _phantom: std::marker::PhantomData
      }
  }

  fn random() -> Self {
      Self {
          a: todo!(),
          bar: Bar::Bar {},
          _phantom: std::marker::PhantomData,
      }
  }
}
```

I'd be happy about any suggestions.

## TODO
   - [x]  deduplicate `use_trivial_constructor` (unclear how to do as it's used in two separate crates)
   - [x]  write tests

Closes #12535
This commit is contained in:
bors 2022-07-16 16:36:57 +00:00
commit 01d251789f
4 changed files with 287 additions and 4 deletions

View file

@ -3,7 +3,10 @@ use hir::{
db::{AstDatabase, HirDatabase},
known, AssocItem, HirDisplay, InFile, Type,
};
use ide_db::{assists::Assist, famous_defs::FamousDefs, source_change::SourceChange, FxHashMap};
use ide_db::{
assists::Assist, famous_defs::FamousDefs, imports::import_assets::item_for_path_search,
source_change::SourceChange, use_trivial_contructor::use_trivial_constructor, FxHashMap,
};
use stdx::format_to;
use syntax::{
algo,
@ -55,6 +58,11 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
let root = ctx.sema.db.parse_or_expand(d.file)?;
let current_module = match &d.field_list_parent {
Either::Left(ptr) => ctx.sema.scope(ptr.to_node(&root).syntax()).map(|it| it.module()),
Either::Right(ptr) => ctx.sema.scope(ptr.to_node(&root).syntax()).map(|it| it.module()),
};
let build_text_edit = |parent_syntax, new_syntax: &SyntaxNode, old_syntax| {
let edit = {
let mut builder = TextEdit::builder();
@ -110,7 +118,26 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
Some(generate_fill_expr(ty))
}
} else {
Some(generate_fill_expr(ty))
let expr = (|| -> Option<ast::Expr> {
let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?));
let type_path = current_module?.find_use_path(
ctx.sema.db,
item_for_path_search(ctx.sema.db, item_in_ns)?,
)?;
use_trivial_constructor(
&ctx.sema.db,
ide_db::helpers::mod_path_to_ast(&type_path),
&ty,
)
})();
if expr.is_some() {
expr
} else {
Some(generate_fill_expr(ty))
}
};
let field = make::record_expr_field(
make::name_ref(&f.name(ctx.sema.db).to_smol_str()),
@ -318,6 +345,92 @@ fn test_fn() {
);
}
#[test]
fn test_fill_struct_zst_fields() {
check_fix(
r#"
struct Empty;
struct TestStruct { one: i32, two: Empty }
fn test_fn() {
let s = TestStruct {$0};
}
"#,
r#"
struct Empty;
struct TestStruct { one: i32, two: Empty }
fn test_fn() {
let s = TestStruct { one: 0, two: Empty };
}
"#,
);
check_fix(
r#"
enum Empty { Foo };
struct TestStruct { one: i32, two: Empty }
fn test_fn() {
let s = TestStruct {$0};
}
"#,
r#"
enum Empty { Foo };
struct TestStruct { one: i32, two: Empty }
fn test_fn() {
let s = TestStruct { one: 0, two: Empty::Foo };
}
"#,
);
// make sure the assist doesn't fill non Unit variants
check_fix(
r#"
struct Empty {};
struct TestStruct { one: i32, two: Empty }
fn test_fn() {
let s = TestStruct {$0};
}
"#,
r#"
struct Empty {};
struct TestStruct { one: i32, two: Empty }
fn test_fn() {
let s = TestStruct { one: 0, two: todo!() };
}
"#,
);
check_fix(
r#"
enum Empty { Foo {} };
struct TestStruct { one: i32, two: Empty }
fn test_fn() {
let s = TestStruct {$0};
}
"#,
r#"
enum Empty { Foo {} };
struct TestStruct { one: i32, two: Empty }
fn test_fn() {
let s = TestStruct { one: 0, two: todo!() };
}
"#,
);
}
#[test]
fn test_fill_struct_fields_self() {
check_fix(