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!()