Skip to content

Commit 4aa2a44

Browse files
bors[bot]elkowar
andauthored
Merge #9962
9962: Add empty-body check to replace_match_with_if_let and re-prioritize choices r=elkowar a=elkowar This PR changes some behaviour of the `replace_match_with_if_let` ide-assist. Concretely, it makes two changes: it introduces a check for empty expression bodies. This means that checks of the shape ```rs match x { A => {} B => { println!("hi"); } } ``` will prefer to use the B branch as the first (and only) variant. It also reprioritizes the importance of "happy" and "sad" patterns. Concretely, if there are reasons to prefer having the sad pattern be the first (/only) pattern, it will follow these. This means that in the case of ```rs match x { Ok(_) => { println!("Success"); } Err(e) => { println!("Failure: {}", e); } } ``` the `Err` variant will correctly be used as the first expression in the generated if. Up until now, the generated code was actually invalid, as it would generate ```rs if let Ok(_) = x { println!("Success"); } else { println!("Failure: {}", e); } ``` where `e` in the else branch is not defined. Co-authored-by: elkowar <[email protected]>
2 parents 13bbed7 + e47c974 commit 4aa2a44

File tree

1 file changed

+82
-8
lines changed

1 file changed

+82
-8
lines changed

crates/ide_assists/src/handlers/replace_if_let_with_match.rs

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use std::iter::{self, successors};
22

33
use either::Either;
4-
use ide_db::{ty_filter::TryEnum, RootDatabase};
4+
use ide_db::{defs::NameClass, ty_filter::TryEnum, RootDatabase};
55
use syntax::{
66
ast::{
77
self,
88
edit::{AstNodeEdit, IndentLevel},
9-
make,
9+
make, NameOwner,
1010
},
1111
AstNode,
1212
};
@@ -235,22 +235,34 @@ fn pick_pattern_and_expr_order(
235235
) -> Option<(ast::Pat, ast::Expr, ast::Expr)> {
236236
let res = match (pat, pat2) {
237237
(ast::Pat::WildcardPat(_), _) => return None,
238-
(pat, sad_pat) if is_sad_pat(sema, &sad_pat) => (pat, expr, expr2),
239-
(sad_pat, pat) if is_sad_pat(sema, &sad_pat) => (pat, expr2, expr),
240-
(pat, pat2) => match (binds_name(&pat), binds_name(&pat2)) {
238+
(pat, _) if is_empty_expr(&expr2) => (pat, expr, expr2),
239+
(_, pat) if is_empty_expr(&expr) => (pat, expr2, expr),
240+
(pat, pat2) => match (binds_name(sema, &pat), binds_name(sema, &pat2)) {
241241
(true, true) => return None,
242242
(true, false) => (pat, expr, expr2),
243243
(false, true) => (pat2, expr2, expr),
244+
_ if is_sad_pat(sema, &pat) => (pat2, expr2, expr),
244245
(false, false) => (pat, expr, expr2),
245246
},
246247
};
247248
Some(res)
248249
}
249250

250-
fn binds_name(pat: &ast::Pat) -> bool {
251-
let binds_name_v = |pat| binds_name(&pat);
251+
fn is_empty_expr(expr: &ast::Expr) -> bool {
252+
match expr {
253+
ast::Expr::BlockExpr(expr) => expr.is_empty(),
254+
ast::Expr::TupleExpr(expr) => expr.fields().next().is_none(),
255+
_ => false,
256+
}
257+
}
258+
259+
fn binds_name(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool {
260+
let binds_name_v = |pat| binds_name(&sema, &pat);
252261
match pat {
253-
ast::Pat::IdentPat(_) => true,
262+
ast::Pat::IdentPat(pat) => !matches!(
263+
pat.name().and_then(|name| NameClass::classify(sema, &name)),
264+
Some(NameClass::ConstReference(_))
265+
),
254266
ast::Pat::MacroPat(_) => true,
255267
ast::Pat::OrPat(pat) => pat.pats().any(binds_name_v),
256268
ast::Pat::SlicePat(pat) => pat.pats().any(binds_name_v),
@@ -702,6 +714,28 @@ fn main() {
702714
)
703715
}
704716

717+
#[test]
718+
fn replace_match_with_if_let_number_body() {
719+
check_assist(
720+
replace_match_with_if_let,
721+
r#"
722+
fn main() {
723+
$0match Ok(()) {
724+
Ok(()) => {},
725+
Err(_) => 0,
726+
}
727+
}
728+
"#,
729+
r#"
730+
fn main() {
731+
if let Err(_) = Ok(()) {
732+
0
733+
}
734+
}
735+
"#,
736+
)
737+
}
738+
705739
#[test]
706740
fn replace_match_with_if_let_exhaustive() {
707741
check_assist(
@@ -762,6 +796,46 @@ fn foo() {
762796
);
763797
}
764798

799+
#[test]
800+
fn replace_match_with_if_let_prefer_nonempty_body() {
801+
check_assist(
802+
replace_match_with_if_let,
803+
r#"
804+
fn foo() {
805+
match $0Ok(0) {
806+
Ok(value) => {},
807+
Err(err) => eprintln!("{}", err),
808+
}
809+
}
810+
"#,
811+
r#"
812+
fn foo() {
813+
if let Err(err) = Ok(0) {
814+
eprintln!("{}", err)
815+
}
816+
}
817+
"#,
818+
);
819+
check_assist(
820+
replace_match_with_if_let,
821+
r#"
822+
fn foo() {
823+
match $0Ok(0) {
824+
Err(err) => eprintln!("{}", err),
825+
Ok(value) => {},
826+
}
827+
}
828+
"#,
829+
r#"
830+
fn foo() {
831+
if let Err(err) = Ok(0) {
832+
eprintln!("{}", err)
833+
}
834+
}
835+
"#,
836+
);
837+
}
838+
765839
#[test]
766840
fn replace_match_with_if_let_rejects_double_name_bindings() {
767841
check_assist_not_applicable(

0 commit comments

Comments
 (0)