aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/handlers/inline_local_variable.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists/src/handlers/inline_local_variable.rs')
-rw-r--r--crates/assists/src/handlers/inline_local_variable.rs695
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 @@
1use ide_db::defs::Definition;
2use syntax::{
3 ast::{self, AstNode, AstToken},
4 TextRange,
5};
6use test_utils::mark;
7
8use 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// ```
29pub(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)]
129mod 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"
141fn bar(a: usize) {}
142fn 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"
155fn bar(a: usize) {}
156fn 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"
175fn bar(a: usize) {}
176fn 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"
189fn bar(a: usize) {}
190fn 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"
209fn bar(a: usize) {}
210fn 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"
223fn bar(a: usize) {}
224fn 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"
243fn bar(a: usize): usize { a }
244fn 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"
257fn bar(a: usize): usize { a }
258fn 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"
277fn 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"
290fn 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"
309fn 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"
322fn 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"
342fn 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"
354fn foo() {
355 let a<|> = bar(10 + 1);
356 let b = a * 10;
357 let c = a as usize;
358}",
359 r"
360fn 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"
372fn 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"
379fn 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"
392fn 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"
399fn 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"
412struct Bar {
413 foo: usize
414}
415
416fn 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"
423struct Bar {
424 foo: usize
425}
426
427fn 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"
440fn 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"
448fn 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"
462fn foo() {
463 let bar = 10;
464 let a<|> = &bar;
465 let b = a * 10;
466}",
467 r"
468fn 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"
480fn foo() {
481 let a<|> = (10, 20);
482 let b = a[0];
483}",
484 r"
485fn 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"
496fn foo() {
497 let a<|> = [1, 2, 3];
498 let b = a.len();
499}",
500 r"
501fn 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"
512fn foo() {
513 let a<|> = (10 + 20);
514 let b = a * 10;
515 let c = a as usize;
516}",
517 r"
518fn 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"
530fn foo() {
531 let d = 10;
532 let a<|> = d;
533 let b = a * 10;
534 let c = a as usize;
535}",
536 r"
537fn 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"
550fn foo() {
551 let a<|> = { 10 };
552 let b = a * 10;
553 let c = a as usize;
554}",
555 r"
556fn 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"
568fn 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"
576fn 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"
590fn foo() {
591 let a<|> = vec![10, 20];
592 for i in a {}
593}",
594 r"
595fn 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"
606fn foo() {
607 let a<|> = 1 > 0;
608 while a {}
609}",
610 r"
611fn 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"
622fn foo() {
623 let a<|> = 1 + 1;
624 loop {
625 break a;
626 }
627}",
628 r"
629fn 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"
642fn foo() {
643 let a<|> = 1 > 0;
644 return a;
645}",
646 r"
647fn 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"
658fn foo() {
659 let a<|> = 1 > 0;
660 match a {}
661}",
662 r"
663fn 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"
675fn 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"
688fn main() {
689 let x = <|>1 + 2;
690 x * 4;
691}
692",
693 )
694 }
695}