Skip to content

Commit 2f02ea0

Browse files
committed
Auto merge of #13041 - DorianListens:dscheidt/gen-fn-self-assoc-2, r=Veykril
feat: Generate static method using Self::assoc() syntax This change improves the `generate_function` assist to support generating static methods/associated functions using the `Self::assoc()` syntax. Previously, one could generate a static method, but only when specifying the type name directly (like `Foo::assoc()`). After this change, `Self` is supported as well as the type name. Fixes #13012
2 parents 62c3107 + 48ea382 commit 2f02ea0

File tree

1 file changed

+106
-36
lines changed

1 file changed

+106
-36
lines changed

crates/ide-assists/src/handlers/generate_function.rs

+106-36
Original file line numberDiff line numberDiff line change
@@ -61,56 +61,72 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
6161
}
6262

6363
let fn_name = &*name_ref.text();
64-
let target_module;
65-
let mut adt_name = None;
64+
let TargetInfo { target_module, adt_name, target, file, insert_offset } =
65+
fn_target_info(ctx, path, &call, fn_name)?;
66+
let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?;
67+
let text_range = call.syntax().text_range();
68+
let label = format!("Generate {} function", function_builder.fn_name);
69+
add_func_to_accumulator(
70+
acc,
71+
ctx,
72+
text_range,
73+
function_builder,
74+
insert_offset,
75+
file,
76+
adt_name,
77+
label,
78+
)
79+
}
80+
81+
struct TargetInfo {
82+
target_module: Option<Module>,
83+
adt_name: Option<hir::Name>,
84+
target: GeneratedFunctionTarget,
85+
file: FileId,
86+
insert_offset: TextSize,
87+
}
6688

67-
let (target, file, insert_offset) = match path.qualifier() {
89+
impl TargetInfo {
90+
fn new(
91+
target_module: Option<Module>,
92+
adt_name: Option<hir::Name>,
93+
target: GeneratedFunctionTarget,
94+
file: FileId,
95+
insert_offset: TextSize,
96+
) -> Self {
97+
Self { target_module, adt_name, target, file, insert_offset }
98+
}
99+
}
100+
101+
fn fn_target_info(
102+
ctx: &AssistContext<'_>,
103+
path: ast::Path,
104+
call: &CallExpr,
105+
fn_name: &str,
106+
) -> Option<TargetInfo> {
107+
match path.qualifier() {
68108
Some(qualifier) => match ctx.sema.resolve_path(&qualifier) {
69109
Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => {
70-
target_module = Some(module);
71-
get_fn_target(ctx, &target_module, call.clone())?
110+
get_fn_target_info(ctx, &Some(module), call.clone())
72111
}
73112
Some(hir::PathResolution::Def(hir::ModuleDef::Adt(adt))) => {
74113
if let hir::Adt::Enum(_) = adt {
75114
// Don't suggest generating function if the name starts with an uppercase letter
76-
if name_ref.text().starts_with(char::is_uppercase) {
115+
if fn_name.starts_with(char::is_uppercase) {
77116
return None;
78117
}
79118
}
80119

81-
let current_module = ctx.sema.scope(call.syntax())?.module();
82-
let module = adt.module(ctx.sema.db);
83-
target_module = if current_module == module { None } else { Some(module) };
84-
if current_module.krate() != module.krate() {
85-
return None;
86-
}
87-
let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?;
88-
let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?;
89-
adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None };
90-
(target, file, insert_offset)
120+
assoc_fn_target_info(ctx, call, adt, fn_name)
91121
}
92-
_ => {
93-
return None;
122+
Some(hir::PathResolution::SelfType(impl_)) => {
123+
let adt = impl_.self_ty(ctx.db()).as_adt()?;
124+
assoc_fn_target_info(ctx, call, adt, fn_name)
94125
}
126+
_ => None,
95127
},
96-
_ => {
97-
target_module = None;
98-
get_fn_target(ctx, &target_module, call.clone())?
99-
}
100-
};
101-
let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?;
102-
let text_range = call.syntax().text_range();
103-
let label = format!("Generate {} function", function_builder.fn_name);
104-
add_func_to_accumulator(
105-
acc,
106-
ctx,
107-
text_range,
108-
function_builder,
109-
insert_offset,
110-
file,
111-
adt_name,
112-
label,
113-
)
128+
_ => get_fn_target_info(ctx, &None, call.clone()),
129+
}
114130
}
115131

116132
fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
@@ -366,6 +382,15 @@ fn make_return_type(
366382
(ret_type, should_focus_return_type)
367383
}
368384

385+
fn get_fn_target_info(
386+
ctx: &AssistContext<'_>,
387+
target_module: &Option<Module>,
388+
call: CallExpr,
389+
) -> Option<TargetInfo> {
390+
let (target, file, insert_offset) = get_fn_target(ctx, target_module, call)?;
391+
Some(TargetInfo::new(*target_module, None, target, file, insert_offset))
392+
}
393+
369394
fn get_fn_target(
370395
ctx: &AssistContext<'_>,
371396
target_module: &Option<Module>,
@@ -399,6 +424,24 @@ fn get_method_target(
399424
Some((target.clone(), get_insert_offset(&target)))
400425
}
401426

427+
fn assoc_fn_target_info(
428+
ctx: &AssistContext<'_>,
429+
call: &CallExpr,
430+
adt: hir::Adt,
431+
fn_name: &str,
432+
) -> Option<TargetInfo> {
433+
let current_module = ctx.sema.scope(call.syntax())?.module();
434+
let module = adt.module(ctx.sema.db);
435+
let target_module = if current_module == module { None } else { Some(module) };
436+
if current_module.krate() != module.krate() {
437+
return None;
438+
}
439+
let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?;
440+
let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?;
441+
let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None };
442+
Some(TargetInfo::new(target_module, adt_name, target, file, insert_offset))
443+
}
444+
402445
fn get_insert_offset(target: &GeneratedFunctionTarget) -> TextSize {
403446
match &target {
404447
GeneratedFunctionTarget::BehindItem(it) => it.text_range().end(),
@@ -1633,6 +1676,33 @@ fn bar() ${0:-> _} {
16331676
)
16341677
}
16351678

1679+
#[test]
1680+
fn create_static_method_within_an_impl_with_self_syntax() {
1681+
check_assist(
1682+
generate_function,
1683+
r"
1684+
struct S;
1685+
impl S {
1686+
fn foo(&self) {
1687+
Self::bar$0();
1688+
}
1689+
}
1690+
",
1691+
r"
1692+
struct S;
1693+
impl S {
1694+
fn foo(&self) {
1695+
Self::bar();
1696+
}
1697+
1698+
fn bar() ${0:-> _} {
1699+
todo!()
1700+
}
1701+
}
1702+
",
1703+
)
1704+
}
1705+
16361706
#[test]
16371707
fn no_panic_on_invalid_global_path() {
16381708
check_assist(

0 commit comments

Comments
 (0)