1use std::collections::BTreeSet;23use 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};2829use crate::{30 AssistId,31 assist_context::{AssistContext, Assists},32};3334// Assist: inline_into_callers35//36// Inline a function or method body into all of its callers where possible, creating a `let` statement per parameter37// unless the parameter can be inlined. The parameter will be inlined either if it the supplied argument is a simple local38// or if the parameter is only accessed inside the function body once.39// If all calls can be inlined the function will be removed.40//41// ```42// fn print(_: &str) {}43// fn foo$0(word: &str) {44// if !word.is_empty() {45// print(word);46// }47// }48// fn bar() {49// foo("안녕하세요");50// foo("여러분");51// }52// ```53// ->54// ```55// fn print(_: &str) {}56//57// fn bar() {58// {59// let word: &str = "안녕하세요";60// if !word.is_empty() {61// print(word);62// }63// };64// {65// let word: &str = "여러분";66// if !word.is_empty() {67// print(word);68// }69// };70// }71// ```72pub(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()?;7980 let function = ctx.sema.to_def(&ast_func)?;8182 let params = get_fn_params(ctx.sema.db, function, ¶m_list)?;8384 let usages = Definition::Function(function).usages(&ctx.sema);85 if !usages.at_least_one() {86 return None;87 }8889 let is_recursive_fn = usages90 .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 }100101 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);108109 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 // The collects are required as we are otherwise iterating while mutating 🙅♀️🙅♂️116 let (name_refs, name_refs_use) = split_refs_and_uses(builder, refs, Some);117 let call_infos: Vec<_> = name_refs118 .into_iter()119 .filter_map(|it| CallInfo::from_name_ref(it, call_krate?.into()))120 // FIXME: do not handle callsites in macros' parameters, because121 // directly inlining into macros may cause errors.122 .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_infos129 .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 // we replaced all usages in this file, so we can remove the imports138 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}156157pub(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}173174// Assist: inline_call175//176// Inlines a function or method body creating a `let` statement per parameter unless the parameter177// can be inlined. The parameter will be inlined either if it the supplied argument is a simple local178// or if the parameter is only accessed inside the function body once.179//180// ```181// # //- minicore: option182// fn foo(name: Option<&str>) {183// let name = name.unwrap$0();184// }185// ```186// ->187// ```188// fn foo(name: Option<&str>) {189// let name = match name {190// Some(val) => val,191// None => panic!("called `Option::unwrap()` on a `None` value"),192// };193// }194// ```195pub(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 };217218 let fn_source = ctx.sema.source(function)?;219 let fn_body = fn_source.value.body()?;220 let param_list = fn_source.value.param_list()?;221222 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)?;228229 if call_info.arguments.len() != params.len() {230 // Can't inline the function because they've passed the wrong number of231 // arguments to this function232 cov_mark::hit!(inline_call_incorrect_number_of_arguments);233 return None;234 }235236 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}248249struct CallInfo {250 node: ast::CallableExpr,251 arguments: Vec<ast::Expr>,252 generic_arg_list: Option<ast::GenericArgList>,253 krate: Crate,254}255256impl 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)?;273274 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 None282 }283 }284}285286fn 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();292293 let mut params = Vec::new();294 if let Some(self_param) = param_list.self_param() {295 // Keep `ref` and `mut` and transform them into `&` and `mut` later296 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 }310311 Some(params)312}313314fn 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 body330 } 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 .references341 .remove(&function_def_file_id)342 .unwrap_or_default()343 .into_iter()344 };345 let param_use_nodes: Vec<Vec<_>> = params346 .iter()347 .map(|(pat, _, param)| {348 if !matches!(pat, ast::Pat::IdentPat(pat) if pat.is_simple_ident()) {349 return Vec::new();350 }351 // FIXME: we need to fetch all locals declared in the parameter here352 // not only the local if it is a simple binding353 match param.as_local(sema.db) {354 Some(l) => usages_for_locals(l)355 .map(|FileReference { name, range, .. }| match name {356 FileReferenceNode::NameRef(_) => body357 .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();370371 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 }390391 // We should place the following code after last usage of `usages_for_locals`392 // because `ted::replace` will change the offset in syntax tree, which makes393 // `FileReference` incorrect394 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) = body400 .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 if !is_in_type_path(&self_tok)407 && let Some(ty) = ast::Type::cast(replace_with.clone())408 && let Some(generic_arg_list) = ty.generic_arg_list()409 {410 ted::remove(generic_arg_list.syntax());411 }412 ted::replace(self_tok, replace_with);413 }414 }415416 let mut func_let_vars: BTreeSet<String> = BTreeSet::new();417418 // grab all of the local variable declarations in the function419 for stmt in fn_body.statements() {420 if let Some(let_stmt) = ast::LetStmt::cast(stmt.syntax().to_owned()) {421 for has_token in let_stmt.syntax().children_with_tokens() {422 if let Some(node) = has_token.as_node()423 && let Some(ident_pat) = ast::IdentPat::cast(node.to_owned())424 {425 func_let_vars.insert(ident_pat.syntax().text().to_string());426 }427 }428 }429 }430431 let mut let_stmts = Vec::new();432433 // Inline parameter expressions or generate `let` statements depending on whether inlining works or not.434 for ((pat, param_ty, param), usages, expr) in izip!(params, param_use_nodes, arguments) {435 // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors436 let usages: &[ast::PathExpr] = &usages;437 let expr: &ast::Expr = expr;438439 let mut insert_let_stmt = || {440 let param_ty = param_ty.clone().map(|param_ty| {441 let file_id = sema.hir_file_for(param_ty.syntax());442 if let Some(macro_file) = file_id.macro_file() {443 let span_map = sema.db.expansion_span_map(macro_file);444 let param_ty_prettified = prettify_macro_expansion(445 sema.db,446 param_ty.syntax().clone(),447 &span_map,448 *krate,449 );450 ast::Type::cast(param_ty_prettified).unwrap_or(param_ty)451 } else {452 param_ty453 }454 });455456 let ty = sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty);457458 let is_self = param.name(sema.db).is_some_and(|name| name == sym::self_);459460 if is_self {461 let mut this_pat = make::ident_pat(false, false, make::name("this"));462 let mut expr = expr.clone();463 if let Pat::IdentPat(pat) = pat {464 match (pat.ref_token(), pat.mut_token()) {465 // self => let this = obj466 (None, None) => {}467 // mut self => let mut this = obj468 (None, Some(_)) => {469 this_pat = make::ident_pat(false, true, make::name("this"));470 }471 // &self => let this = &obj472 (Some(_), None) => {473 expr = make::expr_ref(expr, false);474 }475 // let foo = &mut X; &mut self => let this = &mut obj476 // let mut foo = X; &mut self => let this = &mut *obj (reborrow)477 (Some(_), Some(_)) => {478 let should_reborrow = sema479 .type_of_expr(&expr)480 .map(|ty| ty.original.is_mutable_reference());481 expr = if let Some(true) = should_reborrow {482 make::expr_reborrow(expr)483 } else {484 make::expr_ref(expr, true)485 };486 }487 }488 };489 let_stmts490 .push(make::let_stmt(this_pat.into(), ty, Some(expr)).clone_for_update().into())491 } else {492 let_stmts.push(493 make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(),494 );495 }496 };497498 // check if there is a local var in the function that conflicts with parameter499 // if it does then emit a let statement and continue500 if func_let_vars.contains(&expr.syntax().text().to_string()) {501 insert_let_stmt();502 continue;503 }504505 let inline_direct = |usage, replacement: &ast::Expr| {506 if let Some(field) = path_expr_as_record_field(usage) {507 cov_mark::hit!(inline_call_inline_direct_field);508 field.replace_expr(replacement.clone_for_update());509 } else {510 ted::replace(usage.syntax(), replacement.syntax().clone_for_update());511 }512 };513514 match usages {515 // inline single use closure arguments516 [usage]517 if matches!(expr, ast::Expr::ClosureExpr(_))518 && usage.syntax().parent().and_then(ast::Expr::cast).is_some() =>519 {520 cov_mark::hit!(inline_call_inline_closure);521 let expr = make::expr_paren(expr.clone()).into();522 inline_direct(usage, &expr);523 }524 // inline single use literals525 [usage] if matches!(expr, ast::Expr::Literal(_)) => {526 cov_mark::hit!(inline_call_inline_literal);527 inline_direct(usage, expr);528 }529 // inline direct local arguments530 [_, ..] if expr_as_name_ref(expr).is_some() => {531 cov_mark::hit!(inline_call_inline_locals);532 usages.iter().for_each(|usage| inline_direct(usage, expr));533 }534 // can't inline, emit a let statement535 _ => {536 insert_let_stmt();537 }538 }539 }540541 if let Some(generic_arg_list) = generic_arg_list.clone()542 && let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax()))543 {544 body.reindent_to(IndentLevel(0));545 if let Some(new_body) = ast::BlockExpr::cast(546 PathTransform::function_call(target, source, function, generic_arg_list)547 .apply(body.syntax()),548 ) {549 body = new_body;550 }551 }552553 let is_async_fn = function.is_async(sema.db);554 if is_async_fn {555 cov_mark::hit!(inline_call_async_fn);556 body = make::async_move_block_expr(body.statements(), body.tail_expr()).clone_for_update();557558 // Arguments should be evaluated outside the async block, and then moved into it.559 if !let_stmts.is_empty() {560 cov_mark::hit!(inline_call_async_fn_with_let_stmts);561 body.indent(IndentLevel(1));562 body = make::block_expr(let_stmts, Some(body.into())).clone_for_update();563 }564 } else if let Some(stmt_list) = body.stmt_list() {565 let position = stmt_list.l_curly_token().expect("L_CURLY for StatementList is missing.");566 let_stmts.into_iter().rev().for_each(|let_stmt| {567 ted::insert(ted::Position::after(position.clone()), let_stmt.syntax().clone());568 });569 }570571 let original_indentation = match node {572 ast::CallableExpr::Call(it) => it.indent_level(),573 ast::CallableExpr::MethodCall(it) => it.indent_level(),574 };575 body.reindent_to(original_indentation);576577 let no_stmts = body.statements().next().is_none();578 match body.tail_expr() {579 Some(expr) if matches!(expr, ast::Expr::ClosureExpr(_)) && no_stmts => {580 make::expr_paren(expr).clone_for_update().into()581 }582 Some(expr) if !is_async_fn && no_stmts => expr,583 _ => match node584 .syntax()585 .parent()586 .and_then(ast::BinExpr::cast)587 .and_then(|bin_expr| bin_expr.lhs())588 {589 Some(lhs) if lhs.syntax() == node.syntax() => {590 make::expr_paren(ast::Expr::BlockExpr(body)).clone_for_update().into()591 }592 _ => ast::Expr::BlockExpr(body),593 },594 }595}596597fn is_in_type_path(self_tok: &syntax::SyntaxToken) -> bool {598 self_tok599 .parent_ancestors()600 .skip_while(|it| !ast::Path::can_cast(it.kind()))601 .map_while(ast::Path::cast)602 .last()603 .and_then(|it| it.syntax().parent())604 .and_then(ast::PathType::cast)605 .is_some()606}607608fn path_expr_as_record_field(usage: &PathExpr) -> Option<ast::RecordExprField> {609 let path = usage.path()?;610 let name_ref = path.as_single_name_ref()?;611 ast::RecordExprField::for_name_ref(&name_ref)612}613614#[cfg(test)]615mod tests {616 use crate::tests::{check_assist, check_assist_not_applicable};617618 use super::*;619620 #[test]621 fn no_args_or_return_value_gets_inlined_without_block() {622 check_assist(623 inline_call,624 r#"625fn foo() { println!("Hello, World!"); }626fn main() {627 fo$0o();628}629"#,630 r#"631fn foo() { println!("Hello, World!"); }632fn main() {633 { println!("Hello, World!"); };634}635"#,636 );637 }638639 #[test]640 fn not_applicable_when_incorrect_number_of_parameters_are_provided() {641 cov_mark::check!(inline_call_incorrect_number_of_arguments);642 check_assist_not_applicable(643 inline_call,644 r#"645fn add(a: u32, b: u32) -> u32 { a + b }646fn main() { let x = add$0(42); }647"#,648 );649 }650651 #[test]652 fn args_with_side_effects() {653 check_assist(654 inline_call,655 r#"656fn foo(name: String) {657 println!("Hello, {}!", name);658}659fn main() {660 foo$0(String::from("Michael"));661}662"#,663 r#"664fn foo(name: String) {665 println!("Hello, {}!", name);666}667fn main() {668 {669 let name = String::from("Michael");670 println!("Hello, {}!", name);671 };672}673"#,674 );675 }676677 #[test]678 fn function_with_multiple_statements() {679 check_assist(680 inline_call,681 r#"682fn foo(a: u32, b: u32) -> u32 {683 let x = a + b;684 let y = x - b;685 x * y686}687688fn main() {689 let x = foo$0(1, 2);690}691"#,692 r#"693fn foo(a: u32, b: u32) -> u32 {694 let x = a + b;695 let y = x - b;696 x * y697}698699fn main() {700 let x = {701 let b = 2;702 let x = 1 + b;703 let y = x - b;704 x * y705 };706}707"#,708 );709 }710711 #[test]712 fn function_with_self_param() {713 check_assist(714 inline_call,715 r#"716struct Foo(u32);717718impl Foo {719 fn add(self, a: u32) -> Self {720 Foo(self.0 + a)721 }722}723724fn main() {725 let x = Foo::add$0(Foo(3), 2);726}727"#,728 r#"729struct Foo(u32);730731impl Foo {732 fn add(self, a: u32) -> Self {733 Foo(self.0 + a)734 }735}736737fn main() {738 let x = {739 let this = Foo(3);740 Foo(this.0 + 2)741 };742}743"#,744 );745 }746747 #[test]748 fn method_by_val() {749 check_assist(750 inline_call,751 r#"752struct Foo(u32);753754impl Foo {755 fn add(self, a: u32) -> Self {756 Foo(self.0 + a)757 }758}759760fn main() {761 let x = Foo(3).add$0(2);762}763"#,764 r#"765struct Foo(u32);766767impl Foo {768 fn add(self, a: u32) -> Self {769 Foo(self.0 + a)770 }771}772773fn main() {774 let x = {775 let this = Foo(3);776 Foo(this.0 + 2)777 };778}779"#,780 );781 }782783 #[test]784 fn method_by_ref() {785 check_assist(786 inline_call,787 r#"788struct Foo(u32);789790impl Foo {791 fn add(&self, a: u32) -> Self {792 Foo(self.0 + a)793 }794}795796fn main() {797 let x = Foo(3).add$0(2);798}799"#,800 r#"801struct Foo(u32);802803impl Foo {804 fn add(&self, a: u32) -> Self {805 Foo(self.0 + a)806 }807}808809fn main() {810 let x = {811 let this = &Foo(3);812 Foo(this.0 + 2)813 };814}815"#,816 );817 }818819 #[test]820 fn generic_method_by_ref() {821 check_assist(822 inline_call,823 r#"824struct Foo(u32);825826impl Foo {827 fn add<T>(&self, a: u32) -> Self {828 Foo(self.0 + a)829 }830}831832fn main() {833 let x = Foo(3).add$0::<usize>(2);834}835"#,836 r#"837struct Foo(u32);838839impl Foo {840 fn add<T>(&self, a: u32) -> Self {841 Foo(self.0 + a)842 }843}844845fn main() {846 let x = {847 let this = &Foo(3);848 Foo(this.0 + 2)849 };850}851"#,852 );853 }854855 #[test]856 fn method_by_ref_mut() {857 check_assist(858 inline_call,859 r#"860struct Foo(u32);861862impl Foo {863 fn clear(&mut self) {864 self.0 = 0;865 }866}867868fn main() {869 let mut foo = Foo(3);870 foo.clear$0();871}872"#,873 r#"874struct Foo(u32);875876impl Foo {877 fn clear(&mut self) {878 self.0 = 0;879 }880}881882fn main() {883 let mut foo = Foo(3);884 {885 let this = &mut foo;886 this.0 = 0;887 };888}889"#,890 );891 }892893 #[test]894 fn function_multi_use_expr_in_param() {895 check_assist(896 inline_call,897 r#"898fn square(x: u32) -> u32 {899 x * x900}901fn main() {902 let x = 51;903 let y = square$0(10 + x);904}905"#,906 r#"907fn square(x: u32) -> u32 {908 x * x909}910fn main() {911 let x = 51;912 let y = {913 let x = 10 + x;914 x * x915 };916}917"#,918 );919 }920921 #[test]922 fn function_use_local_in_param() {923 cov_mark::check!(inline_call_inline_locals);924 check_assist(925 inline_call,926 r#"927fn square(x: u32) -> u32 {928 x * x929}930fn main() {931 let local = 51;932 let y = square$0(local);933}934"#,935 r#"936fn square(x: u32) -> u32 {937 x * x938}939fn main() {940 let local = 51;941 let y = local * local;942}943"#,944 );945 }946947 #[test]948 fn method_in_impl() {949 check_assist(950 inline_call,951 r#"952struct Foo;953impl Foo {954 fn foo(&self) {955 self;956 self;957 }958 fn bar(&self) {959 self.foo$0();960 }961}962"#,963 r#"964struct Foo;965impl Foo {966 fn foo(&self) {967 self;968 self;969 }970 fn bar(&self) {971 {972 let this = &self;973 this;974 this;975 };976 }977}978"#,979 );980 }981982 #[test]983 fn wraps_closure_in_paren() {984 cov_mark::check!(inline_call_inline_closure);985 check_assist(986 inline_call,987 r#"988fn foo(x: fn()) {989 x();990}991992fn main() {993 foo$0(|| {})994}995"#,996 r#"997fn foo(x: fn()) {998 x();999}10001001fn main() {1002 {1003 (|| {})();1004 }1005}1006"#,1007 );1008 check_assist(1009 inline_call,1010 r#"1011fn foo(x: fn()) {1012 x();1013}10141015fn main() {1016 foo$0(main)1017}1018"#,1019 r#"1020fn foo(x: fn()) {1021 x();1022}10231024fn main() {1025 {1026 main();1027 }1028}1029"#,1030 );1031 }10321033 #[test]1034 fn inline_single_literal_expr() {1035 cov_mark::check!(inline_call_inline_literal);1036 check_assist(1037 inline_call,1038 r#"1039fn foo(x: u32) -> u32{1040 x1041}10421043fn main() {1044 foo$0(222);1045}1046"#,1047 r#"1048fn foo(x: u32) -> u32{1049 x1050}10511052fn main() {1053 222;1054}1055"#,1056 );1057 }10581059 #[test]1060 fn inline_emits_type_for_coercion() {1061 check_assist(1062 inline_call,1063 r#"1064//- minicore: sized1065fn foo(x: *const u32) -> u32 {1066 x as u321067}10681069fn main() {1070 foo$0(&222);1071}1072"#,1073 r#"1074fn foo(x: *const u32) -> u32 {1075 x as u321076}10771078fn main() {1079 {1080 let x: *const u32 = &222;1081 x as u321082 };1083}1084"#,1085 );1086 }10871088 #[test]1089 fn inline_substitutes_generics() {1090 check_assist(1091 inline_call,1092 r#"1093fn foo<T, const N: usize>() {1094 bar::<T, N>()1095}10961097fn bar<U, const M: usize>() {}10981099fn main() {1100 foo$0::<usize, {0}>();1101}1102"#,1103 r#"1104fn foo<T, const N: usize>() {1105 bar::<T, N>()1106}11071108fn bar<U, const M: usize>() {}11091110fn main() {1111 bar::<usize, {0}>();1112}1113"#,1114 );1115 }11161117 #[test]1118 fn inline_callers() {1119 check_assist(1120 inline_into_callers,1121 r#"1122fn do_the_math$0(b: u32) -> u32 {1123 let foo = 10;1124 foo * b + foo1125}1126fn foo() {1127 do_the_math(0);1128 let bar = 10;1129 do_the_math(bar);1130}1131"#,1132 r#"11331134fn foo() {1135 {1136 let foo = 10;1137 foo * 0 + foo1138 };1139 let bar = 10;1140 {1141 let foo = 10;1142 foo * bar + foo1143 };1144}1145"#,1146 );1147 }11481149 #[test]1150 fn inline_callers_across_files() {1151 check_assist(1152 inline_into_callers,1153 r#"1154//- /lib.rs1155mod foo;1156fn do_the_math$0(b: u32) -> u32 {1157 let foo = 10;1158 foo * b + foo1159}1160//- /foo.rs1161use super::do_the_math;1162fn foo() {1163 do_the_math(0);1164 let bar = 10;1165 do_the_math(bar);1166}1167"#,1168 r#"1169//- /lib.rs1170mod foo;11711172//- /foo.rs1173fn foo() {1174 {1175 let foo = 10;1176 foo * 0 + foo1177 };1178 let bar = 10;1179 {1180 let foo = 10;1181 foo * bar + foo1182 };1183}1184"#,1185 );1186 }11871188 #[test]1189 fn inline_callers_across_files_with_def_file() {1190 check_assist(1191 inline_into_callers,1192 r#"1193//- /lib.rs1194mod foo;1195fn do_the_math$0(b: u32) -> u32 {1196 let foo = 10;1197 foo * b + foo1198}1199fn bar(a: u32, b: u32) -> u32 {1200 do_the_math(0);1201}1202//- /foo.rs1203use super::do_the_math;1204fn foo() {1205 do_the_math(0);1206}1207"#,1208 r#"1209//- /lib.rs1210mod foo;12111212fn bar(a: u32, b: u32) -> u32 {1213 {1214 let foo = 10;1215 foo * 0 + foo1216 };1217}1218//- /foo.rs1219fn foo() {1220 {1221 let foo = 10;1222 foo * 0 + foo1223 };1224}1225"#,1226 );1227 }12281229 #[test]1230 fn inline_callers_recursive() {1231 cov_mark::check!(inline_into_callers_recursive);1232 check_assist_not_applicable(1233 inline_into_callers,1234 r#"1235fn foo$0() {1236 foo();1237}1238"#,1239 );1240 }12411242 #[test]1243 fn inline_call_recursive() {1244 cov_mark::check!(inline_call_recursive);1245 check_assist_not_applicable(1246 inline_call,1247 r#"1248fn foo() {1249 foo$0();1250}1251"#,1252 );1253 }12541255 #[test]1256 fn inline_call_field_shorthand() {1257 cov_mark::check!(inline_call_inline_direct_field);1258 check_assist(1259 inline_call,1260 r#"1261struct Foo {1262 field: u32,1263 field1: u32,1264 field2: u32,1265 field3: u32,1266}1267fn foo(field: u32, field1: u32, val2: u32, val3: u32) -> Foo {1268 Foo {1269 field,1270 field1,1271 field2: val2,1272 field3: val3,1273 }1274}1275fn main() {1276 let bar = 0;1277 let baz = 0;1278 foo$0(bar, 0, baz, 0);1279}1280"#,1281 r#"1282struct Foo {1283 field: u32,1284 field1: u32,1285 field2: u32,1286 field3: u32,1287}1288fn foo(field: u32, field1: u32, val2: u32, val3: u32) -> Foo {1289 Foo {1290 field,1291 field1,1292 field2: val2,1293 field3: val3,1294 }1295}1296fn main() {1297 let bar = 0;1298 let baz = 0;1299 Foo {1300 field: bar,1301 field1: 0,1302 field2: baz,1303 field3: 0,1304 };1305}1306"#,1307 );1308 }13091310 #[test]1311 fn inline_callers_wrapped_in_parentheses() {1312 check_assist(1313 inline_into_callers,1314 r#"1315fn foo$0() -> u32 {1316 let x = 0;1317 x1318}1319fn bar() -> u32 {1320 foo() + foo()1321}1322"#,1323 r#"13241325fn bar() -> u32 {1326 ({1327 let x = 0;1328 x1329 }) + {1330 let x = 0;1331 x1332 }1333}1334"#,1335 )1336 }13371338 #[test]1339 fn inline_call_wrapped_in_parentheses() {1340 check_assist(1341 inline_call,1342 r#"1343fn foo() -> u32 {1344 let x = 0;1345 x1346}1347fn bar() -> u32 {1348 foo$0() + foo()1349}1350"#,1351 r#"1352fn foo() -> u32 {1353 let x = 0;1354 x1355}1356fn bar() -> u32 {1357 ({1358 let x = 0;1359 x1360 }) + foo()1361}1362"#,1363 )1364 }13651366 #[test]1367 fn inline_call_defined_in_macro() {1368 cov_mark::check!(inline_call_defined_in_macro);1369 check_assist(1370 inline_call,1371 r#"1372macro_rules! define_foo {1373 () => { fn foo() -> u32 {1374 let x = 0;1375 x1376 } };1377}1378define_foo!();1379fn bar() -> u32 {1380 foo$0()1381}1382"#,1383 r#"1384macro_rules! define_foo {1385 () => { fn foo() -> u32 {1386 let x = 0;1387 x1388 } };1389}1390define_foo!();1391fn bar() -> u32 {1392 {1393 let x = 0;1394 x1395 }1396}1397"#,1398 )1399 }14001401 #[test]1402 fn inline_call_with_self_type() {1403 check_assist(1404 inline_call,1405 r#"1406struct A(u32);1407impl A {1408 fn f() -> Self { Self(114514) }1409}1410fn main() {1411 A::f$0();1412}1413"#,1414 r#"1415struct A(u32);1416impl A {1417 fn f() -> Self { Self(114514) }1418}1419fn main() {1420 A(114514);1421}1422"#,1423 )1424 }14251426 #[test]1427 fn inline_call_with_self_type_but_within_same_impl() {1428 check_assist(1429 inline_call,1430 r#"1431struct A(u32);1432impl A {1433 fn f() -> Self { Self(1919810) }1434 fn main() {1435 Self::f$0();1436 }1437}1438"#,1439 r#"1440struct A(u32);1441impl A {1442 fn f() -> Self { Self(1919810) }1443 fn main() {1444 Self(1919810);1445 }1446}1447"#,1448 )1449 }14501451 #[test]1452 fn local_variable_shadowing_callers_argument() {1453 check_assist(1454 inline_call,1455 r#"1456fn foo(bar: u32, baz: u32) -> u32 {1457 let a = 1;1458 bar * baz * a * 61459}1460fn main() {1461 let a = 7;1462 let b = 1;1463 let res = foo$0(a, b);1464}1465"#,1466 r#"1467fn foo(bar: u32, baz: u32) -> u32 {1468 let a = 1;1469 bar * baz * a * 61470}1471fn main() {1472 let a = 7;1473 let b = 1;1474 let res = {1475 let bar = a;1476 let a = 1;1477 bar * b * a * 61478 };1479}1480"#,1481 );1482 }14831484 #[test]1485 fn async_fn_single_expression() {1486 cov_mark::check!(inline_call_async_fn);1487 check_assist(1488 inline_call,1489 r#"1490async fn bar(x: u32) -> u32 { x + 1 }1491async fn foo(arg: u32) -> u32 {1492 bar(arg).await * 21493}1494fn spawn<T>(_: T) {}1495fn main() {1496 spawn(foo$0(42));1497}1498"#,1499 r#"1500async fn bar(x: u32) -> u32 { x + 1 }1501async fn foo(arg: u32) -> u32 {1502 bar(arg).await * 21503}1504fn spawn<T>(_: T) {}1505fn main() {1506 spawn(async move {1507 bar(42).await * 21508 });1509}1510"#,1511 );1512 }15131514 #[test]1515 fn async_fn_multiple_statements() {1516 cov_mark::check!(inline_call_async_fn);1517 check_assist(1518 inline_call,1519 r#"1520async fn bar(x: u32) -> u32 { x + 1 }1521async fn foo(arg: u32) -> u32 {1522 bar(arg).await;1523 421524}1525fn spawn<T>(_: T) {}1526fn main() {1527 spawn(foo$0(42));1528}1529"#,1530 r#"1531async fn bar(x: u32) -> u32 { x + 1 }1532async fn foo(arg: u32) -> u32 {1533 bar(arg).await;1534 421535}1536fn spawn<T>(_: T) {}1537fn main() {1538 spawn(async move {1539 bar(42).await;1540 421541 });1542}1543"#,1544 );1545 }15461547 #[test]1548 fn async_fn_with_let_statements() {1549 cov_mark::check!(inline_call_async_fn);1550 cov_mark::check!(inline_call_async_fn_with_let_stmts);1551 check_assist(1552 inline_call,1553 r#"1554async fn bar(x: u32) -> u32 { x + 1 }1555async fn foo(x: u32, y: u32, z: &u32) -> u32 {1556 bar(x).await;1557 y + y + *z1558}1559fn spawn<T>(_: T) {}1560fn main() {1561 let var = 42;1562 spawn(foo$0(var, var + 1, &var));1563}1564"#,1565 r#"1566async fn bar(x: u32) -> u32 { x + 1 }1567async fn foo(x: u32, y: u32, z: &u32) -> u32 {1568 bar(x).await;1569 y + y + *z1570}1571fn spawn<T>(_: T) {}1572fn main() {1573 let var = 42;1574 spawn({1575 let y = var + 1;1576 let z: &u32 = &var;1577 async move {1578 bar(var).await;1579 y + y + *z1580 }1581 });1582}1583"#,1584 );1585 }15861587 #[test]1588 fn inline_call_closure_body() {1589 check_assist(1590 inline_call,1591 r#"1592fn f() -> impl Fn() -> i32 {1593 || 21594}15951596fn main() {1597 let _ = $0f()();1598}1599"#,1600 r#"1601fn f() -> impl Fn() -> i32 {1602 || 21603}16041605fn main() {1606 let _ = (|| 2)();1607}1608"#,1609 );1610 }16111612 #[test]1613 fn inline_call_with_multiple_self_types_eq() {1614 check_assist(1615 inline_call,1616 r#"1617#[derive(PartialEq, Eq)]1618enum Enum {1619 A,1620 B,1621}16221623impl Enum {1624 fn a_or_b_eq(&self) -> bool {1625 self == &Self::A || self == &Self::B1626 }1627}16281629fn a() -> bool {1630 Enum::A.$0a_or_b_eq()1631}1632"#,1633 r#"1634#[derive(PartialEq, Eq)]1635enum Enum {1636 A,1637 B,1638}16391640impl Enum {1641 fn a_or_b_eq(&self) -> bool {1642 self == &Self::A || self == &Self::B1643 }1644}16451646fn a() -> bool {1647 {1648 let this = &Enum::A;1649 this == &Enum::A || this == &Enum::B1650 }1651}1652"#,1653 )1654 }16551656 #[test]1657 fn inline_call_with_self_type_in_macros() {1658 check_assist(1659 inline_call,1660 r#"1661trait Trait<T1> {1662 fn f(a: T1) -> Self;1663}16641665macro_rules! impl_from {1666 ($t: ty) => {1667 impl Trait<$t> for $t {1668 fn f(a: $t) -> Self {1669 a as Self1670 }1671 }1672 };1673}16741675struct A {}16761677impl_from!(A);16781679fn main() {1680 let a: A = A{};1681 let b = <A as Trait<A>>::$0f(a);1682}1683"#,1684 r#"1685trait Trait<T1> {1686 fn f(a: T1) -> Self;1687}16881689macro_rules! impl_from {1690 ($t: ty) => {1691 impl Trait<$t> for $t {1692 fn f(a: $t) -> Self {1693 a as Self1694 }1695 }1696 };1697}16981699struct A {}17001701impl_from!(A);17021703fn main() {1704 let a: A = A{};1705 let b = {1706 let a = a;1707 a as A1708 };1709}1710"#,1711 )1712 }17131714 #[test]1715 fn inline_trait_method_call_with_lifetimes() {1716 check_assist(1717 inline_call,1718 r#"1719trait Trait {1720 fn f() -> Self;1721}1722struct Foo<'a>(&'a ());1723impl<'a> Trait for Foo<'a> {1724 fn f() -> Self { Self(&()) }1725}1726impl Foo<'_> {1727 fn new() -> Self {1728 Self::$0f()1729 }1730}1731"#,1732 r#"1733trait Trait {1734 fn f() -> Self;1735}1736struct Foo<'a>(&'a ());1737impl<'a> Trait for Foo<'a> {1738 fn f() -> Self { Self(&()) }1739}1740impl Foo<'_> {1741 fn new() -> Self {1742 Foo(&())1743 }1744}1745"#,1746 )1747 }17481749 #[test]1750 fn method_by_reborrow() {1751 check_assist(1752 inline_call,1753 r#"1754pub struct Foo(usize);17551756impl Foo {1757 fn add1(&mut self) {1758 self.0 += 1;1759 }1760}17611762pub fn main() {1763 let f = &mut Foo(0);1764 f.add1$0();1765}1766"#,1767 r#"1768pub struct Foo(usize);17691770impl Foo {1771 fn add1(&mut self) {1772 self.0 += 1;1773 }1774}17751776pub fn main() {1777 let f = &mut Foo(0);1778 {1779 let this = &mut *f;1780 this.0 += 1;1781 };1782}1783"#,1784 )1785 }17861787 #[test]1788 fn method_by_mut() {1789 check_assist(1790 inline_call,1791 r#"1792pub struct Foo(usize);17931794impl Foo {1795 fn add1(mut self) {1796 self.0 += 1;1797 }1798}17991800pub fn main() {1801 let mut f = Foo(0);1802 f.add1$0();1803}1804"#,1805 r#"1806pub struct Foo(usize);18071808impl Foo {1809 fn add1(mut self) {1810 self.0 += 1;1811 }1812}18131814pub fn main() {1815 let mut f = Foo(0);1816 {1817 let mut this = f;1818 this.0 += 1;1819 };1820}1821"#,1822 )1823 }18241825 #[test]1826 fn inline_call_with_reference_in_macros() {1827 check_assist(1828 inline_call,1829 r#"1830fn _write_u64(s: &mut u64, x: u64) {1831 *s += x;1832}1833macro_rules! impl_write {1834 ($(($ty:ident, $meth:ident),)*) => {$(1835 fn _hash(inner_self_: &u64, state: &mut u64) {1836 $meth(state, *inner_self_)1837 }1838 )*}1839}1840impl_write! { (u64, _write_u64), }1841fn _hash2(self_: &u64, state: &mut u64) {1842 $0_hash(&self_, state);1843}1844"#,1845 r#"1846fn _write_u64(s: &mut u64, x: u64) {1847 *s += x;1848}1849macro_rules! impl_write {1850 ($(($ty:ident, $meth:ident),)*) => {$(1851 fn _hash(inner_self_: &u64, state: &mut u64) {1852 $meth(state, *inner_self_)1853 }1854 )*}1855}1856impl_write! { (u64, _write_u64), }1857fn _hash2(self_: &u64, state: &mut u64) {1858 {1859 let inner_self_: &u64 = &self_;1860 let state: &mut u64 = state;1861 _write_u64(state, *inner_self_)1862 };1863}1864"#,1865 )1866 }18671868 #[test]1869 fn inline_into_callers_in_macros_not_applicable() {1870 check_assist_not_applicable(1871 inline_into_callers,1872 r#"1873fn foo() -> u32 {1874 421875}18761877macro_rules! bar {1878 ($x:expr) => {1879 $x1880 };1881}18821883fn f() {1884 bar!(foo$0());1885}1886"#,1887 );1888 }1889}