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