]> git.lizzy.rs Git - rust.git/blobdiff - crates/ide_assists/src/handlers/generate_function.rs
Merge #11481
[rust.git] / crates / ide_assists / src / handlers / generate_function.rs
index 8e446549ba2fa30b711a09373caac8391c8eea1c..ac33d56858c03e89b1c354d7b870525d4fd6af0f 100644 (file)
@@ -1,18 +1,25 @@
-use hir::{HasSource, HirDisplay, Module, TypeInfo};
-use ide_db::{base_db::FileId, helpers::SnippetCap};
 use rustc_hash::{FxHashMap, FxHashSet};
+
+use hir::{HasSource, HirDisplay, Module, Semantics, TypeInfo};
+use ide_db::helpers::FamousDefs;
+use ide_db::{
+    base_db::FileId,
+    defs::{Definition, NameRefClass},
+    helpers::SnippetCap,
+    RootDatabase,
+};
 use stdx::to_lower_snake_case;
 use syntax::{
     ast::{
         self,
         edit::{AstNodeEdit, IndentLevel},
-        make, ArgListOwner, AstNode, ModuleItemOwner,
+        make, AstNode, CallExpr, HasArgList, HasModuleItem,
     },
     SyntaxKind, SyntaxNode, TextRange, TextSize,
 };
 
 use crate::{
-    utils::useless_type_special_case,
+    utils::convert_reference_type,
     utils::{find_struct_impl, render_snippet, Cursor},
     AssistContext, AssistId, AssistKind, Assists,
 };
@@ -37,7 +44,7 @@
 //     bar("", baz());
 // }
 //
-// fn bar(arg: &str, baz: Baz) ${0:-> ()} {
+// fn bar(arg: &str, baz: Baz) ${0:-> _} {
 //     todo!()
 // }
 //
@@ -46,53 +53,68 @@ pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext) -> Optio
     gen_fn(acc, ctx).or_else(|| gen_method(acc, ctx))
 }
 
-enum FuncExpr {
-    Func(ast::CallExpr),
-    Method(ast::MethodCallExpr),
-}
-
-impl FuncExpr {
-    fn arg_list(&self) -> Option<ast::ArgList> {
-        match self {
-            FuncExpr::Func(fn_call) => fn_call.arg_list(),
-            FuncExpr::Method(m_call) => m_call.arg_list(),
-        }
-    }
-
-    fn syntax(&self) -> &SyntaxNode {
-        match self {
-            FuncExpr::Func(fn_call) => fn_call.syntax(),
-            FuncExpr::Method(m_call) => m_call.syntax(),
-        }
-    }
-}
-
 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 name_ref = path.segment()?.name_ref()?;
     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 fn_name = &*name_ref.text();
+    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)?;
+                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 (function_builder, inserting_offset, file) =
-        FunctionBuilder::from_call(ctx, &call, &path, target_module)?;
-    let target = call.syntax().text_range();
-    add_func_to_accumulator(acc, ctx, target, function_builder, inserting_offset, file, None)
+    let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?;
+    let text_range = call.syntax().text_range();
+    let label = format!("Generate {} function", function_builder.fn_name);
+    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<()> {
     let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
+    if ctx.sema.resolve_method_call(&call).is_some() {
+        return None;
+    }
+
     let fn_name = call.name_ref()?;
     let adt = ctx.sema.type_of_expr(&call.receiver()?)?.original().strip_references().as_adt()?;
 
@@ -102,59 +124,65 @@ 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 (function_builder, inserting_offset, file) = FunctionBuilder::from_method_call(
-        ctx,
-        &call,
-        &fn_name,
-        &impl_,
-        range.file_id,
-        target_module,
-        current_module,
-    )?;
-    let target = call.syntax().text_range();
+    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)?;
+    let text_range = call.syntax().text_range();
     let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None };
-    add_func_to_accumulator(acc, ctx, target, function_builder, inserting_offset, file, adt_name)
+    let label = format!("Generate {} method", function_builder.fn_name);
+    add_func_to_accumulator(
+        acc,
+        ctx,
+        text_range,
+        function_builder,
+        insert_offset,
+        file,
+        adt_name,
+        label,
+    )
 }
 
 fn add_func_to_accumulator(
     acc: &mut Assists,
     ctx: &AssistContext,
-    target: TextRange,
+    text_range: TextRange,
     function_builder: FunctionBuilder,
     insert_offset: TextSize,
     file: FileId,
     adt_name: Option<hir::Name>,
+    label: String,
 ) -> Option<()> {
-    acc.add(
-        AssistId("generate_function", AssistKind::Generate),
-        format!("Generate `{}` method", function_builder.fn_name),
-        target,
-        |builder| {
-            let function_template = function_builder.render();
-            let mut func = function_template.to_string(ctx.config.snippet_cap);
-            if let Some(name) = adt_name {
-                func = format!("\nimpl {} {{\n{}\n}}", name, func);
-            }
-            builder.edit_file(file);
-            match ctx.config.snippet_cap {
-                Some(cap) => builder.insert_snippet(cap, insert_offset, func),
-                None => builder.insert(insert_offset, func),
-            }
-        },
-    )
+    acc.add(AssistId("generate_function", AssistKind::Generate), label, text_range, |builder| {
+        let function_template = function_builder.render();
+        let mut func = function_template.to_string(ctx.config.snippet_cap);
+        if let Some(name) = adt_name {
+            func = format!("\nimpl {} {{\n{}\n}}", name, func);
+        }
+        builder.edit_file(file);
+        match ctx.config.snippet_cap {
+            Some(cap) => builder.insert_snippet(cap, insert_offset, func),
+            None => builder.insert(insert_offset, func),
+        }
+    })
 }
 
 fn current_module(current_node: &SyntaxNode, ctx: &AssistContext) -> Option<Module> {
     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,
@@ -170,10 +198,9 @@ fn to_string(&self, cap: Option<SnippetCap>) -> String {
             Some(cap) => {
                 let cursor = if self.should_focus_return_type {
                     // Focus the return type if there is one
-                    if let Some(ref ret_type) = self.ret_type {
-                        ret_type.syntax()
-                    } else {
-                        self.tail_expr.syntax()
+                    match self.ret_type {
+                        Some(ref ret_type) => ret_type.syntax(),
+                        None => self.tail_expr.syntax(),
                     }
                 } else {
                     self.tail_expr.syntax()
@@ -194,7 +221,6 @@ struct FunctionBuilder {
     params: ast::ParamList,
     ret_type: Option<ast::RetType>,
     should_focus_return_type: bool,
-    file: FileId,
     needs_pub: bool,
     is_async: bool,
 }
@@ -205,23 +231,15 @@ impl FunctionBuilder {
     fn from_call(
         ctx: &AssistContext,
         call: &ast::CallExpr,
-        path: &ast::Path,
+        fn_name: &str,
         target_module: Option<hir::Module>,
-    ) -> Option<(Self, TextSize, FileId)> {
-        let mut file = ctx.frange.file_id;
-        let target = match &target_module {
-            Some(target_module) => {
-                let module_source = target_module.definition_source(ctx.db());
-                let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?;
-                file = in_file;
-                target
-            }
-            None => next_space_for_fn_after_call_site(FuncExpr::Func(call.clone()))?,
-        };
+        target: GeneratedFunctionTarget,
+    ) -> Option<Self> {
         let needs_pub = target_module.is_some();
         let target_module = target_module.or_else(|| current_module(target.syntax(), ctx))?;
-        let fn_name = fn_name(path)?;
-        let (type_params, params) = fn_args(ctx, target_module, FuncExpr::Func(call.clone()))?;
+        let fn_name = make::name(fn_name);
+        let (type_params, params) =
+            fn_args(ctx, target_module, ast::CallableExpr::Call(call.clone()))?;
 
         let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast);
         let is_async = await_expr.is_some();
@@ -229,53 +247,30 @@ fn from_call(
         let (ret_type, should_focus_return_type) =
             make_return_type(ctx, &ast::Expr::CallExpr(call.clone()), target_module);
 
-        let insert_offset = match &target {
-            GeneratedFunctionTarget::BehindItem(it) => it.text_range().end(),
-            GeneratedFunctionTarget::InEmptyItemList(it) => {
-                it.text_range().start() + TextSize::of('{')
-            }
-        };
-
-        Some((
-            Self {
-                target,
-                fn_name,
-                type_params,
-                params,
-                ret_type,
-                should_focus_return_type,
-                file,
-                needs_pub,
-                is_async,
-            },
-            insert_offset,
-            file,
-        ))
+        Some(Self {
+            target,
+            fn_name,
+            type_params,
+            params,
+            ret_type,
+            should_focus_return_type,
+            needs_pub,
+            is_async,
+        })
     }
 
     fn from_method_call(
         ctx: &AssistContext,
         call: &ast::MethodCallExpr,
         name: &ast::NameRef,
-        impl_: &Option<ast::Impl>,
-        file: FileId,
         target_module: Module,
-        current_module: Module,
-    ) -> Option<(Self, TextSize, FileId)> {
-        let target = match impl_ {
-            Some(impl_) => next_space_for_fn_in_impl(&impl_)?,
-            None => {
-                next_space_for_fn_in_module(
-                    ctx.sema.db,
-                    &target_module.definition_source(ctx.sema.db),
-                )?
-                .1
-            }
-        };
-        let needs_pub = !module_is_descendant(&current_module, &target_module, ctx);
-
+        target: GeneratedFunctionTarget,
+    ) -> Option<Self> {
+        let needs_pub =
+            !module_is_descendant(&current_module(call.syntax(), ctx)?, &target_module, ctx);
         let fn_name = make::name(&name.text());
-        let (type_params, params) = fn_args(ctx, target_module, FuncExpr::Method(call.clone()))?;
+        let (type_params, params) =
+            fn_args(ctx, target_module, ast::CallableExpr::MethodCall(call.clone()))?;
 
         let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast);
         let is_async = await_expr.is_some();
@@ -283,28 +278,16 @@ fn from_method_call(
         let (ret_type, should_focus_return_type) =
             make_return_type(ctx, &ast::Expr::MethodCallExpr(call.clone()), target_module);
 
-        let insert_offset = match &target {
-            GeneratedFunctionTarget::BehindItem(it) => it.text_range().end(),
-            GeneratedFunctionTarget::InEmptyItemList(it) => {
-                it.text_range().start() + TextSize::of('{')
-            }
-        };
-
-        Some((
-            Self {
-                target,
-                fn_name,
-                type_params,
-                params,
-                ret_type,
-                should_focus_return_type,
-                file,
-                needs_pub,
-                is_async,
-            },
-            insert_offset,
-            file,
-        ))
+        Some(Self {
+            target,
+            fn_name,
+            type_params,
+            params,
+            ret_type,
+            should_focus_return_type,
+            needs_pub,
+            is_async,
+        })
     }
 
     fn render(self) -> FunctionTemplate {
@@ -351,7 +334,7 @@ fn render(self) -> FunctionTemplate {
 }
 
 /// Makes an optional return type along with whether the return type should be focused by the cursor.
-/// If we cannot infer what the return type should be, we create unit as a placeholder.
+/// If we cannot infer what the return type should be, we create a placeholder type.
 ///
 /// The rule for whether we focus a return type or not (and thus focus the function body),
 /// is rather simple:
@@ -366,22 +349,63 @@ fn make_return_type(
 ) -> (Option<ast::RetType>, bool) {
     let (ret_ty, should_focus_return_type) = {
         match ctx.sema.type_of_expr(call).map(TypeInfo::original) {
-            Some(ty) if ty.is_unknown() => (Some(make::ty_unit()), true),
-            None => (Some(make::ty_unit()), true),
+            Some(ty) if ty.is_unknown() => (Some(make::ty_placeholder()), true),
+            None => (Some(make::ty_placeholder()), true),
             Some(ty) if ty.is_unit() => (None, false),
             Some(ty) => {
                 let rendered = ty.display_source_code(ctx.db(), target_module.into());
                 match rendered {
                     Ok(rendered) => (Some(make::ty(&rendered)), false),
-                    Err(_) => (Some(make::ty_unit()), true),
+                    Err(_) => (Some(make::ty_placeholder()), true),
                 }
             }
         }
     };
-    let ret_type = ret_ty.map(|rt| make::ret_type(rt));
+    let ret_type = ret_ty.map(make::ret_type);
     (ret_type, should_focus_return_type)
 }
 
+fn get_fn_target(
+    ctx: &AssistContext,
+    target_module: &Option<Module>,
+    call: CallExpr,
+) -> Option<(GeneratedFunctionTarget, FileId, TextSize)> {
+    let mut file = ctx.file_id();
+    let target = match target_module {
+        Some(target_module) => {
+            let module_source = target_module.definition_source(ctx.db());
+            let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?;
+            file = in_file;
+            target
+        }
+        None => next_space_for_fn_after_call_site(ast::CallableExpr::Call(call))?,
+    };
+    Some((target.clone(), file, get_insert_offset(&target)))
+}
+
+fn get_method_target(
+    ctx: &AssistContext,
+    target_module: &Module,
+    impl_: &Option<ast::Impl>,
+) -> Option<(GeneratedFunctionTarget, TextSize)> {
+    let target = match impl_ {
+        Some(impl_) => next_space_for_fn_in_impl(impl_)?,
+        None => {
+            next_space_for_fn_in_module(ctx.sema.db, &target_module.definition_source(ctx.sema.db))?
+                .1
+        }
+    };
+    Some((target.clone(), get_insert_offset(&target)))
+}
+
+fn get_insert_offset(target: &GeneratedFunctionTarget) -> TextSize {
+    match &target {
+        GeneratedFunctionTarget::BehindItem(it) => it.text_range().end(),
+        GeneratedFunctionTarget::InEmptyItemList(it) => it.text_range().start() + TextSize::of('{'),
+    }
+}
+
+#[derive(Clone)]
 enum GeneratedFunctionTarget {
     BehindItem(SyntaxNode),
     InEmptyItemList(SyntaxNode),
@@ -396,38 +420,17 @@ fn syntax(&self) -> &SyntaxNode {
     }
 }
 
-fn fn_name(call: &ast::Path) -> Option<ast::Name> {
-    let name = call.segment()?.syntax().to_string();
-    Some(make::name(&name))
-}
-
 /// Computes the type variables and arguments required for the generated function
 fn fn_args(
     ctx: &AssistContext,
     target_module: hir::Module,
-    call: FuncExpr,
+    call: ast::CallableExpr,
 ) -> Option<(Option<ast::GenericParamList>, ast::ParamList)> {
     let mut arg_names = Vec::new();
     let mut arg_types = Vec::new();
     for arg in call.arg_list()?.args() {
-        arg_names.push(match fn_arg_name(&arg) {
-            Some(name) => name,
-            None => String::from("arg"),
-        });
-        arg_types.push(match fn_arg_type(ctx, target_module, &arg) {
-            Some(ty) => {
-                if ty.len() > 0 && ty.starts_with('&') {
-                    if let Some((new_ty, _)) = useless_type_special_case("", &ty[1..].to_owned()) {
-                        new_ty
-                    } else {
-                        ty
-                    }
-                } else {
-                    ty
-                }
-            }
-            None => String::from("()"),
-        });
+        arg_names.push(fn_arg_name(&ctx.sema, &arg));
+        arg_types.push(fn_arg_type(ctx, target_module, &arg));
     }
     deduplicate_arg_names(&mut arg_names);
     let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| {
@@ -438,8 +441,8 @@ fn fn_args(
         None,
         make::param_list(
             match call {
-                FuncExpr::Func(_) => None,
-                FuncExpr::Method(_) => Some(make::self_param()),
+                ast::CallableExpr::Call(_) => None,
+                ast::CallableExpr::MethodCall(_) => Some(make::self_param()),
             },
             params,
         ),
@@ -457,10 +460,10 @@ fn fn_args(
 /// assert_eq!(names, expected);
 /// ```
 fn deduplicate_arg_names(arg_names: &mut Vec<String>) {
-    let arg_name_counts = arg_names.iter().fold(FxHashMap::default(), |mut m, name| {
-        *m.entry(name).or_insert(0) += 1;
-        m
-    });
+    let mut arg_name_counts = FxHashMap::default();
+    for name in arg_names.iter() {
+        *arg_name_counts.entry(name).or_insert(0) += 1;
+    }
     let duplicate_arg_names: FxHashSet<String> = arg_name_counts
         .into_iter()
         .filter(|(_, count)| *count >= 2)
@@ -478,43 +481,63 @@ fn deduplicate_arg_names(arg_names: &mut Vec<String>) {
     }
 }
 
-fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> {
-    match fn_arg {
-        ast::Expr::CastExpr(cast_expr) => fn_arg_name(&cast_expr.expr()?),
-        _ => {
-            let s = fn_arg
+fn fn_arg_name(sema: &Semantics<RootDatabase>, arg_expr: &ast::Expr) -> String {
+    let name = (|| match arg_expr {
+        ast::Expr::CastExpr(cast_expr) => Some(fn_arg_name(sema, &cast_expr.expr()?)),
+        expr => {
+            let name_ref = expr
                 .syntax()
                 .descendants()
-                .filter(|d| ast::NameRef::can_cast(d.kind()))
-                .last()?
-                .to_string();
-            Some(to_lower_snake_case(&s))
+                .filter_map(ast::NameRef::cast)
+                .filter(|name| name.ident_token().is_some())
+                .last()?;
+            if let Some(NameRefClass::Definition(Definition::Const(_) | Definition::Static(_))) =
+                NameRefClass::classify(sema, &name_ref)
+            {
+                return Some(name_ref.to_string().to_lowercase());
+            };
+            Some(to_lower_snake_case(&name_ref.to_string()))
         }
+    })();
+    match name {
+        Some(mut name) if name.starts_with(|c: char| c.is_ascii_digit()) => {
+            name.insert_str(0, "arg");
+            name
+        }
+        Some(name) => name,
+        None => "arg".to_string(),
     }
 }
 
-fn fn_arg_type(
-    ctx: &AssistContext,
-    target_module: hir::Module,
-    fn_arg: &ast::Expr,
-) -> Option<String> {
-    let ty = ctx.sema.type_of_expr(fn_arg)?.adjusted();
-    if ty.is_unknown() {
-        return None;
-    }
+fn fn_arg_type(ctx: &AssistContext, target_module: hir::Module, fn_arg: &ast::Expr) -> String {
+    fn maybe_displayed_type(
+        ctx: &AssistContext,
+        target_module: hir::Module,
+        fn_arg: &ast::Expr,
+    ) -> Option<String> {
+        let ty = ctx.sema.type_of_expr(fn_arg)?.adjusted();
+        if ty.is_unknown() {
+            return None;
+        }
 
-    if let Ok(rendered) = ty.display_source_code(ctx.db(), target_module.into()) {
-        Some(rendered)
-    } else {
-        None
+        if ty.is_reference() || ty.is_mutable_reference() {
+            let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax()).krate());
+            convert_reference_type(ty.strip_references(), ctx.db(), famous_defs)
+                .map(|conversion| conversion.convert_type(ctx.db()))
+                .or_else(|| ty.display_source_code(ctx.db(), target_module.into()).ok())
+        } else {
+            ty.display_source_code(ctx.db(), target_module.into()).ok()
+        }
     }
+
+    maybe_displayed_type(ctx, target_module, fn_arg).unwrap_or_else(|| String::from("_"))
 }
 
 /// Returns the position inside the current mod or file
 /// directly after the current block
 /// We want to write the generated function directly after
 /// fns, impls or macro calls, but inside mods
-fn next_space_for_fn_after_call_site(expr: FuncExpr) -> Option<GeneratedFunctionTarget> {
+fn next_space_for_fn_after_call_site(expr: ast::CallableExpr) -> Option<GeneratedFunctionTarget> {
     let mut ancestors = expr.syntax().ancestors().peekable();
     let mut last_ancestor: Option<SyntaxNode> = None;
     while let Some(next_ancestor) = ancestors.next() {
@@ -540,20 +563,14 @@ fn next_space_for_fn_in_module(
 ) -> Option<(FileId, GeneratedFunctionTarget)> {
     let file = module_source.file_id.original_file(db);
     let assist_item = match &module_source.value {
-        hir::ModuleSource::SourceFile(it) => {
-            if let Some(last_item) = it.items().last() {
-                GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())
-            } else {
-                GeneratedFunctionTarget::BehindItem(it.syntax().clone())
-            }
-        }
-        hir::ModuleSource::Module(it) => {
-            if let Some(last_item) = it.item_list().and_then(|it| it.items().last()) {
-                GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())
-            } else {
-                GeneratedFunctionTarget::InEmptyItemList(it.item_list()?.syntax().clone())
-            }
-        }
+        hir::ModuleSource::SourceFile(it) => match it.items().last() {
+            Some(last_item) => GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()),
+            None => GeneratedFunctionTarget::BehindItem(it.syntax().clone()),
+        },
+        hir::ModuleSource::Module(it) => match it.item_list().and_then(|it| it.items().last()) {
+            Some(last_item) => GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()),
+            None => GeneratedFunctionTarget::InEmptyItemList(it.item_list()?.syntax().clone()),
+        },
         hir::ModuleSource::BlockExpr(it) => {
             if let Some(last_item) =
                 it.statements().take_while(|stmt| matches!(stmt, ast::Stmt::Item(_))).last()
@@ -607,7 +624,7 @@ fn foo() {
     bar();
 }
 
-fn bar() ${0:-> ()} {
+fn bar() ${0:-> _} {
     todo!()
 }
 ",
@@ -634,7 +651,7 @@ fn foo() {
     }
 }
 
-fn bar() ${0:-> ()} {
+fn bar() ${0:-> _} {
     todo!()
 }
 ",
@@ -658,7 +675,7 @@ fn foo1() {
     bar();
 }
 
-fn bar() ${0:-> ()} {
+fn bar() ${0:-> _} {
     todo!()
 }
 
@@ -684,7 +701,7 @@ fn foo() {
         bar();
     }
 
-    fn bar() ${0:-> ()} {
+    fn bar() ${0:-> _} {
         todo!()
     }
 }
@@ -708,7 +725,7 @@ fn foo() {
     bar(BazBaz);
 }
 
-fn bar(baz_baz: BazBaz) ${0:-> ()} {
+fn bar(baz_baz: BazBaz) ${0:-> _} {
     todo!()
 }
 ",
@@ -731,7 +748,7 @@ fn foo() {
     bar(&BazBaz as *const BazBaz);
 }
 
-fn bar(baz_baz: *const BazBaz) ${0:-> ()} {
+fn bar(baz_baz: *const BazBaz) ${0:-> _} {
     todo!()
 }
 ",
@@ -756,7 +773,7 @@ fn foo() {
     bar(baz());
 }
 
-fn bar(baz: Baz) ${0:-> ()} {
+fn bar(baz: Baz) ${0:-> _} {
     todo!()
 }
 ",
@@ -1059,7 +1076,7 @@ fn foo() {
     bar(Baz::new);
 }
 
-fn bar(new: fn) ${0:-> ()} {
+fn bar(new: fn) ${0:-> _} {
     todo!()
 }
 ",
@@ -1083,7 +1100,7 @@ fn foo() {
     bar(closure)
 }
 
-fn bar(closure: ()) {
+fn bar(closure: _) {
     ${0:todo!()}
 }
 ",
@@ -1091,7 +1108,7 @@ fn bar(closure: ()) {
     }
 
     #[test]
-    fn unresolveable_types_default_to_unit() {
+    fn unresolveable_types_default_to_placeholder() {
         check_assist(
             generate_function,
             r"
@@ -1104,7 +1121,7 @@ fn foo() {
     bar(baz)
 }
 
-fn bar(baz: ()) {
+fn bar(baz: _) {
     ${0:todo!()}
 }
 ",
@@ -1368,7 +1385,7 @@ fn foo(&self) {
         self.bar();
     }
 
-    fn bar(&self) ${0:-> ()} {
+    fn bar(&self) ${0:-> _} {
         todo!()
     }
 }
@@ -1390,7 +1407,7 @@ fn foo() {
     bar(42).await();
 }
 
-async fn bar(arg: i32) ${0:-> ()} {
+async fn bar(arg: i32) ${0:-> _} {
     todo!()
 }
 ",
@@ -1411,7 +1428,7 @@ fn create_method() {
 impl S {
 
 
-fn bar(&self) ${0:-> ()} {
+fn bar(&self) ${0:-> _} {
     todo!()
 }
 }
@@ -1433,7 +1450,7 @@ impl S {}
 struct S;
 fn foo() {S.bar();}
 impl S {
-    fn bar(&self) ${0:-> ()} {
+    fn bar(&self) ${0:-> _} {
         todo!()
     }
 }
@@ -1458,7 +1475,7 @@ mod s {
 impl S {
 
 
-    pub(crate) fn bar(&self) ${0:-> ()} {
+    pub(crate) fn bar(&self) ${0:-> _} {
         todo!()
     }
 }
@@ -1491,7 +1508,7 @@ fn foo() {
 impl S {
 
 
-fn bar(&self) ${0:-> ()} {
+fn bar(&self) ${0:-> _} {
     todo!()
 }
 }
@@ -1514,9 +1531,215 @@ fn create_method_with_cursor_anywhere_on_call_expresion() {
 impl S {
 
 
-fn bar(&self) ${0:-> ()} {
+fn bar(&self) ${0:-> _} {
+    todo!()
+}
+}
+",
+        )
+    }
+
+    #[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!()
+}
+}
+",
+        )
+    }
+
+    #[test]
+    fn no_panic_on_invalid_global_path() {
+        check_assist(
+            generate_function,
+            r"
+fn main() {
+    ::foo$0();
+}
+",
+            r"
+fn main() {
+    ::foo();
+}
+
+fn foo() ${0:-> _} {
+    todo!()
+}
+",
+        )
+    }
+
+    #[test]
+    fn handle_tuple_indexing() {
+        check_assist(
+            generate_function,
+            r"
+fn main() {
+    let a = ((),);
+    foo$0(a.0);
+}
+",
+            r"
+fn main() {
+    let a = ((),);
+    foo(a.0);
+}
+
+fn foo(a: ()) ${0:-> _} {
     todo!()
 }
+",
+        )
+    }
+
+    #[test]
+    fn add_function_with_const_arg() {
+        check_assist(
+            generate_function,
+            r"
+const VALUE: usize = 0;
+fn main() {
+    foo$0(VALUE);
+}
+",
+            r"
+const VALUE: usize = 0;
+fn main() {
+    foo(VALUE);
+}
+
+fn foo(value: usize) ${0:-> _} {
+    todo!()
+}
+",
+        )
+    }
+
+    #[test]
+    fn add_function_with_static_arg() {
+        check_assist(
+            generate_function,
+            r"
+static VALUE: usize = 0;
+fn main() {
+    foo$0(VALUE);
+}
+",
+            r"
+static VALUE: usize = 0;
+fn main() {
+    foo(VALUE);
+}
+
+fn foo(value: usize) ${0:-> _} {
+    todo!()
+}
+",
+        )
+    }
+
+    #[test]
+    fn add_function_with_static_mut_arg() {
+        check_assist(
+            generate_function,
+            r"
+static mut VALUE: usize = 0;
+fn main() {
+    foo$0(VALUE);
+}
+",
+            r"
+static mut VALUE: usize = 0;
+fn main() {
+    foo(VALUE);
+}
+
+fn foo(value: usize) ${0:-> _} {
+    todo!()
 }
 ",
         )