Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/ide-assists/src/handlers/fill_record_pattern_fields.rs354
-rw-r--r--crates/ide-assists/src/lib.rs2
-rw-r--r--crates/ide-assists/src/tests/generated.rs21
3 files changed, 377 insertions, 0 deletions
diff --git a/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs b/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs
new file mode 100644
index 0000000000..42bd0d3e66
--- /dev/null
+++ b/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs
@@ -0,0 +1,354 @@
+use syntax::{
+ ast::{self, make},
+ AstNode,
+};
+
+use crate::{AssistContext, AssistId, Assists};
+
+// Assist: fill_record_pattern_fields
+//
+// Fills fields by replacing rest pattern in record patterns.
+//
+// ```
+// struct Bar { y: Y, z: Z }
+//
+// fn foo(bar: Bar) {
+// let Bar { ..$0 } = bar;
+// }
+// ```
+// ->
+// ```
+// struct Bar { y: Y, z: Z }
+//
+// fn foo(bar: Bar) {
+// let Bar { y, z } = bar;
+// }
+// ```
+pub(crate) fn fill_record_pattern_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let record_pat = ctx.find_node_at_offset::<ast::RecordPat>()?;
+
+ let ellipsis = record_pat.record_pat_field_list().and_then(|r| r.rest_pat())?;
+ if !ellipsis.syntax().text_range().contains_inclusive(ctx.offset()) {
+ return None;
+ }
+
+ let target_range = ellipsis.syntax().text_range();
+
+ let missing_fields = ctx.sema.record_pattern_missing_fields(&record_pat);
+
+ if missing_fields.is_empty() {
+ cov_mark::hit!(no_missing_fields);
+ return None;
+ }
+
+ let old_field_list = record_pat.record_pat_field_list()?;
+ let new_field_list = make::record_pat_field_list(old_field_list.fields()).clone_for_update();
+ for (f, _) in missing_fields.iter() {
+ let field =
+ make::record_pat_field_shorthand(make::name_ref(&f.name(ctx.sema.db).to_smol_str()));
+ new_field_list.add_field(field.clone_for_update());
+ }
+
+ let old_range = ctx.sema.original_range_opt(old_field_list.syntax())?;
+ if old_range.file_id != ctx.file_id() {
+ return None;
+ }
+
+ acc.add(
+ AssistId("fill_record_pattern_fields", crate::AssistKind::RefactorRewrite),
+ "Fill structure fields",
+ target_range,
+ move |builder| builder.replace_ast(old_field_list, new_field_list),
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::tests::{check_assist, check_assist_not_applicable};
+
+ #[test]
+ fn fill_fields_enum_with_only_ellipsis() {
+ check_assist(
+ fill_record_pattern_fields,
+ r#"
+enum Foo {
+ A(X),
+ B{y: Y, z: Z}
+}
+
+fn bar(foo: Foo) {
+ match foo {
+ Foo::A(_) => false,
+ Foo::B{ ..$0 } => true,
+ };
+}
+"#,
+ r#"
+enum Foo {
+ A(X),
+ B{y: Y, z: Z}
+}
+
+fn bar(foo: Foo) {
+ match foo {
+ Foo::A(_) => false,
+ Foo::B{ y, z } => true,
+ };
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn fill_fields_enum_with_fields() {
+ check_assist(
+ fill_record_pattern_fields,
+ r#"
+enum Foo {
+ A(X),
+ B{y: Y, z: Z}
+}
+
+fn bar(foo: Foo) {
+ match foo {
+ Foo::A(_) => false,
+ Foo::B{ y, ..$0 } => true,
+ };
+}
+"#,
+ r#"
+enum Foo {
+ A(X),
+ B{y: Y, z: Z}
+}
+
+fn bar(foo: Foo) {
+ match foo {
+ Foo::A(_) => false,
+ Foo::B{ y, z } => true,
+ };
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn fill_fields_struct_with_only_ellipsis() {
+ check_assist(
+ fill_record_pattern_fields,
+ r#"
+struct Bar {
+ y: Y,
+ z: Z,
+}
+
+fn foo(bar: Bar) {
+ let Bar { ..$0 } = bar;
+}
+"#,
+ r#"
+struct Bar {
+ y: Y,
+ z: Z,
+}
+
+fn foo(bar: Bar) {
+ let Bar { y, z } = bar;
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn fill_fields_struct_with_fields() {
+ check_assist(
+ fill_record_pattern_fields,
+ r#"
+struct Bar {
+ y: Y,
+ z: Z,
+}
+
+fn foo(bar: Bar) {
+ let Bar { y, ..$0 } = bar;
+}
+"#,
+ r#"
+struct Bar {
+ y: Y,
+ z: Z,
+}
+
+fn foo(bar: Bar) {
+ let Bar { y, z } = bar;
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn fill_fields_struct_generated_by_macro() {
+ check_assist(
+ fill_record_pattern_fields,
+ r#"
+macro_rules! position {
+ ($t: ty) => {
+ struct Pos {x: $t, y: $t}
+ };
+}
+
+position!(usize);
+
+fn macro_call(pos: Pos) {
+ let Pos { ..$0 } = pos;
+}
+"#,
+ r#"
+macro_rules! position {
+ ($t: ty) => {
+ struct Pos {x: $t, y: $t}
+ };
+}
+
+position!(usize);
+
+fn macro_call(pos: Pos) {
+ let Pos { x, y } = pos;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn fill_fields_enum_generated_by_macro() {
+ check_assist(
+ fill_record_pattern_fields,
+ r#"
+macro_rules! enum_gen {
+ ($t: ty) => {
+ enum Foo {
+ A($t),
+ B{x: $t, y: $t},
+ }
+ };
+}
+
+enum_gen!(usize);
+
+fn macro_call(foo: Foo) {
+ match foo {
+ Foo::A(_) => false,
+ Foo::B{ ..$0 } => true,
+ }
+}
+"#,
+ r#"
+macro_rules! enum_gen {
+ ($t: ty) => {
+ enum Foo {
+ A($t),
+ B{x: $t, y: $t},
+ }
+ };
+}
+
+enum_gen!(usize);
+
+fn macro_call(foo: Foo) {
+ match foo {
+ Foo::A(_) => false,
+ Foo::B{ x, y } => true,
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn not_applicable_when_not_in_ellipsis() {
+ check_assist_not_applicable(
+ fill_record_pattern_fields,
+ r#"
+enum Foo {
+ A(X),
+ B{y: Y, z: Z}
+}
+
+fn bar(foo: Foo) {
+ match foo {
+ Foo::A(_) => false,
+ Foo::B{..}$0 => true,
+ };
+}
+"#,
+ );
+ check_assist_not_applicable(
+ fill_record_pattern_fields,
+ r#"
+enum Foo {
+ A(X),
+ B{y: Y, z: Z}
+}
+
+fn bar(foo: Foo) {
+ match foo {
+ Foo::A(_) => false,
+ Foo::B$0{..} => true,
+ };
+}
+"#,
+ );
+ check_assist_not_applicable(
+ fill_record_pattern_fields,
+ r#"
+enum Foo {
+ A(X),
+ B{y: Y, z: Z}
+}
+
+fn bar(foo: Foo) {
+ match foo {
+ Foo::A(_) => false,
+ Foo::$0B{..} => true,
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn not_applicable_when_no_missing_fields() {
+ // This is still possible even though it's meaningless
+ cov_mark::check!(no_missing_fields);
+ check_assist_not_applicable(
+ fill_record_pattern_fields,
+ r#"
+enum Foo {
+ A(X),
+ B{y: Y, z: Z}
+}
+
+fn bar(foo: Foo) {
+ match foo {
+ Foo::A(_) => false,
+ Foo::B{y, z, ..$0} => true,
+ };
+}
+"#,
+ );
+ check_assist_not_applicable(
+ fill_record_pattern_fields,
+ r#"
+struct Bar {
+ y: Y,
+ z: Z,
+}
+
+fn foo(bar: Bar) {
+ let Bar { y, z, ..$0 } = bar;
+}
+"#,
+ );
+ }
+}
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index dcc89014b9..5814c3b81e 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -137,6 +137,7 @@ mod handlers {
mod extract_struct_from_enum_variant;
mod extract_type_alias;
mod extract_variable;
+ mod fill_record_pattern_fields;
mod fix_visibility;
mod flip_binexpr;
mod flip_comma;
@@ -254,6 +255,7 @@ mod handlers {
extract_expressions_from_format_string::extract_expressions_from_format_string,
extract_struct_from_enum_variant::extract_struct_from_enum_variant,
extract_type_alias::extract_type_alias,
+ fill_record_pattern_fields::fill_record_pattern_fields,
fix_visibility::fix_visibility,
flip_binexpr::flip_binexpr,
flip_comma::flip_comma,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 268ba3225b..82d05f3920 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -910,6 +910,27 @@ fn main() {
}
#[test]
+fn doctest_fill_record_pattern_fields() {
+ check_doc_test(
+ "fill_record_pattern_fields",
+ r#####"
+struct Bar { y: Y, z: Z }
+
+fn foo(bar: Bar) {
+ let Bar { ..$0 } = bar;
+}
+"#####,
+ r#####"
+struct Bar { y: Y, z: Z }
+
+fn foo(bar: Bar) {
+ let Bar { y, z } = bar;
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_fix_visibility() {
check_doc_test(
"fix_visibility",