Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide_assists/src/handlers/generate_function.rs')
-rw-r--r--crates/ide_assists/src/handlers/generate_function.rs163
1 files changed, 146 insertions, 17 deletions
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs
index 6e5d78b1ea..ad98aa16d8 100644
--- a/crates/ide_assists/src/handlers/generate_function.rs
+++ b/crates/ide_assists/src/handlers/generate_function.rs
@@ -70,26 +70,56 @@ impl FuncExpr {
fn gen_fn(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
-
let path = path_expr.path()?;
+ let fn_name = fn_name(&path)?;
if ctx.sema.resolve_path(&path).is_some() {
// The function call already resolves, no need to add a function
return None;
}
- let target_module = match path.qualifier() {
+ let target_module;
+ let mut adt_name = None;
+
+ let (target, file, insert_offset) = match path.qualifier() {
Some(qualifier) => match ctx.sema.resolve_path(&qualifier) {
- Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => Some(module),
- _ => return None,
+ Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => {
+ target_module = Some(module);
+ get_fn_target(ctx, &target_module, call.clone())?
+ }
+ Some(hir::PathResolution::Def(hir::ModuleDef::Adt(adt))) => {
+ let current_module = current_module(call.syntax(), ctx)?;
+ let module = adt.module(ctx.sema.db);
+ target_module = if current_module == module { None } else { Some(module) };
+ if current_module.krate() != module.krate() {
+ return None;
+ }
+ let (impl_, file) = get_adt_source(ctx, &adt, fn_name.text().as_str())?;
+ let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?;
+ adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None };
+ (target, file, insert_offset)
+ }
+ _ => {
+ return None;
+ }
},
- None => None,
+ _ => {
+ target_module = None;
+ get_fn_target(ctx, &target_module, call.clone())?
+ }
};
-
- let (target, file, insert_offset) = get_fn_target(ctx, &target_module, call.clone())?;
let function_builder = FunctionBuilder::from_call(ctx, &call, &path, target_module, target)?;
- let target = call.syntax().text_range();
+ let text_range = call.syntax().text_range();
let label = format!("Generate {} function", function_builder.fn_name.clone());
- add_func_to_accumulator(acc, ctx, target, function_builder, insert_offset, file, None, label)
+ add_func_to_accumulator(
+ acc,
+ ctx,
+ text_range,
+ function_builder,
+ insert_offset,
+ file,
+ adt_name,
+ label,
+ )
}
fn gen_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
@@ -103,13 +133,7 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
if current_module.krate() != target_module.krate() {
return None;
}
-
- let range = adt.source(ctx.sema.db)?.syntax().original_file_range(ctx.sema.db);
- let file = ctx.sema.parse(range.file_id);
- let adt_source =
- ctx.sema.find_node_at_offset_with_macros(file.syntax(), range.range.start())?;
- let impl_ = find_struct_impl(ctx, &adt_source, fn_name.text().as_str())?;
-
+ let (impl_, file) = get_adt_source(ctx, &adt, fn_name.text().as_str())?;
let (target, insert_offset) = get_method_target(ctx, &target_module, &impl_)?;
let function_builder =
FunctionBuilder::from_method_call(ctx, &call, &fn_name, target_module, target)?;
@@ -122,7 +146,7 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
text_range,
function_builder,
insert_offset,
- range.file_id,
+ file,
adt_name,
label,
)
@@ -156,6 +180,18 @@ fn current_module(current_node: &SyntaxNode, ctx: &AssistContext) -> Option<Modu
ctx.sema.scope(current_node).module()
}
+fn get_adt_source(
+ ctx: &AssistContext,
+ adt: &hir::Adt,
+ fn_name: &str,
+) -> Option<(Option<ast::Impl>, FileId)> {
+ let range = adt.source(ctx.sema.db)?.syntax().original_file_range(ctx.sema.db);
+ let file = ctx.sema.parse(range.file_id);
+ let adt_source =
+ ctx.sema.find_node_at_offset_with_macros(file.syntax(), range.range.start())?;
+ find_struct_impl(ctx, &adt_source, fn_name).map(|impl_| (impl_, range.file_id))
+}
+
struct FunctionTemplate {
leading_ws: String,
fn_def: ast::Fn,
@@ -1517,4 +1553,97 @@ fn bar(&self) ${0:-> ()} {
",
)
}
+
+ #[test]
+ fn create_static_method() {
+ check_assist(
+ generate_function,
+ r"
+struct S;
+fn foo() {S::bar$0();}
+",
+ r"
+struct S;
+fn foo() {S::bar();}
+impl S {
+
+
+fn bar() ${0:-> ()} {
+ todo!()
+}
+}
+",
+ )
+ }
+
+ #[test]
+ fn create_static_method_within_an_impl() {
+ check_assist(
+ generate_function,
+ r"
+struct S;
+fn foo() {S::bar$0();}
+impl S {}
+
+",
+ r"
+struct S;
+fn foo() {S::bar();}
+impl S {
+ fn bar() ${0:-> ()} {
+ todo!()
+ }
+}
+
+",
+ )
+ }
+
+ #[test]
+ fn create_static_method_from_different_module() {
+ check_assist(
+ generate_function,
+ r"
+mod s {
+ pub struct S;
+}
+fn foo() {s::S::bar$0();}
+",
+ r"
+mod s {
+ pub struct S;
+impl S {
+
+
+ pub(crate) fn bar() ${0:-> ()} {
+ todo!()
+ }
+}
+}
+fn foo() {s::S::bar();}
+",
+ )
+ }
+
+ #[test]
+ fn create_static_method_with_cursor_anywhere_on_call_expresion() {
+ check_assist(
+ generate_function,
+ r"
+struct S;
+fn foo() {$0S::bar();}
+",
+ r"
+struct S;
+fn foo() {S::bar();}
+impl S {
+
+
+fn bar() ${0:-> ()} {
+ todo!()
+}
+}
+",
+ )
+ }
}