]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/inline_call.rs
Merge #10364
[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, HasArgList},
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                 if let Some(stmt_list) = body.stmt_list() {
393                     stmt_list.push_front(
394                         make::let_stmt(pat.clone(), ty, Some(expr.clone()))
395                             .clone_for_update()
396                             .into(),
397                     )
398                 }
399             }
400         }
401     }
402     if let Some(generic_arg_list) = generic_arg_list.clone() {
403         PathTransform::function_call(
404             &sema.scope(node.syntax()),
405             &sema.scope(fn_body.syntax()),
406             function,
407             generic_arg_list,
408         )
409         .apply(body.syntax());
410     }
411
412     let original_indentation = match node {
413         CallExprNode::Call(it) => it.indent_level(),
414         CallExprNode::MethodCallExpr(it) => it.indent_level(),
415     };
416     body.reindent_to(original_indentation);
417
418     match body.tail_expr() {
419         Some(expr) if body.statements().next().is_none() => expr,
420         _ => ast::Expr::BlockExpr(body),
421     }
422 }
423
424 #[cfg(test)]
425 mod tests {
426     use crate::tests::{check_assist, check_assist_not_applicable};
427
428     use super::*;
429
430     #[test]
431     fn no_args_or_return_value_gets_inlined_without_block() {
432         check_assist(
433             inline_call,
434             r#"
435 fn foo() { println!("Hello, World!"); }
436 fn main() {
437     fo$0o();
438 }
439 "#,
440             r#"
441 fn foo() { println!("Hello, World!"); }
442 fn main() {
443     { println!("Hello, World!"); };
444 }
445 "#,
446         );
447     }
448
449     #[test]
450     fn not_applicable_when_incorrect_number_of_parameters_are_provided() {
451         cov_mark::check!(inline_call_incorrect_number_of_arguments);
452         check_assist_not_applicable(
453             inline_call,
454             r#"
455 fn add(a: u32, b: u32) -> u32 { a + b }
456 fn main() { let x = add$0(42); }
457 "#,
458         );
459     }
460
461     #[test]
462     fn args_with_side_effects() {
463         check_assist(
464             inline_call,
465             r#"
466 fn foo(name: String) {
467     println!("Hello, {}!", name);
468 }
469 fn main() {
470     foo$0(String::from("Michael"));
471 }
472 "#,
473             r#"
474 fn foo(name: String) {
475     println!("Hello, {}!", name);
476 }
477 fn main() {
478     {
479         let name = String::from("Michael");
480         println!("Hello, {}!", name);
481     };
482 }
483 "#,
484         );
485     }
486
487     #[test]
488     fn function_with_multiple_statements() {
489         check_assist(
490             inline_call,
491             r#"
492 fn foo(a: u32, b: u32) -> u32 {
493     let x = a + b;
494     let y = x - b;
495     x * y
496 }
497
498 fn main() {
499     let x = foo$0(1, 2);
500 }
501 "#,
502             r#"
503 fn foo(a: u32, b: u32) -> u32 {
504     let x = a + b;
505     let y = x - b;
506     x * y
507 }
508
509 fn main() {
510     let x = {
511         let b = 2;
512         let x = 1 + b;
513         let y = x - b;
514         x * y
515     };
516 }
517 "#,
518         );
519     }
520
521     #[test]
522     fn function_with_self_param() {
523         check_assist(
524             inline_call,
525             r#"
526 struct Foo(u32);
527
528 impl Foo {
529     fn add(self, a: u32) -> Self {
530         Foo(self.0 + a)
531     }
532 }
533
534 fn main() {
535     let x = Foo::add$0(Foo(3), 2);
536 }
537 "#,
538             r#"
539 struct Foo(u32);
540
541 impl Foo {
542     fn add(self, a: u32) -> Self {
543         Foo(self.0 + a)
544     }
545 }
546
547 fn main() {
548     let x = {
549         let this = Foo(3);
550         Foo(this.0 + 2)
551     };
552 }
553 "#,
554         );
555     }
556
557     #[test]
558     fn method_by_val() {
559         check_assist(
560             inline_call,
561             r#"
562 struct Foo(u32);
563
564 impl Foo {
565     fn add(self, a: u32) -> Self {
566         Foo(self.0 + a)
567     }
568 }
569
570 fn main() {
571     let x = Foo(3).add$0(2);
572 }
573 "#,
574             r#"
575 struct Foo(u32);
576
577 impl Foo {
578     fn add(self, a: u32) -> Self {
579         Foo(self.0 + a)
580     }
581 }
582
583 fn main() {
584     let x = {
585         let this = Foo(3);
586         Foo(this.0 + 2)
587     };
588 }
589 "#,
590         );
591     }
592
593     #[test]
594     fn method_by_ref() {
595         check_assist(
596             inline_call,
597             r#"
598 struct Foo(u32);
599
600 impl Foo {
601     fn add(&self, a: u32) -> Self {
602         Foo(self.0 + a)
603     }
604 }
605
606 fn main() {
607     let x = Foo(3).add$0(2);
608 }
609 "#,
610             r#"
611 struct Foo(u32);
612
613 impl Foo {
614     fn add(&self, a: u32) -> Self {
615         Foo(self.0 + a)
616     }
617 }
618
619 fn main() {
620     let x = {
621         let ref this = Foo(3);
622         Foo(this.0 + 2)
623     };
624 }
625 "#,
626         );
627     }
628
629     #[test]
630     fn method_by_ref_mut() {
631         check_assist(
632             inline_call,
633             r#"
634 struct Foo(u32);
635
636 impl Foo {
637     fn clear(&mut self) {
638         self.0 = 0;
639     }
640 }
641
642 fn main() {
643     let mut foo = Foo(3);
644     foo.clear$0();
645 }
646 "#,
647             r#"
648 struct Foo(u32);
649
650 impl Foo {
651     fn clear(&mut self) {
652         self.0 = 0;
653     }
654 }
655
656 fn main() {
657     let mut foo = Foo(3);
658     {
659         let ref mut this = foo;
660         this.0 = 0;
661     };
662 }
663 "#,
664         );
665     }
666
667     #[test]
668     fn function_multi_use_expr_in_param() {
669         check_assist(
670             inline_call,
671             r#"
672 fn square(x: u32) -> u32 {
673     x * x
674 }
675 fn main() {
676     let x = 51;
677     let y = square$0(10 + x);
678 }
679 "#,
680             r#"
681 fn square(x: u32) -> u32 {
682     x * x
683 }
684 fn main() {
685     let x = 51;
686     let y = {
687         let x = 10 + x;
688         x * x
689     };
690 }
691 "#,
692         );
693     }
694
695     #[test]
696     fn function_use_local_in_param() {
697         cov_mark::check!(inline_call_inline_locals);
698         check_assist(
699             inline_call,
700             r#"
701 fn square(x: u32) -> u32 {
702     x * x
703 }
704 fn main() {
705     let local = 51;
706     let y = square$0(local);
707 }
708 "#,
709             r#"
710 fn square(x: u32) -> u32 {
711     x * x
712 }
713 fn main() {
714     let local = 51;
715     let y = local * local;
716 }
717 "#,
718         );
719     }
720
721     #[test]
722     fn method_in_impl() {
723         check_assist(
724             inline_call,
725             r#"
726 struct Foo;
727 impl Foo {
728     fn foo(&self) {
729         self;
730         self;
731     }
732     fn bar(&self) {
733         self.foo$0();
734     }
735 }
736 "#,
737             r#"
738 struct Foo;
739 impl Foo {
740     fn foo(&self) {
741         self;
742         self;
743     }
744     fn bar(&self) {
745         {
746             let ref this = self;
747             this;
748             this;
749         };
750     }
751 }
752 "#,
753         );
754     }
755
756     #[test]
757     fn wraps_closure_in_paren() {
758         cov_mark::check!(inline_call_inline_closure);
759         check_assist(
760             inline_call,
761             r#"
762 fn foo(x: fn()) {
763     x();
764 }
765
766 fn main() {
767     foo$0(|| {})
768 }
769 "#,
770             r#"
771 fn foo(x: fn()) {
772     x();
773 }
774
775 fn main() {
776     {
777         (|| {})();
778     }
779 }
780 "#,
781         );
782         check_assist(
783             inline_call,
784             r#"
785 fn foo(x: fn()) {
786     x();
787 }
788
789 fn main() {
790     foo$0(main)
791 }
792 "#,
793             r#"
794 fn foo(x: fn()) {
795     x();
796 }
797
798 fn main() {
799     {
800         main();
801     }
802 }
803 "#,
804         );
805     }
806
807     #[test]
808     fn inline_single_literal_expr() {
809         cov_mark::check!(inline_call_inline_literal);
810         check_assist(
811             inline_call,
812             r#"
813 fn foo(x: u32) -> u32{
814     x
815 }
816
817 fn main() {
818     foo$0(222);
819 }
820 "#,
821             r#"
822 fn foo(x: u32) -> u32{
823     x
824 }
825
826 fn main() {
827     222;
828 }
829 "#,
830         );
831     }
832
833     #[test]
834     fn inline_emits_type_for_coercion() {
835         check_assist(
836             inline_call,
837             r#"
838 fn foo(x: *const u32) -> u32 {
839     x as u32
840 }
841
842 fn main() {
843     foo$0(&222);
844 }
845 "#,
846             r#"
847 fn foo(x: *const u32) -> u32 {
848     x as u32
849 }
850
851 fn main() {
852     {
853         let x: *const u32 = &222;
854         x as u32
855     };
856 }
857 "#,
858         );
859     }
860
861     // FIXME: const generics aren't being substituted, this is blocked on better support for them
862     #[test]
863     fn inline_substitutes_generics() {
864         check_assist(
865             inline_call,
866             r#"
867 fn foo<T, const N: usize>() {
868     bar::<T, N>()
869 }
870
871 fn bar<U, const M: usize>() {}
872
873 fn main() {
874     foo$0::<usize, {0}>();
875 }
876 "#,
877             r#"
878 fn foo<T, const N: usize>() {
879     bar::<T, N>()
880 }
881
882 fn bar<U, const M: usize>() {}
883
884 fn main() {
885     bar::<usize, N>();
886 }
887 "#,
888         );
889     }
890
891     #[test]
892     fn inline_callers() {
893         check_assist(
894             inline_into_callers,
895             r#"
896 fn do_the_math$0(b: u32) -> u32 {
897     let foo = 10;
898     foo * b + foo
899 }
900 fn foo() {
901     do_the_math(0);
902     let bar = 10;
903     do_the_math(bar);
904 }
905 "#,
906             r#"
907
908 fn foo() {
909     {
910         let foo = 10;
911         foo * 0 + foo
912     };
913     let bar = 10;
914     {
915         let foo = 10;
916         foo * bar + foo
917     };
918 }
919 "#,
920         );
921     }
922
923     #[test]
924     fn inline_callers_across_files() {
925         check_assist(
926             inline_into_callers,
927             r#"
928 //- /lib.rs
929 mod foo;
930 fn do_the_math$0(b: u32) -> u32 {
931     let foo = 10;
932     foo * b + foo
933 }
934 //- /foo.rs
935 use super::do_the_math;
936 fn foo() {
937     do_the_math(0);
938     let bar = 10;
939     do_the_math(bar);
940 }
941 "#,
942             r#"
943 //- /lib.rs
944 mod foo;
945
946 //- /foo.rs
947 fn foo() {
948     {
949         let foo = 10;
950         foo * 0 + foo
951     };
952     let bar = 10;
953     {
954         let foo = 10;
955         foo * bar + foo
956     };
957 }
958 "#,
959         );
960     }
961
962     #[test]
963     fn inline_callers_across_files_with_def_file() {
964         check_assist(
965             inline_into_callers,
966             r#"
967 //- /lib.rs
968 mod foo;
969 fn do_the_math$0(b: u32) -> u32 {
970     let foo = 10;
971     foo * b + foo
972 }
973 fn bar(a: u32, b: u32) -> u32 {
974     do_the_math(0);
975 }
976 //- /foo.rs
977 use super::do_the_math;
978 fn foo() {
979     do_the_math(0);
980 }
981 "#,
982             r#"
983 //- /lib.rs
984 mod foo;
985
986 fn bar(a: u32, b: u32) -> u32 {
987     {
988         let foo = 10;
989         foo * 0 + foo
990     };
991 }
992 //- /foo.rs
993 fn foo() {
994     {
995         let foo = 10;
996         foo * 0 + foo
997     };
998 }
999 "#,
1000         );
1001     }
1002
1003     #[test]
1004     fn inline_callers_recursive() {
1005         cov_mark::check!(inline_into_callers_recursive);
1006         check_assist_not_applicable(
1007             inline_into_callers,
1008             r#"
1009 fn foo$0() {
1010     foo();
1011 }
1012 "#,
1013         );
1014     }
1015
1016     #[test]
1017     fn inline_call_recursive() {
1018         cov_mark::check!(inline_call_recursive);
1019         check_assist_not_applicable(
1020             inline_call,
1021             r#"
1022 fn foo() {
1023     foo$0();
1024 }
1025 "#,
1026         );
1027     }
1028 }