Skip to content

Commit 09147c3

Browse files
committed
Add support for fill match arms of boolean values
- Add support for boolean inside tuple
1 parent 7ae0bc1 commit 09147c3

File tree

2 files changed

+207
-23
lines changed

2 files changed

+207
-23
lines changed

crates/ide_assists/src/handlers/fill_match_arms.rs

Lines changed: 199 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
5353
.iter()
5454
.filter_map(ast::MatchArm::pat)
5555
.flat_map(|pat| match pat {
56-
// Special casee OrPat as separate top-level pats
56+
// Special case OrPat as separate top-level pats
5757
Pat::OrPat(or_pat) => Either::Left(or_pat.pats()),
5858
_ => Either::Right(iter::once(pat)),
5959
})
@@ -72,7 +72,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
7272
.filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
7373
.map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
7474
.collect::<Vec<_>>();
75-
if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() {
75+
if Some(enum_def)
76+
== FamousDefs(&ctx.sema, Some(module.krate()))
77+
.core_option_Option()
78+
.map(|x| lift_enum(x))
79+
{
7680
// Match `Some` variant first.
7781
cov_mark::hit!(option_order);
7882
variants.reverse()
@@ -151,49 +155,99 @@ fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
151155
}
152156
}
153157

154-
fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> {
158+
#[derive(Eq, PartialEq, Clone)]
159+
enum ExtendedEnum {
160+
Bool,
161+
Enum(hir::Enum),
162+
}
163+
164+
#[derive(Eq, PartialEq, Clone)]
165+
enum ExtendedVariant {
166+
True,
167+
False,
168+
Variant(hir::Variant),
169+
}
170+
171+
fn lift_enum(e: hir::Enum) -> ExtendedEnum {
172+
ExtendedEnum::Enum(e)
173+
}
174+
175+
impl ExtendedEnum {
176+
fn variants(&self, db: &RootDatabase) -> Vec<ExtendedVariant> {
177+
match self {
178+
ExtendedEnum::Enum(e) => {
179+
e.variants(db).into_iter().map(|x| ExtendedVariant::Variant(x)).collect::<Vec<_>>()
180+
}
181+
ExtendedEnum::Bool => {
182+
Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False])
183+
}
184+
}
185+
}
186+
}
187+
188+
fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
155189
sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
156-
Some(Adt::Enum(e)) => Some(e),
157-
_ => None,
190+
Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
191+
_ => {
192+
if ty.is_bool() {
193+
Some(ExtendedEnum::Bool)
194+
} else {
195+
None
196+
}
197+
}
158198
})
159199
}
160200

161201
fn resolve_tuple_of_enum_def(
162202
sema: &Semantics<RootDatabase>,
163203
expr: &ast::Expr,
164-
) -> Option<Vec<hir::Enum>> {
204+
) -> Option<Vec<ExtendedEnum>> {
165205
sema.type_of_expr(&expr)?
166206
.tuple_fields(sema.db)
167207
.iter()
168208
.map(|ty| {
169209
ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
170-
Some(Adt::Enum(e)) => Some(e),
210+
Some(Adt::Enum(e)) => Some(lift_enum(e)),
171211
// For now we only handle expansion for a tuple of enums. Here
172212
// we map non-enum items to None and rely on `collect` to
173213
// convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
174-
_ => None,
214+
_ => {
215+
if ty.is_bool() {
216+
Some(ExtendedEnum::Bool)
217+
} else {
218+
None
219+
}
220+
}
175221
})
176222
})
177223
.collect()
178224
}
179225

180-
fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Option<ast::Pat> {
181-
let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?);
226+
fn build_pat(db: &RootDatabase, module: hir::Module, var: ExtendedVariant) -> Option<ast::Pat> {
227+
match var {
228+
ExtendedVariant::Variant(var) => {
229+
let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?);
230+
231+
// FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
232+
let pat: ast::Pat = match var.source(db)?.value.kind() {
233+
ast::StructKind::Tuple(field_list) => {
234+
let pats =
235+
iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
236+
make::tuple_struct_pat(path, pats).into()
237+
}
238+
ast::StructKind::Record(field_list) => {
239+
let pats =
240+
field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into());
241+
make::record_pat(path, pats).into()
242+
}
243+
ast::StructKind::Unit => make::path_pat(path),
244+
};
182245

183-
// FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
184-
let pat: ast::Pat = match var.source(db)?.value.kind() {
185-
ast::StructKind::Tuple(field_list) => {
186-
let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
187-
make::tuple_struct_pat(path, pats).into()
188-
}
189-
ast::StructKind::Record(field_list) => {
190-
let pats = field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into());
191-
make::record_pat(path, pats).into()
246+
Some(pat)
192247
}
193-
ast::StructKind::Unit => make::path_pat(path),
194-
};
195-
196-
Some(pat)
248+
ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))),
249+
ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))),
250+
}
197251
}
198252

199253
#[cfg(test)]
@@ -225,6 +279,21 @@ mod tests {
225279
);
226280
}
227281

282+
#[test]
283+
fn all_boolean_match_arms_provided() {
284+
check_assist_not_applicable(
285+
fill_match_arms,
286+
r#"
287+
fn foo(a: bool) {
288+
match a$0 {
289+
true => {}
290+
false => {}
291+
}
292+
}
293+
"#,
294+
)
295+
}
296+
228297
#[test]
229298
fn tuple_of_non_enum() {
230299
// for now this case is not handled, although it potentially could be
@@ -240,6 +309,113 @@ mod tests {
240309
);
241310
}
242311

312+
#[test]
313+
fn fill_match_arms_boolean() {
314+
check_assist(
315+
fill_match_arms,
316+
r#"
317+
fn foo(a: bool) {
318+
match a$0 {
319+
}
320+
}
321+
"#,
322+
r#"
323+
fn foo(a: bool) {
324+
match a {
325+
$0true => {}
326+
false => {}
327+
}
328+
}
329+
"#,
330+
)
331+
}
332+
333+
#[test]
334+
fn partial_fill_boolean() {
335+
check_assist(
336+
fill_match_arms,
337+
r#"
338+
fn foo(a: bool) {
339+
match a$0 {
340+
true => {}
341+
}
342+
}
343+
"#,
344+
r#"
345+
fn foo(a: bool) {
346+
match a {
347+
true => {}
348+
$0false => {}
349+
}
350+
}
351+
"#,
352+
)
353+
}
354+
355+
#[test]
356+
fn all_boolean_tuple_arms_provided() {
357+
check_assist_not_applicable(
358+
fill_match_arms,
359+
r#"
360+
fn foo(a: bool) {
361+
match (a, a)$0 {
362+
(true, true) => {}
363+
(true, false) => {}
364+
(false, true) => {}
365+
(false, false) => {}
366+
}
367+
}
368+
"#,
369+
)
370+
}
371+
372+
#[test]
373+
fn fill_boolean_tuple() {
374+
check_assist(
375+
fill_match_arms,
376+
r#"
377+
fn foo(a: bool) {
378+
match (a, a)$0 {
379+
}
380+
}
381+
"#,
382+
r#"
383+
fn foo(a: bool) {
384+
match (a, a) {
385+
$0(true, true) => {}
386+
(true, false) => {}
387+
(false, true) => {}
388+
(false, false) => {}
389+
}
390+
}
391+
"#,
392+
)
393+
}
394+
395+
#[test]
396+
fn partial_fill_boolean_tuple() {
397+
check_assist(
398+
fill_match_arms,
399+
r#"
400+
fn foo(a: bool) {
401+
match (a, a)$0 {
402+
(false, true) => {}
403+
}
404+
}
405+
"#,
406+
r#"
407+
fn foo(a: bool) {
408+
match (a, a) {
409+
(false, true) => {}
410+
$0(true, true) => {}
411+
(true, false) => {}
412+
(false, false) => {}
413+
}
414+
}
415+
"#,
416+
)
417+
}
418+
243419
#[test]
244420
fn partial_fill_record_tuple() {
245421
check_assist(

crates/syntax/src/ast/make.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,14 @@ pub fn wildcard_pat() -> ast::WildcardPat {
294294
}
295295
}
296296

297+
pub fn literal_pat(lit: &str) -> ast::LiteralPat {
298+
return from_text(lit);
299+
300+
fn from_text(text: &str) -> ast::LiteralPat {
301+
ast_from_text(&format!("fn f() {{ match x {{ {} => {{}} }} }}", text))
302+
}
303+
}
304+
297305
/// Creates a tuple of patterns from an iterator of patterns.
298306
///
299307
/// Invariant: `pats` must be length > 0

0 commit comments

Comments
 (0)