src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs RUST 3,246 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 3,246.
1use hir::{2    Adt, AsAssocItem, HasSource, HirDisplay, Module, PathResolution, Semantics, StructKind, Type,3    TypeInfo,4};5use ide_db::{6    FileId, FxHashMap, FxHashSet, RootDatabase, SnippetCap,7    defs::{Definition, NameRefClass},8    famous_defs::FamousDefs,9    helpers::is_editable_crate,10    path_transform::PathTransform,11    source_change::SourceChangeBuilder,12};13use itertools::Itertools;14use stdx::to_lower_snake_case;15use syntax::{16    Edition, SyntaxKind, SyntaxNode, T, TextRange,17    ast::{18        self, AstNode, BlockExpr, CallExpr, HasArgList, HasGenericParams, HasModuleItem,19        HasTypeBounds, edit::IndentLevel, edit_in_place::Indent, make,20    },21    ted,22};2324use crate::{25    AssistContext, AssistId, Assists,26    utils::{convert_reference_type, expr_fill_default, find_struct_impl},27};2829// Assist: generate_function30//31// Adds a stub function with a signature matching the function under the cursor.32//33// ```34// struct Baz;35// fn baz() -> Baz { Baz }36// fn foo() {37//     bar$0("", baz());38// }39//40// ```41// ->42// ```43// struct Baz;44// fn baz() -> Baz { Baz }45// fn foo() {46//     bar("", baz());47// }48//49// fn bar(arg: &'static str, baz: Baz) ${0:-> _} {50//     todo!()51// }52//53// ```54pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {55    gen_fn(acc, ctx).or_else(|| gen_method(acc, ctx))56}5758fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {59    let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;60    let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;61    let path = path_expr.path()?;62    let name_ref = path.segment()?.name_ref()?;63    if ctx.sema.resolve_path(&path).is_some() {64        // The function call already resolves, no need to add a function65        return None;66    }6768    let fn_name = &*name_ref.text();69    let TargetInfo { target_module, adt_info, target, file } =70        fn_target_info(ctx, path, &call, fn_name)?;7172    if let Some(m) = target_module73        && !is_editable_crate(m.krate(ctx.db()), ctx.db())74    {75        return None;76    }7778    let function_builder =79        FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target, &adt_info)?;80    let text_range = call.syntax().text_range();81    let label = format!("Generate {} function", function_builder.fn_name);82    add_func_to_accumulator(acc, ctx, text_range, function_builder, file, adt_info, label)83}8485struct TargetInfo {86    target_module: Option<Module>,87    adt_info: Option<AdtInfo>,88    target: GeneratedFunctionTarget,89    file: FileId,90}9192impl TargetInfo {93    fn new(94        target_module: Option<Module>,95        adt_info: Option<AdtInfo>,96        target: GeneratedFunctionTarget,97        file: FileId,98    ) -> Self {99        Self { target_module, adt_info, target, file }100    }101}102103fn fn_target_info(104    ctx: &AssistContext<'_>,105    path: ast::Path,106    call: &CallExpr,107    fn_name: &str,108) -> Option<TargetInfo> {109    match path.qualifier() {110        Some(qualifier) => match ctx.sema.resolve_path(&qualifier) {111            Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => {112                get_fn_target_info(ctx, Some(module), call.clone())113            }114            Some(hir::PathResolution::Def(hir::ModuleDef::Adt(adt))) => {115                if let hir::Adt::Enum(_) = adt {116                    // Don't suggest generating function if the name starts with an uppercase letter117                    if fn_name.starts_with(char::is_uppercase) {118                        return None;119                    }120                }121122                assoc_fn_target_info(ctx, call, adt, fn_name)123            }124            Some(hir::PathResolution::SelfType(impl_)) => {125                let adt = impl_.self_ty(ctx.db()).as_adt()?;126                assoc_fn_target_info(ctx, call, adt, fn_name)127            }128            _ => None,129        },130        _ => get_fn_target_info(ctx, None, call.clone()),131    }132}133134fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {135    let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;136    if ctx.sema.resolve_method_call(&call).is_some() {137        return None;138    }139140    let fn_name = call.name_ref()?;141    let receiver_ty = ctx.sema.type_of_expr(&call.receiver()?)?.original().strip_references();142    let adt = receiver_ty.as_adt()?;143144    let target_module = adt.module(ctx.sema.db);145    if !is_editable_crate(target_module.krate(ctx.db()), ctx.db()) {146        return None;147    }148149    let enclosing_impl = ctx.find_node_at_offset::<ast::Impl>();150    let cursor_impl = enclosing_impl.filter(|impl_| {151        ctx.sema.to_def(impl_).map_or(false, |def| def.self_ty(ctx.sema.db).as_adt() == Some(adt))152    });153154    let (impl_, file) = if let Some(impl_) = cursor_impl {155        (Some(impl_), ctx.vfs_file_id())156    } else {157        get_adt_source(ctx, &adt, fn_name.text().as_str())?158    };159    let target = get_method_target(ctx, &impl_, &adt)?;160161    let function_builder = FunctionBuilder::from_method_call(162        ctx,163        &call,164        &fn_name,165        receiver_ty,166        target_module,167        target,168    )?;169    let text_range = call.syntax().text_range();170    let adt_info = AdtInfo::new(adt, impl_.is_some());171    let label = format!("Generate {} method", function_builder.fn_name);172    add_func_to_accumulator(acc, ctx, text_range, function_builder, file, Some(adt_info), label)173}174175fn add_func_to_accumulator(176    acc: &mut Assists,177    ctx: &AssistContext<'_>,178    text_range: TextRange,179    function_builder: FunctionBuilder,180    file: FileId,181    adt_info: Option<AdtInfo>,182    label: String,183) -> Option<()> {184    acc.add(AssistId::generate("generate_function"), label, text_range, |edit| {185        edit.edit_file(file);186187        let target = function_builder.target.clone();188        let edition = function_builder.target_edition;189        let func = function_builder.render(ctx.config.snippet_cap, edit);190191        if let Some(adt) = adt_info192            .and_then(|adt_info| if adt_info.impl_exists { None } else { Some(adt_info.adt) })193        {194            let name = make::ty_path(make::ext::ident_path(&format!(195                "{}",196                adt.name(ctx.db()).display(ctx.db(), edition)197            )));198199            // FIXME: adt may have generic params.200            let impl_ = make::impl_(None, None, None, name, None, None).clone_for_update();201202            func.indent(IndentLevel(1));203            impl_.get_or_create_assoc_item_list().add_item(func.into());204            target.insert_impl_at(edit, impl_);205        } else {206            target.insert_fn_at(edit, func);207        }208    })209}210211fn get_adt_source(212    ctx: &AssistContext<'_>,213    adt: &hir::Adt,214    fn_name: &str,215) -> Option<(Option<ast::Impl>, FileId)> {216    let range = adt.source(ctx.sema.db)?.syntax().original_file_range_rooted(ctx.sema.db);217218    let file = ctx.sema.parse(range.file_id);219    let adt_source =220        ctx.sema.find_node_at_offset_with_macros(file.syntax(), range.range.start())?;221    find_struct_impl(ctx, &adt_source, &[fn_name.to_owned()])222        .map(|impl_| (impl_, range.file_id.file_id(ctx.db())))223}224225struct FunctionBuilder {226    target: GeneratedFunctionTarget,227    fn_name: ast::Name,228    generic_param_list: Option<ast::GenericParamList>,229    where_clause: Option<ast::WhereClause>,230    params: ast::ParamList,231    fn_body: BlockExpr,232    ret_type: Option<ast::RetType>,233    should_focus_return_type: bool,234    visibility: Visibility,235    is_async: bool,236    target_edition: Edition,237}238239impl FunctionBuilder {240    /// Prepares a generated function that matches `call`.241    /// The function is generated in `target_module` or next to `call`242    fn from_call(243        ctx: &AssistContext<'_>,244        call: &ast::CallExpr,245        fn_name: &str,246        target_module: Option<Module>,247        target: GeneratedFunctionTarget,248        adt_info: &Option<AdtInfo>,249    ) -> Option<Self> {250        let target_module =251            target_module.or_else(|| ctx.sema.scope(target.syntax()).map(|it| it.module()))?;252        let target_edition = target_module.krate(ctx.db()).edition(ctx.db());253254        let current_module = ctx.sema.scope(call.syntax())?.module();255        let visibility = calculate_necessary_visibility(current_module, target_module, ctx);256        let fn_name = make::name(fn_name);257        let mut necessary_generic_params = FxHashSet::default();258        let params = fn_args(259            ctx,260            target_module,261            ast::CallableExpr::Call(call.clone()),262            &mut necessary_generic_params,263        )?;264265        let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast);266        let is_async = await_expr.is_some();267268        let ret_type;269        let should_focus_return_type;270        let fn_body;271272        // If generated function has the name "new" and is an associated function, we generate fn body273        // as a constructor and assume a "Self" return type.274        if let Some(body) =275            make_fn_body_as_new_function(ctx, &fn_name.text(), adt_info, target_edition)276        {277            ret_type = Some(make::ret_type(make::ty_path(make::ext::ident_path("Self"))));278            should_focus_return_type = false;279            fn_body = body;280        } else {281            let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into());282            (ret_type, should_focus_return_type) = make_return_type(283                ctx,284                &expr_for_ret_ty,285                target_module,286                &mut necessary_generic_params,287            );288            let placeholder_expr = expr_fill_default(ctx.config);289            fn_body = make::block_expr(vec![], Some(placeholder_expr));290        };291292        let (generic_param_list, where_clause) =293            fn_generic_params(ctx, necessary_generic_params, &target)?;294295        Some(Self {296            target,297            fn_name,298            generic_param_list,299            where_clause,300            params,301            fn_body,302            ret_type,303            should_focus_return_type,304            visibility,305            is_async,306            target_edition,307        })308    }309310    fn from_method_call(311        ctx: &AssistContext<'_>,312        call: &ast::MethodCallExpr,313        name: &ast::NameRef,314        receiver_ty: Type<'_>,315        target_module: Module,316        target: GeneratedFunctionTarget,317    ) -> Option<Self> {318        let target_edition = target_module.krate(ctx.db()).edition(ctx.db());319320        let current_module = ctx.sema.scope(call.syntax())?.module();321        let visibility = calculate_necessary_visibility(current_module, target_module, ctx);322323        let fn_name = make::name(name.ident_token()?.text());324        let mut necessary_generic_params = FxHashSet::default();325        necessary_generic_params.extend(receiver_ty.generic_params(ctx.db()));326        let params = fn_args(327            ctx,328            target_module,329            ast::CallableExpr::MethodCall(call.clone()),330            &mut necessary_generic_params,331        )?;332333        let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast);334        let is_async = await_expr.is_some();335336        let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into());337        let (ret_type, should_focus_return_type) =338            make_return_type(ctx, &expr_for_ret_ty, target_module, &mut necessary_generic_params);339340        let (generic_param_list, where_clause) =341            fn_generic_params(ctx, necessary_generic_params, &target)?;342343        let placeholder_expr = expr_fill_default(ctx.config);344        let fn_body = make::block_expr(vec![], Some(placeholder_expr));345346        Some(Self {347            target,348            fn_name,349            generic_param_list,350            where_clause,351            params,352            fn_body,353            ret_type,354            should_focus_return_type,355            visibility,356            is_async,357            target_edition,358        })359    }360361    fn render(self, cap: Option<SnippetCap>, edit: &mut SourceChangeBuilder) -> ast::Fn {362        let visibility = match self.visibility {363            Visibility::None => None,364            Visibility::Crate => Some(make::visibility_pub_crate()),365            Visibility::Pub => Some(make::visibility_pub()),366        };367        let type_params =368            self.generic_param_list.filter(|list| list.generic_params().next().is_some());369        let fn_def = make::fn_(370            None,371            visibility,372            self.fn_name,373            type_params,374            self.where_clause,375            self.params,376            self.fn_body,377            self.ret_type,378            self.is_async,379            false, // FIXME : const and unsafe are not handled yet.380            false,381            false,382        )383        .clone_for_update();384385        let ret_type = fn_def.ret_type();386        // PANIC: we guarantee we always create a function body with a tail expr387        let tail_expr = fn_def388            .body()389            .expect("generated function should have a body")390            .tail_expr()391            .expect("function body should have a tail expression");392393        if let Some(cap) = cap {394            if self.should_focus_return_type {395                // Focus the return type if there is one396                match ret_type {397                    Some(ret_type) => {398                        edit.add_placeholder_snippet(cap, ret_type);399                    }400                    None => {401                        edit.add_placeholder_snippet(cap, tail_expr);402                    }403                }404            } else {405                edit.add_placeholder_snippet(cap, tail_expr);406            }407        }408409        fn_def410    }411}412413/// Makes an optional return type along with whether the return type should be focused by the cursor.414/// If we cannot infer what the return type should be, we create a placeholder type.415///416/// The rule for whether we focus a return type or not (and thus focus the function body),417/// is rather simple:418/// * If we could *not* infer what the return type should be, focus it (so the user can fill-in419///   the correct return type).420/// * If we could infer the return type, don't focus it (and thus focus the function body) so the421///   user can change the `todo!` function body.422fn make_return_type(423    ctx: &AssistContext<'_>,424    expr: &ast::Expr,425    target_module: Module,426    necessary_generic_params: &mut FxHashSet<hir::GenericParam>,427) -> (Option<ast::RetType>, bool) {428    let (ret_ty, should_focus_return_type) = {429        match ctx.sema.type_of_expr(expr).map(TypeInfo::original) {430            Some(ty) if ty.is_unknown() => (Some(make::ty_placeholder()), true),431            None => (Some(make::ty_placeholder()), true),432            Some(ty) if ty.is_unit() => (None, false),433            Some(ty) => {434                necessary_generic_params.extend(ty.generic_params(ctx.db()));435                let rendered = ty.display_source_code(ctx.db(), target_module.into(), true);436                match rendered {437                    Ok(rendered) => (Some(make::ty(&rendered)), false),438                    Err(_) => (Some(make::ty_placeholder()), true),439                }440            }441        }442    };443    let ret_type = ret_ty.map(make::ret_type);444    (ret_type, should_focus_return_type)445}446447fn make_fn_body_as_new_function(448    ctx: &AssistContext<'_>,449    fn_name: &str,450    adt_info: &Option<AdtInfo>,451    edition: Edition,452) -> Option<ast::BlockExpr> {453    if fn_name != "new" {454        return None;455    };456    let adt_info = adt_info.as_ref()?;457458    let path_self = make::ext::ident_path("Self");459    let placeholder_expr = expr_fill_default(ctx.config);460    let tail_expr = if let Some(strukt) = adt_info.adt.as_struct() {461        match strukt.kind(ctx.db()) {462            StructKind::Record => {463                let fields = strukt464                    .fields(ctx.db())465                    .iter()466                    .map(|field| {467                        make::record_expr_field(468                            make::name_ref(&format!(469                                "{}",470                                field.name(ctx.db()).display(ctx.db(), edition)471                            )),472                            Some(placeholder_expr.clone()),473                        )474                    })475                    .collect::<Vec<_>>();476477                make::record_expr(path_self, make::record_expr_field_list(fields)).into()478            }479            StructKind::Tuple => {480                let args = strukt481                    .fields(ctx.db())482                    .iter()483                    .map(|_| placeholder_expr.clone())484                    .collect::<Vec<_>>();485486                make::expr_call(make::expr_path(path_self), make::arg_list(args)).into()487            }488            StructKind::Unit => make::expr_path(path_self),489        }490    } else {491        placeholder_expr492    };493494    let fn_body = make::block_expr(vec![], Some(tail_expr));495    Some(fn_body)496}497498fn get_fn_target_info(499    ctx: &AssistContext<'_>,500    target_module: Option<Module>,501    call: CallExpr,502) -> Option<TargetInfo> {503    let (target, file) = get_fn_target(ctx, target_module, call)?;504    Some(TargetInfo::new(target_module, None, target, file))505}506507fn get_fn_target(508    ctx: &AssistContext<'_>,509    target_module: Option<Module>,510    call: CallExpr,511) -> Option<(GeneratedFunctionTarget, FileId)> {512    let mut file = ctx.vfs_file_id();513    let target = match target_module {514        Some(target_module) => {515            let (in_file, target) = next_space_for_fn_in_module(ctx.db(), target_module);516            file = in_file;517            target518        }519        None => next_space_for_fn_after_call_site(ast::CallableExpr::Call(call))?,520    };521    Some((target, file))522}523524fn get_method_target(525    ctx: &AssistContext<'_>,526    impl_: &Option<ast::Impl>,527    adt: &Adt,528) -> Option<GeneratedFunctionTarget> {529    let target = match impl_ {530        Some(impl_) => GeneratedFunctionTarget::InImpl(impl_.clone()),531        None => GeneratedFunctionTarget::AfterItem(adt.source(ctx.sema.db)?.syntax().value.clone()),532    };533    Some(target)534}535536fn assoc_fn_target_info(537    ctx: &AssistContext<'_>,538    call: &CallExpr,539    adt: hir::Adt,540    fn_name: &str,541) -> Option<TargetInfo> {542    let current_module = ctx.sema.scope(call.syntax())?.module();543    let module = adt.module(ctx.sema.db);544    let target_module = if current_module == module { None } else { Some(module) };545    if current_module.krate(ctx.db()) != module.krate(ctx.db()) {546        return None;547    }548    let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?;549    let target = get_method_target(ctx, &impl_, &adt)?;550    let adt_info = AdtInfo::new(adt, impl_.is_some());551    Some(TargetInfo::new(target_module, Some(adt_info), target, file))552}553554#[derive(Clone)]555enum GeneratedFunctionTarget {556    AfterItem(SyntaxNode),557    InEmptyItemList(SyntaxNode),558    InImpl(ast::Impl),559}560561impl GeneratedFunctionTarget {562    fn syntax(&self) -> &SyntaxNode {563        match self {564            GeneratedFunctionTarget::AfterItem(it) => it,565            GeneratedFunctionTarget::InEmptyItemList(it) => it,566            GeneratedFunctionTarget::InImpl(it) => it.syntax(),567        }568    }569570    fn parent(&self) -> SyntaxNode {571        match self {572            GeneratedFunctionTarget::AfterItem(it) => it.parent().expect("item without parent"),573            GeneratedFunctionTarget::InEmptyItemList(it) => it.clone(),574            GeneratedFunctionTarget::InImpl(it) => it.syntax().clone(),575        }576    }577578    fn insert_impl_at(&self, edit: &mut SourceChangeBuilder, impl_: ast::Impl) {579        match self {580            GeneratedFunctionTarget::AfterItem(item) => {581                let item = edit.make_syntax_mut(item.clone());582                let position = if item.parent().is_some() {583                    ted::Position::after(&item)584                } else {585                    ted::Position::first_child_of(&item)586                };587588                let indent = IndentLevel::from_node(&item);589                let leading_ws = make::tokens::whitespace(&format!("\n{indent}"));590                impl_.indent(indent);591592                ted::insert_all(position, vec![leading_ws.into(), impl_.syntax().clone().into()]);593            }594            GeneratedFunctionTarget::InEmptyItemList(item_list) => {595                let item_list = edit.make_syntax_mut(item_list.clone());596                let insert_after =597                    item_list.children_with_tokens().find_or_first(|child| child.kind() == T!['{']);598                let position = match insert_after {599                    Some(child) => ted::Position::after(child),600                    None => ted::Position::first_child_of(&item_list),601                };602603                let indent = IndentLevel::from_node(&item_list);604                let leading_indent = indent + 1;605                let leading_ws = make::tokens::whitespace(&format!("\n{leading_indent}"));606                impl_.indent(indent);607608                ted::insert_all(position, vec![leading_ws.into(), impl_.syntax().clone().into()]);609            }610            GeneratedFunctionTarget::InImpl(_) => {611                unreachable!("can't insert an impl inside an impl")612            }613        }614    }615616    fn insert_fn_at(&self, edit: &mut SourceChangeBuilder, func: ast::Fn) {617        match self {618            GeneratedFunctionTarget::AfterItem(item) => {619                let item = edit.make_syntax_mut(item.clone());620                let position = if item.parent().is_some() {621                    ted::Position::after(&item)622                } else {623                    ted::Position::first_child_of(&item)624                };625626                let indent = IndentLevel::from_node(&item);627                let leading_ws = make::tokens::whitespace(&format!("\n\n{indent}"));628                func.indent(indent);629630                ted::insert_all_raw(631                    position,632                    vec![leading_ws.into(), func.syntax().clone().into()],633                );634            }635            GeneratedFunctionTarget::InEmptyItemList(item_list) => {636                let item_list = edit.make_syntax_mut(item_list.clone());637                let insert_after =638                    item_list.children_with_tokens().find_or_first(|child| child.kind() == T!['{']);639                let position = match insert_after {640                    Some(child) => ted::Position::after(child),641                    None => ted::Position::first_child_of(&item_list),642                };643644                let indent = IndentLevel::from_node(&item_list);645                let leading_indent = indent + 1;646                let leading_ws = make::tokens::whitespace(&format!("\n{leading_indent}"));647                let trailing_ws = make::tokens::whitespace(&format!("\n{indent}"));648                func.indent(leading_indent);649650                ted::insert_all(651                    position,652                    vec![leading_ws.into(), func.syntax().clone().into(), trailing_ws.into()],653                );654            }655            GeneratedFunctionTarget::InImpl(impl_) => {656                let impl_ = edit.make_mut(impl_.clone());657658                let leading_indent = impl_.indent_level() + 1;659                func.indent(leading_indent);660661                impl_.get_or_create_assoc_item_list().add_item(func.into());662            }663        }664    }665}666667struct AdtInfo {668    adt: hir::Adt,669    impl_exists: bool,670}671672impl AdtInfo {673    fn new(adt: Adt, impl_exists: bool) -> Self {674        Self { adt, impl_exists }675    }676}677678/// Computes parameter list for the generated function.679fn fn_args(680    ctx: &AssistContext<'_>,681    target_module: Module,682    call: ast::CallableExpr,683    necessary_generic_params: &mut FxHashSet<hir::GenericParam>,684) -> Option<ast::ParamList> {685    let mut arg_names = Vec::new();686    let mut arg_types = Vec::new();687    for arg in call.arg_list()?.args() {688        arg_names.push(fn_arg_name(&ctx.sema, &arg));689        arg_types.push(fn_arg_type(ctx, target_module, &arg, necessary_generic_params));690    }691    deduplicate_arg_names(&mut arg_names);692    let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| {693        make::param(make::ext::simple_ident_pat(make::name(&name)).into(), make::ty(&ty))694    });695696    Some(make::param_list(697        match call {698            ast::CallableExpr::Call(_) => None,699            ast::CallableExpr::MethodCall(_) => Some(make::self_param()),700        },701        params,702    ))703}704705/// Gets parameter bounds and where predicates in scope and filters out irrelevant ones. Returns706/// `None` when it fails to get scope information.707///708/// See comment on `filter_unnecessary_bounds()` for what bounds we consider relevant.709///710/// NOTE: Generic parameters returned from this function may cause name clash at `target`. We don't711/// currently do anything about it because it's actually easy to resolve it after the assist: just712/// use the Rename functionality.713fn fn_generic_params(714    ctx: &AssistContext<'_>,715    necessary_params: FxHashSet<hir::GenericParam>,716    target: &GeneratedFunctionTarget,717) -> Option<(Option<ast::GenericParamList>, Option<ast::WhereClause>)> {718    if necessary_params.is_empty() {719        // Not really needed but fast path.720        return Some((None, None));721    }722723    // 1. Get generic parameters (with bounds) and where predicates in scope.724    let (generic_params, where_preds) = params_and_where_preds_in_scope(ctx);725726    // 2. Extract type parameters included in each bound.727    let mut generic_params = generic_params728        .into_iter()729        .filter_map(|it| compute_contained_params_in_generic_param(ctx, it))730        .collect();731    let mut where_preds = where_preds732        .into_iter()733        .filter_map(|it| compute_contained_params_in_where_pred(ctx, it))734        .collect();735736    // 3. Filter out unnecessary bounds.737    filter_unnecessary_bounds(&mut generic_params, &mut where_preds, necessary_params);738    filter_bounds_in_scope(&mut generic_params, &mut where_preds, ctx, target);739740    let generic_params: Vec<ast::GenericParam> =741        generic_params.into_iter().map(|it| it.node.clone_for_update()).collect();742    let where_preds: Vec<ast::WherePred> =743        where_preds.into_iter().map(|it| it.node.clone_for_update()).collect();744745    let (generic_params, where_preds): (Vec<ast::GenericParam>, Vec<ast::WherePred>) =746        if let Some(param) = generic_params.first()747            && let source_scope = ctx.sema.scope(param.syntax())?748            && let target_scope = ctx.sema.scope(&target.parent())?749            && source_scope.module() != target_scope.module()750        {751            // 4. Rewrite paths752            let transform = PathTransform::generic_transformation(&target_scope, &source_scope);753            let generic_params = generic_params.iter().map(|it| it.syntax());754            let where_preds = where_preds.iter().map(|it| it.syntax());755            transform756                .apply_all(generic_params.chain(where_preds))757                .into_iter()758                .filter_map(|it| {759                    if let Some(it) = ast::GenericParam::cast(it.clone()) {760                        Some(either::Either::Left(it))761                    } else {762                        ast::WherePred::cast(it).map(either::Either::Right)763                    }764                })765                .partition_map(|it| it)766        } else {767            (generic_params, where_preds)768        };769770    let generic_param_list = make::generic_param_list(generic_params);771    let where_clause =772        if where_preds.is_empty() { None } else { Some(make::where_clause(where_preds)) };773774    Some((Some(generic_param_list), where_clause))775}776777fn params_and_where_preds_in_scope(778    ctx: &AssistContext<'_>,779) -> (Vec<ast::GenericParam>, Vec<ast::WherePred>) {780    let Some(body) = containing_body(ctx) else {781        return Default::default();782    };783784    let mut generic_params = Vec::new();785    let mut where_clauses = Vec::new();786787    // There are two items where generic parameters currently in scope may be declared: the item788    // the cursor is at, and its parent (if any).789    //790    // We handle parent first so that their generic parameters appear first in the generic791    // parameter list of the function we're generating.792    let db = ctx.db();793    if let Some(parent) = body.as_assoc_item(db).map(|it| it.container(db)) {794        match parent {795            hir::AssocItemContainer::Impl(it) => {796                let (params, clauses) = get_bounds_in_scope(ctx, it);797                generic_params.extend(params);798                where_clauses.extend(clauses);799            }800            hir::AssocItemContainer::Trait(it) => {801                let (params, clauses) = get_bounds_in_scope(ctx, it);802                generic_params.extend(params);803                where_clauses.extend(clauses);804            }805        }806    }807808    // Other defs with body may inherit generic parameters from its parent, but never have their809    // own generic parameters.810    if let hir::DefWithBody::Function(it) = body {811        let (params, clauses) = get_bounds_in_scope(ctx, it);812        generic_params.extend(params);813        where_clauses.extend(clauses);814    }815816    (generic_params, where_clauses)817}818819fn containing_body(ctx: &AssistContext<'_>) -> Option<hir::DefWithBody> {820    let item: ast::Item = ctx.find_node_at_offset()?;821    let def = match item {822        ast::Item::Fn(it) => ctx.sema.to_def(&it)?.into(),823        ast::Item::Const(it) => ctx.sema.to_def(&it)?.into(),824        ast::Item::Static(it) => ctx.sema.to_def(&it)?.into(),825        _ => return None,826    };827    Some(def)828}829830fn get_bounds_in_scope<D>(831    ctx: &AssistContext<'_>,832    def: D,833) -> (impl Iterator<Item = ast::GenericParam>, impl Iterator<Item = ast::WherePred>)834where835    D: HasSource,836    D::Ast: HasGenericParams,837{838    // This function should be only called with `Impl`, `Trait`, or `Function`, for which it's839    // infallible to get source ast.840    let node = ctx.sema.source(def).expect("definition's source couldn't be found").value;841    let generic_params = node.generic_param_list().into_iter().flat_map(|it| it.generic_params());842    let where_clauses = node.where_clause().into_iter().flat_map(|it| it.predicates());843    (generic_params, where_clauses)844}845846#[derive(Debug)]847struct ParamBoundWithParams {848    node: ast::GenericParam,849    /// Generic parameter `node` introduces.850    ///851    /// ```text852    /// impl<T> S<T> {853    ///     fn f<U: Trait<T>>() {}854    ///          ^ this855    /// }856    /// ```857    ///858    /// `U` in this example.859    self_ty_param: hir::GenericParam,860    /// Generic parameters contained in the trait reference of this bound.861    ///862    /// ```text863    /// impl<T> S<T> {864    ///     fn f<U: Trait<T>>() {}865    ///             ^^^^^^^^ params in this part866    /// }867    /// ```868    ///869    /// `T` in this example.870    other_params: FxHashSet<hir::GenericParam>,871}872873#[derive(Debug)]874struct WherePredWithParams {875    node: ast::WherePred,876    /// Generic parameters contained in the "self type" of this where predicate.877    ///878    /// ```text879    /// Struct<T, U>: Trait<T, Assoc = V>,880    /// ^^^^^^^^^^^^ params in this part881    /// ```882    ///883    /// `T` and `U` in this example.884    self_ty_params: FxHashSet<hir::GenericParam>,885    /// Generic parameters contained in the trait reference of this where predicate.886    ///887    /// ```text888    /// Struct<T, U>: Trait<T, Assoc = V>,889    ///               ^^^^^^^^^^^^^^^^^^^ params in this part890    /// ```891    ///892    /// `T` and `V` in this example.893    other_params: FxHashSet<hir::GenericParam>,894}895896fn compute_contained_params_in_generic_param(897    ctx: &AssistContext<'_>,898    node: ast::GenericParam,899) -> Option<ParamBoundWithParams> {900    match &node {901        ast::GenericParam::TypeParam(ty) => {902            let self_ty_param = ctx.sema.to_def(ty)?.into();903904            let other_params = ty905                .type_bound_list()906                .into_iter()907                .flat_map(|it| it.bounds())908                .flat_map(|bound| bound.syntax().descendants())909                .filter_map(|node| filter_generic_params(ctx, node))910                .collect();911912            Some(ParamBoundWithParams { node, self_ty_param, other_params })913        }914        ast::GenericParam::ConstParam(ct) => {915            let self_ty_param = ctx.sema.to_def(ct)?.into();916            Some(ParamBoundWithParams { node, self_ty_param, other_params: FxHashSet::default() })917        }918        ast::GenericParam::LifetimeParam(_) => {919            // FIXME: It might be a good idea to handle lifetime parameters too.920            None921        }922    }923}924925fn compute_contained_params_in_where_pred(926    ctx: &AssistContext<'_>,927    node: ast::WherePred,928) -> Option<WherePredWithParams> {929    let self_ty = node.ty()?;930    let bound_list = node.type_bound_list()?;931932    let self_ty_params = self_ty933        .syntax()934        .descendants()935        .filter_map(|node| filter_generic_params(ctx, node))936        .collect();937938    let other_params = bound_list939        .bounds()940        .flat_map(|bound| bound.syntax().descendants())941        .filter_map(|node| filter_generic_params(ctx, node))942        .collect();943944    Some(WherePredWithParams { node, self_ty_params, other_params })945}946947fn filter_generic_params(ctx: &AssistContext<'_>, node: SyntaxNode) -> Option<hir::GenericParam> {948    let path = ast::Path::cast(node)?;949    match ctx.sema.resolve_path(&path)? {950        PathResolution::TypeParam(it) => Some(it.into()),951        PathResolution::ConstParam(it) => Some(it.into()),952        _ => None,953    }954}955956/// Filters out irrelevant bounds from `generic_params` and `where_preds`.957///958/// Say we have a trait bound `Struct<T>: Trait<U>`. Given `necessary_params`, when is it relevant959/// and when not? Some observations:960/// - When `necessary_params` contains `T`, it's likely that we want this bound, but now we have961///   an extra param to consider: `U`.962/// - On the other hand, when `necessary_params` contains `U` (but not `T`), then it's unlikely963///   that we want this bound because it doesn't really constrain `U`.964///965/// (FIXME?: The latter clause might be overstating. We may want to include the bound if the self966/// type does *not* include generic params at all - like `Option<i32>: From<U>`)967///968/// Can we make this a bit more formal? Let's define "dependency" between generic parameters and969/// trait bounds:970/// - A generic parameter `T` depends on a trait bound if `T` appears in the self type (i.e. left971///   part) of the bound.972/// - A trait bound depends on a generic parameter `T` if `T` appears in the bound.973///974/// Using the notion, what we want is all the bounds that params in `necessary_params`975/// *transitively* depend on!976///977/// Now it's not hard to solve: we build a dependency graph and compute all reachable nodes from978/// nodes that represent params in `necessary_params` by usual and boring DFS.979///980/// The time complexity is O(|generic_params| + |where_preds| + |necessary_params|).981fn filter_unnecessary_bounds(982    generic_params: &mut Vec<ParamBoundWithParams>,983    where_preds: &mut Vec<WherePredWithParams>,984    necessary_params: FxHashSet<hir::GenericParam>,985) {986    // All `self_ty_param` should be unique as they were collected from `ast::GenericParamList`s.987    let param_map: FxHashMap<hir::GenericParam, usize> =988        generic_params.iter().map(|it| it.self_ty_param).zip(0..).collect();989    let param_count = param_map.len();990    let generic_params_upper_bound = param_count + generic_params.len();991    let node_count = generic_params_upper_bound + where_preds.len();992993    // | node index range                        | what the node represents |994    // |-----------------------------------------|--------------------------|995    // | 0..param_count                          | generic parameter        |996    // | param_count..generic_params_upper_bound | `ast::GenericParam`      |997    // | generic_params_upper_bound..node_count  | `ast::WherePred`         |998    let mut graph = Graph::new(node_count);999    for (pred, pred_idx) in generic_params.iter().zip(param_count..) {1000        let param_idx = param_map[&pred.self_ty_param];1001        graph.add_edge(param_idx, pred_idx);1002        graph.add_edge(pred_idx, param_idx);10031004        for param in &pred.other_params {1005            let param_idx = param_map[param];1006            graph.add_edge(pred_idx, param_idx);1007        }1008    }1009    for (pred, pred_idx) in where_preds.iter().zip(generic_params_upper_bound..) {1010        for param in &pred.self_ty_params {1011            let param_idx = param_map[param];1012            graph.add_edge(param_idx, pred_idx);1013            graph.add_edge(pred_idx, param_idx);1014        }1015        for param in &pred.other_params {1016            let param_idx = param_map[param];1017            graph.add_edge(pred_idx, param_idx);1018        }1019    }10201021    let starting_nodes = necessary_params.iter().flat_map(|param| param_map.get(param).copied());1022    let reachable = graph.compute_reachable_nodes(starting_nodes);10231024    // Not pretty, but effective. If only there were `Vec::retain_index()`...1025    let mut idx = param_count;1026    generic_params.retain(|_| {1027        idx += 1;1028        reachable[idx - 1]1029    });1030    stdx::always!(idx == generic_params_upper_bound, "inconsistent index");1031    where_preds.retain(|_| {1032        idx += 1;1033        reachable[idx - 1]1034    });1035}10361037/// Filters out bounds from impl if we're generating the function into the same impl we're1038/// generating from.1039fn filter_bounds_in_scope(1040    generic_params: &mut Vec<ParamBoundWithParams>,1041    where_preds: &mut Vec<WherePredWithParams>,1042    ctx: &AssistContext<'_>,1043    target: &GeneratedFunctionTarget,1044) -> Option<()> {1045    let target_impl = target.parent().ancestors().find_map(ast::Impl::cast)?;1046    let target_impl = ctx.sema.to_def(&target_impl)?;1047    // It's sufficient to test only the first element of `generic_params` because of the order of1048    // insertion (see `params_and_where_preds_in_scope()`).1049    let def = generic_params.first()?.self_ty_param.parent();1050    if def != hir::GenericDef::Impl(target_impl) {1051        return None;1052    }10531054    // Now we know every element that belongs to an impl would be in scope at `target`, we can1055    // filter them out just by looking at their parent.1056    generic_params.retain(|it| !matches!(it.self_ty_param.parent(), hir::GenericDef::Impl(_)));1057    where_preds.retain(|it| {1058        it.node.syntax().parent().and_then(|it| it.parent()).and_then(ast::Impl::cast).is_none()1059    });10601061    Some(())1062}10631064/// Makes duplicate argument names unique by appending incrementing numbers.1065///1066/// ```ignore1067/// let mut names: Vec<String> =1068///     vec!["foo".into(), "foo".into(), "bar".into(), "baz".into(), "bar".into()];1069/// deduplicate_arg_names(&mut names);1070/// let expected: Vec<String> =1071///     vec!["foo_1".into(), "foo_2".into(), "bar_1".into(), "baz".into(), "bar_2".into()];1072/// assert_eq!(names, expected);1073/// ```1074fn deduplicate_arg_names(arg_names: &mut [String]) {1075    let mut arg_name_counts = FxHashMap::default();1076    for name in arg_names.iter() {1077        *arg_name_counts.entry(name).or_insert(0) += 1;1078    }1079    let duplicate_arg_names: FxHashSet<String> = arg_name_counts1080        .into_iter()1081        .filter(|(_, count)| *count >= 2)1082        .map(|(name, _)| name.clone())1083        .collect();10841085    let mut counter_per_name = FxHashMap::default();1086    for arg_name in arg_names.iter_mut() {1087        if duplicate_arg_names.contains(arg_name) {1088            let counter = counter_per_name.entry(arg_name.clone()).or_insert(1);1089            arg_name.push('_');1090            arg_name.push_str(&counter.to_string());1091            *counter += 1;1092        }1093    }1094}10951096fn fn_arg_name(sema: &Semantics<'_, RootDatabase>, arg_expr: &ast::Expr) -> String {1097    let name = (|| match arg_expr {1098        ast::Expr::CastExpr(cast_expr) => Some(fn_arg_name(sema, &cast_expr.expr()?)),1099        expr => {1100            let name_ref = expr1101                .syntax()1102                .descendants()1103                .filter_map(ast::NameRef::cast)1104                .filter(|name| name.ident_token().is_some())1105                .last()?;1106            if let Some(NameRefClass::Definition(Definition::Const(_) | Definition::Static(_), _)) =1107                NameRefClass::classify(sema, &name_ref)1108            {1109                return Some(name_ref.to_string().to_lowercase());1110            };1111            Some(to_lower_snake_case(&name_ref.to_string()))1112        }1113    })();1114    match name {1115        Some(mut name) if name.starts_with(|c: char| c.is_ascii_digit()) => {1116            name.insert_str(0, "arg");1117            name1118        }1119        Some(name) => name,1120        None => "arg".to_owned(),1121    }1122}11231124fn fn_arg_type(1125    ctx: &AssistContext<'_>,1126    target_module: Module,1127    fn_arg: &ast::Expr,1128    generic_params: &mut FxHashSet<hir::GenericParam>,1129) -> String {1130    fn maybe_displayed_type(1131        ctx: &AssistContext<'_>,1132        target_module: Module,1133        fn_arg: &ast::Expr,1134        generic_params: &mut FxHashSet<hir::GenericParam>,1135    ) -> Option<String> {1136        let ty = ctx.sema.type_of_expr(fn_arg)?.adjusted();1137        if ty.is_unknown() {1138            return None;1139        }11401141        generic_params.extend(ty.generic_params(ctx.db()));11421143        if ty.is_reference() || ty.is_mutable_reference() {1144            let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax())?.krate());1145            convert_reference_type(ty.strip_references(), ctx.db(), famous_defs)1146                .map(|conversion| conversion.convert_type(ctx.db(), target_module).to_string())1147                .or_else(|| ty.display_source_code(ctx.db(), target_module.into(), true).ok())1148        } else {1149            ty.display_source_code(ctx.db(), target_module.into(), true).ok()1150        }1151    }11521153    maybe_displayed_type(ctx, target_module, fn_arg, generic_params)1154        .unwrap_or_else(|| String::from("_"))1155}11561157/// Returns the position inside the current mod or file1158/// directly after the current block1159/// We want to write the generated function directly after1160/// fns, impls or macro calls, but inside mods1161fn next_space_for_fn_after_call_site(expr: ast::CallableExpr) -> Option<GeneratedFunctionTarget> {1162    let mut ancestors = expr.syntax().ancestors().peekable();1163    let mut last_ancestor: Option<SyntaxNode> = None;1164    while let Some(next_ancestor) = ancestors.next() {1165        match next_ancestor.kind() {1166            SyntaxKind::SOURCE_FILE => {1167                break;1168            }1169            SyntaxKind::ITEM_LIST1170                if ancestors.peek().map(|a| a.kind()) == Some(SyntaxKind::MODULE) =>1171            {1172                break;1173            }1174            _ => {}1175        }1176        last_ancestor = Some(next_ancestor);1177    }1178    last_ancestor.map(GeneratedFunctionTarget::AfterItem)1179}11801181fn next_space_for_fn_in_module(1182    db: &dyn hir::db::HirDatabase,1183    target_module: hir::Module,1184) -> (FileId, GeneratedFunctionTarget) {1185    let module_source = target_module.definition_source(db);1186    let file = module_source.file_id.original_file(db);1187    let assist_item = match &module_source.value {1188        hir::ModuleSource::SourceFile(it) => match it.items().last() {1189            Some(last_item) => GeneratedFunctionTarget::AfterItem(last_item.syntax().clone()),1190            None => GeneratedFunctionTarget::AfterItem(it.syntax().clone()),1191        },1192        hir::ModuleSource::Module(it) => match it.item_list().and_then(|it| it.items().last()) {1193            Some(last_item) => GeneratedFunctionTarget::AfterItem(last_item.syntax().clone()),1194            None => {1195                let item_list =1196                    it.item_list().expect("module definition source should have an item list");1197                GeneratedFunctionTarget::InEmptyItemList(item_list.syntax().clone())1198            }1199        },1200        hir::ModuleSource::BlockExpr(it) => {1201            if let Some(last_item) =1202                it.statements().take_while(|stmt| matches!(stmt, ast::Stmt::Item(_))).last()1203            {1204                GeneratedFunctionTarget::AfterItem(last_item.syntax().clone())1205            } else {1206                GeneratedFunctionTarget::InEmptyItemList(it.syntax().clone())1207            }1208        }1209    };12101211    (file.file_id(db), assist_item)1212}12131214#[derive(Clone, Copy)]1215enum Visibility {1216    None,1217    Crate,1218    Pub,1219}12201221fn calculate_necessary_visibility(1222    current_module: Module,1223    target_module: Module,1224    ctx: &AssistContext<'_>,1225) -> Visibility {1226    let db = ctx.db();1227    let current_module = current_module.nearest_non_block_module(db);1228    let target_module = target_module.nearest_non_block_module(db);12291230    if target_module.krate(ctx.db()) != current_module.krate(ctx.db()) {1231        Visibility::Pub1232    } else if current_module.path_to_root(db).contains(&target_module) {1233        Visibility::None1234    } else {1235        Visibility::Crate1236    }1237}12381239// This is never intended to be used as a generic graph structure. If there's ever another need of1240// graph algorithm, consider adding a library for that (and replace the following).1241/// Minimally implemented directed graph structure represented by adjacency list.1242struct Graph {1243    edges: Vec<Vec<usize>>,1244}12451246impl Graph {1247    fn new(node_count: usize) -> Self {1248        Self { edges: vec![Vec::new(); node_count] }1249    }12501251    fn add_edge(&mut self, from: usize, to: usize) {1252        self.edges[from].push(to);1253    }12541255    fn edges_for(&self, node_idx: usize) -> &[usize] {1256        &self.edges[node_idx]1257    }12581259    fn len(&self) -> usize {1260        self.edges.len()1261    }12621263    fn compute_reachable_nodes(1264        &self,1265        starting_nodes: impl IntoIterator<Item = usize>,1266    ) -> Vec<bool> {1267        let mut visitor = Visitor::new(self);1268        for idx in starting_nodes {1269            visitor.mark_reachable(idx);1270        }1271        visitor.visited1272    }1273}12741275struct Visitor<'g> {1276    graph: &'g Graph,1277    visited: Vec<bool>,1278    // Stack is held in this struct so we can reuse its buffer.1279    stack: Vec<usize>,1280}12811282impl<'g> Visitor<'g> {1283    fn new(graph: &'g Graph) -> Self {1284        let visited = vec![false; graph.len()];1285        Self { graph, visited, stack: Vec::new() }1286    }12871288    fn mark_reachable(&mut self, start_idx: usize) {1289        // non-recursive DFS1290        stdx::always!(self.stack.is_empty());12911292        self.stack.push(start_idx);1293        while let Some(idx) = self.stack.pop() {1294            if !self.visited[idx] {1295                self.visited[idx] = true;1296                for &neighbor in self.graph.edges_for(idx) {1297                    if !self.visited[neighbor] {1298                        self.stack.push(neighbor);1299                    }1300                }1301            }1302        }1303    }1304}13051306#[cfg(test)]1307mod tests {1308    use crate::tests::{check_assist, check_assist_not_applicable};13091310    use super::*;13111312    #[test]1313    fn add_function_with_no_args() {1314        check_assist(1315            generate_function,1316            r"1317fn foo() {1318    bar$0();1319}1320",1321            r"1322fn foo() {1323    bar();1324}13251326fn bar() ${0:-> _} {1327    todo!()1328}1329",1330        )1331    }13321333    #[test]1334    fn add_function_from_method() {1335        // This ensures that the function is correctly generated1336        // in the next outer mod or file1337        check_assist(1338            generate_function,1339            r"1340impl Foo {1341    fn foo() {1342        bar$0();1343    }1344}1345",1346            r"1347impl Foo {1348    fn foo() {1349        bar();1350    }1351}13521353fn bar() ${0:-> _} {1354    todo!()1355}1356",1357        )1358    }13591360    #[test]1361    fn add_function_directly_after_current_block() {1362        // The new fn should not be created at the end of the file or module1363        check_assist(1364            generate_function,1365            r"1366fn foo1() {1367    bar$0();1368}13691370fn foo2() {}1371",1372            r"1373fn foo1() {1374    bar();1375}13761377fn bar() ${0:-> _} {1378    todo!()1379}13801381fn foo2() {}1382",1383        )1384    }13851386    #[test]1387    fn add_function_with_no_args_in_same_module() {1388        check_assist(1389            generate_function,1390            r"1391mod baz {1392    fn foo() {1393        bar$0();1394    }1395}1396",1397            r"1398mod baz {1399    fn foo() {1400        bar();1401    }14021403    fn bar() ${0:-> _} {1404        todo!()1405    }1406}1407",1408        )1409    }14101411    #[test]1412    fn add_function_with_upper_camel_case_arg() {1413        check_assist(1414            generate_function,1415            r"1416struct BazBaz;1417fn foo() {1418    bar$0(BazBaz);1419}1420",1421            r"1422struct BazBaz;1423fn foo() {1424    bar(BazBaz);1425}14261427fn bar(baz_baz: BazBaz) ${0:-> _} {1428    todo!()1429}1430",1431        );1432    }14331434    #[test]1435    fn add_function_with_upper_camel_case_arg_as_cast() {1436        check_assist(1437            generate_function,1438            r"1439struct BazBaz;1440fn foo() {1441    bar$0(&BazBaz as *const BazBaz);1442}1443",1444            r"1445struct BazBaz;1446fn foo() {1447    bar(&BazBaz as *const BazBaz);1448}14491450fn bar(baz_baz: *const BazBaz) ${0:-> _} {1451    todo!()1452}1453",1454        );1455    }14561457    #[test]1458    fn add_function_with_function_call_arg() {1459        check_assist(1460            generate_function,1461            r"1462struct Baz;1463fn baz() -> Baz { todo!() }1464fn foo() {1465    bar$0(baz());1466}1467",1468            r"1469struct Baz;1470fn baz() -> Baz { todo!() }1471fn foo() {1472    bar(baz());1473}14741475fn bar(baz: Baz) ${0:-> _} {1476    todo!()1477}1478",1479        );1480    }14811482    #[test]1483    fn add_function_with_method_call_arg() {1484        check_assist(1485            generate_function,1486            r"1487struct Baz;1488impl Baz {1489    fn foo(&self) -> Baz {1490        ba$0r(self.baz())1491    }1492    fn baz(&self) -> Baz {1493        Baz1494    }1495}1496",1497            r"1498struct Baz;1499impl Baz {1500    fn foo(&self) -> Baz {1501        bar(self.baz())1502    }1503    fn baz(&self) -> Baz {1504        Baz1505    }1506}15071508fn bar(baz: Baz) -> Baz {1509    ${0:todo!()}1510}1511",1512        )1513    }15141515    #[test]1516    fn add_function_with_string_literal_arg() {1517        check_assist(1518            generate_function,1519            r#"1520fn foo() {1521    $0bar("bar")1522}1523"#,1524            r#"1525fn foo() {1526    bar("bar")1527}15281529fn bar(arg: &'static str) {1530    ${0:todo!()}1531}1532"#,1533        )1534    }15351536    #[test]1537    fn add_function_with_char_literal_arg() {1538        check_assist(1539            generate_function,1540            r#"1541fn foo() {1542    $0bar('x')1543}1544"#,1545            r#"1546fn foo() {1547    bar('x')1548}15491550fn bar(arg: char) {1551    ${0:todo!()}1552}1553"#,1554        )1555    }15561557    #[test]1558    fn add_function_with_int_literal_arg() {1559        check_assist(1560            generate_function,1561            r"1562fn foo() {1563    $0bar(42)1564}1565",1566            r"1567fn foo() {1568    bar(42)1569}15701571fn bar(arg: i32) {1572    ${0:todo!()}1573}1574",1575        )1576    }15771578    #[test]1579    fn add_function_with_cast_int_literal_arg() {1580        check_assist(1581            generate_function,1582            r"1583fn foo() {1584    $0bar(42 as u8)1585}1586",1587            r"1588fn foo() {1589    bar(42 as u8)1590}15911592fn bar(arg: u8) {1593    ${0:todo!()}1594}1595",1596        )1597    }15981599    #[test]1600    fn name_of_cast_variable_is_used() {1601        // Ensures that the name of the cast type isn't used1602        // in the generated function signature.1603        check_assist(1604            generate_function,1605            r"1606fn foo() {1607    let x = 42;1608    bar$0(x as u8)1609}1610",1611            r"1612fn foo() {1613    let x = 42;1614    bar(x as u8)1615}16161617fn bar(x: u8) {1618    ${0:todo!()}1619}1620",1621        )1622    }16231624    #[test]1625    fn add_function_with_variable_arg() {1626        check_assist(1627            generate_function,1628            r"1629fn foo() {1630    let worble = ();1631    $0bar(worble)1632}1633",1634            r"1635fn foo() {1636    let worble = ();1637    bar(worble)1638}16391640fn bar(worble: ()) {1641    ${0:todo!()}1642}1643",1644        )1645    }16461647    #[test]1648    fn add_function_with_impl_trait_arg() {1649        check_assist(1650            generate_function,1651            r#"1652//- minicore: sized1653trait Foo {}1654fn foo() -> impl Foo {1655    todo!()1656}1657fn baz() {1658    $0bar(foo())1659}1660"#,1661            r#"1662trait Foo {}1663fn foo() -> impl Foo {1664    todo!()1665}1666fn baz() {1667    bar(foo())1668}16691670fn bar(foo: impl Foo) {1671    ${0:todo!()}1672}1673"#,1674        )1675    }16761677    #[test]1678    fn borrowed_arg() {1679        check_assist(1680            generate_function,1681            r"1682struct Baz;1683fn baz() -> Baz { todo!() }16841685fn foo() {1686    bar$0(&baz())1687}1688",1689            r"1690struct Baz;1691fn baz() -> Baz { todo!() }16921693fn foo() {1694    bar(&baz())1695}16961697fn bar(baz: &Baz) {1698    ${0:todo!()}1699}1700",1701        )1702    }17031704    #[test]1705    fn add_function_with_qualified_path_arg() {1706        check_assist(1707            generate_function,1708            r"1709mod Baz {1710    pub struct Bof;1711    pub fn baz() -> Bof { Bof }1712}1713fn foo() {1714    $0bar(Baz::baz())1715}1716",1717            r"1718mod Baz {1719    pub struct Bof;1720    pub fn baz() -> Bof { Bof }1721}1722fn foo() {1723    bar(Baz::baz())1724}17251726fn bar(baz: Baz::Bof) {1727    ${0:todo!()}1728}1729",1730        )1731    }17321733    #[test]1734    fn generate_function_with_generic_param() {1735        check_assist(1736            generate_function,1737            r"1738fn foo<T, const N: usize>(t: [T; N]) { $0bar(t) }1739",1740            r"1741fn foo<T, const N: usize>(t: [T; N]) { bar(t) }17421743fn bar<T, const N: usize>(t: [T; N]) {1744    ${0:todo!()}1745}1746",1747        )1748    }17491750    #[test]1751    fn generate_function_with_parent_generic_param() {1752        check_assist(1753            generate_function,1754            r"1755struct S<T>(T);1756impl<T> S<T> {1757    fn foo<U>(t: T, u: U) { $0bar(t, u) }1758}1759",1760            r"1761struct S<T>(T);1762impl<T> S<T> {1763    fn foo<U>(t: T, u: U) { bar(t, u) }1764}17651766fn bar<T, U>(t: T, u: U) {1767    ${0:todo!()}1768}1769",1770        )1771    }17721773    #[test]1774    fn generic_param_in_receiver_type() {1775        // FIXME: Generic parameter `T` should be part of impl, not method.1776        check_assist(1777            generate_function,1778            r"1779struct S<T>(T);1780fn foo<T, U>(s: S<T>, u: U) { s.$0foo(u) }1781",1782            r"1783struct S<T>(T);1784impl S {1785    fn foo<T, U>(&self, u: U) {1786        ${0:todo!()}1787    }1788}1789fn foo<T, U>(s: S<T>, u: U) { s.foo(u) }1790",1791        )1792    }17931794    #[test]1795    fn generic_param_in_return_type() {1796        check_assist(1797            generate_function,1798            r"1799fn foo<T, const N: usize>() -> [T; N] { $0bar() }1800",1801            r"1802fn foo<T, const N: usize>() -> [T; N] { bar() }18031804fn bar<T, const N: usize>() -> [T; N] {1805    ${0:todo!()}1806}1807",1808        )1809    }18101811    #[test]1812    fn generate_fn_with_bounds() {1813        // FIXME: where predicates should be on next lines.1814        check_assist(1815            generate_function,1816            r"1817trait A<T> {}1818struct S<T>(T);1819impl<T: A<i32>> S<T>1820where1821    T: A<i64>,1822{1823    fn foo<U>(t: T, u: U)1824    where1825        T: A<()>,1826        U: A<i32> + A<i64>,1827    {1828        $0bar(t, u)1829    }1830}1831",1832            r"1833trait A<T> {}1834struct S<T>(T);1835impl<T: A<i32>> S<T>1836where1837    T: A<i64>,1838{1839    fn foo<U>(t: T, u: U)1840    where1841        T: A<()>,1842        U: A<i32> + A<i64>,1843    {1844        bar(t, u)1845    }1846}18471848fn bar<T: A<i32>, U>(t: T, u: U) where T: A<i64>, T: A<()>, U: A<i32> + A<i64> {1849    ${0:todo!()}1850}1851",1852        )1853    }18541855    #[test]1856    fn include_transitive_param_dependency() {1857        // FIXME: where predicates should be on next lines.1858        check_assist(1859            generate_function,1860            r"1861trait A<T> { type Assoc; }1862trait B { type Item; }1863struct S<T>(T);1864impl<T, U, V: B, W> S<(T, U, V, W)>1865where1866    T: A<U, Assoc = V>,1867    S<V::Item>: A<U, Assoc = W>,1868{1869    fn foo<I>(t: T, u: U)1870    where1871        U: A<T, Assoc = I>,1872    {1873        $0bar(u)1874    }1875}1876",1877            r"1878trait A<T> { type Assoc; }1879trait B { type Item; }1880struct S<T>(T);1881impl<T, U, V: B, W> S<(T, U, V, W)>1882where1883    T: A<U, Assoc = V>,1884    S<V::Item>: A<U, Assoc = W>,1885{1886    fn foo<I>(t: T, u: U)1887    where1888        U: A<T, Assoc = I>,1889    {1890        bar(u)1891    }1892}18931894fn bar<T, U, V: B, W, I>(u: U) where T: A<U, Assoc = V>, S<V::Item>: A<U, Assoc = W>, U: A<T, Assoc = I> {1895    ${0:todo!()}1896}1897",1898        )1899    }19001901    #[test]1902    fn irrelevant_bounds_are_filtered_out() {1903        check_assist(1904            generate_function,1905            r"1906trait A<T> {}1907struct S<T>(T);1908impl<T, U, V, W> S<(T, U, V, W)>1909where1910    T: A<U>,1911    V: A<W>,1912{1913    fn foo<I>(t: T, u: U)1914    where1915        U: A<T> + A<I>,1916    {1917        $0bar(u)1918    }1919}1920",1921            r"1922trait A<T> {}1923struct S<T>(T);1924impl<T, U, V, W> S<(T, U, V, W)>1925where1926    T: A<U>,1927    V: A<W>,1928{1929    fn foo<I>(t: T, u: U)1930    where1931        U: A<T> + A<I>,1932    {1933        bar(u)1934    }1935}19361937fn bar<T, U, I>(u: U) where T: A<U>, U: A<T> + A<I> {1938    ${0:todo!()}1939}1940",1941        )1942    }19431944    #[test]1945    fn params_in_trait_arg_are_not_dependency() {1946        // Even though `bar` depends on `U` and `I`, we don't have to copy these bounds:1947        // `T: A<I>` and `T: A<U>`.1948        check_assist(1949            generate_function,1950            r"1951trait A<T> {}1952struct S<T>(T);1953impl<T, U> S<(T, U)>1954where1955    T: A<U>,1956{1957    fn foo<I>(t: T, u: U)1958    where1959        T: A<I>,1960        U: A<I>,1961    {1962        $0bar(u)1963    }1964}1965",1966            r"1967trait A<T> {}1968struct S<T>(T);1969impl<T, U> S<(T, U)>1970where1971    T: A<U>,1972{1973    fn foo<I>(t: T, u: U)1974    where1975        T: A<I>,1976        U: A<I>,1977    {1978        bar(u)1979    }1980}19811982fn bar<U, I>(u: U) where U: A<I> {1983    ${0:todo!()}1984}1985",1986        )1987    }19881989    #[test]1990    fn dont_copy_bounds_already_in_scope() {1991        check_assist(1992            generate_function,1993            r"1994trait A<T> {}1995struct S<T>(T);1996impl<T: A<i32>> S<T>1997where1998    T: A<usize>,1999{2000    fn foo<U: A<()>>(t: T, u: U)

Code quality findings 100

Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
false, // FIXME : const and unsafe are not handled yet.
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("generated function should have a body")
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("function body should have a tail expression");
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
GeneratedFunctionTarget::AfterItem(it) => it.parent().expect("item without parent"),
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 node = ctx.sema.source(def).expect("definition's source couldn't be found").value;
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
let param_idx = param_map[&pred.self_ty_param];
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
let param_idx = param_map[param];
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
let param_idx = param_map[param];
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
let param_idx = param_map[param];
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
reachable[idx - 1]
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
reachable[idx - 1]
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
fn deduplicate_arg_names(arg_names: &mut [String]) {
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
it.item_list().expect("module definition source should have an item list");
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
self.edges[from].push(to);
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
&self.edges[node_idx]
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 !self.visited[idx] {
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
self.visited[idx] = true;
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 !self.visited[neighbor] {
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
// todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
/// user can change the `todo!` function body.
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
arg_names.push(fn_arg_name(&ctx.sema, &arg));
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
arg_types.push(fn_arg_type(ctx, target_module, &arg, necessary_generic_params));
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 def = match item {
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 ctx.sema.resolve_path(&path)? {
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
.map(|(name, _)| name.clone())
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 counter = counter_per_name.entry(arg_name.clone()).or_insert(1);
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
arg_name.push('_');
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
arg_name.push_str(&counter.to_string());
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 next_ancestor.kind() {
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
self.stack.push(start_idx);
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
self.stack.push(neighbor);
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::*;
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
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
bar$0(&BazBaz as *const BazBaz);
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
bar(&BazBaz as *const BazBaz);
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 bar(baz_baz: *const BazBaz) ${0:-> _} {
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
fn baz() -> Baz { todo!() }
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
fn baz() -> Baz { todo!() }
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
fn baz() -> Baz { todo!() }
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
fn baz() -> Baz { todo!() }
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
${0:todo!()}
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
todo!()

Get this view in your editor

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