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