Skip to content

Commit f45fdd9

Browse files
committed
Auto merge of #15260 - DropDemBits:structured-snippet-migrate-3, r=lowr
internal: Migrate assists to the structured snippet API, part 3 Continuing from #15231 Migrates the following assists: - `add_missing_match_arms` - `fix_visibility` - `promote_local_to_const` The `add_missing_match_arms` changes are best reviewed commit-by-commit since they're relatively big changes compared to the rest of the commits.
2 parents 75ac37f + a9889a0 commit f45fdd9

File tree

4 files changed

+182
-133
lines changed

4 files changed

+182
-133
lines changed

crates/ide-assists/src/handlers/add_missing_match_arms.rs

Lines changed: 79 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ use itertools::Itertools;
88
use syntax::ast::edit_in_place::Removable;
99
use syntax::ast::{self, make, AstNode, HasName, MatchArmList, MatchExpr, Pat};
1010

11-
use crate::{
12-
utils::{self, render_snippet, Cursor},
13-
AssistContext, AssistId, AssistKind, Assists,
14-
};
11+
use crate::{utils, AssistContext, AssistId, AssistKind, Assists};
1512

1613
// Assist: add_missing_match_arms
1714
//
@@ -75,14 +72,18 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
7572
.collect();
7673

7774
let module = ctx.sema.scope(expr.syntax())?.module();
78-
let (mut missing_pats, is_non_exhaustive): (
75+
let (mut missing_pats, is_non_exhaustive, has_hidden_variants): (
7976
Peekable<Box<dyn Iterator<Item = (ast::Pat, bool)>>>,
8077
bool,
78+
bool,
8179
) = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
8280
let is_non_exhaustive = enum_def.is_non_exhaustive(ctx.db(), module.krate());
8381

8482
let variants = enum_def.variants(ctx.db());
8583

84+
let has_hidden_variants =
85+
variants.iter().any(|variant| variant.should_be_hidden(ctx.db(), module.krate()));
86+
8687
let missing_pats = variants
8788
.into_iter()
8889
.filter_map(|variant| {
@@ -101,7 +102,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
101102
} else {
102103
Box::new(missing_pats)
103104
};
104-
(missing_pats.peekable(), is_non_exhaustive)
105+
(missing_pats.peekable(), is_non_exhaustive, has_hidden_variants)
105106
} else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
106107
let is_non_exhaustive =
107108
enum_defs.iter().any(|enum_def| enum_def.is_non_exhaustive(ctx.db(), module.krate()));
@@ -124,6 +125,12 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
124125
if n_arms > 256 {
125126
return None;
126127
}
128+
129+
let has_hidden_variants = variants_of_enums
130+
.iter()
131+
.flatten()
132+
.any(|variant| variant.should_be_hidden(ctx.db(), module.krate()));
133+
127134
let missing_pats = variants_of_enums
128135
.into_iter()
129136
.multi_cartesian_product()
@@ -139,7 +146,11 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
139146
(ast::Pat::from(make::tuple_pat(patterns)), is_hidden)
140147
})
141148
.filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
142-
((Box::new(missing_pats) as Box<dyn Iterator<Item = _>>).peekable(), is_non_exhaustive)
149+
(
150+
(Box::new(missing_pats) as Box<dyn Iterator<Item = _>>).peekable(),
151+
is_non_exhaustive,
152+
has_hidden_variants,
153+
)
143154
} else if let Some((enum_def, len)) = resolve_array_of_enum_def(&ctx.sema, &expr) {
144155
let is_non_exhaustive = enum_def.is_non_exhaustive(ctx.db(), module.krate());
145156
let variants = enum_def.variants(ctx.db());
@@ -148,6 +159,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
148159
return None;
149160
}
150161

162+
let has_hidden_variants =
163+
variants.iter().any(|variant| variant.should_be_hidden(ctx.db(), module.krate()));
164+
151165
let variants_of_enums = vec![variants; len];
152166

153167
let missing_pats = variants_of_enums
@@ -164,28 +178,42 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
164178
(ast::Pat::from(make::slice_pat(patterns)), is_hidden)
165179
})
166180
.filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
167-
((Box::new(missing_pats) as Box<dyn Iterator<Item = _>>).peekable(), is_non_exhaustive)
181+
(
182+
(Box::new(missing_pats) as Box<dyn Iterator<Item = _>>).peekable(),
183+
is_non_exhaustive,
184+
has_hidden_variants,
185+
)
168186
} else {
169187
return None;
170188
};
171189

172190
let mut needs_catch_all_arm = is_non_exhaustive && !has_catch_all_arm;
173191

174-
if !needs_catch_all_arm && missing_pats.peek().is_none() {
192+
if !needs_catch_all_arm
193+
&& ((has_hidden_variants && has_catch_all_arm) || missing_pats.peek().is_none())
194+
{
175195
return None;
176196
}
177197

178198
acc.add(
179199
AssistId("add_missing_match_arms", AssistKind::QuickFix),
180200
"Fill match arms",
181201
target_range,
182-
|builder| {
202+
|edit| {
183203
let new_match_arm_list = match_arm_list.clone_for_update();
204+
205+
// having any hidden variants means that we need a catch-all arm
206+
needs_catch_all_arm |= has_hidden_variants;
207+
184208
let missing_arms = missing_pats
185-
.map(|(pat, hidden)| {
186-
(make::match_arm(iter::once(pat), None, make::ext::expr_todo()), hidden)
209+
.filter(|(_, hidden)| {
210+
// filter out hidden patterns because they're handled by the catch-all arm
211+
!hidden
187212
})
188-
.map(|(it, hidden)| (it.clone_for_update(), hidden));
213+
.map(|(pat, _)| {
214+
make::match_arm(iter::once(pat), None, make::ext::expr_todo())
215+
.clone_for_update()
216+
});
189217

190218
let catch_all_arm = new_match_arm_list
191219
.arms()
@@ -204,15 +232,13 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
204232
cov_mark::hit!(add_missing_match_arms_empty_expr);
205233
}
206234
}
235+
207236
let mut first_new_arm = None;
208-
for (arm, hidden) in missing_arms {
209-
if hidden {
210-
needs_catch_all_arm = !has_catch_all_arm;
211-
} else {
212-
first_new_arm.get_or_insert_with(|| arm.clone());
213-
new_match_arm_list.add_arm(arm);
214-
}
237+
for arm in missing_arms {
238+
first_new_arm.get_or_insert_with(|| arm.clone());
239+
new_match_arm_list.add_arm(arm);
215240
}
241+
216242
if needs_catch_all_arm && !has_catch_all_arm {
217243
cov_mark::hit!(added_wildcard_pattern);
218244
let arm = make::match_arm(
@@ -225,24 +251,39 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
225251
new_match_arm_list.add_arm(arm);
226252
}
227253

228-
let old_range = ctx.sema.original_range(match_arm_list.syntax()).range;
229-
match (first_new_arm, ctx.config.snippet_cap) {
230-
(Some(first_new_arm), Some(cap)) => {
231-
let extend_lifetime;
232-
let cursor =
233-
match first_new_arm.syntax().descendants().find_map(ast::WildcardPat::cast)
234-
{
235-
Some(it) => {
236-
extend_lifetime = it.syntax().clone();
237-
Cursor::Replace(&extend_lifetime)
238-
}
239-
None => Cursor::Before(first_new_arm.syntax()),
240-
};
241-
let snippet = render_snippet(cap, new_match_arm_list.syntax(), cursor);
242-
builder.replace_snippet(cap, old_range, snippet);
254+
if let (Some(first_new_arm), Some(cap)) = (first_new_arm, ctx.config.snippet_cap) {
255+
match first_new_arm.syntax().descendants().find_map(ast::WildcardPat::cast) {
256+
Some(it) => edit.add_placeholder_snippet(cap, it),
257+
None => edit.add_tabstop_before(cap, first_new_arm),
243258
}
244-
_ => builder.replace(old_range, new_match_arm_list.to_string()),
245259
}
260+
261+
// FIXME: Hack for mutable syntax trees not having great support for macros
262+
// Just replace the element that the original range came from
263+
let old_place = {
264+
// Find the original element
265+
let old_file_range = ctx.sema.original_range(match_arm_list.syntax());
266+
let file = ctx.sema.parse(old_file_range.file_id);
267+
let old_place = file.syntax().covering_element(old_file_range.range);
268+
269+
// Make `old_place` mut
270+
match old_place {
271+
syntax::SyntaxElement::Node(it) => {
272+
syntax::SyntaxElement::from(edit.make_syntax_mut(it))
273+
}
274+
syntax::SyntaxElement::Token(it) => {
275+
// Don't have a way to make tokens mut, so instead make the parent mut
276+
// and find the token again
277+
let parent = edit.make_syntax_mut(it.parent().unwrap());
278+
let mut_token =
279+
parent.covering_element(it.text_range()).into_token().unwrap();
280+
281+
syntax::SyntaxElement::from(mut_token)
282+
}
283+
}
284+
};
285+
286+
syntax::ted::replace(old_place, new_match_arm_list.syntax());
246287
},
247288
)
248289
}
@@ -1621,10 +1662,9 @@ pub enum E { #[doc(hidden)] A, }
16211662
);
16221663
}
16231664

1624-
// FIXME: I don't think the assist should be applicable in this case
16251665
#[test]
16261666
fn does_not_fill_wildcard_with_wildcard() {
1627-
check_assist(
1667+
check_assist_not_applicable(
16281668
add_missing_match_arms,
16291669
r#"
16301670
//- /main.rs crate:main deps:e
@@ -1635,13 +1675,6 @@ fn foo(t: ::e::E) {
16351675
}
16361676
//- /e.rs crate:e
16371677
pub enum E { #[doc(hidden)] A, }
1638-
"#,
1639-
r#"
1640-
fn foo(t: ::e::E) {
1641-
match t {
1642-
_ => todo!(),
1643-
}
1644-
}
16451678
"#,
16461679
);
16471680
}
@@ -1777,7 +1810,7 @@ fn foo(t: ::e::E, b: bool) {
17771810

17781811
#[test]
17791812
fn does_not_fill_wildcard_with_partial_wildcard_and_wildcard() {
1780-
check_assist(
1813+
check_assist_not_applicable(
17811814
add_missing_match_arms,
17821815
r#"
17831816
//- /main.rs crate:main deps:e
@@ -1789,14 +1822,6 @@ fn foo(t: ::e::E, b: bool) {
17891822
}
17901823
//- /e.rs crate:e
17911824
pub enum E { #[doc(hidden)] A, }"#,
1792-
r#"
1793-
fn foo(t: ::e::E, b: bool) {
1794-
match t {
1795-
_ if b => todo!(),
1796-
_ => todo!(),
1797-
}
1798-
}
1799-
"#,
18001825
);
18011826
}
18021827

0 commit comments

Comments
 (0)