Skip to content

Commit 7c3c289

Browse files
committed
Use specific pattern when translating if-let-else to match
We *probably* should actually use the same machinery here, as we do for fill match arms, but just special-casing options and results seems to be a good first step.
1 parent 73bef85 commit 7c3c289

File tree

4 files changed

+123
-20
lines changed

4 files changed

+123
-20
lines changed

crates/ra_assists/src/handlers/replace_if_let_with_match.rs

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use ra_fmt::unwrap_trivial_block;
22
use ra_syntax::{
3-
ast::{self, make},
3+
ast::{self, edit::IndentLevel, make},
44
AstNode,
55
};
66

7-
use crate::{Assist, AssistCtx, AssistId};
8-
use ast::edit::IndentLevel;
7+
use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
98

109
// Assist: replace_if_let_with_match
1110
//
@@ -44,15 +43,21 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
4443
ast::ElseBranch::IfExpr(_) => return None,
4544
};
4645

47-
ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", |edit| {
46+
let sema = ctx.sema;
47+
ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", move |edit| {
4848
let match_expr = {
4949
let then_arm = {
5050
let then_expr = unwrap_trivial_block(then_block);
51-
make::match_arm(vec![pat], then_expr)
51+
make::match_arm(vec![pat.clone()], then_expr)
5252
};
5353
let else_arm = {
54+
let pattern = sema
55+
.type_of_pat(&pat)
56+
.and_then(|ty| TryEnum::from_ty(sema, &ty))
57+
.map(|it| it.sad_pattern())
58+
.unwrap_or_else(|| make::placeholder_pat().into());
5459
let else_expr = unwrap_trivial_block(else_block);
55-
make::match_arm(vec![make::placeholder_pat().into()], else_expr)
60+
make::match_arm(vec![pattern], else_expr)
5661
};
5762
make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]))
5863
};
@@ -68,6 +73,7 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
6873
#[cfg(test)]
6974
mod tests {
7075
use super::*;
76+
7177
use crate::helpers::{check_assist, check_assist_target};
7278

7379
#[test]
@@ -145,4 +151,64 @@ impl VariantData {
145151
}",
146152
);
147153
}
154+
155+
#[test]
156+
fn special_case_option() {
157+
check_assist(
158+
replace_if_let_with_match,
159+
r#"
160+
enum Option<T> { Some(T), None }
161+
use Option::*;
162+
163+
fn foo(x: Option<i32>) {
164+
<|>if let Some(x) = x {
165+
println!("{}", x)
166+
} else {
167+
println!("none")
168+
}
169+
}
170+
"#,
171+
r#"
172+
enum Option<T> { Some(T), None }
173+
use Option::*;
174+
175+
fn foo(x: Option<i32>) {
176+
<|>match x {
177+
Some(x) => println!("{}", x),
178+
None => println!("none"),
179+
}
180+
}
181+
"#,
182+
);
183+
}
184+
185+
#[test]
186+
fn special_case_result() {
187+
check_assist(
188+
replace_if_let_with_match,
189+
r#"
190+
enum Result<T, E> { Ok(T), Err(E) }
191+
use Result::*;
192+
193+
fn foo(x: Result<i32, ()>) {
194+
<|>if let Ok(x) = x {
195+
println!("{}", x)
196+
} else {
197+
println!("none")
198+
}
199+
}
200+
"#,
201+
r#"
202+
enum Result<T, E> { Ok(T), Err(E) }
203+
use Result::*;
204+
205+
fn foo(x: Result<i32, ()>) {
206+
<|>match x {
207+
Ok(x) => println!("{}", x),
208+
Err(_) => println!("none"),
209+
}
210+
}
211+
"#,
212+
);
213+
}
148214
}

crates/ra_assists/src/handlers/replace_let_with_if_let.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use ra_syntax::{
1111

1212
use crate::{
1313
assist_ctx::{Assist, AssistCtx},
14-
utils::happy_try_variant,
14+
utils::TryEnum,
1515
AssistId,
1616
};
1717

@@ -45,7 +45,7 @@ pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option<Assist> {
4545
let init = let_stmt.initializer()?;
4646
let original_pat = let_stmt.pat()?;
4747
let ty = ctx.sema.type_of_expr(&init)?;
48-
let happy_variant = happy_try_variant(ctx.sema, &ty);
48+
let happy_variant = TryEnum::from_ty(ctx.sema, &ty).map(|it| it.happy_case());
4949

5050
ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", |edit| {
5151
let with_placeholder: ast::Pat = match happy_variant {

crates/ra_assists/src/handlers/replace_unwrap_with_match.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use ra_syntax::{
55
AstNode,
66
};
77

8-
use crate::{utils::happy_try_variant, Assist, AssistCtx, AssistId};
8+
use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
99

1010
// Assist: replace_unwrap_with_match
1111
//
@@ -37,7 +37,7 @@ pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option<Assist> {
3737
}
3838
let caller = method_call.expr()?;
3939
let ty = ctx.sema.type_of_expr(&caller)?;
40-
let happy_variant = happy_try_variant(ctx.sema, &ty)?;
40+
let happy_variant = TryEnum::from_ty(ctx.sema, &ty)?.happy_case();
4141

4242
ctx.add_assist(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", |edit| {
4343
let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));

crates/ra_assists/src/utils.rs

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Assorted functions shared by several assists.
22
pub(crate) mod insert_use;
33

4+
use std::iter;
5+
46
use hir::{Adt, Semantics, Type};
57
use ra_ide_db::RootDatabase;
68
use ra_syntax::{
@@ -100,15 +102,50 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
100102
}
101103
}
102104

103-
pub(crate) fn happy_try_variant(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<&'static str> {
104-
let enum_ = match ty.as_adt() {
105-
Some(Adt::Enum(it)) => it,
106-
_ => return None,
107-
};
108-
[("Result", "Ok"), ("Option", "Some")].iter().find_map(|(known_type, happy_case)| {
109-
if &enum_.name(sema.db).to_string() == known_type {
110-
return Some(*happy_case);
105+
#[derive(Clone, Copy)]
106+
pub(crate) enum TryEnum {
107+
Result,
108+
Option,
109+
}
110+
111+
impl TryEnum {
112+
const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result];
113+
114+
pub(crate) fn from_ty(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<TryEnum> {
115+
let enum_ = match ty.as_adt() {
116+
Some(Adt::Enum(it)) => it,
117+
_ => return None,
118+
};
119+
TryEnum::ALL.iter().find_map(|&var| {
120+
if &enum_.name(sema.db).to_string() == var.type_name() {
121+
return Some(var);
122+
}
123+
None
124+
})
125+
}
126+
127+
pub(crate) fn happy_case(self) -> &'static str {
128+
match self {
129+
TryEnum::Result => "Ok",
130+
TryEnum::Option => "Some",
111131
}
112-
None
113-
})
132+
}
133+
134+
pub(crate) fn sad_pattern(self) -> ast::Pat {
135+
match self {
136+
TryEnum::Result => make::tuple_struct_pat(
137+
make::path_unqualified(make::path_segment(make::name_ref("Err"))),
138+
iter::once(make::placeholder_pat().into()),
139+
)
140+
.into(),
141+
TryEnum::Option => make::bind_pat(make::name("None")).into(),
142+
}
143+
}
144+
145+
fn type_name(self) -> &'static str {
146+
match self {
147+
TryEnum::Result => "Result",
148+
TryEnum::Option => "Option",
149+
}
150+
}
114151
}

0 commit comments

Comments
 (0)