ide_assists/handlers/
inline_call.rs

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
34// Assist: inline_into_callers
35//
36// Inline a function or method body into all of its callers where possible, creating a `let` statement per parameter
37// unless the parameter can be inlined. The parameter will be inlined either if it the supplied argument is a simple local
38// 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()?;
79
80    let function = ctx.sema.to_def(&ast_func)?;
81
82    let params = get_fn_params(ctx.sema.db, function, &param_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                // 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_refs
118                    .into_iter()
119                    .filter_map(|it| CallInfo::from_name_ref(it, call_krate?.into()))
120                    // FIXME: do not handle callsites in macros' parameters, because
121                    // 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_infos
129                    .into_iter()
130                    .map(|(call_info, mut_node)| {
131                        let replacement =
132                            inline(&ctx.sema, def_file, function, &func_body, &params, &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 imports
138                    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
174// Assist: inline_call
175//
176// Inlines a function or method body creating a `let` statement per parameter unless the parameter
177// can be inlined. The parameter will be inlined either if it the supplied argument is a simple local
178// or if the parameter is only accessed inside the function body once.
179//
180// ```
181// # //- minicore: option
182// 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    };
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, &param_list)?;
228
229    if call_info.arguments.len() != params.len() {
230        // Can't inline the function because they've passed the wrong number of
231        // arguments to this function
232        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, &params, &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        // Keep `ref` and `mut` and transform them into `&` and `mut` later
296        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            // FIXME: we need to fetch all locals declared in the parameter here
352            // not only the local if it is a simple binding
353            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    // 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 makes
393    // `FileReference` incorrect
394    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    // grab all of the local variable declarations in the function
413    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    // Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
428    for ((pat, param_ty, param), usages, expr) in izip!(params, param_use_nodes, arguments) {
429        // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors
430        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                        // self => let this = obj
460                        (None, None) => {}
461                        // mut self => let mut this = obj
462                        (None, Some(_)) => {
463                            this_pat = make::ident_pat(false, true, make::name("this"));
464                        }
465                        // &self => let this = &obj
466                        (Some(_), None) => {
467                            expr = make::expr_ref(expr, false);
468                        }
469                        // let foo = &mut X; &mut self => let this = &mut obj
470                        // let mut foo = X;  &mut self => let this = &mut *obj (reborrow)
471                        (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        // check if there is a local var in the function that conflicts with parameter
493        // if it does then emit a let statement and continue
494        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            // inline single use closure arguments
510            [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            // inline single use literals
519            [usage] if matches!(expr, ast::Expr::Literal(_)) => {
520                cov_mark::hit!(inline_call_inline_literal);
521                inline_direct(usage, expr);
522            }
523            // inline direct local arguments
524            [_, ..] 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            // can't inline, emit a let statement
529            _ => {
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        // Arguments should be evaluated outside the async block, and then moved into it.
553        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}