1use std::collections::BTreeSet;
2
3use ast::make;
4use either::Either;
5use hir::{
6 FileRange, PathResolution, Semantics, TypeInfo,
7 db::{ExpandDatabase, HirDatabase},
8 sym,
9};
10use ide_db::{
11 EditionedFileId, RootDatabase,
12 base_db::Crate,
13 defs::Definition,
14 imports::insert_use::remove_path_if_in_use_stmt,
15 path_transform::PathTransform,
16 search::{FileReference, FileReferenceNode, SearchScope},
17 source_change::SourceChangeBuilder,
18 syntax_helpers::{node_ext::expr_as_name_ref, prettify_macro_expansion},
19};
20use itertools::{Itertools, izip};
21use syntax::{
22 AstNode, NodeOrToken, SyntaxKind,
23 ast::{
24 self, HasArgList, HasGenericArgs, Pat, PathExpr, edit::IndentLevel, edit_in_place::Indent,
25 },
26 ted,
27};
28
29use crate::{
30 AssistId,
31 assist_context::{AssistContext, Assists},
32};
33
34pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
73 let def_file = ctx.file_id();
74 let vfs_def_file = ctx.vfs_file_id();
75 let name = ctx.find_node_at_offset::<ast::Name>()?;
76 let ast_func = name.syntax().parent().and_then(ast::Fn::cast)?;
77 let func_body = ast_func.body()?;
78 let param_list = ast_func.param_list()?;
79
80 let function = ctx.sema.to_def(&ast_func)?;
81
82 let params = get_fn_params(ctx.sema.db, function, ¶m_list)?;
83
84 let usages = Definition::Function(function).usages(&ctx.sema);
85 if !usages.at_least_one() {
86 return None;
87 }
88
89 let is_recursive_fn = usages
90 .clone()
91 .in_scope(&SearchScope::file_range(FileRange {
92 file_id: def_file,
93 range: func_body.syntax().text_range(),
94 }))
95 .at_least_one();
96 if is_recursive_fn {
97 cov_mark::hit!(inline_into_callers_recursive);
98 return None;
99 }
100
101 acc.add(
102 AssistId::refactor_inline("inline_into_callers"),
103 "Inline into all callers",
104 name.syntax().text_range(),
105 |builder| {
106 let mut usages = usages.all();
107 let current_file_usage = usages.references.remove(&def_file);
108
109 let mut remove_def = true;
110 let mut inline_refs_for_file = |file_id: EditionedFileId, refs: Vec<FileReference>| {
111 let file_id = file_id.file_id(ctx.db());
112 builder.edit_file(file_id);
113 let call_krate = ctx.sema.file_to_module_def(file_id).map(|it| it.krate(ctx.db()));
114 let count = refs.len();
115 let (name_refs, name_refs_use) = split_refs_and_uses(builder, refs, Some);
117 let call_infos: Vec<_> = name_refs
118 .into_iter()
119 .filter_map(|it| CallInfo::from_name_ref(it, call_krate?.into()))
120 .filter(|call_info| !ctx.sema.hir_file_for(call_info.node.syntax()).is_macro())
123 .map(|call_info| {
124 let mut_node = builder.make_syntax_mut(call_info.node.syntax().clone());
125 (call_info, mut_node)
126 })
127 .collect();
128 let replaced = call_infos
129 .into_iter()
130 .map(|(call_info, mut_node)| {
131 let replacement =
132 inline(&ctx.sema, def_file, function, &func_body, ¶ms, &call_info);
133 ted::replace(mut_node, replacement.syntax());
134 })
135 .count();
136 if replaced + name_refs_use.len() == count {
137 name_refs_use.iter().for_each(remove_path_if_in_use_stmt);
139 } else {
140 remove_def = false;
141 }
142 };
143 for (file_id, refs) in usages.into_iter() {
144 inline_refs_for_file(file_id, refs);
145 }
146 match current_file_usage {
147 Some(refs) => inline_refs_for_file(def_file, refs),
148 None => builder.edit_file(vfs_def_file),
149 }
150 if remove_def {
151 builder.delete(ast_func.syntax().text_range());
152 }
153 },
154 )
155}
156
157pub(super) fn split_refs_and_uses<T: ast::AstNode>(
158 builder: &mut SourceChangeBuilder,
159 iter: impl IntoIterator<Item = FileReference>,
160 mut map_ref: impl FnMut(ast::NameRef) -> Option<T>,
161) -> (Vec<T>, Vec<ast::Path>) {
162 iter.into_iter()
163 .filter_map(|file_ref| match file_ref.name {
164 FileReferenceNode::NameRef(name_ref) => Some(name_ref),
165 _ => None,
166 })
167 .filter_map(|name_ref| match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) {
168 Some(use_tree) => builder.make_mut(use_tree).path().map(Either::Right),
169 None => map_ref(name_ref).map(Either::Left),
170 })
171 .partition_map(|either| either)
172}
173
174pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
196 let name_ref: ast::NameRef = ctx.find_node_at_offset()?;
197 let call_info = CallInfo::from_name_ref(
198 name_ref.clone(),
199 ctx.sema.file_to_module_def(ctx.vfs_file_id())?.krate(ctx.db()).into(),
200 )?;
201 let (function, label) = match &call_info.node {
202 ast::CallableExpr::Call(call) => {
203 let path = match call.expr()? {
204 ast::Expr::PathExpr(path) => path.path(),
205 _ => None,
206 }?;
207 let function = match ctx.sema.resolve_path(&path)? {
208 PathResolution::Def(hir::ModuleDef::Function(f)) => f,
209 _ => return None,
210 };
211 (function, format!("Inline `{path}`"))
212 }
213 ast::CallableExpr::MethodCall(call) => {
214 (ctx.sema.resolve_method_call(call)?, format!("Inline `{name_ref}`"))
215 }
216 };
217
218 let fn_source = ctx.sema.source(function)?;
219 let fn_body = fn_source.value.body()?;
220 let param_list = fn_source.value.param_list()?;
221
222 let FileRange { file_id, range } = fn_source.syntax().original_file_range_rooted(ctx.sema.db);
223 if file_id == ctx.file_id() && range.contains(ctx.offset()) {
224 cov_mark::hit!(inline_call_recursive);
225 return None;
226 }
227 let params = get_fn_params(ctx.sema.db, function, ¶m_list)?;
228
229 if call_info.arguments.len() != params.len() {
230 cov_mark::hit!(inline_call_incorrect_number_of_arguments);
233 return None;
234 }
235
236 let syntax = call_info.node.syntax().clone();
237 acc.add(AssistId::refactor_inline("inline_call"), label, syntax.text_range(), |builder| {
238 let replacement = inline(&ctx.sema, file_id, function, &fn_body, ¶ms, &call_info);
239 builder.replace_ast(
240 match call_info.node {
241 ast::CallableExpr::Call(it) => ast::Expr::CallExpr(it),
242 ast::CallableExpr::MethodCall(it) => ast::Expr::MethodCallExpr(it),
243 },
244 replacement,
245 );
246 })
247}
248
249struct CallInfo {
250 node: ast::CallableExpr,
251 arguments: Vec<ast::Expr>,
252 generic_arg_list: Option<ast::GenericArgList>,
253 krate: Crate,
254}
255
256impl CallInfo {
257 fn from_name_ref(name_ref: ast::NameRef, krate: Crate) -> Option<CallInfo> {
258 let parent = name_ref.syntax().parent()?;
259 if let Some(call) = ast::MethodCallExpr::cast(parent.clone()) {
260 let receiver = call.receiver()?;
261 let mut arguments = vec![receiver];
262 arguments.extend(call.arg_list()?.args());
263 Some(CallInfo {
264 generic_arg_list: call.generic_arg_list(),
265 node: ast::CallableExpr::MethodCall(call),
266 arguments,
267 krate,
268 })
269 } else if let Some(segment) = ast::PathSegment::cast(parent) {
270 let path = segment.syntax().parent().and_then(ast::Path::cast)?;
271 let path = path.syntax().parent().and_then(ast::PathExpr::cast)?;
272 let call = path.syntax().parent().and_then(ast::CallExpr::cast)?;
273
274 Some(CallInfo {
275 arguments: call.arg_list()?.args().collect(),
276 node: ast::CallableExpr::Call(call),
277 generic_arg_list: segment.generic_arg_list(),
278 krate,
279 })
280 } else {
281 None
282 }
283 }
284}
285
286fn get_fn_params<'db>(
287 db: &'db dyn HirDatabase,
288 function: hir::Function,
289 param_list: &ast::ParamList,
290) -> Option<Vec<(ast::Pat, Option<ast::Type>, hir::Param<'db>)>> {
291 let mut assoc_fn_params = function.assoc_fn_params(db).into_iter();
292
293 let mut params = Vec::new();
294 if let Some(self_param) = param_list.self_param() {
295 params.push((
297 make::ident_pat(
298 self_param.amp_token().is_some(),
299 self_param.mut_token().is_some(),
300 make::name("this"),
301 )
302 .into(),
303 None,
304 assoc_fn_params.next()?,
305 ));
306 }
307 for param in param_list.params() {
308 params.push((param.pat()?, param.ty(), assoc_fn_params.next()?));
309 }
310
311 Some(params)
312}
313
314fn inline(
315 sema: &Semantics<'_, RootDatabase>,
316 function_def_file_id: EditionedFileId,
317 function: hir::Function,
318 fn_body: &ast::BlockExpr,
319 params: &[(ast::Pat, Option<ast::Type>, hir::Param<'_>)],
320 CallInfo { node, arguments, generic_arg_list, krate }: &CallInfo,
321) -> ast::Expr {
322 let file_id = sema.hir_file_for(fn_body.syntax());
323 let mut body = if let Some(macro_file) = file_id.macro_file() {
324 cov_mark::hit!(inline_call_defined_in_macro);
325 let span_map = sema.db.expansion_span_map(macro_file);
326 let body_prettified =
327 prettify_macro_expansion(sema.db, fn_body.syntax().clone(), &span_map, *krate);
328 if let Some(body) = ast::BlockExpr::cast(body_prettified) {
329 body
330 } else {
331 fn_body.clone_for_update()
332 }
333 } else {
334 fn_body.clone_for_update()
335 };
336 let usages_for_locals = |local| {
337 Definition::Local(local)
338 .usages(sema)
339 .all()
340 .references
341 .remove(&function_def_file_id)
342 .unwrap_or_default()
343 .into_iter()
344 };
345 let param_use_nodes: Vec<Vec<_>> = params
346 .iter()
347 .map(|(pat, _, param)| {
348 if !matches!(pat, ast::Pat::IdentPat(pat) if pat.is_simple_ident()) {
349 return Vec::new();
350 }
351 match param.as_local(sema.db) {
354 Some(l) => usages_for_locals(l)
355 .map(|FileReference { name, range, .. }| match name {
356 FileReferenceNode::NameRef(_) => body
357 .syntax()
358 .covering_element(range)
359 .ancestors()
360 .nth(3)
361 .and_then(ast::PathExpr::cast),
362 _ => None,
363 })
364 .collect::<Option<Vec<_>>>()
365 .unwrap_or_default(),
366 None => Vec::new(),
367 }
368 })
369 .collect();
370
371 if function.self_param(sema.db).is_some() {
372 let this = || {
373 make::name_ref("this")
374 .syntax()
375 .clone_for_update()
376 .first_token()
377 .expect("NameRef should have had a token.")
378 };
379 if let Some(self_local) = params[0].2.as_local(sema.db) {
380 usages_for_locals(self_local)
381 .filter_map(|FileReference { name, range, .. }| match name {
382 FileReferenceNode::NameRef(_) => Some(body.syntax().covering_element(range)),
383 _ => None,
384 })
385 .for_each(|usage| {
386 ted::replace(usage, this());
387 });
388 }
389 }
390
391 if let Some(imp) =
395 sema.ancestors_with_macros(fn_body.syntax().clone()).find_map(ast::Impl::cast)
396 && !node.syntax().ancestors().any(|anc| &anc == imp.syntax())
397 && let Some(t) = imp.self_ty()
398 {
399 while let Some(self_tok) = body
400 .syntax()
401 .descendants_with_tokens()
402 .filter_map(NodeOrToken::into_token)
403 .find(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW)
404 {
405 let replace_with = t.clone_subtree().syntax().clone_for_update();
406 ted::replace(self_tok, replace_with);
407 }
408 }
409
410 let mut func_let_vars: BTreeSet<String> = BTreeSet::new();
411
412 for stmt in fn_body.statements() {
414 if let Some(let_stmt) = ast::LetStmt::cast(stmt.syntax().to_owned()) {
415 for has_token in let_stmt.syntax().children_with_tokens() {
416 if let Some(node) = has_token.as_node()
417 && let Some(ident_pat) = ast::IdentPat::cast(node.to_owned())
418 {
419 func_let_vars.insert(ident_pat.syntax().text().to_string());
420 }
421 }
422 }
423 }
424
425 let mut let_stmts = Vec::new();
426
427 for ((pat, param_ty, param), usages, expr) in izip!(params, param_use_nodes, arguments) {
429 let usages: &[ast::PathExpr] = &usages;
431 let expr: &ast::Expr = expr;
432
433 let mut insert_let_stmt = || {
434 let param_ty = param_ty.clone().map(|param_ty| {
435 let file_id = sema.hir_file_for(param_ty.syntax());
436 if let Some(macro_file) = file_id.macro_file() {
437 let span_map = sema.db.expansion_span_map(macro_file);
438 let param_ty_prettified = prettify_macro_expansion(
439 sema.db,
440 param_ty.syntax().clone(),
441 &span_map,
442 *krate,
443 );
444 ast::Type::cast(param_ty_prettified).unwrap_or(param_ty)
445 } else {
446 param_ty
447 }
448 });
449
450 let ty = sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty);
451
452 let is_self = param.name(sema.db).is_some_and(|name| name == sym::self_);
453
454 if is_self {
455 let mut this_pat = make::ident_pat(false, false, make::name("this"));
456 let mut expr = expr.clone();
457 if let Pat::IdentPat(pat) = pat {
458 match (pat.ref_token(), pat.mut_token()) {
459 (None, None) => {}
461 (None, Some(_)) => {
463 this_pat = make::ident_pat(false, true, make::name("this"));
464 }
465 (Some(_), None) => {
467 expr = make::expr_ref(expr, false);
468 }
469 (Some(_), Some(_)) => {
472 let should_reborrow = sema
473 .type_of_expr(&expr)
474 .map(|ty| ty.original.is_mutable_reference());
475 expr = if let Some(true) = should_reborrow {
476 make::expr_reborrow(expr)
477 } else {
478 make::expr_ref(expr, true)
479 };
480 }
481 }
482 };
483 let_stmts
484 .push(make::let_stmt(this_pat.into(), ty, Some(expr)).clone_for_update().into())
485 } else {
486 let_stmts.push(
487 make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(),
488 );
489 }
490 };
491
492 if func_let_vars.contains(&expr.syntax().text().to_string()) {
495 insert_let_stmt();
496 continue;
497 }
498
499 let inline_direct = |usage, replacement: &ast::Expr| {
500 if let Some(field) = path_expr_as_record_field(usage) {
501 cov_mark::hit!(inline_call_inline_direct_field);
502 field.replace_expr(replacement.clone_for_update());
503 } else {
504 ted::replace(usage.syntax(), replacement.syntax().clone_for_update());
505 }
506 };
507
508 match usages {
509 [usage]
511 if matches!(expr, ast::Expr::ClosureExpr(_))
512 && usage.syntax().parent().and_then(ast::Expr::cast).is_some() =>
513 {
514 cov_mark::hit!(inline_call_inline_closure);
515 let expr = make::expr_paren(expr.clone()).into();
516 inline_direct(usage, &expr);
517 }
518 [usage] if matches!(expr, ast::Expr::Literal(_)) => {
520 cov_mark::hit!(inline_call_inline_literal);
521 inline_direct(usage, expr);
522 }
523 [_, ..] if expr_as_name_ref(expr).is_some() => {
525 cov_mark::hit!(inline_call_inline_locals);
526 usages.iter().for_each(|usage| inline_direct(usage, expr));
527 }
528 _ => {
530 insert_let_stmt();
531 }
532 }
533 }
534
535 if let Some(generic_arg_list) = generic_arg_list.clone()
536 && let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax()))
537 {
538 body.reindent_to(IndentLevel(0));
539 if let Some(new_body) = ast::BlockExpr::cast(
540 PathTransform::function_call(target, source, function, generic_arg_list)
541 .apply(body.syntax()),
542 ) {
543 body = new_body;
544 }
545 }
546
547 let is_async_fn = function.is_async(sema.db);
548 if is_async_fn {
549 cov_mark::hit!(inline_call_async_fn);
550 body = make::async_move_block_expr(body.statements(), body.tail_expr()).clone_for_update();
551
552 if !let_stmts.is_empty() {
554 cov_mark::hit!(inline_call_async_fn_with_let_stmts);
555 body.indent(IndentLevel(1));
556 body = make::block_expr(let_stmts, Some(body.into())).clone_for_update();
557 }
558 } else if let Some(stmt_list) = body.stmt_list() {
559 let position = stmt_list.l_curly_token().expect("L_CURLY for StatementList is missing.");
560 let_stmts.into_iter().rev().for_each(|let_stmt| {
561 ted::insert(ted::Position::after(position.clone()), let_stmt.syntax().clone());
562 });
563 }
564
565 let original_indentation = match node {
566 ast::CallableExpr::Call(it) => it.indent_level(),
567 ast::CallableExpr::MethodCall(it) => it.indent_level(),
568 };
569 body.reindent_to(original_indentation);
570
571 let no_stmts = body.statements().next().is_none();
572 match body.tail_expr() {
573 Some(expr) if matches!(expr, ast::Expr::ClosureExpr(_)) && no_stmts => {
574 make::expr_paren(expr).clone_for_update().into()
575 }
576 Some(expr) if !is_async_fn && no_stmts => expr,
577 _ => match node
578 .syntax()
579 .parent()
580 .and_then(ast::BinExpr::cast)
581 .and_then(|bin_expr| bin_expr.lhs())
582 {
583 Some(lhs) if lhs.syntax() == node.syntax() => {
584 make::expr_paren(ast::Expr::BlockExpr(body)).clone_for_update().into()
585 }
586 _ => ast::Expr::BlockExpr(body),
587 },
588 }
589}
590
591fn path_expr_as_record_field(usage: &PathExpr) -> Option<ast::RecordExprField> {
592 let path = usage.path()?;
593 let name_ref = path.as_single_name_ref()?;
594 ast::RecordExprField::for_name_ref(&name_ref)
595}
596
597#[cfg(test)]
598mod tests {
599 use crate::tests::{check_assist, check_assist_not_applicable};
600
601 use super::*;
602
603 #[test]
604 fn no_args_or_return_value_gets_inlined_without_block() {
605 check_assist(
606 inline_call,
607 r#"
608fn foo() { println!("Hello, World!"); }
609fn main() {
610 fo$0o();
611}
612"#,
613 r#"
614fn foo() { println!("Hello, World!"); }
615fn main() {
616 { println!("Hello, World!"); };
617}
618"#,
619 );
620 }
621
622 #[test]
623 fn not_applicable_when_incorrect_number_of_parameters_are_provided() {
624 cov_mark::check!(inline_call_incorrect_number_of_arguments);
625 check_assist_not_applicable(
626 inline_call,
627 r#"
628fn add(a: u32, b: u32) -> u32 { a + b }
629fn main() { let x = add$0(42); }
630"#,
631 );
632 }
633
634 #[test]
635 fn args_with_side_effects() {
636 check_assist(
637 inline_call,
638 r#"
639fn foo(name: String) {
640 println!("Hello, {}!", name);
641}
642fn main() {
643 foo$0(String::from("Michael"));
644}
645"#,
646 r#"
647fn foo(name: String) {
648 println!("Hello, {}!", name);
649}
650fn main() {
651 {
652 let name = String::from("Michael");
653 println!("Hello, {}!", name);
654 };
655}
656"#,
657 );
658 }
659
660 #[test]
661 fn function_with_multiple_statements() {
662 check_assist(
663 inline_call,
664 r#"
665fn foo(a: u32, b: u32) -> u32 {
666 let x = a + b;
667 let y = x - b;
668 x * y
669}
670
671fn main() {
672 let x = foo$0(1, 2);
673}
674"#,
675 r#"
676fn foo(a: u32, b: u32) -> u32 {
677 let x = a + b;
678 let y = x - b;
679 x * y
680}
681
682fn main() {
683 let x = {
684 let b = 2;
685 let x = 1 + b;
686 let y = x - b;
687 x * y
688 };
689}
690"#,
691 );
692 }
693
694 #[test]
695 fn function_with_self_param() {
696 check_assist(
697 inline_call,
698 r#"
699struct Foo(u32);
700
701impl Foo {
702 fn add(self, a: u32) -> Self {
703 Foo(self.0 + a)
704 }
705}
706
707fn main() {
708 let x = Foo::add$0(Foo(3), 2);
709}
710"#,
711 r#"
712struct Foo(u32);
713
714impl Foo {
715 fn add(self, a: u32) -> Self {
716 Foo(self.0 + a)
717 }
718}
719
720fn main() {
721 let x = {
722 let this = Foo(3);
723 Foo(this.0 + 2)
724 };
725}
726"#,
727 );
728 }
729
730 #[test]
731 fn method_by_val() {
732 check_assist(
733 inline_call,
734 r#"
735struct Foo(u32);
736
737impl Foo {
738 fn add(self, a: u32) -> Self {
739 Foo(self.0 + a)
740 }
741}
742
743fn main() {
744 let x = Foo(3).add$0(2);
745}
746"#,
747 r#"
748struct Foo(u32);
749
750impl Foo {
751 fn add(self, a: u32) -> Self {
752 Foo(self.0 + a)
753 }
754}
755
756fn main() {
757 let x = {
758 let this = Foo(3);
759 Foo(this.0 + 2)
760 };
761}
762"#,
763 );
764 }
765
766 #[test]
767 fn method_by_ref() {
768 check_assist(
769 inline_call,
770 r#"
771struct Foo(u32);
772
773impl Foo {
774 fn add(&self, a: u32) -> Self {
775 Foo(self.0 + a)
776 }
777}
778
779fn main() {
780 let x = Foo(3).add$0(2);
781}
782"#,
783 r#"
784struct Foo(u32);
785
786impl Foo {
787 fn add(&self, a: u32) -> Self {
788 Foo(self.0 + a)
789 }
790}
791
792fn main() {
793 let x = {
794 let this = &Foo(3);
795 Foo(this.0 + 2)
796 };
797}
798"#,
799 );
800 }
801
802 #[test]
803 fn generic_method_by_ref() {
804 check_assist(
805 inline_call,
806 r#"
807struct Foo(u32);
808
809impl Foo {
810 fn add<T>(&self, a: u32) -> Self {
811 Foo(self.0 + a)
812 }
813}
814
815fn main() {
816 let x = Foo(3).add$0::<usize>(2);
817}
818"#,
819 r#"
820struct Foo(u32);
821
822impl Foo {
823 fn add<T>(&self, a: u32) -> Self {
824 Foo(self.0 + a)
825 }
826}
827
828fn main() {
829 let x = {
830 let this = &Foo(3);
831 Foo(this.0 + 2)
832 };
833}
834"#,
835 );
836 }
837
838 #[test]
839 fn method_by_ref_mut() {
840 check_assist(
841 inline_call,
842 r#"
843struct Foo(u32);
844
845impl Foo {
846 fn clear(&mut self) {
847 self.0 = 0;
848 }
849}
850
851fn main() {
852 let mut foo = Foo(3);
853 foo.clear$0();
854}
855"#,
856 r#"
857struct Foo(u32);
858
859impl Foo {
860 fn clear(&mut self) {
861 self.0 = 0;
862 }
863}
864
865fn main() {
866 let mut foo = Foo(3);
867 {
868 let this = &mut foo;
869 this.0 = 0;
870 };
871}
872"#,
873 );
874 }
875
876 #[test]
877 fn function_multi_use_expr_in_param() {
878 check_assist(
879 inline_call,
880 r#"
881fn square(x: u32) -> u32 {
882 x * x
883}
884fn main() {
885 let x = 51;
886 let y = square$0(10 + x);
887}
888"#,
889 r#"
890fn square(x: u32) -> u32 {
891 x * x
892}
893fn main() {
894 let x = 51;
895 let y = {
896 let x = 10 + x;
897 x * x
898 };
899}
900"#,
901 );
902 }
903
904 #[test]
905 fn function_use_local_in_param() {
906 cov_mark::check!(inline_call_inline_locals);
907 check_assist(
908 inline_call,
909 r#"
910fn square(x: u32) -> u32 {
911 x * x
912}
913fn main() {
914 let local = 51;
915 let y = square$0(local);
916}
917"#,
918 r#"
919fn square(x: u32) -> u32 {
920 x * x
921}
922fn main() {
923 let local = 51;
924 let y = local * local;
925}
926"#,
927 );
928 }
929
930 #[test]
931 fn method_in_impl() {
932 check_assist(
933 inline_call,
934 r#"
935struct Foo;
936impl Foo {
937 fn foo(&self) {
938 self;
939 self;
940 }
941 fn bar(&self) {
942 self.foo$0();
943 }
944}
945"#,
946 r#"
947struct Foo;
948impl Foo {
949 fn foo(&self) {
950 self;
951 self;
952 }
953 fn bar(&self) {
954 {
955 let this = &self;
956 this;
957 this;
958 };
959 }
960}
961"#,
962 );
963 }
964
965 #[test]
966 fn wraps_closure_in_paren() {
967 cov_mark::check!(inline_call_inline_closure);
968 check_assist(
969 inline_call,
970 r#"
971fn foo(x: fn()) {
972 x();
973}
974
975fn main() {
976 foo$0(|| {})
977}
978"#,
979 r#"
980fn foo(x: fn()) {
981 x();
982}
983
984fn main() {
985 {
986 (|| {})();
987 }
988}
989"#,
990 );
991 check_assist(
992 inline_call,
993 r#"
994fn foo(x: fn()) {
995 x();
996}
997
998fn main() {
999 foo$0(main)
1000}
1001"#,
1002 r#"
1003fn foo(x: fn()) {
1004 x();
1005}
1006
1007fn main() {
1008 {
1009 main();
1010 }
1011}
1012"#,
1013 );
1014 }
1015
1016 #[test]
1017 fn inline_single_literal_expr() {
1018 cov_mark::check!(inline_call_inline_literal);
1019 check_assist(
1020 inline_call,
1021 r#"
1022fn foo(x: u32) -> u32{
1023 x
1024}
1025
1026fn main() {
1027 foo$0(222);
1028}
1029"#,
1030 r#"
1031fn foo(x: u32) -> u32{
1032 x
1033}
1034
1035fn main() {
1036 222;
1037}
1038"#,
1039 );
1040 }
1041
1042 #[test]
1043 fn inline_emits_type_for_coercion() {
1044 check_assist(
1045 inline_call,
1046 r#"
1047//- minicore: sized
1048fn foo(x: *const u32) -> u32 {
1049 x as u32
1050}
1051
1052fn main() {
1053 foo$0(&222);
1054}
1055"#,
1056 r#"
1057fn foo(x: *const u32) -> u32 {
1058 x as u32
1059}
1060
1061fn main() {
1062 {
1063 let x: *const u32 = &222;
1064 x as u32
1065 };
1066}
1067"#,
1068 );
1069 }
1070
1071 #[test]
1072 fn inline_substitutes_generics() {
1073 check_assist(
1074 inline_call,
1075 r#"
1076fn foo<T, const N: usize>() {
1077 bar::<T, N>()
1078}
1079
1080fn bar<U, const M: usize>() {}
1081
1082fn main() {
1083 foo$0::<usize, {0}>();
1084}
1085"#,
1086 r#"
1087fn foo<T, const N: usize>() {
1088 bar::<T, N>()
1089}
1090
1091fn bar<U, const M: usize>() {}
1092
1093fn main() {
1094 bar::<usize, {0}>();
1095}
1096"#,
1097 );
1098 }
1099
1100 #[test]
1101 fn inline_callers() {
1102 check_assist(
1103 inline_into_callers,
1104 r#"
1105fn do_the_math$0(b: u32) -> u32 {
1106 let foo = 10;
1107 foo * b + foo
1108}
1109fn foo() {
1110 do_the_math(0);
1111 let bar = 10;
1112 do_the_math(bar);
1113}
1114"#,
1115 r#"
1116
1117fn foo() {
1118 {
1119 let foo = 10;
1120 foo * 0 + foo
1121 };
1122 let bar = 10;
1123 {
1124 let foo = 10;
1125 foo * bar + foo
1126 };
1127}
1128"#,
1129 );
1130 }
1131
1132 #[test]
1133 fn inline_callers_across_files() {
1134 check_assist(
1135 inline_into_callers,
1136 r#"
1137//- /lib.rs
1138mod foo;
1139fn do_the_math$0(b: u32) -> u32 {
1140 let foo = 10;
1141 foo * b + foo
1142}
1143//- /foo.rs
1144use super::do_the_math;
1145fn foo() {
1146 do_the_math(0);
1147 let bar = 10;
1148 do_the_math(bar);
1149}
1150"#,
1151 r#"
1152//- /lib.rs
1153mod foo;
1154
1155//- /foo.rs
1156fn foo() {
1157 {
1158 let foo = 10;
1159 foo * 0 + foo
1160 };
1161 let bar = 10;
1162 {
1163 let foo = 10;
1164 foo * bar + foo
1165 };
1166}
1167"#,
1168 );
1169 }
1170
1171 #[test]
1172 fn inline_callers_across_files_with_def_file() {
1173 check_assist(
1174 inline_into_callers,
1175 r#"
1176//- /lib.rs
1177mod foo;
1178fn do_the_math$0(b: u32) -> u32 {
1179 let foo = 10;
1180 foo * b + foo
1181}
1182fn bar(a: u32, b: u32) -> u32 {
1183 do_the_math(0);
1184}
1185//- /foo.rs
1186use super::do_the_math;
1187fn foo() {
1188 do_the_math(0);
1189}
1190"#,
1191 r#"
1192//- /lib.rs
1193mod foo;
1194
1195fn bar(a: u32, b: u32) -> u32 {
1196 {
1197 let foo = 10;
1198 foo * 0 + foo
1199 };
1200}
1201//- /foo.rs
1202fn foo() {
1203 {
1204 let foo = 10;
1205 foo * 0 + foo
1206 };
1207}
1208"#,
1209 );
1210 }
1211
1212 #[test]
1213 fn inline_callers_recursive() {
1214 cov_mark::check!(inline_into_callers_recursive);
1215 check_assist_not_applicable(
1216 inline_into_callers,
1217 r#"
1218fn foo$0() {
1219 foo();
1220}
1221"#,
1222 );
1223 }
1224
1225 #[test]
1226 fn inline_call_recursive() {
1227 cov_mark::check!(inline_call_recursive);
1228 check_assist_not_applicable(
1229 inline_call,
1230 r#"
1231fn foo() {
1232 foo$0();
1233}
1234"#,
1235 );
1236 }
1237
1238 #[test]
1239 fn inline_call_field_shorthand() {
1240 cov_mark::check!(inline_call_inline_direct_field);
1241 check_assist(
1242 inline_call,
1243 r#"
1244struct Foo {
1245 field: u32,
1246 field1: u32,
1247 field2: u32,
1248 field3: u32,
1249}
1250fn foo(field: u32, field1: u32, val2: u32, val3: u32) -> Foo {
1251 Foo {
1252 field,
1253 field1,
1254 field2: val2,
1255 field3: val3,
1256 }
1257}
1258fn main() {
1259 let bar = 0;
1260 let baz = 0;
1261 foo$0(bar, 0, baz, 0);
1262}
1263"#,
1264 r#"
1265struct Foo {
1266 field: u32,
1267 field1: u32,
1268 field2: u32,
1269 field3: u32,
1270}
1271fn foo(field: u32, field1: u32, val2: u32, val3: u32) -> Foo {
1272 Foo {
1273 field,
1274 field1,
1275 field2: val2,
1276 field3: val3,
1277 }
1278}
1279fn main() {
1280 let bar = 0;
1281 let baz = 0;
1282 Foo {
1283 field: bar,
1284 field1: 0,
1285 field2: baz,
1286 field3: 0,
1287 };
1288}
1289"#,
1290 );
1291 }
1292
1293 #[test]
1294 fn inline_callers_wrapped_in_parentheses() {
1295 check_assist(
1296 inline_into_callers,
1297 r#"
1298fn foo$0() -> u32 {
1299 let x = 0;
1300 x
1301}
1302fn bar() -> u32 {
1303 foo() + foo()
1304}
1305"#,
1306 r#"
1307
1308fn bar() -> u32 {
1309 ({
1310 let x = 0;
1311 x
1312 }) + {
1313 let x = 0;
1314 x
1315 }
1316}
1317"#,
1318 )
1319 }
1320
1321 #[test]
1322 fn inline_call_wrapped_in_parentheses() {
1323 check_assist(
1324 inline_call,
1325 r#"
1326fn foo() -> u32 {
1327 let x = 0;
1328 x
1329}
1330fn bar() -> u32 {
1331 foo$0() + foo()
1332}
1333"#,
1334 r#"
1335fn foo() -> u32 {
1336 let x = 0;
1337 x
1338}
1339fn bar() -> u32 {
1340 ({
1341 let x = 0;
1342 x
1343 }) + foo()
1344}
1345"#,
1346 )
1347 }
1348
1349 #[test]
1350 fn inline_call_defined_in_macro() {
1351 cov_mark::check!(inline_call_defined_in_macro);
1352 check_assist(
1353 inline_call,
1354 r#"
1355macro_rules! define_foo {
1356 () => { fn foo() -> u32 {
1357 let x = 0;
1358 x
1359 } };
1360}
1361define_foo!();
1362fn bar() -> u32 {
1363 foo$0()
1364}
1365"#,
1366 r#"
1367macro_rules! define_foo {
1368 () => { fn foo() -> u32 {
1369 let x = 0;
1370 x
1371 } };
1372}
1373define_foo!();
1374fn bar() -> u32 {
1375 {
1376 let x = 0;
1377 x
1378 }
1379}
1380"#,
1381 )
1382 }
1383
1384 #[test]
1385 fn inline_call_with_self_type() {
1386 check_assist(
1387 inline_call,
1388 r#"
1389struct A(u32);
1390impl A {
1391 fn f() -> Self { Self(114514) }
1392}
1393fn main() {
1394 A::f$0();
1395}
1396"#,
1397 r#"
1398struct A(u32);
1399impl A {
1400 fn f() -> Self { Self(114514) }
1401}
1402fn main() {
1403 A(114514);
1404}
1405"#,
1406 )
1407 }
1408
1409 #[test]
1410 fn inline_call_with_self_type_but_within_same_impl() {
1411 check_assist(
1412 inline_call,
1413 r#"
1414struct A(u32);
1415impl A {
1416 fn f() -> Self { Self(1919810) }
1417 fn main() {
1418 Self::f$0();
1419 }
1420}
1421"#,
1422 r#"
1423struct A(u32);
1424impl A {
1425 fn f() -> Self { Self(1919810) }
1426 fn main() {
1427 Self(1919810);
1428 }
1429}
1430"#,
1431 )
1432 }
1433
1434 #[test]
1435 fn local_variable_shadowing_callers_argument() {
1436 check_assist(
1437 inline_call,
1438 r#"
1439fn foo(bar: u32, baz: u32) -> u32 {
1440 let a = 1;
1441 bar * baz * a * 6
1442}
1443fn main() {
1444 let a = 7;
1445 let b = 1;
1446 let res = foo$0(a, b);
1447}
1448"#,
1449 r#"
1450fn foo(bar: u32, baz: u32) -> u32 {
1451 let a = 1;
1452 bar * baz * a * 6
1453}
1454fn main() {
1455 let a = 7;
1456 let b = 1;
1457 let res = {
1458 let bar = a;
1459 let a = 1;
1460 bar * b * a * 6
1461 };
1462}
1463"#,
1464 );
1465 }
1466
1467 #[test]
1468 fn async_fn_single_expression() {
1469 cov_mark::check!(inline_call_async_fn);
1470 check_assist(
1471 inline_call,
1472 r#"
1473async fn bar(x: u32) -> u32 { x + 1 }
1474async fn foo(arg: u32) -> u32 {
1475 bar(arg).await * 2
1476}
1477fn spawn<T>(_: T) {}
1478fn main() {
1479 spawn(foo$0(42));
1480}
1481"#,
1482 r#"
1483async fn bar(x: u32) -> u32 { x + 1 }
1484async fn foo(arg: u32) -> u32 {
1485 bar(arg).await * 2
1486}
1487fn spawn<T>(_: T) {}
1488fn main() {
1489 spawn(async move {
1490 bar(42).await * 2
1491 });
1492}
1493"#,
1494 );
1495 }
1496
1497 #[test]
1498 fn async_fn_multiple_statements() {
1499 cov_mark::check!(inline_call_async_fn);
1500 check_assist(
1501 inline_call,
1502 r#"
1503async fn bar(x: u32) -> u32 { x + 1 }
1504async fn foo(arg: u32) -> u32 {
1505 bar(arg).await;
1506 42
1507}
1508fn spawn<T>(_: T) {}
1509fn main() {
1510 spawn(foo$0(42));
1511}
1512"#,
1513 r#"
1514async fn bar(x: u32) -> u32 { x + 1 }
1515async fn foo(arg: u32) -> u32 {
1516 bar(arg).await;
1517 42
1518}
1519fn spawn<T>(_: T) {}
1520fn main() {
1521 spawn(async move {
1522 bar(42).await;
1523 42
1524 });
1525}
1526"#,
1527 );
1528 }
1529
1530 #[test]
1531 fn async_fn_with_let_statements() {
1532 cov_mark::check!(inline_call_async_fn);
1533 cov_mark::check!(inline_call_async_fn_with_let_stmts);
1534 check_assist(
1535 inline_call,
1536 r#"
1537async fn bar(x: u32) -> u32 { x + 1 }
1538async fn foo(x: u32, y: u32, z: &u32) -> u32 {
1539 bar(x).await;
1540 y + y + *z
1541}
1542fn spawn<T>(_: T) {}
1543fn main() {
1544 let var = 42;
1545 spawn(foo$0(var, var + 1, &var));
1546}
1547"#,
1548 r#"
1549async fn bar(x: u32) -> u32 { x + 1 }
1550async fn foo(x: u32, y: u32, z: &u32) -> u32 {
1551 bar(x).await;
1552 y + y + *z
1553}
1554fn spawn<T>(_: T) {}
1555fn main() {
1556 let var = 42;
1557 spawn({
1558 let y = var + 1;
1559 let z: &u32 = &var;
1560 async move {
1561 bar(var).await;
1562 y + y + *z
1563 }
1564 });
1565}
1566"#,
1567 );
1568 }
1569
1570 #[test]
1571 fn inline_call_closure_body() {
1572 check_assist(
1573 inline_call,
1574 r#"
1575fn f() -> impl Fn() -> i32 {
1576 || 2
1577}
1578
1579fn main() {
1580 let _ = $0f()();
1581}
1582"#,
1583 r#"
1584fn f() -> impl Fn() -> i32 {
1585 || 2
1586}
1587
1588fn main() {
1589 let _ = (|| 2)();
1590}
1591"#,
1592 );
1593 }
1594
1595 #[test]
1596 fn inline_call_with_multiple_self_types_eq() {
1597 check_assist(
1598 inline_call,
1599 r#"
1600#[derive(PartialEq, Eq)]
1601enum Enum {
1602 A,
1603 B,
1604}
1605
1606impl Enum {
1607 fn a_or_b_eq(&self) -> bool {
1608 self == &Self::A || self == &Self::B
1609 }
1610}
1611
1612fn a() -> bool {
1613 Enum::A.$0a_or_b_eq()
1614}
1615"#,
1616 r#"
1617#[derive(PartialEq, Eq)]
1618enum Enum {
1619 A,
1620 B,
1621}
1622
1623impl Enum {
1624 fn a_or_b_eq(&self) -> bool {
1625 self == &Self::A || self == &Self::B
1626 }
1627}
1628
1629fn a() -> bool {
1630 {
1631 let this = &Enum::A;
1632 this == &Enum::A || this == &Enum::B
1633 }
1634}
1635"#,
1636 )
1637 }
1638
1639 #[test]
1640 fn inline_call_with_self_type_in_macros() {
1641 check_assist(
1642 inline_call,
1643 r#"
1644trait Trait<T1> {
1645 fn f(a: T1) -> Self;
1646}
1647
1648macro_rules! impl_from {
1649 ($t: ty) => {
1650 impl Trait<$t> for $t {
1651 fn f(a: $t) -> Self {
1652 a as Self
1653 }
1654 }
1655 };
1656}
1657
1658struct A {}
1659
1660impl_from!(A);
1661
1662fn main() {
1663 let a: A = A{};
1664 let b = <A as Trait<A>>::$0f(a);
1665}
1666"#,
1667 r#"
1668trait Trait<T1> {
1669 fn f(a: T1) -> Self;
1670}
1671
1672macro_rules! impl_from {
1673 ($t: ty) => {
1674 impl Trait<$t> for $t {
1675 fn f(a: $t) -> Self {
1676 a as Self
1677 }
1678 }
1679 };
1680}
1681
1682struct A {}
1683
1684impl_from!(A);
1685
1686fn main() {
1687 let a: A = A{};
1688 let b = {
1689 let a = a;
1690 a as A
1691 };
1692}
1693"#,
1694 )
1695 }
1696
1697 #[test]
1698 fn method_by_reborrow() {
1699 check_assist(
1700 inline_call,
1701 r#"
1702pub struct Foo(usize);
1703
1704impl Foo {
1705 fn add1(&mut self) {
1706 self.0 += 1;
1707 }
1708}
1709
1710pub fn main() {
1711 let f = &mut Foo(0);
1712 f.add1$0();
1713}
1714"#,
1715 r#"
1716pub struct Foo(usize);
1717
1718impl Foo {
1719 fn add1(&mut self) {
1720 self.0 += 1;
1721 }
1722}
1723
1724pub fn main() {
1725 let f = &mut Foo(0);
1726 {
1727 let this = &mut *f;
1728 this.0 += 1;
1729 };
1730}
1731"#,
1732 )
1733 }
1734
1735 #[test]
1736 fn method_by_mut() {
1737 check_assist(
1738 inline_call,
1739 r#"
1740pub struct Foo(usize);
1741
1742impl Foo {
1743 fn add1(mut self) {
1744 self.0 += 1;
1745 }
1746}
1747
1748pub fn main() {
1749 let mut f = Foo(0);
1750 f.add1$0();
1751}
1752"#,
1753 r#"
1754pub struct Foo(usize);
1755
1756impl Foo {
1757 fn add1(mut self) {
1758 self.0 += 1;
1759 }
1760}
1761
1762pub fn main() {
1763 let mut f = Foo(0);
1764 {
1765 let mut this = f;
1766 this.0 += 1;
1767 };
1768}
1769"#,
1770 )
1771 }
1772
1773 #[test]
1774 fn inline_call_with_reference_in_macros() {
1775 check_assist(
1776 inline_call,
1777 r#"
1778fn _write_u64(s: &mut u64, x: u64) {
1779 *s += x;
1780}
1781macro_rules! impl_write {
1782 ($(($ty:ident, $meth:ident),)*) => {$(
1783 fn _hash(inner_self_: &u64, state: &mut u64) {
1784 $meth(state, *inner_self_)
1785 }
1786 )*}
1787}
1788impl_write! { (u64, _write_u64), }
1789fn _hash2(self_: &u64, state: &mut u64) {
1790 $0_hash(&self_, state);
1791}
1792"#,
1793 r#"
1794fn _write_u64(s: &mut u64, x: u64) {
1795 *s += x;
1796}
1797macro_rules! impl_write {
1798 ($(($ty:ident, $meth:ident),)*) => {$(
1799 fn _hash(inner_self_: &u64, state: &mut u64) {
1800 $meth(state, *inner_self_)
1801 }
1802 )*}
1803}
1804impl_write! { (u64, _write_u64), }
1805fn _hash2(self_: &u64, state: &mut u64) {
1806 {
1807 let inner_self_: &u64 = &self_;
1808 let state: &mut u64 = state;
1809 _write_u64(state, *inner_self_)
1810 };
1811}
1812"#,
1813 )
1814 }
1815
1816 #[test]
1817 fn inline_into_callers_in_macros_not_applicable() {
1818 check_assist_not_applicable(
1819 inline_into_callers,
1820 r#"
1821fn foo() -> u32 {
1822 42
1823}
1824
1825macro_rules! bar {
1826 ($x:expr) => {
1827 $x
1828 };
1829}
1830
1831fn f() {
1832 bar!(foo$0());
1833}
1834"#,
1835 );
1836 }
1837}