1 use std::collections::BTreeSet;
2
3 use ast::make;
4 use either::Either;
5 use hir::{db::HirDatabase, PathResolution, Semantics, TypeInfo};
6 use ide_db::{
7 base_db::{FileId, FileRange},
8 defs::Definition,
9 imports::insert_use::remove_path_if_in_use_stmt,
10 path_transform::PathTransform,
11 search::{FileReference, SearchScope},
12 source_change::SourceChangeBuilder,
13 syntax_helpers::{insert_whitespace_into_node::insert_ws_into, node_ext::expr_as_name_ref},
14 RootDatabase,
15 };
16 use itertools::{izip, Itertools};
17 use syntax::{
18 ast::{self, edit_in_place::Indent, HasArgList, PathExpr},
19 ted, AstNode, NodeOrToken, SyntaxKind,
20 };
21
22 use crate::{
23 assist_context::{AssistContext, Assists},
24 AssistId, AssistKind,
25 };
26
27 // Assist: inline_into_callers
28 //
29 // Inline a function or method body into all of its callers where possible, creating a `let` statement per parameter
30 // unless the parameter can be inlined. The parameter will be inlined either if it the supplied argument is a simple local
31 // or if the parameter is only accessed inside the function body once.
32 // If all calls can be inlined the function will be removed.
33 //
34 // ```
35 // fn print(_: &str) {}
36 // fn foo$0(word: &str) {
37 // if !word.is_empty() {
38 // print(word);
39 // }
40 // }
41 // fn bar() {
42 // foo("안녕하세요");
43 // foo("여러분");
44 // }
45 // ```
46 // ->
47 // ```
48 // fn print(_: &str) {}
49 //
50 // fn bar() {
51 // {
52 // let word = "안녕하세요";
53 // if !word.is_empty() {
54 // print(word);
55 // }
56 // };
57 // {
58 // let word = "여러분";
59 // if !word.is_empty() {
60 // print(word);
61 // }
62 // };
63 // }
64 // ```
inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()>65 pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
66 let def_file = ctx.file_id();
67 let name = ctx.find_node_at_offset::<ast::Name>()?;
68 let ast_func = name.syntax().parent().and_then(ast::Fn::cast)?;
69 let func_body = ast_func.body()?;
70 let param_list = ast_func.param_list()?;
71
72 let function = ctx.sema.to_def(&ast_func)?;
73
74 let params = get_fn_params(ctx.sema.db, function, ¶m_list)?;
75
76 let usages = Definition::Function(function).usages(&ctx.sema);
77 if !usages.at_least_one() {
78 return None;
79 }
80
81 let is_recursive_fn = usages
82 .clone()
83 .in_scope(SearchScope::file_range(FileRange {
84 file_id: def_file,
85 range: func_body.syntax().text_range(),
86 }))
87 .at_least_one();
88 if is_recursive_fn {
89 cov_mark::hit!(inline_into_callers_recursive);
90 return None;
91 }
92
93 acc.add(
94 AssistId("inline_into_callers", AssistKind::RefactorInline),
95 "Inline into all callers",
96 name.syntax().text_range(),
97 |builder| {
98 let mut usages = usages.all();
99 let current_file_usage = usages.references.remove(&def_file);
100
101 let mut remove_def = true;
102 let mut inline_refs_for_file = |file_id, refs: Vec<FileReference>| {
103 builder.edit_file(file_id);
104 let count = refs.len();
105 // The collects are required as we are otherwise iterating while mutating ♀️♂️
106 let (name_refs, name_refs_use) = split_refs_and_uses(builder, refs, Some);
107 let call_infos: Vec<_> = name_refs
108 .into_iter()
109 .filter_map(CallInfo::from_name_ref)
110 .map(|call_info| {
111 let mut_node = builder.make_syntax_mut(call_info.node.syntax().clone());
112 (call_info, mut_node)
113 })
114 .collect();
115 let replaced = call_infos
116 .into_iter()
117 .map(|(call_info, mut_node)| {
118 let replacement =
119 inline(&ctx.sema, def_file, function, &func_body, ¶ms, &call_info);
120 ted::replace(mut_node, replacement.syntax());
121 })
122 .count();
123 if replaced + name_refs_use.len() == count {
124 // we replaced all usages in this file, so we can remove the imports
125 name_refs_use.iter().for_each(remove_path_if_in_use_stmt);
126 } else {
127 remove_def = false;
128 }
129 };
130 for (file_id, refs) in usages.into_iter() {
131 inline_refs_for_file(file_id, refs);
132 }
133 match current_file_usage {
134 Some(refs) => inline_refs_for_file(def_file, refs),
135 None => builder.edit_file(def_file),
136 }
137 if remove_def {
138 builder.delete(ast_func.syntax().text_range());
139 }
140 },
141 )
142 }
143
split_refs_and_uses<T: ast::AstNode>( builder: &mut SourceChangeBuilder, iter: impl IntoIterator<Item = FileReference>, mut map_ref: impl FnMut(ast::NameRef) -> Option<T>, ) -> (Vec<T>, Vec<ast::Path>)144 pub(super) fn split_refs_and_uses<T: ast::AstNode>(
145 builder: &mut SourceChangeBuilder,
146 iter: impl IntoIterator<Item = FileReference>,
147 mut map_ref: impl FnMut(ast::NameRef) -> Option<T>,
148 ) -> (Vec<T>, Vec<ast::Path>) {
149 iter.into_iter()
150 .filter_map(|file_ref| match file_ref.name {
151 ast::NameLike::NameRef(name_ref) => Some(name_ref),
152 _ => None,
153 })
154 .filter_map(|name_ref| match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) {
155 Some(use_tree) => builder.make_mut(use_tree).path().map(Either::Right),
156 None => map_ref(name_ref).map(Either::Left),
157 })
158 .partition_map(|either| either)
159 }
160
161 // Assist: inline_call
162 //
163 // Inlines a function or method body creating a `let` statement per parameter unless the parameter
164 // can be inlined. The parameter will be inlined either if it the supplied argument is a simple local
165 // or if the parameter is only accessed inside the function body once.
166 //
167 // ```
168 // # //- minicore: option
169 // fn foo(name: Option<&str>) {
170 // let name = name.unwrap$0();
171 // }
172 // ```
173 // ->
174 // ```
175 // fn foo(name: Option<&str>) {
176 // let name = match name {
177 // Some(val) => val,
178 // None => panic!("called `Option::unwrap()` on a `None` value"),
179 // };
180 // }
181 // ```
inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()>182 pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
183 let name_ref: ast::NameRef = ctx.find_node_at_offset()?;
184 let call_info = CallInfo::from_name_ref(name_ref.clone())?;
185 let (function, label) = match &call_info.node {
186 ast::CallableExpr::Call(call) => {
187 let path = match call.expr()? {
188 ast::Expr::PathExpr(path) => path.path(),
189 _ => None,
190 }?;
191 let function = match ctx.sema.resolve_path(&path)? {
192 PathResolution::Def(hir::ModuleDef::Function(f)) => f,
193 _ => return None,
194 };
195 (function, format!("Inline `{path}`"))
196 }
197 ast::CallableExpr::MethodCall(call) => {
198 (ctx.sema.resolve_method_call(call)?, format!("Inline `{name_ref}`"))
199 }
200 };
201
202 let fn_source = ctx.sema.source(function)?;
203 let fn_body = fn_source.value.body()?;
204 let param_list = fn_source.value.param_list()?;
205
206 let FileRange { file_id, range } = fn_source.syntax().original_file_range(ctx.sema.db);
207 if file_id == ctx.file_id() && range.contains(ctx.offset()) {
208 cov_mark::hit!(inline_call_recursive);
209 return None;
210 }
211 let params = get_fn_params(ctx.sema.db, function, ¶m_list)?;
212
213 if call_info.arguments.len() != params.len() {
214 // Can't inline the function because they've passed the wrong number of
215 // arguments to this function
216 cov_mark::hit!(inline_call_incorrect_number_of_arguments);
217 return None;
218 }
219
220 let syntax = call_info.node.syntax().clone();
221 acc.add(
222 AssistId("inline_call", AssistKind::RefactorInline),
223 label,
224 syntax.text_range(),
225 |builder| {
226 let replacement = inline(&ctx.sema, file_id, function, &fn_body, ¶ms, &call_info);
227
228 builder.replace_ast(
229 match call_info.node {
230 ast::CallableExpr::Call(it) => ast::Expr::CallExpr(it),
231 ast::CallableExpr::MethodCall(it) => ast::Expr::MethodCallExpr(it),
232 },
233 replacement,
234 );
235 },
236 )
237 }
238
239 struct CallInfo {
240 node: ast::CallableExpr,
241 arguments: Vec<ast::Expr>,
242 generic_arg_list: Option<ast::GenericArgList>,
243 }
244
245 impl CallInfo {
from_name_ref(name_ref: ast::NameRef) -> Option<CallInfo>246 fn from_name_ref(name_ref: ast::NameRef) -> Option<CallInfo> {
247 let parent = name_ref.syntax().parent()?;
248 if let Some(call) = ast::MethodCallExpr::cast(parent.clone()) {
249 let receiver = call.receiver()?;
250 let mut arguments = vec![receiver];
251 arguments.extend(call.arg_list()?.args());
252 Some(CallInfo {
253 generic_arg_list: call.generic_arg_list(),
254 node: ast::CallableExpr::MethodCall(call),
255 arguments,
256 })
257 } else if let Some(segment) = ast::PathSegment::cast(parent) {
258 let path = segment.syntax().parent().and_then(ast::Path::cast)?;
259 let path = path.syntax().parent().and_then(ast::PathExpr::cast)?;
260 let call = path.syntax().parent().and_then(ast::CallExpr::cast)?;
261
262 Some(CallInfo {
263 arguments: call.arg_list()?.args().collect(),
264 node: ast::CallableExpr::Call(call),
265 generic_arg_list: segment.generic_arg_list(),
266 })
267 } else {
268 None
269 }
270 }
271 }
272
get_fn_params( db: &dyn HirDatabase, function: hir::Function, param_list: &ast::ParamList, ) -> Option<Vec<(ast::Pat, Option<ast::Type>, hir::Param)>>273 fn get_fn_params(
274 db: &dyn HirDatabase,
275 function: hir::Function,
276 param_list: &ast::ParamList,
277 ) -> Option<Vec<(ast::Pat, Option<ast::Type>, hir::Param)>> {
278 let mut assoc_fn_params = function.assoc_fn_params(db).into_iter();
279
280 let mut params = Vec::new();
281 if let Some(self_param) = param_list.self_param() {
282 // FIXME this should depend on the receiver as well as the self_param
283 params.push((
284 make::ident_pat(
285 self_param.amp_token().is_some(),
286 self_param.mut_token().is_some(),
287 make::name("this"),
288 )
289 .into(),
290 None,
291 assoc_fn_params.next()?,
292 ));
293 }
294 for param in param_list.params() {
295 params.push((param.pat()?, param.ty(), assoc_fn_params.next()?));
296 }
297
298 Some(params)
299 }
300
301 fn inline(
302 sema: &Semantics<'_, RootDatabase>,
303 function_def_file_id: FileId,
304 function: hir::Function,
305 fn_body: &ast::BlockExpr,
306 params: &[(ast::Pat, Option<ast::Type>, hir::Param)],
307 CallInfo { node, arguments, generic_arg_list }: &CallInfo,
308 ) -> ast::Expr {
309 let body = if sema.hir_file_for(fn_body.syntax()).is_macro() {
310 cov_mark::hit!(inline_call_defined_in_macro);
311 if let Some(body) = ast::BlockExpr::cast(insert_ws_into(fn_body.syntax().clone())) {
312 body
313 } else {
314 fn_body.clone_for_update()
315 }
316 } else {
317 fn_body.clone_for_update()
318 };
319 if let Some(imp) = body.syntax().ancestors().find_map(ast::Impl::cast) {
320 if !node.syntax().ancestors().any(|anc| &anc == imp.syntax()) {
321 if let Some(t) = imp.self_ty() {
322 body.syntax()
323 .descendants_with_tokens()
324 .filter_map(NodeOrToken::into_token)
325 .filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW)
326 .for_each(|tok| ted::replace(tok, t.syntax()));
327 }
328 }
329 }
330 let usages_for_locals = |local| {
331 Definition::Local(local)
332 .usages(sema)
333 .all()
334 .references
335 .remove(&function_def_file_id)
336 .unwrap_or_default()
337 .into_iter()
338 };
339 let param_use_nodes: Vec<Vec<_>> = params
340 .iter()
341 .map(|(pat, _, param)| {
342 if !matches!(pat, ast::Pat::IdentPat(pat) if pat.is_simple_ident()) {
343 return Vec::new();
344 }
345 // FIXME: we need to fetch all locals declared in the parameter here
346 // not only the local if it is a simple binding
347 match param.as_local(sema.db) {
348 Some(l) => usages_for_locals(l)
349 .map(|FileReference { name, range, .. }| match name {
350 ast::NameLike::NameRef(_) => body
351 .syntax()
352 .covering_element(range)
353 .ancestors()
354 .nth(3)
355 .and_then(ast::PathExpr::cast),
356 _ => None,
357 })
358 .collect::<Option<Vec<_>>>()
359 .unwrap_or_default(),
360 None => Vec::new(),
361 }
362 })
363 .collect();
364
365 if function.self_param(sema.db).is_some() {
366 let this = || make::name_ref("this").syntax().clone_for_update().first_token().unwrap();
367 if let Some(self_local) = params[0].2.as_local(sema.db) {
368 usages_for_locals(self_local)
369 .filter_map(|FileReference { name, range, .. }| match name {
370 ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)),
371 _ => None,
372 })
373 .for_each(|it| {
374 ted::replace(it, &this());
375 })
376 }
377 }
378
379 let mut func_let_vars: BTreeSet<String> = BTreeSet::new();
380
381 // grab all of the local variable declarations in the function
382 for stmt in fn_body.statements() {
383 if let Some(let_stmt) = ast::LetStmt::cast(stmt.syntax().to_owned()) {
384 for has_token in let_stmt.syntax().children_with_tokens() {
385 if let Some(node) = has_token.as_node() {
386 if let Some(ident_pat) = ast::IdentPat::cast(node.to_owned()) {
387 func_let_vars.insert(ident_pat.syntax().text().to_string());
388 }
389 }
390 }
391 }
392 }
393
394 // Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
395 for ((pat, param_ty, _), usages, expr) in izip!(params, param_use_nodes, arguments).rev() {
396 // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors
397 let usages: &[ast::PathExpr] = &usages;
398 let expr: &ast::Expr = expr;
399
400 let insert_let_stmt = || {
401 let ty = sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone());
402 if let Some(stmt_list) = body.stmt_list() {
403 stmt_list.push_front(
404 make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(),
405 )
406 }
407 };
408
409 // check if there is a local var in the function that conflicts with parameter
410 // if it does then emit a let statement and continue
411 if func_let_vars.contains(&expr.syntax().text().to_string()) {
412 insert_let_stmt();
413 continue;
414 }
415
416 let inline_direct = |usage, replacement: &ast::Expr| {
417 if let Some(field) = path_expr_as_record_field(usage) {
418 cov_mark::hit!(inline_call_inline_direct_field);
419 field.replace_expr(replacement.clone_for_update());
420 } else {
421 ted::replace(usage.syntax(), &replacement.syntax().clone_for_update());
422 }
423 };
424
425 match usages {
426 // inline single use closure arguments
427 [usage]
428 if matches!(expr, ast::Expr::ClosureExpr(_))
429 && usage.syntax().parent().and_then(ast::Expr::cast).is_some() =>
430 {
431 cov_mark::hit!(inline_call_inline_closure);
432 let expr = make::expr_paren(expr.clone());
433 inline_direct(usage, &expr);
434 }
435 // inline single use literals
436 [usage] if matches!(expr, ast::Expr::Literal(_)) => {
437 cov_mark::hit!(inline_call_inline_literal);
438 inline_direct(usage, expr);
439 }
440 // inline direct local arguments
441 [_, ..] if expr_as_name_ref(expr).is_some() => {
442 cov_mark::hit!(inline_call_inline_locals);
443 usages.iter().for_each(|usage| inline_direct(usage, expr));
444 }
445 // can't inline, emit a let statement
446 _ => {
447 insert_let_stmt();
448 }
449 }
450 }
451
452 if let Some(generic_arg_list) = generic_arg_list.clone() {
453 if let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax()))
454 {
455 PathTransform::function_call(target, source, function, generic_arg_list)
456 .apply(body.syntax());
457 }
458 }
459
460 let original_indentation = match node {
461 ast::CallableExpr::Call(it) => it.indent_level(),
462 ast::CallableExpr::MethodCall(it) => it.indent_level(),
463 };
464 body.reindent_to(original_indentation);
465
466 match body.tail_expr() {
467 Some(expr) if body.statements().next().is_none() => expr,
468 _ => match node
469 .syntax()
470 .parent()
471 .and_then(ast::BinExpr::cast)
472 .and_then(|bin_expr| bin_expr.lhs())
473 {
474 Some(lhs) if lhs.syntax() == node.syntax() => {
475 make::expr_paren(ast::Expr::BlockExpr(body)).clone_for_update()
476 }
477 _ => ast::Expr::BlockExpr(body),
478 },
479 }
480 }
481
path_expr_as_record_field(usage: &PathExpr) -> Option<ast::RecordExprField>482 fn path_expr_as_record_field(usage: &PathExpr) -> Option<ast::RecordExprField> {
483 let path = usage.path()?;
484 let name_ref = path.as_single_name_ref()?;
485 ast::RecordExprField::for_name_ref(&name_ref)
486 }
487
488 #[cfg(test)]
489 mod tests {
490 use crate::tests::{check_assist, check_assist_not_applicable};
491
492 use super::*;
493
494 #[test]
no_args_or_return_value_gets_inlined_without_block()495 fn no_args_or_return_value_gets_inlined_without_block() {
496 check_assist(
497 inline_call,
498 r#"
499 fn foo() { println!("Hello, World!"); }
500 fn main() {
501 fo$0o();
502 }
503 "#,
504 r#"
505 fn foo() { println!("Hello, World!"); }
506 fn main() {
507 { println!("Hello, World!"); };
508 }
509 "#,
510 );
511 }
512
513 #[test]
not_applicable_when_incorrect_number_of_parameters_are_provided()514 fn not_applicable_when_incorrect_number_of_parameters_are_provided() {
515 cov_mark::check!(inline_call_incorrect_number_of_arguments);
516 check_assist_not_applicable(
517 inline_call,
518 r#"
519 fn add(a: u32, b: u32) -> u32 { a + b }
520 fn main() { let x = add$0(42); }
521 "#,
522 );
523 }
524
525 #[test]
args_with_side_effects()526 fn args_with_side_effects() {
527 check_assist(
528 inline_call,
529 r#"
530 fn foo(name: String) {
531 println!("Hello, {}!", name);
532 }
533 fn main() {
534 foo$0(String::from("Michael"));
535 }
536 "#,
537 r#"
538 fn foo(name: String) {
539 println!("Hello, {}!", name);
540 }
541 fn main() {
542 {
543 let name = String::from("Michael");
544 println!("Hello, {}!", name);
545 };
546 }
547 "#,
548 );
549 }
550
551 #[test]
function_with_multiple_statements()552 fn function_with_multiple_statements() {
553 check_assist(
554 inline_call,
555 r#"
556 fn foo(a: u32, b: u32) -> u32 {
557 let x = a + b;
558 let y = x - b;
559 x * y
560 }
561
562 fn main() {
563 let x = foo$0(1, 2);
564 }
565 "#,
566 r#"
567 fn foo(a: u32, b: u32) -> u32 {
568 let x = a + b;
569 let y = x - b;
570 x * y
571 }
572
573 fn main() {
574 let x = {
575 let b = 2;
576 let x = 1 + b;
577 let y = x - b;
578 x * y
579 };
580 }
581 "#,
582 );
583 }
584
585 #[test]
function_with_self_param()586 fn function_with_self_param() {
587 check_assist(
588 inline_call,
589 r#"
590 struct Foo(u32);
591
592 impl Foo {
593 fn add(self, a: u32) -> Self {
594 Foo(self.0 + a)
595 }
596 }
597
598 fn main() {
599 let x = Foo::add$0(Foo(3), 2);
600 }
601 "#,
602 r#"
603 struct Foo(u32);
604
605 impl Foo {
606 fn add(self, a: u32) -> Self {
607 Foo(self.0 + a)
608 }
609 }
610
611 fn main() {
612 let x = {
613 let this = Foo(3);
614 Foo(this.0 + 2)
615 };
616 }
617 "#,
618 );
619 }
620
621 #[test]
method_by_val()622 fn method_by_val() {
623 check_assist(
624 inline_call,
625 r#"
626 struct Foo(u32);
627
628 impl Foo {
629 fn add(self, a: u32) -> Self {
630 Foo(self.0 + a)
631 }
632 }
633
634 fn main() {
635 let x = Foo(3).add$0(2);
636 }
637 "#,
638 r#"
639 struct Foo(u32);
640
641 impl Foo {
642 fn add(self, a: u32) -> Self {
643 Foo(self.0 + a)
644 }
645 }
646
647 fn main() {
648 let x = {
649 let this = Foo(3);
650 Foo(this.0 + 2)
651 };
652 }
653 "#,
654 );
655 }
656
657 #[test]
method_by_ref()658 fn method_by_ref() {
659 check_assist(
660 inline_call,
661 r#"
662 struct Foo(u32);
663
664 impl Foo {
665 fn add(&self, a: u32) -> Self {
666 Foo(self.0 + a)
667 }
668 }
669
670 fn main() {
671 let x = Foo(3).add$0(2);
672 }
673 "#,
674 r#"
675 struct Foo(u32);
676
677 impl Foo {
678 fn add(&self, a: u32) -> Self {
679 Foo(self.0 + a)
680 }
681 }
682
683 fn main() {
684 let x = {
685 let ref this = Foo(3);
686 Foo(this.0 + 2)
687 };
688 }
689 "#,
690 );
691 }
692
693 #[test]
generic_method_by_ref()694 fn generic_method_by_ref() {
695 check_assist(
696 inline_call,
697 r#"
698 struct Foo(u32);
699
700 impl Foo {
701 fn add<T>(&self, a: u32) -> Self {
702 Foo(self.0 + a)
703 }
704 }
705
706 fn main() {
707 let x = Foo(3).add$0::<usize>(2);
708 }
709 "#,
710 r#"
711 struct Foo(u32);
712
713 impl Foo {
714 fn add<T>(&self, a: u32) -> Self {
715 Foo(self.0 + a)
716 }
717 }
718
719 fn main() {
720 let x = {
721 let ref this = Foo(3);
722 Foo(this.0 + 2)
723 };
724 }
725 "#,
726 );
727 }
728
729 #[test]
method_by_ref_mut()730 fn method_by_ref_mut() {
731 check_assist(
732 inline_call,
733 r#"
734 struct Foo(u32);
735
736 impl Foo {
737 fn clear(&mut self) {
738 self.0 = 0;
739 }
740 }
741
742 fn main() {
743 let mut foo = Foo(3);
744 foo.clear$0();
745 }
746 "#,
747 r#"
748 struct Foo(u32);
749
750 impl Foo {
751 fn clear(&mut self) {
752 self.0 = 0;
753 }
754 }
755
756 fn main() {
757 let mut foo = Foo(3);
758 {
759 let ref mut this = foo;
760 this.0 = 0;
761 };
762 }
763 "#,
764 );
765 }
766
767 #[test]
function_multi_use_expr_in_param()768 fn function_multi_use_expr_in_param() {
769 check_assist(
770 inline_call,
771 r#"
772 fn square(x: u32) -> u32 {
773 x * x
774 }
775 fn main() {
776 let x = 51;
777 let y = square$0(10 + x);
778 }
779 "#,
780 r#"
781 fn square(x: u32) -> u32 {
782 x * x
783 }
784 fn main() {
785 let x = 51;
786 let y = {
787 let x = 10 + x;
788 x * x
789 };
790 }
791 "#,
792 );
793 }
794
795 #[test]
function_use_local_in_param()796 fn function_use_local_in_param() {
797 cov_mark::check!(inline_call_inline_locals);
798 check_assist(
799 inline_call,
800 r#"
801 fn square(x: u32) -> u32 {
802 x * x
803 }
804 fn main() {
805 let local = 51;
806 let y = square$0(local);
807 }
808 "#,
809 r#"
810 fn square(x: u32) -> u32 {
811 x * x
812 }
813 fn main() {
814 let local = 51;
815 let y = local * local;
816 }
817 "#,
818 );
819 }
820
821 #[test]
method_in_impl()822 fn method_in_impl() {
823 check_assist(
824 inline_call,
825 r#"
826 struct Foo;
827 impl Foo {
828 fn foo(&self) {
829 self;
830 self;
831 }
832 fn bar(&self) {
833 self.foo$0();
834 }
835 }
836 "#,
837 r#"
838 struct Foo;
839 impl Foo {
840 fn foo(&self) {
841 self;
842 self;
843 }
844 fn bar(&self) {
845 {
846 let ref this = self;
847 this;
848 this;
849 };
850 }
851 }
852 "#,
853 );
854 }
855
856 #[test]
wraps_closure_in_paren()857 fn wraps_closure_in_paren() {
858 cov_mark::check!(inline_call_inline_closure);
859 check_assist(
860 inline_call,
861 r#"
862 fn foo(x: fn()) {
863 x();
864 }
865
866 fn main() {
867 foo$0(|| {})
868 }
869 "#,
870 r#"
871 fn foo(x: fn()) {
872 x();
873 }
874
875 fn main() {
876 {
877 (|| {})();
878 }
879 }
880 "#,
881 );
882 check_assist(
883 inline_call,
884 r#"
885 fn foo(x: fn()) {
886 x();
887 }
888
889 fn main() {
890 foo$0(main)
891 }
892 "#,
893 r#"
894 fn foo(x: fn()) {
895 x();
896 }
897
898 fn main() {
899 {
900 main();
901 }
902 }
903 "#,
904 );
905 }
906
907 #[test]
inline_single_literal_expr()908 fn inline_single_literal_expr() {
909 cov_mark::check!(inline_call_inline_literal);
910 check_assist(
911 inline_call,
912 r#"
913 fn foo(x: u32) -> u32{
914 x
915 }
916
917 fn main() {
918 foo$0(222);
919 }
920 "#,
921 r#"
922 fn foo(x: u32) -> u32{
923 x
924 }
925
926 fn main() {
927 222;
928 }
929 "#,
930 );
931 }
932
933 #[test]
inline_emits_type_for_coercion()934 fn inline_emits_type_for_coercion() {
935 check_assist(
936 inline_call,
937 r#"
938 fn foo(x: *const u32) -> u32 {
939 x as u32
940 }
941
942 fn main() {
943 foo$0(&222);
944 }
945 "#,
946 r#"
947 fn foo(x: *const u32) -> u32 {
948 x as u32
949 }
950
951 fn main() {
952 {
953 let x: *const u32 = &222;
954 x as u32
955 };
956 }
957 "#,
958 );
959 }
960
961 #[test]
inline_substitutes_generics()962 fn inline_substitutes_generics() {
963 check_assist(
964 inline_call,
965 r#"
966 fn foo<T, const N: usize>() {
967 bar::<T, N>()
968 }
969
970 fn bar<U, const M: usize>() {}
971
972 fn main() {
973 foo$0::<usize, {0}>();
974 }
975 "#,
976 r#"
977 fn foo<T, const N: usize>() {
978 bar::<T, N>()
979 }
980
981 fn bar<U, const M: usize>() {}
982
983 fn main() {
984 bar::<usize, {0}>();
985 }
986 "#,
987 );
988 }
989
990 #[test]
inline_callers()991 fn inline_callers() {
992 check_assist(
993 inline_into_callers,
994 r#"
995 fn do_the_math$0(b: u32) -> u32 {
996 let foo = 10;
997 foo * b + foo
998 }
999 fn foo() {
1000 do_the_math(0);
1001 let bar = 10;
1002 do_the_math(bar);
1003 }
1004 "#,
1005 r#"
1006
1007 fn foo() {
1008 {
1009 let foo = 10;
1010 foo * 0 + foo
1011 };
1012 let bar = 10;
1013 {
1014 let foo = 10;
1015 foo * bar + foo
1016 };
1017 }
1018 "#,
1019 );
1020 }
1021
1022 #[test]
inline_callers_across_files()1023 fn inline_callers_across_files() {
1024 check_assist(
1025 inline_into_callers,
1026 r#"
1027 //- /lib.rs
1028 mod foo;
1029 fn do_the_math$0(b: u32) -> u32 {
1030 let foo = 10;
1031 foo * b + foo
1032 }
1033 //- /foo.rs
1034 use super::do_the_math;
1035 fn foo() {
1036 do_the_math(0);
1037 let bar = 10;
1038 do_the_math(bar);
1039 }
1040 "#,
1041 r#"
1042 //- /lib.rs
1043 mod foo;
1044
1045 //- /foo.rs
1046 fn foo() {
1047 {
1048 let foo = 10;
1049 foo * 0 + foo
1050 };
1051 let bar = 10;
1052 {
1053 let foo = 10;
1054 foo * bar + foo
1055 };
1056 }
1057 "#,
1058 );
1059 }
1060
1061 #[test]
inline_callers_across_files_with_def_file()1062 fn inline_callers_across_files_with_def_file() {
1063 check_assist(
1064 inline_into_callers,
1065 r#"
1066 //- /lib.rs
1067 mod foo;
1068 fn do_the_math$0(b: u32) -> u32 {
1069 let foo = 10;
1070 foo * b + foo
1071 }
1072 fn bar(a: u32, b: u32) -> u32 {
1073 do_the_math(0);
1074 }
1075 //- /foo.rs
1076 use super::do_the_math;
1077 fn foo() {
1078 do_the_math(0);
1079 }
1080 "#,
1081 r#"
1082 //- /lib.rs
1083 mod foo;
1084
1085 fn bar(a: u32, b: u32) -> u32 {
1086 {
1087 let foo = 10;
1088 foo * 0 + foo
1089 };
1090 }
1091 //- /foo.rs
1092 fn foo() {
1093 {
1094 let foo = 10;
1095 foo * 0 + foo
1096 };
1097 }
1098 "#,
1099 );
1100 }
1101
1102 #[test]
inline_callers_recursive()1103 fn inline_callers_recursive() {
1104 cov_mark::check!(inline_into_callers_recursive);
1105 check_assist_not_applicable(
1106 inline_into_callers,
1107 r#"
1108 fn foo$0() {
1109 foo();
1110 }
1111 "#,
1112 );
1113 }
1114
1115 #[test]
inline_call_recursive()1116 fn inline_call_recursive() {
1117 cov_mark::check!(inline_call_recursive);
1118 check_assist_not_applicable(
1119 inline_call,
1120 r#"
1121 fn foo() {
1122 foo$0();
1123 }
1124 "#,
1125 );
1126 }
1127
1128 #[test]
inline_call_field_shorthand()1129 fn inline_call_field_shorthand() {
1130 cov_mark::check!(inline_call_inline_direct_field);
1131 check_assist(
1132 inline_call,
1133 r#"
1134 struct Foo {
1135 field: u32,
1136 field1: u32,
1137 field2: u32,
1138 field3: u32,
1139 }
1140 fn foo(field: u32, field1: u32, val2: u32, val3: u32) -> Foo {
1141 Foo {
1142 field,
1143 field1,
1144 field2: val2,
1145 field3: val3,
1146 }
1147 }
1148 fn main() {
1149 let bar = 0;
1150 let baz = 0;
1151 foo$0(bar, 0, baz, 0);
1152 }
1153 "#,
1154 r#"
1155 struct Foo {
1156 field: u32,
1157 field1: u32,
1158 field2: u32,
1159 field3: u32,
1160 }
1161 fn foo(field: u32, field1: u32, val2: u32, val3: u32) -> Foo {
1162 Foo {
1163 field,
1164 field1,
1165 field2: val2,
1166 field3: val3,
1167 }
1168 }
1169 fn main() {
1170 let bar = 0;
1171 let baz = 0;
1172 Foo {
1173 field: bar,
1174 field1: 0,
1175 field2: baz,
1176 field3: 0,
1177 };
1178 }
1179 "#,
1180 );
1181 }
1182
1183 #[test]
inline_callers_wrapped_in_parentheses()1184 fn inline_callers_wrapped_in_parentheses() {
1185 check_assist(
1186 inline_into_callers,
1187 r#"
1188 fn foo$0() -> u32 {
1189 let x = 0;
1190 x
1191 }
1192 fn bar() -> u32 {
1193 foo() + foo()
1194 }
1195 "#,
1196 r#"
1197
1198 fn bar() -> u32 {
1199 ({
1200 let x = 0;
1201 x
1202 }) + {
1203 let x = 0;
1204 x
1205 }
1206 }
1207 "#,
1208 )
1209 }
1210
1211 #[test]
inline_call_wrapped_in_parentheses()1212 fn inline_call_wrapped_in_parentheses() {
1213 check_assist(
1214 inline_call,
1215 r#"
1216 fn foo() -> u32 {
1217 let x = 0;
1218 x
1219 }
1220 fn bar() -> u32 {
1221 foo$0() + foo()
1222 }
1223 "#,
1224 r#"
1225 fn foo() -> u32 {
1226 let x = 0;
1227 x
1228 }
1229 fn bar() -> u32 {
1230 ({
1231 let x = 0;
1232 x
1233 }) + foo()
1234 }
1235 "#,
1236 )
1237 }
1238
1239 #[test]
inline_call_defined_in_macro()1240 fn inline_call_defined_in_macro() {
1241 cov_mark::check!(inline_call_defined_in_macro);
1242 check_assist(
1243 inline_call,
1244 r#"
1245 macro_rules! define_foo {
1246 () => { fn foo() -> u32 {
1247 let x = 0;
1248 x
1249 } };
1250 }
1251 define_foo!();
1252 fn bar() -> u32 {
1253 foo$0()
1254 }
1255 "#,
1256 r#"
1257 macro_rules! define_foo {
1258 () => { fn foo() -> u32 {
1259 let x = 0;
1260 x
1261 } };
1262 }
1263 define_foo!();
1264 fn bar() -> u32 {
1265 {
1266 let x = 0;
1267 x
1268 }
1269 }
1270 "#,
1271 )
1272 }
1273
1274 #[test]
inline_call_with_self_type()1275 fn inline_call_with_self_type() {
1276 check_assist(
1277 inline_call,
1278 r#"
1279 struct A(u32);
1280 impl A {
1281 fn f() -> Self { Self(114514) }
1282 }
1283 fn main() {
1284 A::f$0();
1285 }
1286 "#,
1287 r#"
1288 struct A(u32);
1289 impl A {
1290 fn f() -> Self { Self(114514) }
1291 }
1292 fn main() {
1293 A(114514);
1294 }
1295 "#,
1296 )
1297 }
1298
1299 #[test]
inline_call_with_self_type_but_within_same_impl()1300 fn inline_call_with_self_type_but_within_same_impl() {
1301 check_assist(
1302 inline_call,
1303 r#"
1304 struct A(u32);
1305 impl A {
1306 fn f() -> Self { Self(1919810) }
1307 fn main() {
1308 Self::f$0();
1309 }
1310 }
1311 "#,
1312 r#"
1313 struct A(u32);
1314 impl A {
1315 fn f() -> Self { Self(1919810) }
1316 fn main() {
1317 Self(1919810);
1318 }
1319 }
1320 "#,
1321 )
1322 }
1323
1324 #[test]
local_variable_shadowing_callers_argument()1325 fn local_variable_shadowing_callers_argument() {
1326 check_assist(
1327 inline_call,
1328 r#"
1329 fn foo(bar: u32, baz: u32) -> u32 {
1330 let a = 1;
1331 bar * baz * a * 6
1332 }
1333 fn main() {
1334 let a = 7;
1335 let b = 1;
1336 let res = foo$0(a, b);
1337 }
1338 "#,
1339 r#"
1340 fn foo(bar: u32, baz: u32) -> u32 {
1341 let a = 1;
1342 bar * baz * a * 6
1343 }
1344 fn main() {
1345 let a = 7;
1346 let b = 1;
1347 let res = {
1348 let bar = a;
1349 let a = 1;
1350 bar * b * a * 6
1351 };
1352 }
1353 "#,
1354 );
1355 }
1356 }
1357