]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/inline_call.rs
Merge #10438
[rust.git] / crates / ide_assists / src / handlers / inline_call.rs
1 use ast::make;
2 use either::Either;
3 use hir::{db::HirDatabase, HasSource, PathResolution, Semantics, TypeInfo};
4 use ide_db::{
5     base_db::{FileId, FileRange},
6     defs::Definition,
7     helpers::{insert_use::remove_path_if_in_use_stmt, node_ext::expr_as_name_ref},
8     path_transform::PathTransform,
9     search::{FileReference, SearchScope},
10     RootDatabase,
11 };
12 use itertools::{izip, Itertools};
13 use syntax::{
14     ast::{self, edit_in_place::Indent, HasArgList, PathExpr},
15     ted, AstNode,
16 };
17
18 use crate::{
19     assist_context::{AssistContext, Assists},
20     AssistId, AssistKind,
21 };
22
23 // Assist: inline_into_callers
24 //
25 // Inline a function or method body into all of its callers where possible, creating a `let` statement per parameter
26 // unless the parameter can be inlined. The parameter will be inlined either if it the supplied argument is a simple local
27 // or if the parameter is only accessed inside the function body once.
28 // If all calls can be inlined the function will be removed.
29 //
30 // ```
31 // fn print(_: &str) {}
32 // fn foo$0(word: &str) {
33 //     if !word.is_empty() {
34 //         print(word);
35 //     }
36 // }
37 // fn bar() {
38 //     foo("안녕하세요");
39 //     foo("여러분");
40 // }
41 // ```
42 // ->
43 // ```
44 // fn print(_: &str) {}
45 //
46 // fn bar() {
47 //     {
48 //         let word = "안녕하세요";
49 //         if !word.is_empty() {
50 //             print(word);
51 //         }
52 //     };
53 //     {
54 //         let word = "여러분";
55 //         if !word.is_empty() {
56 //             print(word);
57 //         }
58 //     };
59 // }
60 // ```
61 pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
62     let def_file = ctx.frange.file_id;
63     let name = ctx.find_node_at_offset::<ast::Name>()?;
64     let ast_func = name.syntax().parent().and_then(ast::Fn::cast)?;
65     let func_body = ast_func.body()?;
66     let param_list = ast_func.param_list()?;
67
68     let function = ctx.sema.to_def(&ast_func)?;
69
70     let params = get_fn_params(ctx.sema.db, function, &param_list)?;
71
72     let usages = Definition::ModuleDef(hir::ModuleDef::Function(function)).usages(&ctx.sema);
73     if !usages.at_least_one() {
74         return None;
75     }
76
77     let is_recursive_fn = usages
78         .clone()
79         .in_scope(SearchScope::file_range(FileRange {
80             file_id: def_file,
81             range: func_body.syntax().text_range(),
82         }))
83         .at_least_one();
84     if is_recursive_fn {
85         cov_mark::hit!(inline_into_callers_recursive);
86         return None;
87     }
88
89     acc.add(
90         AssistId("inline_into_callers", AssistKind::RefactorInline),
91         "Inline into all callers",
92         name.syntax().text_range(),
93         |builder| {
94             let mut usages = usages.all();
95             let current_file_usage = usages.references.remove(&def_file);
96
97             let mut remove_def = true;
98             let mut inline_refs_for_file = |file_id, refs: Vec<FileReference>| {
99                 builder.edit_file(file_id);
100                 let count = refs.len();
101                 // The collects are required as we are otherwise iterating while mutating 🙅‍♀️🙅‍♂️
102                 let (name_refs, name_refs_use): (Vec<_>, Vec<_>) = refs
103                     .into_iter()
104                     .filter_map(|file_ref| match file_ref.name {
105                         ast::NameLike::NameRef(name_ref) => Some(name_ref),
106                         _ => None,
107                     })
108                     .partition_map(|name_ref| {
109                         match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) {
110                             Some(use_tree) => Either::Right(builder.make_mut(use_tree)),
111                             None => Either::Left(name_ref),
112                         }
113                     });
114                 let call_infos: Vec<_> = name_refs
115                     .into_iter()
116                     .filter_map(CallInfo::from_name_ref)
117                     .map(|call_info| {
118                         let mut_node = builder.make_syntax_mut(call_info.node.syntax().clone());
119                         (call_info, mut_node)
120                     })
121                     .collect();
122                 let replaced = call_infos
123                     .into_iter()
124                     .map(|(call_info, mut_node)| {
125                         let replacement =
126                             inline(&ctx.sema, def_file, function, &func_body, &params, &call_info);
127                         ted::replace(mut_node, replacement.syntax());
128                     })
129                     .count();
130                 if replaced + name_refs_use.len() == count {
131                     // we replaced all usages in this file, so we can remove the imports
132                     name_refs_use.into_iter().for_each(|use_tree| {
133                         if let Some(path) = use_tree.path() {
134                             remove_path_if_in_use_stmt(&path);
135                         }
136                     })
137                 } else {
138                     remove_def = false;
139                 }
140             };
141             for (file_id, refs) in usages.into_iter() {
142                 inline_refs_for_file(file_id, refs);
143             }
144             if let Some(refs) = current_file_usage {
145                 inline_refs_for_file(def_file, refs);
146             } else {
147                 builder.edit_file(def_file);
148             }
149             if remove_def {
150                 builder.delete(ast_func.syntax().text_range());
151             }
152         },
153     )
154 }
155
156 // Assist: inline_call
157 //
158 // Inlines a function or method body creating a `let` statement per parameter unless the parameter
159 // can be inlined. The parameter will be inlined either if it the supplied argument is a simple local
160 // or if the parameter is only accessed inside the function body once.
161 //
162 // ```
163 // # //- minicore: option
164 // fn foo(name: Option<&str>) {
165 //     let name = name.unwrap$0();
166 // }
167 // ```
168 // ->
169 // ```
170 // fn foo(name: Option<&str>) {
171 //     let name = match name {
172 //             Some(val) => val,
173 //             None => panic!("called `Option::unwrap()` on a `None` value"),
174 //         };
175 // }
176 // ```
177 pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
178     let name_ref: ast::NameRef = ctx.find_node_at_offset()?;
179     let call_info = CallInfo::from_name_ref(name_ref.clone())?;
180     let (function, label) = match &call_info.node {
181         ast::CallableExpr::Call(call) => {
182             let path = match call.expr()? {
183                 ast::Expr::PathExpr(path) => path.path(),
184                 _ => None,
185             }?;
186             let function = match ctx.sema.resolve_path(&path)? {
187                 PathResolution::Def(hir::ModuleDef::Function(f)) => f,
188                 PathResolution::AssocItem(hir::AssocItem::Function(f)) => f,
189                 _ => return None,
190             };
191             (function, format!("Inline `{}`", path))
192         }
193         ast::CallableExpr::MethodCall(call) => {
194             (ctx.sema.resolve_method_call(call)?, format!("Inline `{}`", name_ref))
195         }
196     };
197
198     let fn_source = function.source(ctx.db())?;
199     let fn_body = fn_source.value.body()?;
200     let param_list = fn_source.value.param_list()?;
201
202     let FileRange { file_id, range } = fn_source.syntax().original_file_range(ctx.sema.db);
203     if file_id == ctx.frange.file_id && range.contains(ctx.frange.range.start()) {
204         cov_mark::hit!(inline_call_recursive);
205         return None;
206     }
207     let params = get_fn_params(ctx.sema.db, function, &param_list)?;
208
209     if call_info.arguments.len() != params.len() {
210         // Can't inline the function because they've passed the wrong number of
211         // arguments to this function
212         cov_mark::hit!(inline_call_incorrect_number_of_arguments);
213         return None;
214     }
215
216     let syntax = call_info.node.syntax().clone();
217     acc.add(
218         AssistId("inline_call", AssistKind::RefactorInline),
219         label,
220         syntax.text_range(),
221         |builder| {
222             let replacement = inline(&ctx.sema, file_id, function, &fn_body, &params, &call_info);
223
224             builder.replace_ast(
225                 match call_info.node {
226                     ast::CallableExpr::Call(it) => ast::Expr::CallExpr(it),
227                     ast::CallableExpr::MethodCall(it) => ast::Expr::MethodCallExpr(it),
228                 },
229                 replacement,
230             );
231         },
232     )
233 }
234
235 struct CallInfo {
236     node: ast::CallableExpr,
237     arguments: Vec<ast::Expr>,
238     generic_arg_list: Option<ast::GenericArgList>,
239 }
240
241 impl CallInfo {
242     fn from_name_ref(name_ref: ast::NameRef) -> Option<CallInfo> {
243         let parent = name_ref.syntax().parent()?;
244         if let Some(call) = ast::MethodCallExpr::cast(parent.clone()) {
245             let receiver = call.receiver()?;
246             let mut arguments = vec![receiver];
247             arguments.extend(call.arg_list()?.args());
248             Some(CallInfo {
249                 generic_arg_list: call.generic_arg_list(),
250                 node: ast::CallableExpr::MethodCall(call),
251                 arguments,
252             })
253         } else if let Some(segment) = ast::PathSegment::cast(parent) {
254             let path = segment.syntax().parent().and_then(ast::Path::cast)?;
255             let path = path.syntax().parent().and_then(ast::PathExpr::cast)?;
256             let call = path.syntax().parent().and_then(ast::CallExpr::cast)?;
257
258             Some(CallInfo {
259                 arguments: call.arg_list()?.args().collect(),
260                 node: ast::CallableExpr::Call(call),
261                 generic_arg_list: segment.generic_arg_list(),
262             })
263         } else {
264             None
265         }
266     }
267 }
268
269 fn get_fn_params(
270     db: &dyn HirDatabase,
271     function: hir::Function,
272     param_list: &ast::ParamList,
273 ) -> Option<Vec<(ast::Pat, Option<ast::Type>, hir::Param)>> {
274     let mut assoc_fn_params = function.assoc_fn_params(db).into_iter();
275
276     let mut params = Vec::new();
277     if let Some(self_param) = param_list.self_param() {
278         // FIXME this should depend on the receiver as well as the self_param
279         params.push((
280             make::ident_pat(
281                 self_param.amp_token().is_some(),
282                 self_param.mut_token().is_some(),
283                 make::name("this"),
284             )
285             .into(),
286             None,
287             assoc_fn_params.next()?,
288         ));
289     }
290     for param in param_list.params() {
291         params.push((param.pat()?, param.ty(), assoc_fn_params.next()?));
292     }
293
294     Some(params)
295 }
296
297 fn inline(
298     sema: &Semantics<RootDatabase>,
299     function_def_file_id: FileId,
300     function: hir::Function,
301     fn_body: &ast::BlockExpr,
302     params: &[(ast::Pat, Option<ast::Type>, hir::Param)],
303     CallInfo { node, arguments, generic_arg_list }: &CallInfo,
304 ) -> ast::Expr {
305     let body = fn_body.clone_for_update();
306     let usages_for_locals = |local| {
307         Definition::Local(local)
308             .usages(&sema)
309             .all()
310             .references
311             .remove(&function_def_file_id)
312             .unwrap_or_default()
313             .into_iter()
314     };
315     let param_use_nodes: Vec<Vec<_>> = params
316         .iter()
317         .map(|(pat, _, param)| {
318             if !matches!(pat, ast::Pat::IdentPat(pat) if pat.is_simple_ident()) {
319                 return Vec::new();
320             }
321             usages_for_locals(param.as_local(sema.db))
322                 .map(|FileReference { name, range, .. }| match name {
323                     ast::NameLike::NameRef(_) => body
324                         .syntax()
325                         .covering_element(range)
326                         .ancestors()
327                         .nth(3)
328                         .and_then(ast::PathExpr::cast),
329                     _ => None,
330                 })
331                 .collect::<Option<Vec<_>>>()
332                 .unwrap_or_default()
333         })
334         .collect();
335     if function.self_param(sema.db).is_some() {
336         let this = || make::name_ref("this").syntax().clone_for_update();
337         usages_for_locals(params[0].2.as_local(sema.db))
338             .flat_map(|FileReference { name, range, .. }| match name {
339                 ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)),
340                 _ => None,
341             })
342             .for_each(|it| {
343                 ted::replace(it, &this());
344             })
345     }
346     // Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
347     for ((pat, param_ty, _), usages, expr) in izip!(params, param_use_nodes, arguments).rev() {
348         let inline_direct = |usage, replacement: &ast::Expr| {
349             if let Some(field) = path_expr_as_record_field(usage) {
350                 cov_mark::hit!(inline_call_inline_direct_field);
351                 field.replace_expr(replacement.clone_for_update());
352             } else {
353                 ted::replace(usage.syntax(), &replacement.syntax().clone_for_update());
354             }
355         };
356         // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors
357         let usages: &[ast::PathExpr] = &*usages;
358         let expr: &ast::Expr = expr;
359         match usages {
360             // inline single use closure arguments
361             [usage]
362                 if matches!(expr, ast::Expr::ClosureExpr(_))
363                     && usage.syntax().parent().and_then(ast::Expr::cast).is_some() =>
364             {
365                 cov_mark::hit!(inline_call_inline_closure);
366                 let expr = make::expr_paren(expr.clone());
367                 inline_direct(usage, &expr);
368             }
369             // inline single use literals
370             [usage] if matches!(expr, ast::Expr::Literal(_)) => {
371                 cov_mark::hit!(inline_call_inline_literal);
372                 inline_direct(usage, &expr);
373             }
374             // inline direct local arguments
375             [_, ..] if expr_as_name_ref(&expr).is_some() => {
376                 cov_mark::hit!(inline_call_inline_locals);
377                 usages.into_iter().for_each(|usage| inline_direct(usage, &expr));
378             }
379             // can't inline, emit a let statement
380             _ => {
381                 let ty =
382                     sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone());
383                 if let Some(stmt_list) = body.stmt_list() {
384                     stmt_list.push_front(
385                         make::let_stmt(pat.clone(), ty, Some(expr.clone()))
386                             .clone_for_update()
387                             .into(),
388                     )
389                 }
390             }
391         }
392     }
393     if let Some(generic_arg_list) = generic_arg_list.clone() {
394         PathTransform::function_call(
395             &sema.scope(node.syntax()),
396             &sema.scope(fn_body.syntax()),
397             function,
398             generic_arg_list,
399         )
400         .apply(body.syntax());
401     }
402
403     let original_indentation = match node {
404         ast::CallableExpr::Call(it) => it.indent_level(),
405         ast::CallableExpr::MethodCall(it) => it.indent_level(),
406     };
407     body.reindent_to(original_indentation);
408
409     match body.tail_expr() {
410         Some(expr) if body.statements().next().is_none() => expr,
411         _ => ast::Expr::BlockExpr(body),
412     }
413 }
414
415 fn path_expr_as_record_field(usage: &PathExpr) -> Option<ast::RecordExprField> {
416     let path = usage.path()?;
417     let name_ref = path.as_single_name_ref()?;
418     ast::RecordExprField::for_name_ref(&name_ref)
419 }
420
421 #[cfg(test)]
422 mod tests {
423     use crate::tests::{check_assist, check_assist_not_applicable};
424
425     use super::*;
426
427     #[test]
428     fn no_args_or_return_value_gets_inlined_without_block() {
429         check_assist(
430             inline_call,
431             r#"
432 fn foo() { println!("Hello, World!"); }
433 fn main() {
434     fo$0o();
435 }
436 "#,
437             r#"
438 fn foo() { println!("Hello, World!"); }
439 fn main() {
440     { println!("Hello, World!"); };
441 }
442 "#,
443         );
444     }
445
446     #[test]
447     fn not_applicable_when_incorrect_number_of_parameters_are_provided() {
448         cov_mark::check!(inline_call_incorrect_number_of_arguments);
449         check_assist_not_applicable(
450             inline_call,
451             r#"
452 fn add(a: u32, b: u32) -> u32 { a + b }
453 fn main() { let x = add$0(42); }
454 "#,
455         );
456     }
457
458     #[test]
459     fn args_with_side_effects() {
460         check_assist(
461             inline_call,
462             r#"
463 fn foo(name: String) {
464     println!("Hello, {}!", name);
465 }
466 fn main() {
467     foo$0(String::from("Michael"));
468 }
469 "#,
470             r#"
471 fn foo(name: String) {
472     println!("Hello, {}!", name);
473 }
474 fn main() {
475     {
476         let name = String::from("Michael");
477         println!("Hello, {}!", name);
478     };
479 }
480 "#,
481         );
482     }
483
484     #[test]
485     fn function_with_multiple_statements() {
486         check_assist(
487             inline_call,
488             r#"
489 fn foo(a: u32, b: u32) -> u32 {
490     let x = a + b;
491     let y = x - b;
492     x * y
493 }
494
495 fn main() {
496     let x = foo$0(1, 2);
497 }
498 "#,
499             r#"
500 fn foo(a: u32, b: u32) -> u32 {
501     let x = a + b;
502     let y = x - b;
503     x * y
504 }
505
506 fn main() {
507     let x = {
508         let b = 2;
509         let x = 1 + b;
510         let y = x - b;
511         x * y
512     };
513 }
514 "#,
515         );
516     }
517
518     #[test]
519     fn function_with_self_param() {
520         check_assist(
521             inline_call,
522             r#"
523 struct Foo(u32);
524
525 impl Foo {
526     fn add(self, a: u32) -> Self {
527         Foo(self.0 + a)
528     }
529 }
530
531 fn main() {
532     let x = Foo::add$0(Foo(3), 2);
533 }
534 "#,
535             r#"
536 struct Foo(u32);
537
538 impl Foo {
539     fn add(self, a: u32) -> Self {
540         Foo(self.0 + a)
541     }
542 }
543
544 fn main() {
545     let x = {
546         let this = Foo(3);
547         Foo(this.0 + 2)
548     };
549 }
550 "#,
551         );
552     }
553
554     #[test]
555     fn method_by_val() {
556         check_assist(
557             inline_call,
558             r#"
559 struct Foo(u32);
560
561 impl Foo {
562     fn add(self, a: u32) -> Self {
563         Foo(self.0 + a)
564     }
565 }
566
567 fn main() {
568     let x = Foo(3).add$0(2);
569 }
570 "#,
571             r#"
572 struct Foo(u32);
573
574 impl Foo {
575     fn add(self, a: u32) -> Self {
576         Foo(self.0 + a)
577     }
578 }
579
580 fn main() {
581     let x = {
582         let this = Foo(3);
583         Foo(this.0 + 2)
584     };
585 }
586 "#,
587         );
588     }
589
590     #[test]
591     fn method_by_ref() {
592         check_assist(
593             inline_call,
594             r#"
595 struct Foo(u32);
596
597 impl Foo {
598     fn add(&self, a: u32) -> Self {
599         Foo(self.0 + a)
600     }
601 }
602
603 fn main() {
604     let x = Foo(3).add$0(2);
605 }
606 "#,
607             r#"
608 struct Foo(u32);
609
610 impl Foo {
611     fn add(&self, a: u32) -> Self {
612         Foo(self.0 + a)
613     }
614 }
615
616 fn main() {
617     let x = {
618         let ref this = Foo(3);
619         Foo(this.0 + 2)
620     };
621 }
622 "#,
623         );
624     }
625
626     #[test]
627     fn method_by_ref_mut() {
628         check_assist(
629             inline_call,
630             r#"
631 struct Foo(u32);
632
633 impl Foo {
634     fn clear(&mut self) {
635         self.0 = 0;
636     }
637 }
638
639 fn main() {
640     let mut foo = Foo(3);
641     foo.clear$0();
642 }
643 "#,
644             r#"
645 struct Foo(u32);
646
647 impl Foo {
648     fn clear(&mut self) {
649         self.0 = 0;
650     }
651 }
652
653 fn main() {
654     let mut foo = Foo(3);
655     {
656         let ref mut this = foo;
657         this.0 = 0;
658     };
659 }
660 "#,
661         );
662     }
663
664     #[test]
665     fn function_multi_use_expr_in_param() {
666         check_assist(
667             inline_call,
668             r#"
669 fn square(x: u32) -> u32 {
670     x * x
671 }
672 fn main() {
673     let x = 51;
674     let y = square$0(10 + x);
675 }
676 "#,
677             r#"
678 fn square(x: u32) -> u32 {
679     x * x
680 }
681 fn main() {
682     let x = 51;
683     let y = {
684         let x = 10 + x;
685         x * x
686     };
687 }
688 "#,
689         );
690     }
691
692     #[test]
693     fn function_use_local_in_param() {
694         cov_mark::check!(inline_call_inline_locals);
695         check_assist(
696             inline_call,
697             r#"
698 fn square(x: u32) -> u32 {
699     x * x
700 }
701 fn main() {
702     let local = 51;
703     let y = square$0(local);
704 }
705 "#,
706             r#"
707 fn square(x: u32) -> u32 {
708     x * x
709 }
710 fn main() {
711     let local = 51;
712     let y = local * local;
713 }
714 "#,
715         );
716     }
717
718     #[test]
719     fn method_in_impl() {
720         check_assist(
721             inline_call,
722             r#"
723 struct Foo;
724 impl Foo {
725     fn foo(&self) {
726         self;
727         self;
728     }
729     fn bar(&self) {
730         self.foo$0();
731     }
732 }
733 "#,
734             r#"
735 struct Foo;
736 impl Foo {
737     fn foo(&self) {
738         self;
739         self;
740     }
741     fn bar(&self) {
742         {
743             let ref this = self;
744             this;
745             this;
746         };
747     }
748 }
749 "#,
750         );
751     }
752
753     #[test]
754     fn wraps_closure_in_paren() {
755         cov_mark::check!(inline_call_inline_closure);
756         check_assist(
757             inline_call,
758             r#"
759 fn foo(x: fn()) {
760     x();
761 }
762
763 fn main() {
764     foo$0(|| {})
765 }
766 "#,
767             r#"
768 fn foo(x: fn()) {
769     x();
770 }
771
772 fn main() {
773     {
774         (|| {})();
775     }
776 }
777 "#,
778         );
779         check_assist(
780             inline_call,
781             r#"
782 fn foo(x: fn()) {
783     x();
784 }
785
786 fn main() {
787     foo$0(main)
788 }
789 "#,
790             r#"
791 fn foo(x: fn()) {
792     x();
793 }
794
795 fn main() {
796     {
797         main();
798     }
799 }
800 "#,
801         );
802     }
803
804     #[test]
805     fn inline_single_literal_expr() {
806         cov_mark::check!(inline_call_inline_literal);
807         check_assist(
808             inline_call,
809             r#"
810 fn foo(x: u32) -> u32{
811     x
812 }
813
814 fn main() {
815     foo$0(222);
816 }
817 "#,
818             r#"
819 fn foo(x: u32) -> u32{
820     x
821 }
822
823 fn main() {
824     222;
825 }
826 "#,
827         );
828     }
829
830     #[test]
831     fn inline_emits_type_for_coercion() {
832         check_assist(
833             inline_call,
834             r#"
835 fn foo(x: *const u32) -> u32 {
836     x as u32
837 }
838
839 fn main() {
840     foo$0(&222);
841 }
842 "#,
843             r#"
844 fn foo(x: *const u32) -> u32 {
845     x as u32
846 }
847
848 fn main() {
849     {
850         let x: *const u32 = &222;
851         x as u32
852     };
853 }
854 "#,
855         );
856     }
857
858     // FIXME: const generics aren't being substituted, this is blocked on better support for them
859     #[test]
860     fn inline_substitutes_generics() {
861         check_assist(
862             inline_call,
863             r#"
864 fn foo<T, const N: usize>() {
865     bar::<T, N>()
866 }
867
868 fn bar<U, const M: usize>() {}
869
870 fn main() {
871     foo$0::<usize, {0}>();
872 }
873 "#,
874             r#"
875 fn foo<T, const N: usize>() {
876     bar::<T, N>()
877 }
878
879 fn bar<U, const M: usize>() {}
880
881 fn main() {
882     bar::<usize, N>();
883 }
884 "#,
885         );
886     }
887
888     #[test]
889     fn inline_callers() {
890         check_assist(
891             inline_into_callers,
892             r#"
893 fn do_the_math$0(b: u32) -> u32 {
894     let foo = 10;
895     foo * b + foo
896 }
897 fn foo() {
898     do_the_math(0);
899     let bar = 10;
900     do_the_math(bar);
901 }
902 "#,
903             r#"
904
905 fn foo() {
906     {
907         let foo = 10;
908         foo * 0 + foo
909     };
910     let bar = 10;
911     {
912         let foo = 10;
913         foo * bar + foo
914     };
915 }
916 "#,
917         );
918     }
919
920     #[test]
921     fn inline_callers_across_files() {
922         check_assist(
923             inline_into_callers,
924             r#"
925 //- /lib.rs
926 mod foo;
927 fn do_the_math$0(b: u32) -> u32 {
928     let foo = 10;
929     foo * b + foo
930 }
931 //- /foo.rs
932 use super::do_the_math;
933 fn foo() {
934     do_the_math(0);
935     let bar = 10;
936     do_the_math(bar);
937 }
938 "#,
939             r#"
940 //- /lib.rs
941 mod foo;
942
943 //- /foo.rs
944 fn foo() {
945     {
946         let foo = 10;
947         foo * 0 + foo
948     };
949     let bar = 10;
950     {
951         let foo = 10;
952         foo * bar + foo
953     };
954 }
955 "#,
956         );
957     }
958
959     #[test]
960     fn inline_callers_across_files_with_def_file() {
961         check_assist(
962             inline_into_callers,
963             r#"
964 //- /lib.rs
965 mod foo;
966 fn do_the_math$0(b: u32) -> u32 {
967     let foo = 10;
968     foo * b + foo
969 }
970 fn bar(a: u32, b: u32) -> u32 {
971     do_the_math(0);
972 }
973 //- /foo.rs
974 use super::do_the_math;
975 fn foo() {
976     do_the_math(0);
977 }
978 "#,
979             r#"
980 //- /lib.rs
981 mod foo;
982
983 fn bar(a: u32, b: u32) -> u32 {
984     {
985         let foo = 10;
986         foo * 0 + foo
987     };
988 }
989 //- /foo.rs
990 fn foo() {
991     {
992         let foo = 10;
993         foo * 0 + foo
994     };
995 }
996 "#,
997         );
998     }
999
1000     #[test]
1001     fn inline_callers_recursive() {
1002         cov_mark::check!(inline_into_callers_recursive);
1003         check_assist_not_applicable(
1004             inline_into_callers,
1005             r#"
1006 fn foo$0() {
1007     foo();
1008 }
1009 "#,
1010         );
1011     }
1012
1013     #[test]
1014     fn inline_call_recursive() {
1015         cov_mark::check!(inline_call_recursive);
1016         check_assist_not_applicable(
1017             inline_call,
1018             r#"
1019 fn foo() {
1020     foo$0();
1021 }
1022 "#,
1023         );
1024     }
1025
1026     #[test]
1027     fn inline_call_field_shorthand() {
1028         cov_mark::check!(inline_call_inline_direct_field);
1029         check_assist(
1030             inline_call,
1031             r#"
1032 struct Foo {
1033     field: u32,
1034     field1: u32,
1035     field2: u32,
1036     field3: u32,
1037 }
1038 fn foo(field: u32, field1: u32, val2: u32, val3: u32) -> Foo {
1039     Foo {
1040         field,
1041         field1,
1042         field2: val2,
1043         field3: val3,
1044     }
1045 }
1046 fn main() {
1047     let bar = 0;
1048     let baz = 0;
1049     foo$0(bar, 0, baz, 0);
1050 }
1051 "#,
1052             r#"
1053 struct Foo {
1054     field: u32,
1055     field1: u32,
1056     field2: u32,
1057     field3: u32,
1058 }
1059 fn foo(field: u32, field1: u32, val2: u32, val3: u32) -> Foo {
1060     Foo {
1061         field,
1062         field1,
1063         field2: val2,
1064         field3: val3,
1065     }
1066 }
1067 fn main() {
1068     let bar = 0;
1069     let baz = 0;
1070     Foo {
1071             field: bar,
1072             field1: 0,
1073             field2: baz,
1074             field3: 0,
1075         };
1076 }
1077 "#,
1078         );
1079     }
1080 }