aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-04-29 10:57:06 +0100
committerAleksey Kladov <[email protected]>2020-04-29 10:59:11 +0100
commit7c3c289dab46d1dbe9196549c81307f291e631f7 (patch)
tree9f9249e03c3ecf09bff04bc1fa80c00280ff6be6
parent73bef854ab854ab1a289944966444453e6f4aadf (diff)
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.
-rw-r--r--crates/ra_assists/src/handlers/replace_if_let_with_match.rs78
-rw-r--r--crates/ra_assists/src/handlers/replace_let_with_if_let.rs4
-rw-r--r--crates/ra_assists/src/handlers/replace_unwrap_with_match.rs4
-rw-r--r--crates/ra_assists/src/utils.rs57
4 files changed, 123 insertions, 20 deletions
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
index 0a0a88f3d..9841f6980 100644
--- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
@@ -1,11 +1,10 @@
1use ra_fmt::unwrap_trivial_block; 1use ra_fmt::unwrap_trivial_block;
2use ra_syntax::{ 2use ra_syntax::{
3 ast::{self, make}, 3 ast::{self, edit::IndentLevel, make},
4 AstNode, 4 AstNode,
5}; 5};
6 6
7use crate::{Assist, AssistCtx, AssistId}; 7use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
8use ast::edit::IndentLevel;
9 8
10// Assist: replace_if_let_with_match 9// Assist: replace_if_let_with_match
11// 10//
@@ -44,15 +43,21 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
44 ast::ElseBranch::IfExpr(_) => return None, 43 ast::ElseBranch::IfExpr(_) => return None,
45 }; 44 };
46 45
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| {
48 let match_expr = { 48 let match_expr = {
49 let then_arm = { 49 let then_arm = {
50 let then_expr = unwrap_trivial_block(then_block); 50 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)
52 }; 52 };
53 let else_arm = { 53 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());
54 let else_expr = unwrap_trivial_block(else_block); 59 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)
56 }; 61 };
57 make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) 62 make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]))
58 }; 63 };
@@ -68,6 +73,7 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
68#[cfg(test)] 73#[cfg(test)]
69mod tests { 74mod tests {
70 use super::*; 75 use super::*;
76
71 use crate::helpers::{check_assist, check_assist_target}; 77 use crate::helpers::{check_assist, check_assist_target};
72 78
73 #[test] 79 #[test]
@@ -145,4 +151,64 @@ impl VariantData {
145 }", 151 }",
146 ); 152 );
147 } 153 }
154
155 #[test]
156 fn special_case_option() {
157 check_assist(
158 replace_if_let_with_match,
159 r#"
160enum Option<T> { Some(T), None }
161use Option::*;
162
163fn foo(x: Option<i32>) {
164 <|>if let Some(x) = x {
165 println!("{}", x)
166 } else {
167 println!("none")
168 }
169}
170 "#,
171 r#"
172enum Option<T> { Some(T), None }
173use Option::*;
174
175fn 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#"
190enum Result<T, E> { Ok(T), Err(E) }
191use Result::*;
192
193fn foo(x: Result<i32, ()>) {
194 <|>if let Ok(x) = x {
195 println!("{}", x)
196 } else {
197 println!("none")
198 }
199}
200 "#,
201 r#"
202enum Result<T, E> { Ok(T), Err(E) }
203use Result::*;
204
205fn foo(x: Result<i32, ()>) {
206 <|>match x {
207 Ok(x) => println!("{}", x),
208 Err(_) => println!("none"),
209 }
210}
211 "#,
212 );
213 }
148} 214}
diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
index fb134f677..0cf23b754 100644
--- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
+++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11 11
12use crate::{ 12use crate::{
13 assist_ctx::{Assist, AssistCtx}, 13 assist_ctx::{Assist, AssistCtx},
14 utils::happy_try_variant, 14 utils::TryEnum,
15 AssistId, 15 AssistId,
16}; 16};
17 17
@@ -45,7 +45,7 @@ pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option<Assist> {
45 let init = let_stmt.initializer()?; 45 let init = let_stmt.initializer()?;
46 let original_pat = let_stmt.pat()?; 46 let original_pat = let_stmt.pat()?;
47 let ty = ctx.sema.type_of_expr(&init)?; 47 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());
49 49
50 ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", |edit| { 50 ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", |edit| {
51 let with_placeholder: ast::Pat = match happy_variant { 51 let with_placeholder: ast::Pat = match happy_variant {
diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
index 89211b44a..62d4ea522 100644
--- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
@@ -5,7 +5,7 @@ use ra_syntax::{
5 AstNode, 5 AstNode,
6}; 6};
7 7
8use crate::{utils::happy_try_variant, Assist, AssistCtx, AssistId}; 8use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
9 9
10// Assist: replace_unwrap_with_match 10// Assist: replace_unwrap_with_match
11// 11//
@@ -37,7 +37,7 @@ pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option<Assist> {
37 } 37 }
38 let caller = method_call.expr()?; 38 let caller = method_call.expr()?;
39 let ty = ctx.sema.type_of_expr(&caller)?; 39 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();
41 41
42 ctx.add_assist(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", |edit| { 42 ctx.add_assist(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", |edit| {
43 let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); 43 let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs
index 316c8b20f..61f8bd1dc 100644
--- a/crates/ra_assists/src/utils.rs
+++ b/crates/ra_assists/src/utils.rs
@@ -1,6 +1,8 @@
1//! Assorted functions shared by several assists. 1//! Assorted functions shared by several assists.
2pub(crate) mod insert_use; 2pub(crate) mod insert_use;
3 3
4use std::iter;
5
4use hir::{Adt, Semantics, Type}; 6use hir::{Adt, Semantics, Type};
5use ra_ide_db::RootDatabase; 7use ra_ide_db::RootDatabase;
6use ra_syntax::{ 8use ra_syntax::{
@@ -100,15 +102,50 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
100 } 102 }
101} 103}
102 104
103pub(crate) fn happy_try_variant(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<&'static str> { 105#[derive(Clone, Copy)]
104 let enum_ = match ty.as_adt() { 106pub(crate) enum TryEnum {
105 Some(Adt::Enum(it)) => it, 107 Result,
106 _ => return None, 108 Option,
107 }; 109}
108 [("Result", "Ok"), ("Option", "Some")].iter().find_map(|(known_type, happy_case)| { 110
109 if &enum_.name(sema.db).to_string() == known_type { 111impl TryEnum {
110 return Some(*happy_case); 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",
111 } 131 }
112 None 132 }
113 }) 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 }
114} 151}