Skip to content

Commit b75803a

Browse files
committed
Auto merge of rust-lang#14057 - jonas-schievink:improve-match-to-let-else, r=jonas-schievink
feat: Improve "match to let else" assist Closes rust-lang/rust-analyzer#13540 Handles complex `let` patterns (rather than just idents), and diverging block expressions have their `{`/`}` stripped to create nicer code.
2 parents f1b257f + 6321b25 commit b75803a

File tree

1 file changed

+62
-23
lines changed

1 file changed

+62
-23
lines changed

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

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,23 @@ use crate::{
3030
// ```
3131
pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
3232
let let_stmt: ast::LetStmt = ctx.find_node_at_offset()?;
33-
let binding = find_binding(let_stmt.pat()?)?;
33+
let binding = let_stmt.pat()?;
3434

35-
let initializer = match let_stmt.initializer() {
36-
Some(ast::Expr::MatchExpr(it)) => it,
37-
_ => return None,
38-
};
35+
let Some(ast::Expr::MatchExpr(initializer)) = let_stmt.initializer() else { return None };
3936
let initializer_expr = initializer.expr()?;
4037

41-
let (extracting_arm, diverging_arm) = match find_arms(ctx, &initializer) {
42-
Some(it) => it,
43-
None => return None,
44-
};
38+
let Some((extracting_arm, diverging_arm)) = find_arms(ctx, &initializer) else { return None };
4539
if extracting_arm.guard().is_some() {
4640
cov_mark::hit!(extracting_arm_has_guard);
4741
return None;
4842
}
4943

50-
let diverging_arm_expr = diverging_arm.expr()?;
44+
let diverging_arm_expr = match diverging_arm.expr()? {
45+
ast::Expr::BlockExpr(block) if block.modifier().is_none() && block.label().is_none() => {
46+
block.to_string()
47+
}
48+
other => format!("{{ {other} }}"),
49+
};
5150
let extracting_arm_pat = extracting_arm.pat()?;
5251
let extracted_variable = find_extracted_variable(ctx, &extracting_arm)?;
5352

@@ -56,24 +55,16 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'
5655
"Convert match to let-else",
5756
let_stmt.syntax().text_range(),
5857
|builder| {
59-
let extracting_arm_pat = rename_variable(&extracting_arm_pat, extracted_variable, binding);
58+
let extracting_arm_pat =
59+
rename_variable(&extracting_arm_pat, extracted_variable, binding);
6060
builder.replace(
6161
let_stmt.syntax().text_range(),
62-
format!("let {extracting_arm_pat} = {initializer_expr} else {{ {diverging_arm_expr} }};")
62+
format!("let {extracting_arm_pat} = {initializer_expr} else {diverging_arm_expr};"),
6363
)
6464
},
6565
)
6666
}
6767

68-
// Given a pattern, find the name introduced to the surrounding scope.
69-
fn find_binding(pat: ast::Pat) -> Option<ast::IdentPat> {
70-
if let ast::Pat::IdentPat(ident) = pat {
71-
Some(ident)
72-
} else {
73-
None
74-
}
75-
}
76-
7768
// Given a match expression, find extracting and diverging arms.
7869
fn find_arms(
7970
ctx: &AssistContext<'_>,
@@ -124,7 +115,7 @@ fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Opti
124115
}
125116

126117
// Rename `extracted` with `binding` in `pat`.
127-
fn rename_variable(pat: &ast::Pat, extracted: ast::Name, binding: ast::IdentPat) -> SyntaxNode {
118+
fn rename_variable(pat: &ast::Pat, extracted: ast::Name, binding: ast::Pat) -> SyntaxNode {
128119
let syntax = pat.syntax().clone_for_update();
129120
let extracted_syntax = syntax.covering_element(extracted.syntax().text_range());
130121

@@ -136,7 +127,7 @@ fn rename_variable(pat: &ast::Pat, extracted: ast::Name, binding: ast::IdentPat)
136127
if let Some(name_ref) = record_pat_field.field_name() {
137128
ted::replace(
138129
record_pat_field.syntax(),
139-
ast::make::record_pat_field(ast::make::name_ref(&name_ref.text()), binding.into())
130+
ast::make::record_pat_field(ast::make::name_ref(&name_ref.text()), binding)
140131
.syntax()
141132
.clone_for_update(),
142133
);
@@ -410,4 +401,52 @@ fn foo(opt: Option<i32>) -> Option<i32> {
410401
"#,
411402
);
412403
}
404+
405+
#[test]
406+
fn complex_pattern() {
407+
check_assist(
408+
convert_match_to_let_else,
409+
r#"
410+
//- minicore: option
411+
fn f() {
412+
let (x, y) = $0match Some((0, 1)) {
413+
Some(it) => it,
414+
None => return,
415+
};
416+
}
417+
"#,
418+
r#"
419+
fn f() {
420+
let Some((x, y)) = Some((0, 1)) else { return };
421+
}
422+
"#,
423+
);
424+
}
425+
426+
#[test]
427+
fn diverging_block() {
428+
check_assist(
429+
convert_match_to_let_else,
430+
r#"
431+
//- minicore: option
432+
fn f() {
433+
let x = $0match Some(()) {
434+
Some(it) => it,
435+
None => {//comment
436+
println!("nope");
437+
return
438+
},
439+
};
440+
}
441+
"#,
442+
r#"
443+
fn f() {
444+
let Some(x) = Some(()) else {//comment
445+
println!("nope");
446+
return
447+
};
448+
}
449+
"#,
450+
);
451+
}
413452
}

0 commit comments

Comments
 (0)