src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs RUST 1,890 lines View on github.com → Search inside
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, &param_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, &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 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, &param_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, &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}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}

Code quality findings 24

Warning: '.expect()' will panic with a custom message on None/Err. While better than unwrap() for debugging, prefer non-panicking error handling in production code (match, if let, ?).
warning correctness expect-usage
.expect("NameRef should have had a token.")
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>.
warning correctness unchecked-indexing
if let Some(self_local) = params[0].2.as_local(sema.db) {
Warning: '.expect()' will panic with a custom message on None/Err. While better than unwrap() for debugging, prefer non-panicking error handling in production code (match, if let, ?).
warning correctness expect-usage
let position = stmt_list.l_curly_token().expect("L_CURLY for StatementList is missing.");
Warning: Ignoring a Result or Option using 'let _ =' can hide errors or unexpected None values. Ensure the value is handled appropriately (match, if let, ?, expect) unless intentionally discarded with justification.
warning correctness discarded-result
let _ = $0f()();
Warning: Ignoring a Result or Option using 'let _ =' can hide errors or unexpected None values. Ensure the value is handled appropriately (match, if let, ?, expect) unless intentionally discarded with justification.
warning correctness discarded-result
let _ = (|| 2)();
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
.filter_map(|file_ref| match file_ref.name {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
.filter_map(|name_ref| match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let (function, label) = match &call_info.node {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let path = match call.expr()? {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let function = match ctx.sema.resolve_path(&path)? {
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
params.push((param.pat()?, param.ty(), assoc_fn_params.next()?));
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match param.as_local(sema.db) {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
.map(|FileReference { name, range, .. }| match name {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
.filter_map(|FileReference { name, range, .. }| match name {
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info performance clone-in-loop
sema.ancestors_with_macros(fn_body.syntax().clone()).find_map(ast::Impl::cast)
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info performance clone-in-loop
&& let Some(ty) = ast::Type::cast(replace_with.clone())
Performance Info: Calling .to_string() (especially on &str) allocates a new String. If done repeatedly in loops, consider alternatives like working with &str or using crates like `itoa`/`ryu` for number-to-string conversion.
info performance to-string-in-loop
func_let_vars.insert(ident_pat.syntax().text().to_string());
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info performance clone-in-loop
let param_ty = param_ty.clone().map(|param_ty| {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
_ => match node
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use super::*;
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
println!("Hello, {}!", name);
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
fn foo(x: *const u32) -> u32 {
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
fn foo(x: *const u32) -> u32 {
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
let x: *const u32 = &222;

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.