aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists/src/handlers')
-rw-r--r--crates/assists/src/handlers/pull_assignment_up.rs (renamed from crates/assists/src/handlers/extract_assignment.rs)149
1 files changed, 112 insertions, 37 deletions
diff --git a/crates/assists/src/handlers/extract_assignment.rs b/crates/assists/src/handlers/pull_assignment_up.rs
index 281cf5d24..560d93e10 100644
--- a/crates/assists/src/handlers/extract_assignment.rs
+++ b/crates/assists/src/handlers/pull_assignment_up.rs
@@ -1,4 +1,3 @@
1use hir::AsName;
2use syntax::{ 1use syntax::{
3 ast::{self, edit::AstNodeEdit, make}, 2 ast::{self, edit::AstNodeEdit, make},
4 AstNode, 3 AstNode,
@@ -10,9 +9,9 @@ use crate::{
10 AssistId, AssistKind, 9 AssistId, AssistKind,
11}; 10};
12 11
13// Assist: extract_assignment 12// Assist: pull_assignment_up
14// 13//
15// Extracts variable assigment to outside an if or match statement. 14// Extracts variable assignment to outside an if or match statement.
16// 15//
17// ``` 16// ```
18// fn main() { 17// fn main() {
@@ -37,16 +36,24 @@ use crate::{
37// }; 36// };
38// } 37// }
39// ``` 38// ```
40pub(crate) fn extract_assigment(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 39pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
41 let name = ctx.find_node_at_offset::<ast::NameRef>()?.as_name(); 40 let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
41 let name_expr = if assign_expr.op_kind()? == ast::BinOp::Assignment {
42 assign_expr.lhs()?
43 } else {
44 return None;
45 };
42 46
43 let (old_stmt, new_stmt) = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() { 47 let (old_stmt, new_stmt) = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() {
44 ( 48 (
45 ast::Expr::cast(if_expr.syntax().to_owned())?, 49 ast::Expr::cast(if_expr.syntax().to_owned())?,
46 exprify_if(&if_expr, &name)?.indent(if_expr.indent_level()), 50 exprify_if(&if_expr, &ctx.sema, &name_expr)?.indent(if_expr.indent_level()),
47 ) 51 )
48 } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() { 52 } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() {
49 (ast::Expr::cast(match_expr.syntax().to_owned())?, exprify_match(&match_expr, &name)?) 53 (
54 ast::Expr::cast(match_expr.syntax().to_owned())?,
55 exprify_match(&match_expr, &ctx.sema, &name_expr)?,
56 )
50 } else { 57 } else {
51 return None; 58 return None;
52 }; 59 };
@@ -54,22 +61,26 @@ pub(crate) fn extract_assigment(acc: &mut Assists, ctx: &AssistContext) -> Optio
54 let expr_stmt = make::expr_stmt(new_stmt); 61 let expr_stmt = make::expr_stmt(new_stmt);
55 62
56 acc.add( 63 acc.add(
57 AssistId("extract_assignment", AssistKind::RefactorExtract), 64 AssistId("pull_assignment_up", AssistKind::RefactorExtract),
58 "Extract assignment", 65 "Pull assignment up",
59 old_stmt.syntax().text_range(), 66 old_stmt.syntax().text_range(),
60 move |edit| { 67 move |edit| {
61 edit.replace(old_stmt.syntax().text_range(), format!("{} = {};", name, expr_stmt)); 68 edit.replace(old_stmt.syntax().text_range(), format!("{} = {};", name_expr, expr_stmt));
62 }, 69 },
63 ) 70 )
64} 71}
65 72
66fn exprify_match(match_expr: &ast::MatchExpr, name: &hir::Name) -> Option<ast::Expr> { 73fn exprify_match(
74 match_expr: &ast::MatchExpr,
75 sema: &hir::Semantics<ide_db::RootDatabase>,
76 name: &ast::Expr,
77) -> Option<ast::Expr> {
67 let new_arm_list = match_expr 78 let new_arm_list = match_expr
68 .match_arm_list()? 79 .match_arm_list()?
69 .arms() 80 .arms()
70 .map(|arm| { 81 .map(|arm| {
71 if let ast::Expr::BlockExpr(block) = arm.expr()? { 82 if let ast::Expr::BlockExpr(block) = arm.expr()? {
72 let new_block = exprify_block(&block, name)?.indent(block.indent_level()); 83 let new_block = exprify_block(&block, sema, name)?.indent(block.indent_level());
73 Some(arm.replace_descendant(block, new_block)) 84 Some(arm.replace_descendant(block, new_block))
74 } else { 85 } else {
75 None 86 None
@@ -82,21 +93,31 @@ fn exprify_match(match_expr: &ast::MatchExpr, name: &hir::Name) -> Option<ast::E
82 Some(make::expr_match(match_expr.expr()?, new_arm_list)) 93 Some(make::expr_match(match_expr.expr()?, new_arm_list))
83} 94}
84 95
85fn exprify_if(statement: &ast::IfExpr, name: &hir::Name) -> Option<ast::Expr> { 96fn exprify_if(
86 let then_branch = exprify_block(&statement.then_branch()?, name)?; 97 statement: &ast::IfExpr,
98 sema: &hir::Semantics<ide_db::RootDatabase>,
99 name: &ast::Expr,
100) -> Option<ast::Expr> {
101 let then_branch = exprify_block(&statement.then_branch()?, sema, name)?;
87 let else_branch = match statement.else_branch()? { 102 let else_branch = match statement.else_branch()? {
88 ast::ElseBranch::Block(ref block) => ast::ElseBranch::Block(exprify_block(block, name)?), 103 ast::ElseBranch::Block(ref block) => {
104 ast::ElseBranch::Block(exprify_block(block, sema, name)?)
105 }
89 ast::ElseBranch::IfExpr(expr) => { 106 ast::ElseBranch::IfExpr(expr) => {
90 mark::hit!(test_extract_assigment_chained_if); 107 mark::hit!(test_pull_assignment_up_chained_if);
91 ast::ElseBranch::IfExpr(ast::IfExpr::cast( 108 ast::ElseBranch::IfExpr(ast::IfExpr::cast(
92 exprify_if(&expr, name)?.syntax().to_owned(), 109 exprify_if(&expr, sema, name)?.syntax().to_owned(),
93 )?) 110 )?)
94 } 111 }
95 }; 112 };
96 Some(make::expr_if(statement.condition()?, then_branch, Some(else_branch))) 113 Some(make::expr_if(statement.condition()?, then_branch, Some(else_branch)))
97} 114}
98 115
99fn exprify_block(block: &ast::BlockExpr, name: &hir::Name) -> Option<ast::BlockExpr> { 116fn exprify_block(
117 block: &ast::BlockExpr,
118 sema: &hir::Semantics<ide_db::RootDatabase>,
119 name: &ast::Expr,
120) -> Option<ast::BlockExpr> {
100 if block.expr().is_some() { 121 if block.expr().is_some() {
101 return None; 122 return None;
102 } 123 }
@@ -106,8 +127,7 @@ fn exprify_block(block: &ast::BlockExpr, name: &hir::Name) -> Option<ast::BlockE
106 127
107 if let ast::Stmt::ExprStmt(stmt) = stmt { 128 if let ast::Stmt::ExprStmt(stmt) = stmt {
108 if let ast::Expr::BinExpr(expr) = stmt.expr()? { 129 if let ast::Expr::BinExpr(expr) = stmt.expr()? {
109 if expr.op_kind()? == ast::BinOp::Assignment 130 if expr.op_kind()? == ast::BinOp::Assignment && is_equivalent(sema, &expr.lhs()?, name)
110 && &expr.lhs()?.name_ref()?.as_name() == name
111 { 131 {
112 // The last statement in the block is an assignment to the name we want 132 // The last statement in the block is an assignment to the name we want
113 return Some(make::block_expr(stmts, Some(expr.rhs()?))); 133 return Some(make::block_expr(stmts, Some(expr.rhs()?)));
@@ -117,6 +137,29 @@ fn exprify_block(block: &ast::BlockExpr, name: &hir::Name) -> Option<ast::BlockE
117 None 137 None
118} 138}
119 139
140fn is_equivalent(
141 sema: &hir::Semantics<ide_db::RootDatabase>,
142 expr0: &ast::Expr,
143 expr1: &ast::Expr,
144) -> bool {
145 match (expr0, expr1) {
146 (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => {
147 mark::hit!(test_pull_assignment_up_field_assignment);
148 sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1)
149 }
150 (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => {
151 let path0 = path0.path();
152 let path1 = path1.path();
153 if let (Some(path0), Some(path1)) = (path0, path1) {
154 sema.resolve_path(&path0) == sema.resolve_path(&path1)
155 } else {
156 false
157 }
158 }
159 _ => false,
160 }
161}
162
120#[cfg(test)] 163#[cfg(test)]
121mod tests { 164mod tests {
122 use super::*; 165 use super::*;
@@ -124,9 +167,9 @@ mod tests {
124 use crate::tests::{check_assist, check_assist_not_applicable}; 167 use crate::tests::{check_assist, check_assist_not_applicable};
125 168
126 #[test] 169 #[test]
127 fn test_extract_assignment_if() { 170 fn test_pull_assignment_up_if() {
128 check_assist( 171 check_assist(
129 extract_assigment, 172 pull_assignment_up,
130 r#" 173 r#"
131fn foo() { 174fn foo() {
132 let mut a = 1; 175 let mut a = 1;
@@ -151,9 +194,9 @@ fn foo() {
151 } 194 }
152 195
153 #[test] 196 #[test]
154 fn test_extract_assignment_match() { 197 fn test_pull_assignment_up_match() {
155 check_assist( 198 check_assist(
156 extract_assigment, 199 pull_assignment_up,
157 r#" 200 r#"
158fn foo() { 201fn foo() {
159 let mut a = 1; 202 let mut a = 1;
@@ -190,9 +233,9 @@ fn foo() {
190 } 233 }
191 234
192 #[test] 235 #[test]
193 fn test_extract_assignment_not_last_not_applicable() { 236 fn test_pull_assignment_up_not_last_not_applicable() {
194 check_assist_not_applicable( 237 check_assist_not_applicable(
195 extract_assigment, 238 pull_assignment_up,
196 r#" 239 r#"
197fn foo() { 240fn foo() {
198 let mut a = 1; 241 let mut a = 1;
@@ -208,10 +251,10 @@ fn foo() {
208 } 251 }
209 252
210 #[test] 253 #[test]
211 fn test_extract_assignment_chained_if() { 254 fn test_pull_assignment_up_chained_if() {
212 mark::check!(test_extract_assigment_chained_if); 255 mark::check!(test_pull_assignment_up_chained_if);
213 check_assist( 256 check_assist(
214 extract_assigment, 257 pull_assignment_up,
215 r#" 258 r#"
216fn foo() { 259fn foo() {
217 let mut a = 1; 260 let mut a = 1;
@@ -240,9 +283,9 @@ fn foo() {
240 } 283 }
241 284
242 #[test] 285 #[test]
243 fn test_extract_assigment_retains_stmts() { 286 fn test_pull_assignment_up_retains_stmts() {
244 check_assist( 287 check_assist(
245 extract_assigment, 288 pull_assignment_up,
246 r#" 289 r#"
247fn foo() { 290fn foo() {
248 let mut a = 1; 291 let mut a = 1;
@@ -271,9 +314,9 @@ fn foo() {
271 } 314 }
272 315
273 #[test] 316 #[test]
274 fn extract_assignment_let_stmt_not_applicable() { 317 fn pull_assignment_up_let_stmt_not_applicable() {
275 check_assist_not_applicable( 318 check_assist_not_applicable(
276 extract_assigment, 319 pull_assignment_up,
277 r#" 320 r#"
278fn foo() { 321fn foo() {
279 let mut a = 1; 322 let mut a = 1;
@@ -288,9 +331,9 @@ fn foo() {
288 } 331 }
289 332
290 #[test] 333 #[test]
291 fn extract_assignment_if_missing_assigment_not_applicable() { 334 fn pull_assignment_up_if_missing_assigment_not_applicable() {
292 check_assist_not_applicable( 335 check_assist_not_applicable(
293 extract_assigment, 336 pull_assignment_up,
294 r#" 337 r#"
295fn foo() { 338fn foo() {
296 let mut a = 1; 339 let mut a = 1;
@@ -303,9 +346,9 @@ fn foo() {
303 } 346 }
304 347
305 #[test] 348 #[test]
306 fn extract_assignment_match_missing_assigment_not_applicable() { 349 fn pull_assignment_up_match_missing_assigment_not_applicable() {
307 check_assist_not_applicable( 350 check_assist_not_applicable(
308 extract_assigment, 351 pull_assignment_up,
309 r#" 352 r#"
310fn foo() { 353fn foo() {
311 let mut a = 1; 354 let mut a = 1;
@@ -322,4 +365,36 @@ fn foo() {
322}"#, 365}"#,
323 ) 366 )
324 } 367 }
368
369 #[test]
370 fn test_pull_assignment_up_field_assignment() {
371 mark::check!(test_pull_assignment_up_field_assignment);
372 check_assist(
373 pull_assignment_up,
374 r#"
375struct A(usize);
376
377fn foo() {
378 let mut a = A(1);
379
380 if true {
381 <|>a.0 = 2;
382 } else {
383 a.0 = 3;
384 }
385}"#,
386 r#"
387struct A(usize);
388
389fn foo() {
390 let mut a = A(1);
391
392 a.0 = if true {
393 2
394 } else {
395 3
396 };
397}"#,
398 )
399 }
325} 400}