about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs187
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs25
-rw-r--r--src/tools/rust-analyzer/docs/book/src/assists_generated.md28
4 files changed, 217 insertions, 25 deletions
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 55448d4ae86..7549d54a544 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -296,6 +296,7 @@ pub enum ModuleDef {
     Function(Function),
     Adt(Adt),
     // Can't be directly declared, but can be imported.
+    // FIXME: Rename to `EnumVariant`
     Variant(Variant),
     Const(Const),
     Static(Static),
@@ -1564,6 +1565,7 @@ impl From<&Variant> for DefWithBodyId {
     }
 }
 
+// FIXME: Rename to `EnumVariant`
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct Variant {
     pub(crate) id: EnumVariantId,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs
index 315d7e727b4..c79a982c38d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs
@@ -1,3 +1,5 @@
+use hir::{PathResolution, StructKind};
+use ide_db::syntax_helpers::suggest_name::NameGenerator;
 use syntax::{
     ast::{self, make},
     match_ast, AstNode, ToSmolStr,
@@ -5,7 +7,23 @@ use syntax::{
 
 use crate::{AssistContext, AssistId, Assists};
 
-// Assist: expand_rest_pattern
+pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let rest_pat = ctx.find_node_at_offset::<ast::RestPat>()?;
+    let parent = rest_pat.syntax().parent()?;
+    match_ast! {
+        match parent {
+            ast::RecordPatFieldList(it) => expand_record_rest_pattern(acc, ctx, it.syntax().parent().and_then(ast::RecordPat::cast)?, rest_pat),
+            ast::TupleStructPat(it) => expand_tuple_struct_rest_pattern(acc, ctx, it, rest_pat),
+            // FIXME
+            // ast::TuplePat(it) => (),
+            // FIXME
+            // ast::SlicePat(it) => (),
+            _ => return None,
+        }
+    }
+}
+
+// Assist: expand_record_rest_pattern
 //
 // Fills fields by replacing rest pattern in record patterns.
 //
@@ -24,22 +42,12 @@ use crate::{AssistContext, AssistId, Assists};
 //     let Bar { y, z  } = bar;
 // }
 // ```
-pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
-    let rest_pat = ctx.find_node_at_offset::<ast::RestPat>()?;
-    let parent = rest_pat.syntax().parent()?;
-    let record_pat = match_ast! {
-        match parent {
-            ast::RecordPatFieldList(it) => ast::RecordPat::cast(it.syntax().parent()?)?,
-            // ast::TupleStructPat(it) => (),
-            // ast::TuplePat(it) => (),
-            // ast::SlicePat(it) => (),
-            _ => return None,
-        }
-    };
-
-    let ellipsis = record_pat.record_pat_field_list().and_then(|r| r.rest_pat())?;
-    let target_range = ellipsis.syntax().text_range();
-
+fn expand_record_rest_pattern(
+    acc: &mut Assists,
+    ctx: &AssistContext<'_>,
+    record_pat: ast::RecordPat,
+    rest_pat: ast::RestPat,
+) -> Option<()> {
     let missing_fields = ctx.sema.record_pattern_missing_fields(&record_pat);
 
     if missing_fields.is_empty() {
@@ -48,6 +56,11 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) ->
     }
 
     let old_field_list = record_pat.record_pat_field_list()?;
+    let old_range = ctx.sema.original_range_opt(old_field_list.syntax())?;
+    if old_range.file_id != ctx.file_id() {
+        return None;
+    }
+
     let new_field_list =
         make::record_pat_field_list(old_field_list.fields(), None).clone_for_update();
     for (f, _) in missing_fields.iter() {
@@ -58,16 +71,93 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) ->
         new_field_list.add_field(field.clone_for_update());
     }
 
-    let old_range = ctx.sema.original_range_opt(old_field_list.syntax())?;
+    let target_range = rest_pat.syntax().text_range();
+    acc.add(
+        AssistId("expand_record_rest_pattern", crate::AssistKind::RefactorRewrite),
+        "Fill struct fields",
+        target_range,
+        move |builder| builder.replace_ast(old_field_list, new_field_list),
+    )
+}
+// Assist: expand_tuple_struct_rest_pattern
+//
+// Fills fields by replacing rest pattern in tuple struct patterns.
+//
+// ```
+// struct Bar(Y, Z);
+//
+// fn foo(bar: Bar) {
+//     let Bar(..$0) = bar;
+// }
+// ```
+// ->
+// ```
+// struct Bar(Y, Z);
+//
+// fn foo(bar: Bar) {
+//     let Bar(_0, _1) = bar;
+// }
+// ```
+fn expand_tuple_struct_rest_pattern(
+    acc: &mut Assists,
+    ctx: &AssistContext<'_>,
+    pat: ast::TupleStructPat,
+    rest_pat: ast::RestPat,
+) -> Option<()> {
+    let path = pat.path()?;
+    let fields = match ctx.sema.type_of_pat(&pat.clone().into())?.original.as_adt()? {
+        hir::Adt::Struct(s) if s.kind(ctx.sema.db) == StructKind::Tuple => s.fields(ctx.sema.db),
+        hir::Adt::Enum(_) => match ctx.sema.resolve_path(&path)? {
+            PathResolution::Def(hir::ModuleDef::Variant(v))
+                if v.kind(ctx.sema.db) == StructKind::Tuple =>
+            {
+                v.fields(ctx.sema.db)
+            }
+            _ => return None,
+        },
+        _ => return None,
+    };
+
+    let rest_pat = rest_pat.into();
+    let mut pats = pat.fields();
+    let prefix_count = pats.by_ref().position(|p| p == rest_pat)?;
+    let suffix_count = pats.count();
+
+    if fields.len().saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 {
+        cov_mark::hit!(no_missing_fields_tuple_struct);
+        return None;
+    }
+
+    let old_range = ctx.sema.original_range_opt(pat.syntax())?;
     if old_range.file_id != ctx.file_id() {
         return None;
     }
 
+    let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax()));
+    let new_pat = make::tuple_struct_pat(
+        path,
+        pat.fields()
+            .take(prefix_count)
+            .chain(fields[prefix_count..fields.len() - suffix_count].iter().map(|f| {
+                make::ident_pat(
+                    false,
+                    false,
+                    match name_gen.for_type(&f.ty(ctx.sema.db), ctx.sema.db, ctx.edition()) {
+                        Some(name) => make::name(&name),
+                        None => make::name(&format!("_{}", f.index())),
+                    },
+                )
+                .into()
+            }))
+            .chain(pat.fields().skip(prefix_count + 1)),
+    );
+
+    let target_range = rest_pat.syntax().text_range();
     acc.add(
-        AssistId("expand_rest_pattern", crate::AssistKind::RefactorRewrite),
-        "Fill structure fields",
+        AssistId("expand_tuple_struct_rest_pattern", crate::AssistKind::RefactorRewrite),
+        "Fill tuple struct fields",
         target_range,
-        move |builder| builder.replace_ast(old_field_list, new_field_list),
+        move |builder| builder.replace_ast(pat, new_pat),
     )
 }
 
@@ -166,6 +256,27 @@ fn foo(bar: Bar) {
     let Bar { y, z  } = bar;
 }
 "#,
+        );
+        check_assist(
+            expand_rest_pattern,
+            r#"
+struct Y;
+struct Z;
+struct Bar(Y, Z)
+
+fn foo(bar: Bar) {
+    let Bar(..$0) = bar;
+}
+"#,
+            r#"
+struct Y;
+struct Z;
+struct Bar(Y, Z)
+
+fn foo(bar: Bar) {
+    let Bar(y, z) = bar;
+}
+"#,
         )
     }
 
@@ -193,6 +304,29 @@ fn foo(bar: Bar) {
     let Bar { y, z } = bar;
 }
 "#,
+        );
+        check_assist(
+            expand_rest_pattern,
+            r#"
+struct X;
+struct Y;
+struct Z;
+struct Bar(X, Y, Z)
+
+fn foo(bar: Bar) {
+    let Bar(x, ..$0, z) = bar;
+}
+"#,
+            r#"
+struct X;
+struct Y;
+struct Z;
+struct Bar(X, Y, Z)
+
+fn foo(bar: Bar) {
+    let Bar(x, y, z) = bar;
+}
+"#,
         )
     }
 
@@ -330,6 +464,7 @@ fn bar(foo: Foo) {
     fn not_applicable_when_no_missing_fields() {
         // This is still possible even though it's meaningless
         cov_mark::check!(no_missing_fields);
+        cov_mark::check!(no_missing_fields_tuple_struct);
         check_assist_not_applicable(
             expand_rest_pattern,
             r#"
@@ -359,5 +494,15 @@ fn foo(bar: Bar) {
 }
 "#,
         );
+        check_assist_not_applicable(
+            expand_rest_pattern,
+            r#"
+struct Bar(Y, Z)
+
+fn foo(bar: Bar) {
+    let Bar(y, ..$0, z) = bar;
+}
+"#,
+        );
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index dd994ef4a64..46688a22f33 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -956,9 +956,9 @@ pub use foo::{Bar, Baz};
 }
 
 #[test]
-fn doctest_expand_rest_pattern() {
+fn doctest_expand_record_rest_pattern() {
     check_doc_test(
-        "expand_rest_pattern",
+        "expand_record_rest_pattern",
         r#####"
 struct Bar { y: Y, z: Z }
 
@@ -977,6 +977,27 @@ fn foo(bar: Bar) {
 }
 
 #[test]
+fn doctest_expand_tuple_struct_rest_pattern() {
+    check_doc_test(
+        "expand_tuple_struct_rest_pattern",
+        r#####"
+struct Bar(Y, Z);
+
+fn foo(bar: Bar) {
+    let Bar(..$0) = bar;
+}
+"#####,
+        r#####"
+struct Bar(Y, Z);
+
+fn foo(bar: Bar) {
+    let Bar(_0, _1) = bar;
+}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_extract_constant() {
     check_doc_test(
         "extract_constant",
diff --git a/src/tools/rust-analyzer/docs/book/src/assists_generated.md b/src/tools/rust-analyzer/docs/book/src/assists_generated.md
index 743e5c5e10d..b815cad0a23 100644
--- a/src/tools/rust-analyzer/docs/book/src/assists_generated.md
+++ b/src/tools/rust-analyzer/docs/book/src/assists_generated.md
@@ -1069,8 +1069,8 @@ pub use foo::{Bar, Baz};
 ```
 
 
-### `expand_rest_pattern`
-**Source:**  [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L8) 
+### `expand_record_rest_pattern`
+**Source:**  [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L24) 
 
 Fills fields by replacing rest pattern in record patterns.
 
@@ -1093,6 +1093,30 @@ fn foo(bar: Bar) {
 ```
 
 
+### `expand_tuple_struct_rest_pattern`
+**Source:**  [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L80) 
+
+Fills fields by replacing rest pattern in tuple struct patterns.
+
+#### Before
+```rust
+struct Bar(Y, Z);
+
+fn foo(bar: Bar) {
+    let Bar(..┃) = bar;
+}
+```
+
+#### After
+```rust
+struct Bar(Y, Z);
+
+fn foo(bar: Bar) {
+    let Bar(_0, _1) = bar;
+}
+```
+
+
 ### `extract_constant`
 **Source:**  [extract_variable.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/extract_variable.rs#L35)