]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/inline_call.rs
Remove imports when inlining all calls in a file
[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,
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, ArgListOwner},
15     ted, AstNode, SyntaxNode,
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         CallExprNode::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         CallExprNode::MethodCallExpr(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                     CallExprNode::Call(it) => ast::Expr::CallExpr(it),
227                     CallExprNode::MethodCallExpr(it) => ast::Expr::MethodCallExpr(it),
228                 },
229                 replacement,
230             );
231         },
232     )
233 }
234
235 enum CallExprNode {
236     Call(ast::CallExpr),
237     MethodCallExpr(ast::MethodCallExpr),
238 }
239
240 impl CallExprNode {
241     fn syntax(&self) -> &SyntaxNode {
242         match self {
243             CallExprNode::Call(it) => it.syntax(),
244             CallExprNode::MethodCallExpr(it) => it.syntax(),
245         }
246     }
247 }
248
249 struct CallInfo {
250     node: CallExprNode,
251     arguments: Vec<ast::Expr>,
252     generic_arg_list: Option<ast::GenericArgList>,
253 }
254
255 impl CallInfo {
256     fn from_name_ref(name_ref: ast::NameRef) -> Option<CallInfo> {
257         let parent = name_ref.syntax().parent()?;
258         if let Some(call) = ast::MethodCallExpr::cast(parent.clone()) {
259             let receiver = call.receiver()?;
260             let mut arguments = vec![receiver];
261             arguments.extend(call.arg_list()?.args());
262             Some(CallInfo {
263                 generic_arg_list: call.generic_arg_list(),
264                 node: CallExprNode::MethodCallExpr(call),
265                 arguments,
266             })
267         } else if let Some(segment) = ast::PathSegment::cast(parent) {
268             let path = segment.syntax().parent().and_then(ast::Path::cast)?;
269             let path = path.syntax().parent().and_then(ast::PathExpr::cast)?;
270             let call = path.syntax().parent().and_then(ast::CallExpr::cast)?;
271
272             Some(CallInfo {
273                 arguments: call.arg_list()?.args().collect(),
274                 node: CallExprNode::Call(call),
275                 generic_arg_list: segment.generic_arg_list(),
276             })
277         } else {
278             None
279         }
280     }
281 }
282
283 fn get_fn_params(
284     db: &dyn HirDatabase,
285     function: hir::Function,
286     param_list: &ast::ParamList,
287 ) -> Option<Vec<(ast::Pat, Option<ast::Type>, hir::Param)>> {
288     let mut assoc_fn_params = function.assoc_fn_params(db).into_iter();
289
290     let mut params = Vec::new();
291     if let Some(self_param) = param_list.self_param() {
292         // FIXME this should depend on the receiver as well as the self_param
293         params.push((
294             make::ident_pat(
295                 self_param.amp_token().is_some(),
296                 self_param.mut_token().is_some(),
297                 make::name("this"),
298             )
299             .into(),
300             None,
301             assoc_fn_params.next()?,
302         ));
303     }
304     for param in param_list.params() {
305         params.push((param.pat()?, param.ty(), assoc_fn_params.next()?));
306     }
307
308     Some(params)
309 }
310
311 fn inline(
312     sema: &Semantics<RootDatabase>,
313     function_def_file_id: FileId,
314     function: hir::Function,
315     fn_body: &ast::BlockExpr,
316     params: &[(ast::Pat, Option<ast::Type>, hir::Param)],
317     CallInfo { node, arguments, generic_arg_list }: &CallInfo,
318 ) -> ast::Expr {
319     let body = fn_body.clone_for_update();
320     let usages_for_locals = |local| {
321         Definition::Local(local)
322             .usages(&sema)
323             .all()
324             .references
325             .remove(&function_def_file_id)
326             .unwrap_or_default()
327             .into_iter()
328     };
329     let param_use_nodes: Vec<Vec<_>> = params
330         .iter()
331         .map(|(pat, _, param)| {
332             if !matches!(pat, ast::Pat::IdentPat(pat) if pat.is_simple_ident()) {
333                 return Vec::new();
334             }
335             usages_for_locals(param.as_local(sema.db))
336                 .map(|FileReference { name, range, .. }| match name {
337                     ast::NameLike::NameRef(_) => body
338                         .syntax()
339                         .covering_element(range)
340                         .ancestors()
341                         .nth(3)
342                         .and_then(ast::PathExpr::cast),
343                     _ => None,
344                 })
345                 .collect::<Option<Vec<_>>>()
346                 .unwrap_or_default()
347         })
348         .collect();
349     if function.self_param(sema.db).is_some() {
350         let this = || make::name_ref("this").syntax().clone_for_update();
351         usages_for_locals(params[0].2.as_local(sema.db))
352             .flat_map(|FileReference { name, range, .. }| match name {
353                 ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)),
354                 _ => None,
355             })
356             .for_each(|it| {
357                 ted::replace(it, &this());
358             })
359     }
360     // Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
361     for ((pat, param_ty, _), usages, expr) in izip!(params, param_use_nodes, arguments).rev() {
362         let expr_is_name_ref = matches!(&expr,
363             ast::Expr::PathExpr(expr)
364                 if expr.path().and_then(|path| path.as_single_name_ref()).is_some()
365         );
366         match &*usages {
367             // inline single use closure arguments
368             [usage]
369                 if matches!(expr, ast::Expr::ClosureExpr(_))
370                     && usage.syntax().parent().and_then(ast::Expr::cast).is_some() =>
371             {
372                 cov_mark::hit!(inline_call_inline_closure);
373                 let expr = make::expr_paren(expr.clone());
374                 ted::replace(usage.syntax(), expr.syntax().clone_for_update());
375             }
376             // inline single use literals
377             [usage] if matches!(expr, ast::Expr::Literal(_)) => {
378                 cov_mark::hit!(inline_call_inline_literal);
379                 ted::replace(usage.syntax(), expr.syntax().clone_for_update());
380             }
381             // inline direct local arguments
382             [_, ..] if expr_is_name_ref => {
383                 cov_mark::hit!(inline_call_inline_locals);
384                 usages.into_iter().for_each(|usage| {
385                     ted::replace(usage.syntax(), &expr.syntax().clone_for_update());
386                 });
387             }
388             // cant inline, emit a let statement
389             _ => {
390                 let ty =
391                     sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone());
392                 body.push_front(
393                     make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(),
394                 )
395             }
396         }
397     }
398     if let Some(generic_arg_list) = generic_arg_list.clone() {
399         PathTransform::function_call(
400             &sema.scope(node.syntax()),
401             &sema.scope(fn_body.syntax()),
402             function,
403             generic_arg_list,
404         )
405         .apply(body.syntax());
406     }
407
408     let original_indentation = match node {
409         CallExprNode::Call(it) => it.indent_level(),
410         CallExprNode::MethodCallExpr(it) => it.indent_level(),
411     };
412     body.reindent_to(original_indentation);
413
414     match body.tail_expr() {
415         Some(expr) if body.statements().next().is_none() => expr,
416         _ => ast::Expr::BlockExpr(body),
417     }
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 }