mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 21:43:37 +00:00
Auto merge of #17138 - Kohei316:generate-function-assist-for-new, r=Veykril
feature: Make generate function assist generate a function as a constructor if the generated function has the name "new" and is an asscociated function. close #17050 This PR makes `generate function assist` generate a function as a constructor if the generated function has the name "new" and is an asscociated function. If the asscociate type is a record struct, it generates the constructor like this. ```rust impl Foo { fn new() -> Self { Self { field_1: todo!(), field_2: todo!() } } } ``` If the asscociate type is a tuple struct, it generates the constructor like this. ```rust impl Foo { fn new() -> Self { Self(todo!(), todo!()) } } ``` If the asscociate type is a unit struct, it generates the constructor like this. ```rust impl Foo { fn new() -> Self { Self } } ``` If the asscociate type is another adt, it generates the constructor like this. ```rust impl Foo { fn new() -> Self { todo!() } } ```
This commit is contained in:
commit
84ef3cfa91
2 changed files with 270 additions and 26 deletions
|
@ -1489,6 +1489,14 @@ impl Adt {
|
||||||
.map(|arena| arena.1.clone())
|
.map(|arena| arena.1.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_struct(&self) -> Option<Struct> {
|
||||||
|
if let Self::Struct(v) = self {
|
||||||
|
Some(*v)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_enum(&self) -> Option<Enum> {
|
pub fn as_enum(&self) -> Option<Enum> {
|
||||||
if let Self::Enum(v) = self {
|
if let Self::Enum(v) = self {
|
||||||
Some(*v)
|
Some(*v)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use hir::{
|
use hir::{
|
||||||
Adt, AsAssocItem, HasSource, HirDisplay, HirFileIdExt, Module, PathResolution, Semantics, Type,
|
Adt, AsAssocItem, HasSource, HirDisplay, HirFileIdExt, Module, PathResolution, Semantics,
|
||||||
TypeInfo,
|
StructKind, Type, TypeInfo,
|
||||||
};
|
};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::FileId,
|
base_db::FileId,
|
||||||
|
@ -15,8 +15,8 @@ use itertools::Itertools;
|
||||||
use stdx::to_lower_snake_case;
|
use stdx::to_lower_snake_case;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{
|
ast::{
|
||||||
self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, CallExpr, HasArgList,
|
self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, BlockExpr, CallExpr,
|
||||||
HasGenericParams, HasModuleItem, HasTypeBounds,
|
HasArgList, HasGenericParams, HasModuleItem, HasTypeBounds,
|
||||||
},
|
},
|
||||||
ted, SyntaxKind, SyntaxNode, TextRange, T,
|
ted, SyntaxKind, SyntaxNode, TextRange, T,
|
||||||
};
|
};
|
||||||
|
@ -66,7 +66,7 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_name = &*name_ref.text();
|
let fn_name = &*name_ref.text();
|
||||||
let TargetInfo { target_module, adt_name, target, file } =
|
let TargetInfo { target_module, adt_info, target, file } =
|
||||||
fn_target_info(ctx, path, &call, fn_name)?;
|
fn_target_info(ctx, path, &call, fn_name)?;
|
||||||
|
|
||||||
if let Some(m) = target_module {
|
if let Some(m) = target_module {
|
||||||
|
@ -75,15 +75,16 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?;
|
let function_builder =
|
||||||
|
FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target, &adt_info)?;
|
||||||
let text_range = call.syntax().text_range();
|
let text_range = call.syntax().text_range();
|
||||||
let label = format!("Generate {} function", function_builder.fn_name);
|
let label = format!("Generate {} function", function_builder.fn_name);
|
||||||
add_func_to_accumulator(acc, ctx, text_range, function_builder, file, adt_name, label)
|
add_func_to_accumulator(acc, ctx, text_range, function_builder, file, adt_info, label)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TargetInfo {
|
struct TargetInfo {
|
||||||
target_module: Option<Module>,
|
target_module: Option<Module>,
|
||||||
adt_name: Option<hir::Name>,
|
adt_info: Option<AdtInfo>,
|
||||||
target: GeneratedFunctionTarget,
|
target: GeneratedFunctionTarget,
|
||||||
file: FileId,
|
file: FileId,
|
||||||
}
|
}
|
||||||
|
@ -91,11 +92,11 @@ struct TargetInfo {
|
||||||
impl TargetInfo {
|
impl TargetInfo {
|
||||||
fn new(
|
fn new(
|
||||||
target_module: Option<Module>,
|
target_module: Option<Module>,
|
||||||
adt_name: Option<hir::Name>,
|
adt_info: Option<AdtInfo>,
|
||||||
target: GeneratedFunctionTarget,
|
target: GeneratedFunctionTarget,
|
||||||
file: FileId,
|
file: FileId,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self { target_module, adt_name, target, file }
|
Self { target_module, adt_info, target, file }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,9 +158,9 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
target,
|
target,
|
||||||
)?;
|
)?;
|
||||||
let text_range = call.syntax().text_range();
|
let text_range = call.syntax().text_range();
|
||||||
let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None };
|
let adt_info = AdtInfo::new(adt, impl_.is_some());
|
||||||
let label = format!("Generate {} method", function_builder.fn_name);
|
let label = format!("Generate {} method", function_builder.fn_name);
|
||||||
add_func_to_accumulator(acc, ctx, text_range, function_builder, file, adt_name, label)
|
add_func_to_accumulator(acc, ctx, text_range, function_builder, file, Some(adt_info), label)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_func_to_accumulator(
|
fn add_func_to_accumulator(
|
||||||
|
@ -168,7 +169,7 @@ fn add_func_to_accumulator(
|
||||||
text_range: TextRange,
|
text_range: TextRange,
|
||||||
function_builder: FunctionBuilder,
|
function_builder: FunctionBuilder,
|
||||||
file: FileId,
|
file: FileId,
|
||||||
adt_name: Option<hir::Name>,
|
adt_info: Option<AdtInfo>,
|
||||||
label: String,
|
label: String,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
acc.add(AssistId("generate_function", AssistKind::Generate), label, text_range, |edit| {
|
acc.add(AssistId("generate_function", AssistKind::Generate), label, text_range, |edit| {
|
||||||
|
@ -177,8 +178,14 @@ fn add_func_to_accumulator(
|
||||||
let target = function_builder.target.clone();
|
let target = function_builder.target.clone();
|
||||||
let func = function_builder.render(ctx.config.snippet_cap, edit);
|
let func = function_builder.render(ctx.config.snippet_cap, edit);
|
||||||
|
|
||||||
if let Some(name) = adt_name {
|
if let Some(adt) =
|
||||||
let name = make::ty_path(make::ext::ident_path(&format!("{}", name.display(ctx.db()))));
|
adt_info
|
||||||
|
.and_then(|adt_info| if adt_info.impl_exists { None } else { Some(adt_info.adt) })
|
||||||
|
{
|
||||||
|
let name = make::ty_path(make::ext::ident_path(&format!(
|
||||||
|
"{}",
|
||||||
|
adt.name(ctx.db()).display(ctx.db())
|
||||||
|
)));
|
||||||
|
|
||||||
// FIXME: adt may have generic params.
|
// FIXME: adt may have generic params.
|
||||||
let impl_ = make::impl_(None, None, name, None, None).clone_for_update();
|
let impl_ = make::impl_(None, None, name, None, None).clone_for_update();
|
||||||
|
@ -210,6 +217,7 @@ struct FunctionBuilder {
|
||||||
generic_param_list: Option<ast::GenericParamList>,
|
generic_param_list: Option<ast::GenericParamList>,
|
||||||
where_clause: Option<ast::WhereClause>,
|
where_clause: Option<ast::WhereClause>,
|
||||||
params: ast::ParamList,
|
params: ast::ParamList,
|
||||||
|
fn_body: BlockExpr,
|
||||||
ret_type: Option<ast::RetType>,
|
ret_type: Option<ast::RetType>,
|
||||||
should_focus_return_type: bool,
|
should_focus_return_type: bool,
|
||||||
visibility: Visibility,
|
visibility: Visibility,
|
||||||
|
@ -225,6 +233,7 @@ impl FunctionBuilder {
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
target_module: Option<Module>,
|
target_module: Option<Module>,
|
||||||
target: GeneratedFunctionTarget,
|
target: GeneratedFunctionTarget,
|
||||||
|
adt_info: &Option<AdtInfo>,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
let target_module =
|
let target_module =
|
||||||
target_module.or_else(|| ctx.sema.scope(target.syntax()).map(|it| it.module()))?;
|
target_module.or_else(|| ctx.sema.scope(target.syntax()).map(|it| it.module()))?;
|
||||||
|
@ -243,9 +252,27 @@ impl FunctionBuilder {
|
||||||
let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast);
|
let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast);
|
||||||
let is_async = await_expr.is_some();
|
let is_async = await_expr.is_some();
|
||||||
|
|
||||||
|
let ret_type;
|
||||||
|
let should_focus_return_type;
|
||||||
|
let fn_body;
|
||||||
|
|
||||||
|
// If generated function has the name "new" and is an associated function, we generate fn body
|
||||||
|
// as a constructor and assume a "Self" return type.
|
||||||
|
if let Some(body) = make_fn_body_as_new_function(ctx, &fn_name.text(), adt_info) {
|
||||||
|
ret_type = Some(make::ret_type(make::ty_path(make::ext::ident_path("Self"))));
|
||||||
|
should_focus_return_type = false;
|
||||||
|
fn_body = body;
|
||||||
|
} else {
|
||||||
let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into());
|
let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into());
|
||||||
let (ret_type, should_focus_return_type) =
|
(ret_type, should_focus_return_type) = make_return_type(
|
||||||
make_return_type(ctx, &expr_for_ret_ty, target_module, &mut necessary_generic_params);
|
ctx,
|
||||||
|
&expr_for_ret_ty,
|
||||||
|
target_module,
|
||||||
|
&mut necessary_generic_params,
|
||||||
|
);
|
||||||
|
let placeholder_expr = make::ext::expr_todo();
|
||||||
|
fn_body = make::block_expr(vec![], Some(placeholder_expr));
|
||||||
|
};
|
||||||
|
|
||||||
let (generic_param_list, where_clause) =
|
let (generic_param_list, where_clause) =
|
||||||
fn_generic_params(ctx, necessary_generic_params, &target)?;
|
fn_generic_params(ctx, necessary_generic_params, &target)?;
|
||||||
|
@ -256,6 +283,7 @@ impl FunctionBuilder {
|
||||||
generic_param_list,
|
generic_param_list,
|
||||||
where_clause,
|
where_clause,
|
||||||
params,
|
params,
|
||||||
|
fn_body,
|
||||||
ret_type,
|
ret_type,
|
||||||
should_focus_return_type,
|
should_focus_return_type,
|
||||||
visibility,
|
visibility,
|
||||||
|
@ -294,12 +322,16 @@ impl FunctionBuilder {
|
||||||
let (generic_param_list, where_clause) =
|
let (generic_param_list, where_clause) =
|
||||||
fn_generic_params(ctx, necessary_generic_params, &target)?;
|
fn_generic_params(ctx, necessary_generic_params, &target)?;
|
||||||
|
|
||||||
|
let placeholder_expr = make::ext::expr_todo();
|
||||||
|
let fn_body = make::block_expr(vec![], Some(placeholder_expr));
|
||||||
|
|
||||||
Some(Self {
|
Some(Self {
|
||||||
target,
|
target,
|
||||||
fn_name,
|
fn_name,
|
||||||
generic_param_list,
|
generic_param_list,
|
||||||
where_clause,
|
where_clause,
|
||||||
params,
|
params,
|
||||||
|
fn_body,
|
||||||
ret_type,
|
ret_type,
|
||||||
should_focus_return_type,
|
should_focus_return_type,
|
||||||
visibility,
|
visibility,
|
||||||
|
@ -308,8 +340,6 @@ impl FunctionBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(self, cap: Option<SnippetCap>, edit: &mut SourceChangeBuilder) -> ast::Fn {
|
fn render(self, cap: Option<SnippetCap>, edit: &mut SourceChangeBuilder) -> ast::Fn {
|
||||||
let placeholder_expr = make::ext::expr_todo();
|
|
||||||
let fn_body = make::block_expr(vec![], Some(placeholder_expr));
|
|
||||||
let visibility = match self.visibility {
|
let visibility = match self.visibility {
|
||||||
Visibility::None => None,
|
Visibility::None => None,
|
||||||
Visibility::Crate => Some(make::visibility_pub_crate()),
|
Visibility::Crate => Some(make::visibility_pub_crate()),
|
||||||
|
@ -321,7 +351,7 @@ impl FunctionBuilder {
|
||||||
self.generic_param_list,
|
self.generic_param_list,
|
||||||
self.where_clause,
|
self.where_clause,
|
||||||
self.params,
|
self.params,
|
||||||
fn_body,
|
self.fn_body,
|
||||||
self.ret_type,
|
self.ret_type,
|
||||||
self.is_async,
|
self.is_async,
|
||||||
false, // FIXME : const and unsafe are not handled yet.
|
false, // FIXME : const and unsafe are not handled yet.
|
||||||
|
@ -391,6 +421,53 @@ fn make_return_type(
|
||||||
(ret_type, should_focus_return_type)
|
(ret_type, should_focus_return_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_fn_body_as_new_function(
|
||||||
|
ctx: &AssistContext<'_>,
|
||||||
|
fn_name: &str,
|
||||||
|
adt_info: &Option<AdtInfo>,
|
||||||
|
) -> Option<ast::BlockExpr> {
|
||||||
|
if fn_name != "new" {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let adt_info = adt_info.as_ref()?;
|
||||||
|
|
||||||
|
let path_self = make::ext::ident_path("Self");
|
||||||
|
let placeholder_expr = make::ext::expr_todo();
|
||||||
|
let tail_expr = if let Some(strukt) = adt_info.adt.as_struct() {
|
||||||
|
match strukt.kind(ctx.db()) {
|
||||||
|
StructKind::Record => {
|
||||||
|
let fields = strukt
|
||||||
|
.fields(ctx.db())
|
||||||
|
.iter()
|
||||||
|
.map(|field| {
|
||||||
|
make::record_expr_field(
|
||||||
|
make::name_ref(&format!("{}", field.name(ctx.db()).display(ctx.db()))),
|
||||||
|
Some(placeholder_expr.clone()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
make::record_expr(path_self, make::record_expr_field_list(fields)).into()
|
||||||
|
}
|
||||||
|
StructKind::Tuple => {
|
||||||
|
let args = strukt
|
||||||
|
.fields(ctx.db())
|
||||||
|
.iter()
|
||||||
|
.map(|_| placeholder_expr.clone())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
make::expr_call(make::expr_path(path_self), make::arg_list(args))
|
||||||
|
}
|
||||||
|
StructKind::Unit => make::expr_path(path_self),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
placeholder_expr
|
||||||
|
};
|
||||||
|
|
||||||
|
let fn_body = make::block_expr(vec![], Some(tail_expr));
|
||||||
|
Some(fn_body)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_fn_target_info(
|
fn get_fn_target_info(
|
||||||
ctx: &AssistContext<'_>,
|
ctx: &AssistContext<'_>,
|
||||||
target_module: Option<Module>,
|
target_module: Option<Module>,
|
||||||
|
@ -443,8 +520,8 @@ fn assoc_fn_target_info(
|
||||||
}
|
}
|
||||||
let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?;
|
let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?;
|
||||||
let target = get_method_target(ctx, &impl_, &adt)?;
|
let target = get_method_target(ctx, &impl_, &adt)?;
|
||||||
let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None };
|
let adt_info = AdtInfo::new(adt, impl_.is_some());
|
||||||
Some(TargetInfo::new(target_module, adt_name, target, file))
|
Some(TargetInfo::new(target_module, Some(adt_info), target, file))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -560,6 +637,17 @@ impl GeneratedFunctionTarget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct AdtInfo {
|
||||||
|
adt: hir::Adt,
|
||||||
|
impl_exists: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AdtInfo {
|
||||||
|
fn new(adt: Adt, impl_exists: bool) -> Self {
|
||||||
|
Self { adt, impl_exists }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Computes parameter list for the generated function.
|
/// Computes parameter list for the generated function.
|
||||||
fn fn_args(
|
fn fn_args(
|
||||||
ctx: &AssistContext<'_>,
|
ctx: &AssistContext<'_>,
|
||||||
|
@ -2758,18 +2846,18 @@ fn main() {
|
||||||
r"
|
r"
|
||||||
enum Foo {}
|
enum Foo {}
|
||||||
fn main() {
|
fn main() {
|
||||||
Foo::new$0();
|
Foo::bar$0();
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
r"
|
r"
|
||||||
enum Foo {}
|
enum Foo {}
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn new() ${0:-> _} {
|
fn bar() ${0:-> _} {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn main() {
|
fn main() {
|
||||||
Foo::new();
|
Foo::bar();
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
|
@ -2849,4 +2937,152 @@ fn main() {
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_function_assume_self_type() {
|
||||||
|
check_assist(
|
||||||
|
generate_function,
|
||||||
|
r"
|
||||||
|
pub struct Foo {
|
||||||
|
field_1: usize,
|
||||||
|
field_2: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = Foo::new$0();
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
pub struct Foo {
|
||||||
|
field_1: usize,
|
||||||
|
field_2: String,
|
||||||
|
}
|
||||||
|
impl Foo {
|
||||||
|
fn new() -> Self {
|
||||||
|
${0:Self { field_1: todo!(), field_2: todo!() }}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = Foo::new();
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_function_assume_self_type_for_tuple_struct() {
|
||||||
|
check_assist(
|
||||||
|
generate_function,
|
||||||
|
r"
|
||||||
|
pub struct Foo (usize, String);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = Foo::new$0();
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
pub struct Foo (usize, String);
|
||||||
|
impl Foo {
|
||||||
|
fn new() -> Self {
|
||||||
|
${0:Self(todo!(), todo!())}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = Foo::new();
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_function_assume_self_type_for_unit_struct() {
|
||||||
|
check_assist(
|
||||||
|
generate_function,
|
||||||
|
r"
|
||||||
|
pub struct Foo;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = Foo::new$0();
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
pub struct Foo;
|
||||||
|
impl Foo {
|
||||||
|
fn new() -> Self {
|
||||||
|
${0:Self}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = Foo::new();
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_function_assume_self_type_for_enum() {
|
||||||
|
check_assist(
|
||||||
|
generate_function,
|
||||||
|
r"
|
||||||
|
pub enum Foo {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = Foo::new$0();
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
pub enum Foo {}
|
||||||
|
impl Foo {
|
||||||
|
fn new() -> Self {
|
||||||
|
${0:todo!()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = Foo::new();
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_function_assume_self_type_with_args() {
|
||||||
|
check_assist(
|
||||||
|
generate_function,
|
||||||
|
r#"
|
||||||
|
pub struct Foo {
|
||||||
|
field_1: usize,
|
||||||
|
field_2: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Baz;
|
||||||
|
fn baz() -> Baz { Baz }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = Foo::new$0(baz(), baz(), "foo", "bar");
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
pub struct Foo {
|
||||||
|
field_1: usize,
|
||||||
|
field_2: String,
|
||||||
|
}
|
||||||
|
impl Foo {
|
||||||
|
fn new(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) -> Self {
|
||||||
|
${0:Self { field_1: todo!(), field_2: todo!() }}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Baz;
|
||||||
|
fn baz() -> Baz { Baz }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = Foo::new(baz(), baz(), "foo", "bar");
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue