Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
params[0]
1use std::collections::BTreeSet;23use either::Either;4use hir::{FileRange, PathResolution, Semantics, TypeInfo, db::HirDatabase, sym};5use ide_db::{6 EditionedFileId, FxHashMap, RootDatabase,7 base_db::Crate,8 defs::Definition,9 imports::insert_use::remove_use_tree_if_simple,10 path_transform::PathTransform,11 search::{FileReference, FileReferenceNode, SearchScope},12 syntax_helpers::{node_ext::expr_as_name_ref, prettify_macro_expansion},13};14use itertools::{Itertools, izip};15use syntax::{16 AstNode, NodeOrToken, SyntaxKind, TextRange,17 ast::{18 self, HasArgList, HasGenericArgs, Pat, PathExpr,19 edit::{AstNodeEdit, IndentLevel},20 syntax_factory::SyntaxFactory,21 },22 syntax_editor::SyntaxEditor,23};2425use crate::{26 AssistId,27 assist_context::{AssistContext, Assists},28};2930// Assist: inline_into_callers31//32// Inline a function or method body into all of its callers where possible, creating a `let` statement per parameter33// unless the parameter can be inlined. The parameter will be inlined either if it the supplied argument is a simple local34// or if the parameter is only accessed inside the function body once.35// If all calls can be inlined the function will be removed.36//37// ```38// fn print(_: &str) {}39// fn foo$0(word: &str) {40// if !word.is_empty() {41// print(word);42// }43// }44// fn bar() {45// foo("안녕하세요");46// foo("여러분");47// }48// ```49// ->50// ```51// fn print(_: &str) {}52//53// fn bar() {54// {55// let word: &str = "안녕하세요";56// if !word.is_empty() {57// print(word);58// }59// };60// {61// let word: &str = "여러분";62// if !word.is_empty() {63// print(word);64// }65// };66// }67// ```68pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {69 let def_file = ctx.file_id();70 let vfs_def_file = ctx.vfs_file_id();71 let name = ctx.find_node_at_offset::<ast::Name>()?;72 let ast_func = name.syntax().parent().and_then(ast::Fn::cast)?;73 let func_body = ast_func.body()?;74 let param_list = ast_func.param_list()?;7576 let function = ctx.sema.to_def(&ast_func)?;7778 let def_file_editor = SyntaxEditor::new(ast_func.syntax().tree_top()).0;79 let params = get_fn_params(ctx.sema.db, function, ¶m_list, def_file_editor.make())?;8081 let mut file_editors = FxHashMap::default();82 file_editors.insert(vfs_def_file, def_file_editor);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 let (name_refs, use_trees) = split_refs_and_uses(refs, Some);116 let call_infos: Vec<_> = name_refs117 .into_iter()118 .filter_map(|it| CallInfo::from_name_ref(it, call_krate?.into()))119 // FIXME: do not handle callsites in macros' parameters, because120 // directly inlining into macros may cause errors.121 .filter(|call_info| !ctx.sema.hir_file_for(call_info.node.syntax()).is_macro())122 .collect();123124 // Skip calls nested inside other calls being inlined to avoid overlapping125 // edits. Nested calls are implicitly replaced when the outer call is inlined.126 let all_ranges: Vec<TextRange> =127 call_infos.iter().map(|ci| ci.node.syntax().text_range()).collect();128 let (call_infos, nested_infos): (Vec<_>, Vec<_>) =129 call_infos.into_iter().partition(|ci| {130 let r = ci.node.syntax().text_range();131 !all_ranges.iter().any(|&other| other != r && other.contains_range(r))132 });133 let nested_count = nested_infos.len();134135 let anchor = call_infos136 .first()137 .map(|ci| ci.node.syntax().clone())138 .or_else(|| use_trees.first().map(|ut| ut.syntax().clone()));139 if let Some(anchor) = anchor {140 let editor =141 file_editors.entry(file_id).or_insert_with(|| builder.make_editor(&anchor));142 let replaced = call_infos143 .into_iter()144 .map(|call_info| {145 let indent = call_info.node.indent_level();146 let replacement = inline(147 &ctx.sema, def_file, function, &func_body, ¶ms, &call_info,148 editor, indent,149 );150 editor.replace(call_info.node.syntax(), replacement.syntax());151 })152 .count();153 if replaced + nested_count + use_trees.len() == count {154 // we replaced all usages in this file, so we can remove the imports155 for use_tree in &use_trees {156 remove_use_tree_if_simple(use_tree, editor);157 }158 } else {159 remove_def = false;160 }161 } else if use_trees.len() != count {162 remove_def = false;163 }164 };165 for (file_id, refs) in usages.into_iter() {166 inline_refs_for_file(file_id, refs);167 }168 match current_file_usage {169 Some(refs) => inline_refs_for_file(def_file, refs),170 None => builder.edit_file(vfs_def_file),171 }172 if remove_def {173 file_editors174 .entry(vfs_def_file)175 .or_insert_with(|| builder.make_editor(ast_func.syntax()))176 .delete(ast_func.syntax());177 }178 for (file_id, editor) in file_editors {179 builder.add_file_edits(file_id, editor);180 }181 },182 )183}184185pub(super) fn split_refs_and_uses<T: ast::AstNode>(186 iter: impl IntoIterator<Item = FileReference>,187 mut map_ref: impl FnMut(ast::NameRef) -> Option<T>,188) -> (Vec<T>, Vec<ast::UseTree>) {189 iter.into_iter()190 .filter_map(|file_ref| match file_ref.name {191 FileReferenceNode::NameRef(name_ref) => Some(name_ref),192 _ => None,193 })194 .filter_map(|name_ref| match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) {195 Some(use_tree) => Some(Either::Right(use_tree)),196 None => map_ref(name_ref).map(Either::Left),197 })198 .partition_map(|either| either)199}200201// Assist: inline_call202//203// Inlines a function or method body creating a `let` statement per parameter unless the parameter204// can be inlined. The parameter will be inlined either if it the supplied argument is a simple local205// or if the parameter is only accessed inside the function body once.206//207// ```208// # //- minicore: option209// fn foo(name: Option<&str>) {210// let name = name.unwrap$0();211// }212// ```213// ->214// ```215// fn foo(name: Option<&str>) {216// let name = match name {217// Some(val) => val,218// None => panic!("called `Option::unwrap()` on a `None` value"),219// };220// }221// ```222pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {223 let name_ref: ast::NameRef = ctx.find_node_at_offset_with_descend()?;224 let call_info = CallInfo::from_name_ref(225 name_ref.clone(),226 ctx.sema.file_to_module_def(ctx.vfs_file_id())?.krate(ctx.db()).into(),227 )?;228 let (function, label) = match &call_info.node {229 ast::CallableExpr::Call(call) => {230 let path = match call.expr()? {231 ast::Expr::PathExpr(path) => path.path(),232 _ => None,233 }?;234 let function = match ctx.sema.resolve_path(&path)? {235 PathResolution::Def(hir::ModuleDef::Function(f)) => f,236 _ => return None,237 };238 (function, format!("Inline `{path}`"))239 }240 ast::CallableExpr::MethodCall(call) => {241 (ctx.sema.resolve_method_call(call)?, format!("Inline `{name_ref}`"))242 }243 };244245 let fn_source = ctx.sema.source(function)?;246 let fn_body = fn_source.value.body()?;247 let param_list = fn_source.value.param_list()?;248249 let FileRange { file_id, range } = fn_source.syntax().original_file_range_rooted(ctx.sema.db);250 if file_id == ctx.file_id() && range.contains(ctx.offset()) {251 cov_mark::hit!(inline_call_recursive);252 return None;253 }254 let original = ctx.sema.original_range_opt(call_info.node.syntax())?.range;255 let edit_range = crate::utils::cover_edit_range(ctx.source_file().syntax(), original);256 let editor = SyntaxEditor::new(ctx.source_file().syntax().clone()).0;257 let text_range = edit_range.start().text_range().cover(edit_range.end().text_range());258 let params = get_fn_params(ctx.sema.db, function, ¶m_list, editor.make())?;259260 if call_info.arguments.len() != params.len() {261 // Can't inline the function because they've passed the wrong number of262 // arguments to this function263 cov_mark::hit!(inline_call_incorrect_number_of_arguments);264 return None;265 }266267 acc.add(AssistId::refactor_inline("inline_call"), label, text_range, |builder| {268 let replacement = inline(269 &ctx.sema,270 file_id,271 function,272 &fn_body,273 ¶ms,274 &call_info,275 &editor,276 IndentLevel::from_element(edit_range.start()),277 );278 editor.replace_all(edit_range, vec![replacement.syntax().clone().into()]);279 builder.add_file_edits(ctx.vfs_file_id(), editor);280 })281}282283struct CallInfo {284 node: ast::CallableExpr,285 arguments: Vec<ast::Expr>,286 generic_arg_list: Option<ast::GenericArgList>,287 krate: Crate,288}289290impl CallInfo {291 fn from_name_ref(name_ref: ast::NameRef, krate: Crate) -> Option<CallInfo> {292 let parent = name_ref.syntax().parent()?;293 if let Some(call) = ast::MethodCallExpr::cast(parent.clone()) {294 let receiver = call.receiver()?;295 let mut arguments = vec![receiver];296 arguments.extend(call.arg_list()?.args());297 Some(CallInfo {298 generic_arg_list: call.generic_arg_list(),299 node: ast::CallableExpr::MethodCall(call),300 arguments,301 krate,302 })303 } else if let Some(segment) = ast::PathSegment::cast(parent) {304 let path = segment.syntax().parent().and_then(ast::Path::cast)?;305 let path = path.syntax().parent().and_then(ast::PathExpr::cast)?;306 let call = path.syntax().parent().and_then(ast::CallExpr::cast)?;307308 Some(CallInfo {309 arguments: call.arg_list()?.args().collect(),310 node: ast::CallableExpr::Call(call),311 generic_arg_list: segment.generic_arg_list(),312 krate,313 })314 } else {315 None316 }317 }318}319320fn get_fn_params<'db>(321 db: &'db dyn HirDatabase,322 function: hir::Function,323 param_list: &ast::ParamList,324 make: &SyntaxFactory,325) -> Option<Vec<(ast::Pat, Option<ast::Type>, hir::Param<'db>)>> {326 let mut assoc_fn_params = function.assoc_fn_params(db).into_iter();327328 let mut params = Vec::new();329 if let Some(self_param) = param_list.self_param() {330 // Keep `ref` and `mut` and transform them into `&` and `mut` later331 params.push((332 make.ident_pat(333 self_param.amp_token().is_some(),334 self_param.mut_token().is_some(),335 make.name("this"),336 )337 .into(),338 None,339 assoc_fn_params.next()?,340 ));341 }342 for param in param_list.params() {343 params.push((param.pat()?, param.ty(), assoc_fn_params.next()?));344 }345346 Some(params)347}348349fn inline<'db>(350 sema: &Semantics<'db, RootDatabase>,351 function_def_file_id: EditionedFileId,352 function: hir::Function,353 fn_body: &ast::BlockExpr,354 params: &[(ast::Pat, Option<ast::Type>, hir::Param<'db>)],355 CallInfo { node, arguments, generic_arg_list, krate }: &CallInfo,356 file_editor: &SyntaxEditor,357 original_indentation: IndentLevel,358) -> ast::Expr {359 let make = file_editor.make();360 let file_id = sema.hir_file_for(fn_body.syntax());361362 // Capture before `with_ast_node` re-roots and loses the source-relative position.363 let mut original_body_indent = IndentLevel::from_node(fn_body.syntax());364 let body_offset = fn_body.syntax().text_range().start();365 let (editor, body) = SyntaxEditor::with_ast_node(fn_body);366367 let usages_for_locals = |local| {368 Definition::Local(local)369 .usages(sema)370 .all()371 .references372 .remove(&function_def_file_id)373 .unwrap_or_default()374 .into_iter()375 };376 let param_use_nodes: Vec<Vec<_>> = params377 .iter()378 .map(|(pat, _, param)| {379 if !matches!(pat, ast::Pat::IdentPat(pat) if pat.is_simple_ident()) {380 return Vec::new();381 }382 // FIXME: we need to fetch all locals declared in the parameter here383 // not only the local if it is a simple binding384 match param.as_local(sema.db) {385 Some(l) => usages_for_locals(l)386 .map(|FileReference { name, .. }| match name {387 FileReferenceNode::NameRef(it) => body388 .syntax()389 .covering_element(it.syntax().text_range() - body_offset)390 .ancestors()391 .nth(3)392 .and_then(ast::PathExpr::cast),393 _ => None,394 })395 .collect::<Option<Vec<_>>>()396 .unwrap_or_default(),397 None => Vec::new(),398 }399 })400 .collect();401402 let has_self_param = function.self_param(sema.db).is_some();403404 // Collect all self token usages upfront (needed when a let binding is emitted).405 // When self can be directly inlined, the parameter loop handles those PathExprs;406 // when a let stmt is needed, we rename all self tokens to `this` here.407 let self_token_usages: Vec<_> = if has_self_param {408 params[0]409 .2410 .as_local(sema.db)411 .map(|self_local| {412 usages_for_locals(self_local)413 .filter_map(|FileReference { name, .. }| match name {414 FileReferenceNode::NameRef(it) => Some(415 body.syntax().covering_element(it.syntax().text_range() - body_offset),416 ),417 _ => None,418 })419 .collect()420 })421 .unwrap_or_default()422 } else {423 Vec::new()424 };425426 // Replace `Self` type keywords with the actual impl type427 if let Some(imp) =428 sema.ancestors_with_macros(fn_body.syntax().clone()).find_map(ast::Impl::cast)429 && !node.syntax().ancestors().any(|anc| &anc == imp.syntax())430 && let Some(t) = imp.self_ty()431 {432 let self_tokens: Vec<_> = body433 .syntax()434 .descendants_with_tokens()435 .filter_map(NodeOrToken::into_token)436 .filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW)437 .collect();438 for self_tok in self_tokens {439 let replace_with = if !is_in_type_path(&self_tok)440 && let Some(generic_arg_list) = t.generic_arg_list()441 {442 // Strip the outer generic arg list and reparse, since turbofish-less443 // generics aren't valid in expression position. The outermost444 // `GenericArgList` text is unique within `t`'s text (any inner generics445 // are nested inside it), so `replacen(.., 1)` is safe.446 let stripped = t.syntax().text().to_string().replacen(447 &generic_arg_list.syntax().text().to_string(),448 "",449 1,450 );451 editor.make().ty(&stripped).syntax().clone()452 } else {453 t.syntax().clone()454 };455 editor.replace(self_tok, replace_with);456 }457 }458459 let mut func_let_vars: BTreeSet<String> = BTreeSet::new();460461 // grab all of the local variable declarations in the function462 for stmt in fn_body.statements() {463 if let Some(let_stmt) = ast::LetStmt::cast(stmt.syntax().to_owned()) {464 for has_token in let_stmt.syntax().children_with_tokens() {465 if let Some(node) = has_token.as_node()466 && let Some(ident_pat) = ast::IdentPat::cast(node.to_owned())467 {468 func_let_vars.insert(ident_pat.syntax().text().to_string());469 }470 }471 }472 }473474 let mut let_stmts: Vec<ast::Stmt> = Vec::new();475476 let this_token = editor477 .make()478 .name_ref("this")479 .syntax()480 .first_token()481 .expect("NameRef should have had a token.");482 let rewrite_self_to_this = |editor: &SyntaxEditor| {483 for usage in &self_token_usages {484 editor.replace(usage.clone(), this_token.clone());485 }486 };487488 // Inline parameter expressions or generate `let` statements depending on whether inlining works or not.489 for ((pat, param_ty, param), usages, expr) in izip!(params, param_use_nodes, arguments) {490 // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors491 let usages: &[ast::PathExpr] = &usages;492 let expr: &ast::Expr = expr;493494 let mut insert_let_stmt = || {495 let param_ty = param_ty.clone().map(|param_ty| {496 let file_id = sema.hir_file_for(param_ty.syntax());497 if let Some(macro_file) = file_id.macro_file() {498 let span_map = macro_file.expansion_span_map(sema.db);499 let param_ty_prettified = prettify_macro_expansion(500 sema.db,501 param_ty.syntax().clone(),502 span_map,503 *krate,504 );505 ast::Type::cast(param_ty_prettified).unwrap_or(param_ty)506 } else {507 param_ty508 }509 });510511 let ty = sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty);512513 let is_self = param.name(sema.db).is_some_and(|name| name == sym::self_);514515 if is_self {516 let mut this_pat = make.ident_pat(false, false, make.name("this"));517 let mut expr = expr.clone();518 if let Pat::IdentPat(pat) = pat {519 match (pat.ref_token(), pat.mut_token()) {520 // self => let this = obj521 (None, None) => {}522 // mut self => let mut this = obj523 (None, Some(_)) => {524 this_pat = make.ident_pat(false, true, make.name("this"));525 }526 // &self => let this = &obj527 (Some(_), None) => {528 expr = make.expr_ref(expr, false);529 }530 // let foo = &mut X; &mut self => let this = &mut obj531 // let mut foo = X; &mut self => let this = &mut *obj (reborrow)532 (Some(_), Some(_)) => {533 let should_reborrow = sema534 .type_of_expr(&expr)535 .map(|ty| ty.original.is_mutable_reference());536 expr = if let Some(true) = should_reborrow {537 make.expr_reborrow(expr)538 } else {539 make.expr_ref(expr, true)540 };541 }542 }543 };544 let_stmts.push(make.let_stmt(this_pat.into(), ty, Some(expr)).into())545 } else {546 let_stmts.push(make.let_stmt(pat.clone(), ty, Some(expr.clone())).into());547 }548 };549550 let is_self_param =551 has_self_param && param.name(sema.db).is_some_and(|name| name == sym::self_);552553 // check if there is a local var in the function that conflicts with parameter554 // if it does then emit a let statement and continue555 if func_let_vars.contains(&expr.syntax().text().to_string()) {556 if is_self_param {557 rewrite_self_to_this(&editor);558 }559 insert_let_stmt();560 continue;561 }562563 let inline_direct = |editor: &SyntaxEditor, usage: &PathExpr, replacement: &ast::Expr| {564 if let Some(field) = path_expr_as_record_field(usage) {565 cov_mark::hit!(inline_call_inline_direct_field);566 field.replace_expr(editor, replacement.clone());567 } else {568 editor.replace(usage.syntax(), replacement.syntax());569 }570 };571572 match usages {573 // inline single use closure arguments574 [usage]575 if matches!(expr, ast::Expr::ClosureExpr(_))576 && usage.syntax().parent().and_then(ast::Expr::cast).is_some() =>577 {578 cov_mark::hit!(inline_call_inline_closure);579 let expr = editor.make().expr_paren(expr.clone()).into();580 inline_direct(&editor, usage, &expr);581 }582 // inline single use literals583 [usage] if matches!(expr, ast::Expr::Literal(_)) => {584 cov_mark::hit!(inline_call_inline_literal);585 inline_direct(&editor, usage, expr);586 }587 // inline direct local arguments588 [_, ..] if expr_as_name_ref(expr).is_some() => {589 cov_mark::hit!(inline_call_inline_locals);590 usages.iter().for_each(|usage| inline_direct(&editor, usage, expr));591 }592 // can't inline, emit a let statement593 _ => {594 if is_self_param {595 // Rename all `self` tokens to `this` so the let binding matches.596 rewrite_self_to_this(&editor);597 }598 insert_let_stmt();599 }600 }601 }602603 // Apply all edits to get the transformed body604 let edit = editor.finish();605 let mut body = ast::BlockExpr::cast(edit.new_root().clone())606 .expect("editor root should still be a BlockExpr");607608 // Apply generic substitution (needs immutable tree)609 if let Some(generic_arg_list) = generic_arg_list.clone()610 && let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax()))611 && let Some(new_body) = ast::BlockExpr::cast(612 PathTransform::function_call(target, source, function, generic_arg_list)613 .apply(body.syntax()),614 )615 {616 body = new_body;617 }618 if let Some(macro_file) = file_id.macro_file() {619 cov_mark::hit!(inline_call_defined_in_macro);620 let span_map = macro_file.expansion_span_map(sema.db);621 let body_prettified =622 prettify_macro_expansion(sema.db, body.syntax().clone(), span_map, *krate);623 body = ast::BlockExpr::cast(body_prettified).unwrap();624 }625626 let is_async_fn = function.is_async(sema.db);627 if is_async_fn {628 cov_mark::hit!(inline_call_async_fn);629 body = make.async_move_block_expr(body.statements(), body.tail_expr());630631 // Arguments should be evaluated outside the async block, and then moved into it.632 if !let_stmts.is_empty() {633 cov_mark::hit!(inline_call_async_fn_with_let_stmts);634 body = body.indent(IndentLevel(1));635 body = make.block_expr(let_stmts, Some(body.into()));636 }637 } else if !let_stmts.is_empty() {638 // Prepend let statements to the body's existing statements639 let stmts: Vec<ast::Stmt> = let_stmts.into_iter().chain(body.statements()).collect();640 body = make.block_expr(stmts, body.tail_expr());641 original_body_indent = IndentLevel(0);642 }643644 body = body.dedent(original_body_indent).indent(original_indentation);645646 let no_stmts = body.statements().next().is_none();647 match body.tail_expr() {648 Some(expr) if matches!(expr, ast::Expr::ClosureExpr(_)) && no_stmts => {649 make.expr_paren(expr).into()650 }651 Some(expr) if !is_async_fn && no_stmts => expr,652 _ => match node653 .syntax()654 .parent()655 .and_then(ast::BinExpr::cast)656 .and_then(|bin_expr| bin_expr.lhs())657 {658 Some(lhs) if lhs.syntax() == node.syntax() => {659 make.expr_paren(ast::Expr::BlockExpr(body)).into()660 }661 _ => ast::Expr::BlockExpr(body),662 },663 }664}665666fn is_in_type_path(self_tok: &syntax::SyntaxToken) -> bool {667 self_tok668 .parent_ancestors()669 .skip_while(|it| !ast::Path::can_cast(it.kind()))670 .map_while(ast::Path::cast)671 .last()672 .and_then(|it| it.syntax().parent())673 .and_then(ast::PathType::cast)674 .is_some()675}676677fn path_expr_as_record_field(usage: &PathExpr) -> Option<ast::RecordExprField> {678 let path = usage.path()?;679 let name_ref = path.as_single_name_ref()?;680 ast::RecordExprField::for_name_ref(&name_ref)681}682683#[cfg(test)]684mod tests {685 use crate::tests::{check_assist, check_assist_not_applicable};686687 use super::*;688689 #[test]690 fn no_args_or_return_value_gets_inlined_without_block() {691 check_assist(692 inline_call,693 r#"694fn foo() { println!("Hello, World!"); }695fn main() {696 fo$0o();697}698"#,699 r#"700fn foo() { println!("Hello, World!"); }701fn main() {702 { println!("Hello, World!"); };703}704"#,705 );706 }707708 #[test]709 fn not_applicable_when_incorrect_number_of_parameters_are_provided() {710 cov_mark::check!(inline_call_incorrect_number_of_arguments);711 check_assist_not_applicable(712 inline_call,713 r#"714fn add(a: u32, b: u32) -> u32 { a + b }715fn main() { let x = add$0(42); }716"#,717 );718 }719720 #[test]721 fn args_with_side_effects() {722 check_assist(723 inline_call,724 r#"725fn foo(name: String) {726 println!("Hello, {}!", name);727}728fn main() {729 foo$0(String::from("Michael"));730}731"#,732 r#"733fn foo(name: String) {734 println!("Hello, {}!", name);735}736fn main() {737 {738 let name = String::from("Michael");739 println!("Hello, {}!", name);740 };741}742"#,743 );744 }745746 #[test]747 fn function_with_multiple_statements() {748 check_assist(749 inline_call,750 r#"751fn foo(a: u32, b: u32) -> u32 {752 let x = a + b;753 let y = x - b;754 x * y755}756757fn main() {758 let x = foo$0(1, 2);759}760"#,761 r#"762fn foo(a: u32, b: u32) -> u32 {763 let x = a + b;764 let y = x - b;765 x * y766}767768fn main() {769 let x = {770 let b = 2;771 let x = 1 + b;772 let y = x - b;773 x * y774 };775}776"#,777 );778 }779780 #[test]781 fn call_inside_macro() {782 check_assist(783 inline_call,784 r#"785macro_rules! identity { ($($t:tt)*) => { $($t)* }; }786fn foo(a: u32, b: u32) -> u32 {787 let x = a + b;788 let y = x - b;789 x * y790}791792fn main() {793 identity! {794 let x = foo$0(1, 2);795 }796}797"#,798 r#"799macro_rules! identity { ($($t:tt)*) => { $($t)* }; }800fn foo(a: u32, b: u32) -> u32 {801 let x = a + b;802 let y = x - b;803 x * y804}805806fn main() {807 identity! {808 let x = {809 let b = 2;810 let x = 1 + b;811 let y = x - b;812 x * y813 };814 }815}816"#,817 );818 }819820 #[test]821 fn function_with_self_param() {822 check_assist(823 inline_call,824 r#"825struct Foo(u32);826827impl Foo {828 fn add(self, a: u32) -> Self {829 Foo(self.0 + a)830 }831}832833fn main() {834 let x = Foo::add$0(Foo(3), 2);835}836"#,837 r#"838struct Foo(u32);839840impl Foo {841 fn add(self, a: u32) -> Self {842 Foo(self.0 + a)843 }844}845846fn main() {847 let x = {848 let this = Foo(3);849 Foo(this.0 + 2)850 };851}852"#,853 );854 }855856 #[test]857 fn method_by_val() {858 check_assist(859 inline_call,860 r#"861struct Foo(u32);862863impl Foo {864 fn add(self, a: u32) -> Self {865 Foo(self.0 + a)866 }867}868869fn main() {870 let x = Foo(3).add$0(2);871}872"#,873 r#"874struct Foo(u32);875876impl Foo {877 fn add(self, a: u32) -> Self {878 Foo(self.0 + a)879 }880}881882fn main() {883 let x = {884 let this = Foo(3);885 Foo(this.0 + 2)886 };887}888"#,889 );890 }891892 #[test]893 fn method_by_ref() {894 check_assist(895 inline_call,896 r#"897struct Foo(u32);898899impl Foo {900 fn add(&self, a: u32) -> Self {901 Foo(self.0 + a)902 }903}904905fn main() {906 let x = Foo(3).add$0(2);907}908"#,909 r#"910struct Foo(u32);911912impl Foo {913 fn add(&self, a: u32) -> Self {914 Foo(self.0 + a)915 }916}917918fn main() {919 let x = {920 let this = &Foo(3);921 Foo(this.0 + 2)922 };923}924"#,925 );926 }927928 #[test]929 fn generic_method_by_ref() {930 check_assist(931 inline_call,932 r#"933struct Foo(u32);934935impl Foo {936 fn add<T>(&self, a: u32) -> Self {937 Foo(self.0 + a)938 }939}940941fn main() {942 let x = Foo(3).add$0::<usize>(2);943}944"#,945 r#"946struct Foo(u32);947948impl Foo {949 fn add<T>(&self, a: u32) -> Self {950 Foo(self.0 + a)951 }952}953954fn main() {955 let x = {956 let this = &Foo(3);957 Foo(this.0 + 2)958 };959}960"#,961 );962 }963964 #[test]965 fn method_by_ref_mut() {966 check_assist(967 inline_call,968 r#"969struct Foo(u32);970971impl Foo {972 fn clear(&mut self) {973 self.0 = 0;974 }975}976977fn main() {978 let mut foo = Foo(3);979 foo.clear$0();980}981"#,982 r#"983struct Foo(u32);984985impl Foo {986 fn clear(&mut self) {987 self.0 = 0;988 }989}990991fn main() {992 let mut foo = Foo(3);993 {994 let this = &mut foo;995 this.0 = 0;996 };997}998"#,999 );1000 }10011002 #[test]1003 fn function_multi_use_expr_in_param() {1004 check_assist(1005 inline_call,1006 r#"1007fn square(x: u32) -> u32 {1008 x * x1009}1010fn main() {1011 let x = 51;1012 let y = square$0(10 + x);1013}1014"#,1015 r#"1016fn square(x: u32) -> u32 {1017 x * x1018}1019fn main() {1020 let x = 51;1021 let y = {1022 let x = 10 + x;1023 x * x1024 };1025}1026"#,1027 );1028 }10291030 #[test]1031 fn function_use_local_in_param() {1032 cov_mark::check!(inline_call_inline_locals);1033 check_assist(1034 inline_call,1035 r#"1036fn square(x: u32) -> u32 {1037 x * x1038}1039fn main() {1040 let local = 51;1041 let y = square$0(local);1042}1043"#,1044 r#"1045fn square(x: u32) -> u32 {1046 x * x1047}1048fn main() {1049 let local = 51;1050 let y = local * local;1051}1052"#,1053 );1054 }10551056 #[test]1057 fn method_in_impl() {1058 check_assist(1059 inline_call,1060 r#"1061struct Foo;1062impl Foo {1063 fn foo(&self) {1064 self;1065 self;1066 }1067 fn bar(&self) {1068 self.foo$0();1069 }1070}1071"#,1072 r#"1073struct Foo;1074impl Foo {1075 fn foo(&self) {1076 self;1077 self;1078 }1079 fn bar(&self) {1080 {1081 let this = &self;1082 this;1083 this;1084 };1085 }1086}1087"#,1088 );1089 }10901091 #[test]1092 fn wraps_closure_in_paren() {1093 cov_mark::check!(inline_call_inline_closure);1094 check_assist(1095 inline_call,1096 r#"1097fn foo(x: fn()) {1098 x();1099}11001101fn main() {1102 foo$0(|| {})1103}1104"#,1105 r#"1106fn foo(x: fn()) {1107 x();1108}11091110fn main() {1111 {1112 (|| {})();1113 }1114}1115"#,1116 );1117 check_assist(1118 inline_call,1119 r#"1120fn foo(x: fn()) {1121 x();1122}11231124fn main() {1125 foo$0(main)1126}1127"#,1128 r#"1129fn foo(x: fn()) {1130 x();1131}11321133fn main() {1134 {1135 main();1136 }1137}1138"#,1139 );1140 }11411142 #[test]1143 fn inline_single_literal_expr() {1144 cov_mark::check!(inline_call_inline_literal);1145 check_assist(1146 inline_call,1147 r#"1148fn foo(x: u32) -> u32{1149 x1150}11511152fn main() {1153 foo$0(222);1154}1155"#,1156 r#"1157fn foo(x: u32) -> u32{1158 x1159}11601161fn main() {1162 222;1163}1164"#,1165 );1166 }11671168 #[test]1169 fn inline_emits_type_for_coercion() {1170 check_assist(1171 inline_call,1172 r#"1173//- minicore: sized1174fn foo(x: *const u32) -> u32 {1175 x as u321176}11771178fn main() {1179 foo$0(&222);1180}1181"#,1182 r#"1183fn foo(x: *const u32) -> u32 {1184 x as u321185}11861187fn main() {1188 {1189 let x: *const u32 = &222;1190 x as u321191 };1192}1193"#,1194 );1195 }11961197 #[test]1198 fn inline_substitutes_generics() {1199 check_assist(1200 inline_call,1201 r#"1202fn foo<T, const N: usize>() {1203 bar::<T, N>()1204}12051206fn bar<U, const M: usize>() {}12071208fn main() {1209 foo$0::<usize, {0}>();1210}1211"#,1212 r#"1213fn foo<T, const N: usize>() {1214 bar::<T, N>()1215}12161217fn bar<U, const M: usize>() {}12181219fn main() {1220 bar::<usize, {0}>();1221}1222"#,1223 );1224 }12251226 #[test]1227 fn inline_callers() {1228 check_assist(1229 inline_into_callers,1230 r#"1231fn do_the_math$0(b: u32) -> u32 {1232 let foo = 10;1233 foo * b + foo1234}1235fn foo() {1236 do_the_math(0);1237 let bar = 10;1238 do_the_math(bar);1239}1240"#,1241 r#"12421243fn foo() {1244 {1245 let foo = 10;1246 foo * 0 + foo1247 };1248 let bar = 10;1249 {1250 let foo = 10;1251 foo * bar + foo1252 };1253}1254"#,1255 );1256 }12571258 #[test]1259 fn inline_callers_across_files() {1260 check_assist(1261 inline_into_callers,1262 r#"1263//- /lib.rs1264mod foo;1265fn do_the_math$0(b: u32) -> u32 {1266 let foo = 10;1267 foo * b + foo1268}1269//- /foo.rs1270use super::do_the_math;1271fn foo() {1272 do_the_math(0);1273 let bar = 10;1274 do_the_math(bar);1275}1276"#,1277 r#"1278//- /lib.rs1279mod foo;12801281//- /foo.rs1282fn foo() {1283 {1284 let foo = 10;1285 foo * 0 + foo1286 };1287 let bar = 10;1288 {1289 let foo = 10;1290 foo * bar + foo1291 };1292}1293"#,1294 );1295 }12961297 #[test]1298 fn inline_callers_across_files_with_def_file() {1299 check_assist(1300 inline_into_callers,1301 r#"1302//- /lib.rs1303mod foo;1304fn do_the_math$0(b: u32) -> u32 {1305 let foo = 10;1306 foo * b + foo1307}1308fn bar(a: u32, b: u32) -> u32 {1309 do_the_math(0);1310}1311//- /foo.rs1312use super::do_the_math;1313fn foo() {1314 do_the_math(0);1315}1316"#,1317 r#"1318//- /lib.rs1319mod foo;13201321fn bar(a: u32, b: u32) -> u32 {1322 {1323 let foo = 10;1324 foo * 0 + foo1325 };1326}1327//- /foo.rs1328fn foo() {1329 {1330 let foo = 10;1331 foo * 0 + foo1332 };1333}1334"#,1335 );1336 }13371338 #[test]1339 fn inline_callers_recursive() {1340 cov_mark::check!(inline_into_callers_recursive);1341 check_assist_not_applicable(1342 inline_into_callers,1343 r#"1344fn foo$0() {1345 foo();1346}1347"#,1348 );1349 }13501351 #[test]1352 fn inline_call_recursive() {1353 cov_mark::check!(inline_call_recursive);1354 check_assist_not_applicable(1355 inline_call,1356 r#"1357fn foo() {1358 foo$0();1359}1360"#,1361 );1362 }13631364 #[test]1365 fn inline_call_field_shorthand() {1366 cov_mark::check!(inline_call_inline_direct_field);1367 check_assist(1368 inline_call,1369 r#"1370struct Foo {1371 field: u32,1372 field1: u32,1373 field2: u32,1374 field3: u32,1375}1376fn foo(field: u32, field1: u32, val2: u32, val3: u32) -> Foo {1377 Foo {1378 field,1379 field1,1380 field2: val2,1381 field3: val3,1382 }1383}1384fn main() {1385 let bar = 0;1386 let baz = 0;1387 foo$0(bar, 0, baz, 0);1388}1389"#,1390 r#"1391struct Foo {1392 field: u32,1393 field1: u32,1394 field2: u32,1395 field3: u32,1396}1397fn foo(field: u32, field1: u32, val2: u32, val3: u32) -> Foo {1398 Foo {1399 field,1400 field1,1401 field2: val2,1402 field3: val3,1403 }1404}1405fn main() {1406 let bar = 0;1407 let baz = 0;1408 Foo {1409 field: bar,1410 field1: 0,1411 field2: baz,1412 field3: 0,1413 };1414}1415"#,1416 );1417 }14181419 #[test]1420 fn inline_callers_wrapped_in_parentheses() {1421 check_assist(1422 inline_into_callers,1423 r#"1424fn foo$0() -> u32 {1425 let x = 0;1426 x1427}1428fn bar() -> u32 {1429 foo() + foo()1430}1431"#,1432 r#"14331434fn bar() -> u32 {1435 ({1436 let x = 0;1437 x1438 }) + {1439 let x = 0;1440 x1441 }1442}1443"#,1444 )1445 }14461447 #[test]1448 fn inline_call_wrapped_in_parentheses() {1449 check_assist(1450 inline_call,1451 r#"1452fn foo() -> u32 {1453 let x = 0;1454 x1455}1456fn bar() -> u32 {1457 foo$0() + foo()1458}1459"#,1460 r#"1461fn foo() -> u32 {1462 let x = 0;1463 x1464}1465fn bar() -> u32 {1466 ({1467 let x = 0;1468 x1469 }) + foo()1470}1471"#,1472 )1473 }14741475 #[test]1476 fn inline_call_defined_in_macro() {1477 cov_mark::check!(inline_call_defined_in_macro);1478 check_assist(1479 inline_call,1480 r#"1481macro_rules! define_foo {1482 () => { fn foo() -> u32 {1483 let x = 0;1484 x1485 } };1486}1487define_foo!();1488fn bar() -> u32 {1489 foo$0()1490}1491"#,1492 r#"1493macro_rules! define_foo {1494 () => { fn foo() -> u32 {1495 let x = 0;1496 x1497 } };1498}1499define_foo!();1500fn bar() -> u32 {1501 {1502 let x = 0;1503 x1504 }1505}1506"#,1507 )1508 }15091510 #[test]1511 fn inline_call_define_passed_in_macro() {1512 cov_mark::check!(inline_call_defined_in_macro);1513 check_assist(1514 inline_call,1515 r#"1516macro_rules! identity { ($($t:tt)*) => { $($t)* }; }1517identity! {1518 fn htons(hostshort: u16) {1519 let _ = hostshort;1520 }1521}1522fn foo() {1523 htons$0(123)1524}1525"#,1526 r#"1527macro_rules! identity { ($($t:tt)*) => { $($t)* }; }1528identity! {1529 fn htons(hostshort: u16) {1530 let _ = hostshort;1531 }1532}1533fn foo() {1534 {1535 let _ = 123;1536 }1537}1538"#,1539 )1540 }15411542 #[test]1543 fn inline_call_with_self_type() {1544 check_assist(1545 inline_call,1546 r#"1547struct A(u32);1548impl A {1549 fn f() -> Self { Self(114514) }1550}1551fn main() {1552 A::f$0();1553}1554"#,1555 r#"1556struct A(u32);1557impl A {1558 fn f() -> Self { Self(114514) }1559}1560fn main() {1561 A(114514);1562}1563"#,1564 )1565 }15661567 #[test]1568 fn inline_call_with_self_type_but_within_same_impl() {1569 check_assist(1570 inline_call,1571 r#"1572struct A(u32);1573impl A {1574 fn f() -> Self { Self(1919810) }1575 fn main() {1576 Self::f$0();1577 }1578}1579"#,1580 r#"1581struct A(u32);1582impl A {1583 fn f() -> Self { Self(1919810) }1584 fn main() {1585 Self(1919810);1586 }1587}1588"#,1589 )1590 }15911592 #[test]1593 fn local_variable_shadowing_callers_argument() {1594 check_assist(1595 inline_call,1596 r#"1597fn foo(bar: u32, baz: u32) -> u32 {1598 let a = 1;1599 bar * baz * a * 61600}1601fn main() {1602 let a = 7;1603 let b = 1;1604 let res = foo$0(a, b);1605}1606"#,1607 r#"1608fn foo(bar: u32, baz: u32) -> u32 {1609 let a = 1;1610 bar * baz * a * 61611}1612fn main() {1613 let a = 7;1614 let b = 1;1615 let res = {1616 let bar = a;1617 let a = 1;1618 bar * b * a * 61619 };1620}1621"#,1622 );1623 }16241625 #[test]1626 fn async_fn_single_expression() {1627 cov_mark::check!(inline_call_async_fn);1628 check_assist(1629 inline_call,1630 r#"1631async fn bar(x: u32) -> u32 { x + 1 }1632async fn foo(arg: u32) -> u32 {1633 bar(arg).await * 21634}1635fn spawn<T>(_: T) {}1636fn main() {1637 spawn(foo$0(42));1638}1639"#,1640 r#"1641async fn bar(x: u32) -> u32 { x + 1 }1642async fn foo(arg: u32) -> u32 {1643 bar(arg).await * 21644}1645fn spawn<T>(_: T) {}1646fn main() {1647 spawn(async move {1648 bar(42).await * 21649 });1650}1651"#,1652 );1653 }16541655 #[test]1656 fn async_fn_multiple_statements() {1657 cov_mark::check!(inline_call_async_fn);1658 check_assist(1659 inline_call,1660 r#"1661async fn bar(x: u32) -> u32 { x + 1 }1662async fn foo(arg: u32) -> u32 {1663 bar(arg).await;1664 421665}1666fn spawn<T>(_: T) {}1667fn main() {1668 spawn(foo$0(42));1669}1670"#,1671 r#"1672async fn bar(x: u32) -> u32 { x + 1 }1673async fn foo(arg: u32) -> u32 {1674 bar(arg).await;1675 421676}1677fn spawn<T>(_: T) {}1678fn main() {1679 spawn(async move {1680 bar(42).await;1681 421682 });1683}1684"#,1685 );1686 }16871688 #[test]1689 fn async_fn_with_let_statements() {1690 cov_mark::check!(inline_call_async_fn);1691 cov_mark::check!(inline_call_async_fn_with_let_stmts);1692 check_assist(1693 inline_call,1694 r#"1695async fn bar(x: u32) -> u32 { x + 1 }1696async fn foo(x: u32, y: u32, z: &u32) -> u32 {1697 bar(x).await;1698 y + y + *z1699}1700fn spawn<T>(_: T) {}1701fn main() {1702 let var = 42;1703 spawn(foo$0(var, var + 1, &var));1704}1705"#,1706 r#"1707async fn bar(x: u32) -> u32 { x + 1 }1708async fn foo(x: u32, y: u32, z: &u32) -> u32 {1709 bar(x).await;1710 y + y + *z1711}1712fn spawn<T>(_: T) {}1713fn main() {1714 let var = 42;1715 spawn({1716 let y = var + 1;1717 let z: &u32 = &var;1718 async move {1719 bar(var).await;1720 y + y + *z1721 }1722 });1723}1724"#,1725 );1726 }17271728 #[test]1729 fn inline_call_closure_body() {1730 check_assist(1731 inline_call,1732 r#"1733fn f() -> impl Fn() -> i32 {1734 || 21735}17361737fn main() {1738 let _ = $0f()();1739}1740"#,1741 r#"1742fn f() -> impl Fn() -> i32 {1743 || 21744}17451746fn main() {1747 let _ = (|| 2)();1748}1749"#,1750 );1751 }17521753 #[test]1754 fn inline_call_with_multiple_self_types_eq() {1755 check_assist(1756 inline_call,1757 r#"1758#[derive(PartialEq, Eq)]1759enum Enum {1760 A,1761 B,1762}17631764impl Enum {1765 fn a_or_b_eq(&self) -> bool {1766 self == &Self::A || self == &Self::B1767 }1768}17691770fn a() -> bool {1771 Enum::A.$0a_or_b_eq()1772}1773"#,1774 r#"1775#[derive(PartialEq, Eq)]1776enum Enum {1777 A,1778 B,1779}17801781impl Enum {1782 fn a_or_b_eq(&self) -> bool {1783 self == &Self::A || self == &Self::B1784 }1785}17861787fn a() -> bool {1788 {1789 let this = &Enum::A;1790 this == &Enum::A || this == &Enum::B1791 }1792}1793"#,1794 )1795 }17961797 #[test]1798 fn inline_call_with_self_type_in_macros() {1799 check_assist(1800 inline_call,1801 r#"1802trait Trait<T1> {1803 fn f(a: T1) -> Self;1804}18051806macro_rules! impl_from {1807 ($t: ty) => {1808 impl Trait<$t> for $t {1809 fn f(a: $t) -> Self {1810 a as Self1811 }1812 }1813 };1814}18151816struct A {}18171818impl_from!(A);18191820fn main() {1821 let a: A = A{};1822 let b = <A as Trait<A>>::$0f(a);1823}1824"#,1825 r#"1826trait Trait<T1> {1827 fn f(a: T1) -> Self;1828}18291830macro_rules! impl_from {1831 ($t: ty) => {1832 impl Trait<$t> for $t {1833 fn f(a: $t) -> Self {1834 a as Self1835 }1836 }1837 };1838}18391840struct A {}18411842impl_from!(A);18431844fn main() {1845 let a: A = A{};1846 let b = {1847 let a = a;1848 a as A1849 };1850}1851"#,1852 )1853 }18541855 #[test]1856 fn inline_trait_method_call_with_lifetimes() {1857 check_assist(1858 inline_call,1859 r#"1860trait Trait {1861 fn f() -> Self;1862}1863struct Foo<'a>(&'a ());1864impl<'a> Trait for Foo<'a> {1865 fn f() -> Self { Self(&()) }1866}1867impl Foo<'_> {1868 fn new() -> Self {1869 Self::$0f()1870 }1871}1872"#,1873 r#"1874trait Trait {1875 fn f() -> Self;1876}1877struct Foo<'a>(&'a ());1878impl<'a> Trait for Foo<'a> {1879 fn f() -> Self { Self(&()) }1880}1881impl Foo<'_> {1882 fn new() -> Self {1883 Foo(&())1884 }1885}1886"#,1887 )1888 }18891890 #[test]1891 fn method_by_reborrow() {1892 check_assist(1893 inline_call,1894 r#"1895pub struct Foo(usize);18961897impl Foo {1898 fn add1(&mut self) {1899 self.0 += 1;1900 }1901}19021903pub fn main() {1904 let f = &mut Foo(0);1905 f.add1$0();1906}1907"#,1908 r#"1909pub struct Foo(usize);19101911impl Foo {1912 fn add1(&mut self) {1913 self.0 += 1;1914 }1915}19161917pub fn main() {1918 let f = &mut Foo(0);1919 {1920 let this = &mut *f;1921 this.0 += 1;1922 };1923}1924"#,1925 )1926 }19271928 #[test]1929 fn method_by_mut() {1930 check_assist(1931 inline_call,1932 r#"1933pub struct Foo(usize);19341935impl Foo {1936 fn add1(mut self) {1937 self.0 += 1;1938 }1939}19401941pub fn main() {1942 let mut f = Foo(0);1943 f.add1$0();1944}1945"#,1946 r#"1947pub struct Foo(usize);19481949impl Foo {1950 fn add1(mut self) {1951 self.0 += 1;1952 }1953}19541955pub fn main() {1956 let mut f = Foo(0);1957 {1958 let mut this = f;1959 this.0 += 1;1960 };1961}1962"#,1963 )1964 }19651966 #[test]1967 fn inline_call_with_reference_in_macros() {1968 check_assist(1969 inline_call,1970 r#"1971fn _write_u64(s: &mut u64, x: u64) {1972 *s += x;1973}1974macro_rules! impl_write {1975 ($(($ty:ident, $meth:ident),)*) => {$(1976 fn _hash(inner_self_: &u64, state: &mut u64) {1977 $meth(state, *inner_self_)1978 }1979 )*}1980}1981impl_write! { (u64, _write_u64), }1982fn _hash2(self_: &u64, state: &mut u64) {1983 $0_hash(&self_, state);1984}1985"#,1986 r#"1987fn _write_u64(s: &mut u64, x: u64) {1988 *s += x;1989}1990macro_rules! impl_write {1991 ($(($ty:ident, $meth:ident),)*) => {$(1992 fn _hash(inner_self_: &u64, state: &mut u64) {1993 $meth(state, *inner_self_)1994 }1995 )*}1996}1997impl_write! { (u64, _write_u64), }1998fn _hash2(self_: &u64, state: &mut u64) {1999 {2000 let inner_self_: &u64 = &self_;
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.
Generated by searchcode.com · powered by code_get_file + code_get_findings