diff options
author | Zac Pullar-Strecker <[email protected]> | 2020-08-24 10:19:53 +0100 |
---|---|---|
committer | Zac Pullar-Strecker <[email protected]> | 2020-08-24 10:20:13 +0100 |
commit | 7bbca7a1b3f9293d2f5cc5745199bc5f8396f2f0 (patch) | |
tree | bdb47765991cb973b2cd5481a088fac636bd326c /crates/assists/src/handlers/inline_local_variable.rs | |
parent | ca464650eeaca6195891199a93f4f76cf3e7e697 (diff) | |
parent | e65d48d1fb3d4d91d9dc1148a7a836ff5c9a3c87 (diff) |
Merge remote-tracking branch 'upstream/master' into 503-hover-doc-links
Diffstat (limited to 'crates/assists/src/handlers/inline_local_variable.rs')
-rw-r--r-- | crates/assists/src/handlers/inline_local_variable.rs | 695 |
1 files changed, 695 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs new file mode 100644 index 000000000..164bbce86 --- /dev/null +++ b/crates/assists/src/handlers/inline_local_variable.rs | |||
@@ -0,0 +1,695 @@ | |||
1 | use ide_db::defs::Definition; | ||
2 | use syntax::{ | ||
3 | ast::{self, AstNode, AstToken}, | ||
4 | TextRange, | ||
5 | }; | ||
6 | use test_utils::mark; | ||
7 | |||
8 | use crate::{ | ||
9 | assist_context::{AssistContext, Assists}, | ||
10 | AssistId, AssistKind, | ||
11 | }; | ||
12 | |||
13 | // Assist: inline_local_variable | ||
14 | // | ||
15 | // Inlines local variable. | ||
16 | // | ||
17 | // ``` | ||
18 | // fn main() { | ||
19 | // let x<|> = 1 + 2; | ||
20 | // x * 4; | ||
21 | // } | ||
22 | // ``` | ||
23 | // -> | ||
24 | // ``` | ||
25 | // fn main() { | ||
26 | // (1 + 2) * 4; | ||
27 | // } | ||
28 | // ``` | ||
29 | pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
30 | let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?; | ||
31 | let bind_pat = match let_stmt.pat()? { | ||
32 | ast::Pat::IdentPat(pat) => pat, | ||
33 | _ => return None, | ||
34 | }; | ||
35 | if bind_pat.mut_token().is_some() { | ||
36 | mark::hit!(test_not_inline_mut_variable); | ||
37 | return None; | ||
38 | } | ||
39 | if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { | ||
40 | mark::hit!(not_applicable_outside_of_bind_pat); | ||
41 | return None; | ||
42 | } | ||
43 | let initializer_expr = let_stmt.initializer()?; | ||
44 | |||
45 | let def = ctx.sema.to_def(&bind_pat)?; | ||
46 | let def = Definition::Local(def); | ||
47 | let refs = def.usages(&ctx.sema).all(); | ||
48 | if refs.is_empty() { | ||
49 | mark::hit!(test_not_applicable_if_variable_unused); | ||
50 | return None; | ||
51 | }; | ||
52 | |||
53 | let delete_range = if let Some(whitespace) = let_stmt | ||
54 | .syntax() | ||
55 | .next_sibling_or_token() | ||
56 | .and_then(|it| ast::Whitespace::cast(it.as_token()?.clone())) | ||
57 | { | ||
58 | TextRange::new( | ||
59 | let_stmt.syntax().text_range().start(), | ||
60 | whitespace.syntax().text_range().end(), | ||
61 | ) | ||
62 | } else { | ||
63 | let_stmt.syntax().text_range() | ||
64 | }; | ||
65 | |||
66 | let mut wrap_in_parens = vec![true; refs.len()]; | ||
67 | |||
68 | for (i, desc) in refs.iter().enumerate() { | ||
69 | let usage_node = ctx | ||
70 | .covering_node_for_range(desc.file_range.range) | ||
71 | .ancestors() | ||
72 | .find_map(ast::PathExpr::cast)?; | ||
73 | let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast); | ||
74 | let usage_parent = match usage_parent_option { | ||
75 | Some(u) => u, | ||
76 | None => { | ||
77 | wrap_in_parens[i] = false; | ||
78 | continue; | ||
79 | } | ||
80 | }; | ||
81 | |||
82 | wrap_in_parens[i] = match (&initializer_expr, usage_parent) { | ||
83 | (ast::Expr::CallExpr(_), _) | ||
84 | | (ast::Expr::IndexExpr(_), _) | ||
85 | | (ast::Expr::MethodCallExpr(_), _) | ||
86 | | (ast::Expr::FieldExpr(_), _) | ||
87 | | (ast::Expr::TryExpr(_), _) | ||
88 | | (ast::Expr::RefExpr(_), _) | ||
89 | | (ast::Expr::Literal(_), _) | ||
90 | | (ast::Expr::TupleExpr(_), _) | ||
91 | | (ast::Expr::ArrayExpr(_), _) | ||
92 | | (ast::Expr::ParenExpr(_), _) | ||
93 | | (ast::Expr::PathExpr(_), _) | ||
94 | | (ast::Expr::BlockExpr(_), _) | ||
95 | | (ast::Expr::EffectExpr(_), _) | ||
96 | | (_, ast::Expr::CallExpr(_)) | ||
97 | | (_, ast::Expr::TupleExpr(_)) | ||
98 | | (_, ast::Expr::ArrayExpr(_)) | ||
99 | | (_, ast::Expr::ParenExpr(_)) | ||
100 | | (_, ast::Expr::ForExpr(_)) | ||
101 | | (_, ast::Expr::WhileExpr(_)) | ||
102 | | (_, ast::Expr::BreakExpr(_)) | ||
103 | | (_, ast::Expr::ReturnExpr(_)) | ||
104 | | (_, ast::Expr::MatchExpr(_)) => false, | ||
105 | _ => true, | ||
106 | }; | ||
107 | } | ||
108 | |||
109 | let init_str = initializer_expr.syntax().text().to_string(); | ||
110 | let init_in_paren = format!("({})", &init_str); | ||
111 | |||
112 | let target = bind_pat.syntax().text_range(); | ||
113 | acc.add( | ||
114 | AssistId("inline_local_variable", AssistKind::RefactorInline), | ||
115 | "Inline variable", | ||
116 | target, | ||
117 | move |builder| { | ||
118 | builder.delete(delete_range); | ||
119 | for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { | ||
120 | let replacement = | ||
121 | if should_wrap { init_in_paren.clone() } else { init_str.clone() }; | ||
122 | builder.replace(desc.file_range.range, replacement) | ||
123 | } | ||
124 | }, | ||
125 | ) | ||
126 | } | ||
127 | |||
128 | #[cfg(test)] | ||
129 | mod tests { | ||
130 | use test_utils::mark; | ||
131 | |||
132 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
133 | |||
134 | use super::*; | ||
135 | |||
136 | #[test] | ||
137 | fn test_inline_let_bind_literal_expr() { | ||
138 | check_assist( | ||
139 | inline_local_variable, | ||
140 | r" | ||
141 | fn bar(a: usize) {} | ||
142 | fn foo() { | ||
143 | let a<|> = 1; | ||
144 | a + 1; | ||
145 | if a > 10 { | ||
146 | } | ||
147 | |||
148 | while a > 10 { | ||
149 | |||
150 | } | ||
151 | let b = a * 10; | ||
152 | bar(a); | ||
153 | }", | ||
154 | r" | ||
155 | fn bar(a: usize) {} | ||
156 | fn foo() { | ||
157 | 1 + 1; | ||
158 | if 1 > 10 { | ||
159 | } | ||
160 | |||
161 | while 1 > 10 { | ||
162 | |||
163 | } | ||
164 | let b = 1 * 10; | ||
165 | bar(1); | ||
166 | }", | ||
167 | ); | ||
168 | } | ||
169 | |||
170 | #[test] | ||
171 | fn test_inline_let_bind_bin_expr() { | ||
172 | check_assist( | ||
173 | inline_local_variable, | ||
174 | r" | ||
175 | fn bar(a: usize) {} | ||
176 | fn foo() { | ||
177 | let a<|> = 1 + 1; | ||
178 | a + 1; | ||
179 | if a > 10 { | ||
180 | } | ||
181 | |||
182 | while a > 10 { | ||
183 | |||
184 | } | ||
185 | let b = a * 10; | ||
186 | bar(a); | ||
187 | }", | ||
188 | r" | ||
189 | fn bar(a: usize) {} | ||
190 | fn foo() { | ||
191 | (1 + 1) + 1; | ||
192 | if (1 + 1) > 10 { | ||
193 | } | ||
194 | |||
195 | while (1 + 1) > 10 { | ||
196 | |||
197 | } | ||
198 | let b = (1 + 1) * 10; | ||
199 | bar(1 + 1); | ||
200 | }", | ||
201 | ); | ||
202 | } | ||
203 | |||
204 | #[test] | ||
205 | fn test_inline_let_bind_function_call_expr() { | ||
206 | check_assist( | ||
207 | inline_local_variable, | ||
208 | r" | ||
209 | fn bar(a: usize) {} | ||
210 | fn foo() { | ||
211 | let a<|> = bar(1); | ||
212 | a + 1; | ||
213 | if a > 10 { | ||
214 | } | ||
215 | |||
216 | while a > 10 { | ||
217 | |||
218 | } | ||
219 | let b = a * 10; | ||
220 | bar(a); | ||
221 | }", | ||
222 | r" | ||
223 | fn bar(a: usize) {} | ||
224 | fn foo() { | ||
225 | bar(1) + 1; | ||
226 | if bar(1) > 10 { | ||
227 | } | ||
228 | |||
229 | while bar(1) > 10 { | ||
230 | |||
231 | } | ||
232 | let b = bar(1) * 10; | ||
233 | bar(bar(1)); | ||
234 | }", | ||
235 | ); | ||
236 | } | ||
237 | |||
238 | #[test] | ||
239 | fn test_inline_let_bind_cast_expr() { | ||
240 | check_assist( | ||
241 | inline_local_variable, | ||
242 | r" | ||
243 | fn bar(a: usize): usize { a } | ||
244 | fn foo() { | ||
245 | let a<|> = bar(1) as u64; | ||
246 | a + 1; | ||
247 | if a > 10 { | ||
248 | } | ||
249 | |||
250 | while a > 10 { | ||
251 | |||
252 | } | ||
253 | let b = a * 10; | ||
254 | bar(a); | ||
255 | }", | ||
256 | r" | ||
257 | fn bar(a: usize): usize { a } | ||
258 | fn foo() { | ||
259 | (bar(1) as u64) + 1; | ||
260 | if (bar(1) as u64) > 10 { | ||
261 | } | ||
262 | |||
263 | while (bar(1) as u64) > 10 { | ||
264 | |||
265 | } | ||
266 | let b = (bar(1) as u64) * 10; | ||
267 | bar(bar(1) as u64); | ||
268 | }", | ||
269 | ); | ||
270 | } | ||
271 | |||
272 | #[test] | ||
273 | fn test_inline_let_bind_block_expr() { | ||
274 | check_assist( | ||
275 | inline_local_variable, | ||
276 | r" | ||
277 | fn foo() { | ||
278 | let a<|> = { 10 + 1 }; | ||
279 | a + 1; | ||
280 | if a > 10 { | ||
281 | } | ||
282 | |||
283 | while a > 10 { | ||
284 | |||
285 | } | ||
286 | let b = a * 10; | ||
287 | bar(a); | ||
288 | }", | ||
289 | r" | ||
290 | fn foo() { | ||
291 | { 10 + 1 } + 1; | ||
292 | if { 10 + 1 } > 10 { | ||
293 | } | ||
294 | |||
295 | while { 10 + 1 } > 10 { | ||
296 | |||
297 | } | ||
298 | let b = { 10 + 1 } * 10; | ||
299 | bar({ 10 + 1 }); | ||
300 | }", | ||
301 | ); | ||
302 | } | ||
303 | |||
304 | #[test] | ||
305 | fn test_inline_let_bind_paren_expr() { | ||
306 | check_assist( | ||
307 | inline_local_variable, | ||
308 | r" | ||
309 | fn foo() { | ||
310 | let a<|> = ( 10 + 1 ); | ||
311 | a + 1; | ||
312 | if a > 10 { | ||
313 | } | ||
314 | |||
315 | while a > 10 { | ||
316 | |||
317 | } | ||
318 | let b = a * 10; | ||
319 | bar(a); | ||
320 | }", | ||
321 | r" | ||
322 | fn foo() { | ||
323 | ( 10 + 1 ) + 1; | ||
324 | if ( 10 + 1 ) > 10 { | ||
325 | } | ||
326 | |||
327 | while ( 10 + 1 ) > 10 { | ||
328 | |||
329 | } | ||
330 | let b = ( 10 + 1 ) * 10; | ||
331 | bar(( 10 + 1 )); | ||
332 | }", | ||
333 | ); | ||
334 | } | ||
335 | |||
336 | #[test] | ||
337 | fn test_not_inline_mut_variable() { | ||
338 | mark::check!(test_not_inline_mut_variable); | ||
339 | check_assist_not_applicable( | ||
340 | inline_local_variable, | ||
341 | r" | ||
342 | fn foo() { | ||
343 | let mut a<|> = 1 + 1; | ||
344 | a + 1; | ||
345 | }", | ||
346 | ); | ||
347 | } | ||
348 | |||
349 | #[test] | ||
350 | fn test_call_expr() { | ||
351 | check_assist( | ||
352 | inline_local_variable, | ||
353 | r" | ||
354 | fn foo() { | ||
355 | let a<|> = bar(10 + 1); | ||
356 | let b = a * 10; | ||
357 | let c = a as usize; | ||
358 | }", | ||
359 | r" | ||
360 | fn foo() { | ||
361 | let b = bar(10 + 1) * 10; | ||
362 | let c = bar(10 + 1) as usize; | ||
363 | }", | ||
364 | ); | ||
365 | } | ||
366 | |||
367 | #[test] | ||
368 | fn test_index_expr() { | ||
369 | check_assist( | ||
370 | inline_local_variable, | ||
371 | r" | ||
372 | fn foo() { | ||
373 | let x = vec![1, 2, 3]; | ||
374 | let a<|> = x[0]; | ||
375 | let b = a * 10; | ||
376 | let c = a as usize; | ||
377 | }", | ||
378 | r" | ||
379 | fn foo() { | ||
380 | let x = vec![1, 2, 3]; | ||
381 | let b = x[0] * 10; | ||
382 | let c = x[0] as usize; | ||
383 | }", | ||
384 | ); | ||
385 | } | ||
386 | |||
387 | #[test] | ||
388 | fn test_method_call_expr() { | ||
389 | check_assist( | ||
390 | inline_local_variable, | ||
391 | r" | ||
392 | fn foo() { | ||
393 | let bar = vec![1]; | ||
394 | let a<|> = bar.len(); | ||
395 | let b = a * 10; | ||
396 | let c = a as usize; | ||
397 | }", | ||
398 | r" | ||
399 | fn foo() { | ||
400 | let bar = vec![1]; | ||
401 | let b = bar.len() * 10; | ||
402 | let c = bar.len() as usize; | ||
403 | }", | ||
404 | ); | ||
405 | } | ||
406 | |||
407 | #[test] | ||
408 | fn test_field_expr() { | ||
409 | check_assist( | ||
410 | inline_local_variable, | ||
411 | r" | ||
412 | struct Bar { | ||
413 | foo: usize | ||
414 | } | ||
415 | |||
416 | fn foo() { | ||
417 | let bar = Bar { foo: 1 }; | ||
418 | let a<|> = bar.foo; | ||
419 | let b = a * 10; | ||
420 | let c = a as usize; | ||
421 | }", | ||
422 | r" | ||
423 | struct Bar { | ||
424 | foo: usize | ||
425 | } | ||
426 | |||
427 | fn foo() { | ||
428 | let bar = Bar { foo: 1 }; | ||
429 | let b = bar.foo * 10; | ||
430 | let c = bar.foo as usize; | ||
431 | }", | ||
432 | ); | ||
433 | } | ||
434 | |||
435 | #[test] | ||
436 | fn test_try_expr() { | ||
437 | check_assist( | ||
438 | inline_local_variable, | ||
439 | r" | ||
440 | fn foo() -> Option<usize> { | ||
441 | let bar = Some(1); | ||
442 | let a<|> = bar?; | ||
443 | let b = a * 10; | ||
444 | let c = a as usize; | ||
445 | None | ||
446 | }", | ||
447 | r" | ||
448 | fn foo() -> Option<usize> { | ||
449 | let bar = Some(1); | ||
450 | let b = bar? * 10; | ||
451 | let c = bar? as usize; | ||
452 | None | ||
453 | }", | ||
454 | ); | ||
455 | } | ||
456 | |||
457 | #[test] | ||
458 | fn test_ref_expr() { | ||
459 | check_assist( | ||
460 | inline_local_variable, | ||
461 | r" | ||
462 | fn foo() { | ||
463 | let bar = 10; | ||
464 | let a<|> = &bar; | ||
465 | let b = a * 10; | ||
466 | }", | ||
467 | r" | ||
468 | fn foo() { | ||
469 | let bar = 10; | ||
470 | let b = &bar * 10; | ||
471 | }", | ||
472 | ); | ||
473 | } | ||
474 | |||
475 | #[test] | ||
476 | fn test_tuple_expr() { | ||
477 | check_assist( | ||
478 | inline_local_variable, | ||
479 | r" | ||
480 | fn foo() { | ||
481 | let a<|> = (10, 20); | ||
482 | let b = a[0]; | ||
483 | }", | ||
484 | r" | ||
485 | fn foo() { | ||
486 | let b = (10, 20)[0]; | ||
487 | }", | ||
488 | ); | ||
489 | } | ||
490 | |||
491 | #[test] | ||
492 | fn test_array_expr() { | ||
493 | check_assist( | ||
494 | inline_local_variable, | ||
495 | r" | ||
496 | fn foo() { | ||
497 | let a<|> = [1, 2, 3]; | ||
498 | let b = a.len(); | ||
499 | }", | ||
500 | r" | ||
501 | fn foo() { | ||
502 | let b = [1, 2, 3].len(); | ||
503 | }", | ||
504 | ); | ||
505 | } | ||
506 | |||
507 | #[test] | ||
508 | fn test_paren() { | ||
509 | check_assist( | ||
510 | inline_local_variable, | ||
511 | r" | ||
512 | fn foo() { | ||
513 | let a<|> = (10 + 20); | ||
514 | let b = a * 10; | ||
515 | let c = a as usize; | ||
516 | }", | ||
517 | r" | ||
518 | fn foo() { | ||
519 | let b = (10 + 20) * 10; | ||
520 | let c = (10 + 20) as usize; | ||
521 | }", | ||
522 | ); | ||
523 | } | ||
524 | |||
525 | #[test] | ||
526 | fn test_path_expr() { | ||
527 | check_assist( | ||
528 | inline_local_variable, | ||
529 | r" | ||
530 | fn foo() { | ||
531 | let d = 10; | ||
532 | let a<|> = d; | ||
533 | let b = a * 10; | ||
534 | let c = a as usize; | ||
535 | }", | ||
536 | r" | ||
537 | fn foo() { | ||
538 | let d = 10; | ||
539 | let b = d * 10; | ||
540 | let c = d as usize; | ||
541 | }", | ||
542 | ); | ||
543 | } | ||
544 | |||
545 | #[test] | ||
546 | fn test_block_expr() { | ||
547 | check_assist( | ||
548 | inline_local_variable, | ||
549 | r" | ||
550 | fn foo() { | ||
551 | let a<|> = { 10 }; | ||
552 | let b = a * 10; | ||
553 | let c = a as usize; | ||
554 | }", | ||
555 | r" | ||
556 | fn foo() { | ||
557 | let b = { 10 } * 10; | ||
558 | let c = { 10 } as usize; | ||
559 | }", | ||
560 | ); | ||
561 | } | ||
562 | |||
563 | #[test] | ||
564 | fn test_used_in_different_expr1() { | ||
565 | check_assist( | ||
566 | inline_local_variable, | ||
567 | r" | ||
568 | fn foo() { | ||
569 | let a<|> = 10 + 20; | ||
570 | let b = a * 10; | ||
571 | let c = (a, 20); | ||
572 | let d = [a, 10]; | ||
573 | let e = (a); | ||
574 | }", | ||
575 | r" | ||
576 | fn foo() { | ||
577 | let b = (10 + 20) * 10; | ||
578 | let c = (10 + 20, 20); | ||
579 | let d = [10 + 20, 10]; | ||
580 | let e = (10 + 20); | ||
581 | }", | ||
582 | ); | ||
583 | } | ||
584 | |||
585 | #[test] | ||
586 | fn test_used_in_for_expr() { | ||
587 | check_assist( | ||
588 | inline_local_variable, | ||
589 | r" | ||
590 | fn foo() { | ||
591 | let a<|> = vec![10, 20]; | ||
592 | for i in a {} | ||
593 | }", | ||
594 | r" | ||
595 | fn foo() { | ||
596 | for i in vec![10, 20] {} | ||
597 | }", | ||
598 | ); | ||
599 | } | ||
600 | |||
601 | #[test] | ||
602 | fn test_used_in_while_expr() { | ||
603 | check_assist( | ||
604 | inline_local_variable, | ||
605 | r" | ||
606 | fn foo() { | ||
607 | let a<|> = 1 > 0; | ||
608 | while a {} | ||
609 | }", | ||
610 | r" | ||
611 | fn foo() { | ||
612 | while 1 > 0 {} | ||
613 | }", | ||
614 | ); | ||
615 | } | ||
616 | |||
617 | #[test] | ||
618 | fn test_used_in_break_expr() { | ||
619 | check_assist( | ||
620 | inline_local_variable, | ||
621 | r" | ||
622 | fn foo() { | ||
623 | let a<|> = 1 + 1; | ||
624 | loop { | ||
625 | break a; | ||
626 | } | ||
627 | }", | ||
628 | r" | ||
629 | fn foo() { | ||
630 | loop { | ||
631 | break 1 + 1; | ||
632 | } | ||
633 | }", | ||
634 | ); | ||
635 | } | ||
636 | |||
637 | #[test] | ||
638 | fn test_used_in_return_expr() { | ||
639 | check_assist( | ||
640 | inline_local_variable, | ||
641 | r" | ||
642 | fn foo() { | ||
643 | let a<|> = 1 > 0; | ||
644 | return a; | ||
645 | }", | ||
646 | r" | ||
647 | fn foo() { | ||
648 | return 1 > 0; | ||
649 | }", | ||
650 | ); | ||
651 | } | ||
652 | |||
653 | #[test] | ||
654 | fn test_used_in_match_expr() { | ||
655 | check_assist( | ||
656 | inline_local_variable, | ||
657 | r" | ||
658 | fn foo() { | ||
659 | let a<|> = 1 > 0; | ||
660 | match a {} | ||
661 | }", | ||
662 | r" | ||
663 | fn foo() { | ||
664 | match 1 > 0 {} | ||
665 | }", | ||
666 | ); | ||
667 | } | ||
668 | |||
669 | #[test] | ||
670 | fn test_not_applicable_if_variable_unused() { | ||
671 | mark::check!(test_not_applicable_if_variable_unused); | ||
672 | check_assist_not_applicable( | ||
673 | inline_local_variable, | ||
674 | r" | ||
675 | fn foo() { | ||
676 | let <|>a = 0; | ||
677 | } | ||
678 | ", | ||
679 | ) | ||
680 | } | ||
681 | |||
682 | #[test] | ||
683 | fn not_applicable_outside_of_bind_pat() { | ||
684 | mark::check!(not_applicable_outside_of_bind_pat); | ||
685 | check_assist_not_applicable( | ||
686 | inline_local_variable, | ||
687 | r" | ||
688 | fn main() { | ||
689 | let x = <|>1 + 2; | ||
690 | x * 4; | ||
691 | } | ||
692 | ", | ||
693 | ) | ||
694 | } | ||
695 | } | ||