Skip to content

Commit c315ad9

Browse files
committed
Support tuple struct patterns for expand_rest_pattern assist
1 parent 43c5e95 commit c315ad9

File tree

4 files changed

+217
-25
lines changed

4 files changed

+217
-25
lines changed

src/tools/rust-analyzer/crates/hir/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ pub enum ModuleDef {
296296
Function(Function),
297297
Adt(Adt),
298298
// Can't be directly declared, but can be imported.
299+
// FIXME: Rename to `EnumVariant`
299300
Variant(Variant),
300301
Const(Const),
301302
Static(Static),
@@ -1564,6 +1565,7 @@ impl From<&Variant> for DefWithBodyId {
15641565
}
15651566
}
15661567

1568+
// FIXME: Rename to `EnumVariant`
15671569
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15681570
pub struct Variant {
15691571
pub(crate) id: EnumVariantId,

src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs

Lines changed: 166 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
1+
use hir::{PathResolution, StructKind};
2+
use ide_db::syntax_helpers::suggest_name::NameGenerator;
13
use syntax::{
24
ast::{self, make},
35
match_ast, AstNode, ToSmolStr,
46
};
57

68
use crate::{AssistContext, AssistId, Assists};
79

8-
// Assist: expand_rest_pattern
10+
pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
11+
let rest_pat = ctx.find_node_at_offset::<ast::RestPat>()?;
12+
let parent = rest_pat.syntax().parent()?;
13+
match_ast! {
14+
match parent {
15+
ast::RecordPatFieldList(it) => expand_record_rest_pattern(acc, ctx, it.syntax().parent().and_then(ast::RecordPat::cast)?, rest_pat),
16+
ast::TupleStructPat(it) => expand_tuple_struct_rest_pattern(acc, ctx, it, rest_pat),
17+
// FIXME
18+
// ast::TuplePat(it) => (),
19+
// FIXME
20+
// ast::SlicePat(it) => (),
21+
_ => return None,
22+
}
23+
}
24+
}
25+
26+
// Assist: expand_record_rest_pattern
927
//
1028
// Fills fields by replacing rest pattern in record patterns.
1129
//
@@ -24,22 +42,12 @@ use crate::{AssistContext, AssistId, Assists};
2442
// let Bar { y, z } = bar;
2543
// }
2644
// ```
27-
pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
28-
let rest_pat = ctx.find_node_at_offset::<ast::RestPat>()?;
29-
let parent = rest_pat.syntax().parent()?;
30-
let record_pat = match_ast! {
31-
match parent {
32-
ast::RecordPatFieldList(it) => ast::RecordPat::cast(it.syntax().parent()?)?,
33-
// ast::TupleStructPat(it) => (),
34-
// ast::TuplePat(it) => (),
35-
// ast::SlicePat(it) => (),
36-
_ => return None,
37-
}
38-
};
39-
40-
let ellipsis = record_pat.record_pat_field_list().and_then(|r| r.rest_pat())?;
41-
let target_range = ellipsis.syntax().text_range();
42-
45+
fn expand_record_rest_pattern(
46+
acc: &mut Assists,
47+
ctx: &AssistContext<'_>,
48+
record_pat: ast::RecordPat,
49+
rest_pat: ast::RestPat,
50+
) -> Option<()> {
4351
let missing_fields = ctx.sema.record_pattern_missing_fields(&record_pat);
4452

4553
if missing_fields.is_empty() {
@@ -48,6 +56,11 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) ->
4856
}
4957

5058
let old_field_list = record_pat.record_pat_field_list()?;
59+
let old_range = ctx.sema.original_range_opt(old_field_list.syntax())?;
60+
if old_range.file_id != ctx.file_id() {
61+
return None;
62+
}
63+
5164
let new_field_list =
5265
make::record_pat_field_list(old_field_list.fields(), None).clone_for_update();
5366
for (f, _) in missing_fields.iter() {
@@ -58,16 +71,93 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) ->
5871
new_field_list.add_field(field.clone_for_update());
5972
}
6073

61-
let old_range = ctx.sema.original_range_opt(old_field_list.syntax())?;
74+
let target_range = rest_pat.syntax().text_range();
75+
acc.add(
76+
AssistId("expand_record_rest_pattern", crate::AssistKind::RefactorRewrite),
77+
"Fill struct fields",
78+
target_range,
79+
move |builder| builder.replace_ast(old_field_list, new_field_list),
80+
)
81+
}
82+
// Assist: expand_tuple_struct_rest_pattern
83+
//
84+
// Fills fields by replacing rest pattern in tuple struct patterns.
85+
//
86+
// ```
87+
// struct Bar(Y, Z);
88+
//
89+
// fn foo(bar: Bar) {
90+
// let Bar(..$0) = bar;
91+
// }
92+
// ```
93+
// ->
94+
// ```
95+
// struct Bar(Y, Z);
96+
//
97+
// fn foo(bar: Bar) {
98+
// let Bar(_0, _1) = bar;
99+
// }
100+
// ```
101+
fn expand_tuple_struct_rest_pattern(
102+
acc: &mut Assists,
103+
ctx: &AssistContext<'_>,
104+
pat: ast::TupleStructPat,
105+
rest_pat: ast::RestPat,
106+
) -> Option<()> {
107+
let path = pat.path()?;
108+
let fields = match ctx.sema.type_of_pat(&pat.clone().into())?.original.as_adt()? {
109+
hir::Adt::Struct(s) if s.kind(ctx.sema.db) == StructKind::Tuple => s.fields(ctx.sema.db),
110+
hir::Adt::Enum(_) => match ctx.sema.resolve_path(&path)? {
111+
PathResolution::Def(hir::ModuleDef::Variant(v))
112+
if v.kind(ctx.sema.db) == StructKind::Tuple =>
113+
{
114+
v.fields(ctx.sema.db)
115+
}
116+
_ => return None,
117+
},
118+
_ => return None,
119+
};
120+
121+
let rest_pat = rest_pat.into();
122+
let mut pats = pat.fields();
123+
let prefix_count = pats.by_ref().position(|p| p == rest_pat)?;
124+
let suffix_count = pats.count();
125+
126+
if fields.len().saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 {
127+
cov_mark::hit!(no_missing_fields_tuple_struct);
128+
return None;
129+
}
130+
131+
let old_range = ctx.sema.original_range_opt(pat.syntax())?;
62132
if old_range.file_id != ctx.file_id() {
63133
return None;
64134
}
65135

136+
let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax()));
137+
let new_pat = make::tuple_struct_pat(
138+
path,
139+
pat.fields()
140+
.take(prefix_count)
141+
.chain(fields[prefix_count..fields.len() - suffix_count].iter().map(|f| {
142+
make::ident_pat(
143+
false,
144+
false,
145+
match name_gen.for_type(&f.ty(ctx.sema.db), ctx.sema.db, ctx.edition()) {
146+
Some(name) => make::name(&name),
147+
None => make::name(&format!("_{}", f.index())),
148+
},
149+
)
150+
.into()
151+
}))
152+
.chain(pat.fields().skip(prefix_count + 1)),
153+
);
154+
155+
let target_range = rest_pat.syntax().text_range();
66156
acc.add(
67-
AssistId("expand_rest_pattern", crate::AssistKind::RefactorRewrite),
68-
"Fill structure fields",
157+
AssistId("expand_tuple_struct_rest_pattern", crate::AssistKind::RefactorRewrite),
158+
"Fill tuple struct fields",
69159
target_range,
70-
move |builder| builder.replace_ast(old_field_list, new_field_list),
160+
move |builder| builder.replace_ast(pat, new_pat),
71161
)
72162
}
73163

@@ -165,6 +255,27 @@ struct Bar {
165255
fn foo(bar: Bar) {
166256
let Bar { y, z } = bar;
167257
}
258+
"#,
259+
);
260+
check_assist(
261+
expand_rest_pattern,
262+
r#"
263+
struct Y;
264+
struct Z;
265+
struct Bar(Y, Z)
266+
267+
fn foo(bar: Bar) {
268+
let Bar(..$0) = bar;
269+
}
270+
"#,
271+
r#"
272+
struct Y;
273+
struct Z;
274+
struct Bar(Y, Z)
275+
276+
fn foo(bar: Bar) {
277+
let Bar(y, z) = bar;
278+
}
168279
"#,
169280
)
170281
}
@@ -192,6 +303,29 @@ struct Bar {
192303
fn foo(bar: Bar) {
193304
let Bar { y, z } = bar;
194305
}
306+
"#,
307+
);
308+
check_assist(
309+
expand_rest_pattern,
310+
r#"
311+
struct X;
312+
struct Y;
313+
struct Z;
314+
struct Bar(X, Y, Z)
315+
316+
fn foo(bar: Bar) {
317+
let Bar(x, ..$0, z) = bar;
318+
}
319+
"#,
320+
r#"
321+
struct X;
322+
struct Y;
323+
struct Z;
324+
struct Bar(X, Y, Z)
325+
326+
fn foo(bar: Bar) {
327+
let Bar(x, y, z) = bar;
328+
}
195329
"#,
196330
)
197331
}
@@ -330,6 +464,7 @@ fn bar(foo: Foo) {
330464
fn not_applicable_when_no_missing_fields() {
331465
// This is still possible even though it's meaningless
332466
cov_mark::check!(no_missing_fields);
467+
cov_mark::check!(no_missing_fields_tuple_struct);
333468
check_assist_not_applicable(
334469
expand_rest_pattern,
335470
r#"
@@ -357,6 +492,16 @@ struct Bar {
357492
fn foo(bar: Bar) {
358493
let Bar { y, z, ..$0 } = bar;
359494
}
495+
"#,
496+
);
497+
check_assist_not_applicable(
498+
expand_rest_pattern,
499+
r#"
500+
struct Bar(Y, Z)
501+
502+
fn foo(bar: Bar) {
503+
let Bar(y, ..$0, z) = bar;
504+
}
360505
"#,
361506
);
362507
}

src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -956,9 +956,9 @@ pub use foo::{Bar, Baz};
956956
}
957957

958958
#[test]
959-
fn doctest_expand_rest_pattern() {
959+
fn doctest_expand_record_rest_pattern() {
960960
check_doc_test(
961-
"expand_rest_pattern",
961+
"expand_record_rest_pattern",
962962
r#####"
963963
struct Bar { y: Y, z: Z }
964964
@@ -976,6 +976,27 @@ fn foo(bar: Bar) {
976976
)
977977
}
978978

979+
#[test]
980+
fn doctest_expand_tuple_struct_rest_pattern() {
981+
check_doc_test(
982+
"expand_tuple_struct_rest_pattern",
983+
r#####"
984+
struct Bar(Y, Z);
985+
986+
fn foo(bar: Bar) {
987+
let Bar(..$0) = bar;
988+
}
989+
"#####,
990+
r#####"
991+
struct Bar(Y, Z);
992+
993+
fn foo(bar: Bar) {
994+
let Bar(_0, _1) = bar;
995+
}
996+
"#####,
997+
)
998+
}
999+
9791000
#[test]
9801001
fn doctest_extract_constant() {
9811002
check_doc_test(

src/tools/rust-analyzer/docs/book/src/assists_generated.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,8 +1069,8 @@ pub use foo::{Bar, Baz};
10691069
```
10701070

10711071

1072-
### `expand_rest_pattern`
1073-
**Source:** [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L8)
1072+
### `expand_record_rest_pattern`
1073+
**Source:** [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L24)
10741074

10751075
Fills fields by replacing rest pattern in record patterns.
10761076

@@ -1093,6 +1093,30 @@ fn foo(bar: Bar) {
10931093
```
10941094

10951095

1096+
### `expand_tuple_struct_rest_pattern`
1097+
**Source:** [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L80)
1098+
1099+
Fills fields by replacing rest pattern in tuple struct patterns.
1100+
1101+
#### Before
1102+
```rust
1103+
struct Bar(Y, Z);
1104+
1105+
fn foo(bar: Bar) {
1106+
let Bar(..┃) = bar;
1107+
}
1108+
```
1109+
1110+
#### After
1111+
```rust
1112+
struct Bar(Y, Z);
1113+
1114+
fn foo(bar: Bar) {
1115+
let Bar(_0, _1) = bar;
1116+
}
1117+
```
1118+
1119+
10961120
### `extract_constant`
10971121
**Source:** [extract_variable.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/extract_variable.rs#L35)
10981122

0 commit comments

Comments
 (0)