about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/ide-assists/src/handlers/bind_unused_param.rs130
-rw-r--r--crates/ide-assists/src/handlers/remove_unused_param.rs16
-rw-r--r--crates/ide-assists/src/lib.rs2
-rw-r--r--crates/ide-assists/src/tests/generated.rs15
4 files changed, 156 insertions, 7 deletions
diff --git a/crates/ide-assists/src/handlers/bind_unused_param.rs b/crates/ide-assists/src/handlers/bind_unused_param.rs
new file mode 100644
index 00000000000..0a22d26193a
--- /dev/null
+++ b/crates/ide-assists/src/handlers/bind_unused_param.rs
@@ -0,0 +1,130 @@
+use crate::assist_context::{AssistContext, Assists};
+use ide_db::{
+    assists::{AssistId, AssistKind},
+    defs::Definition,
+    LineIndexDatabase,
+};
+use syntax::{
+    ast::{self, edit_in_place::Indent},
+    AstNode,
+};
+
+use super::remove_unused_param::is_trait_impl;
+
+// Assist: bind_unused_param
+//
+// Binds unused function parameter to an underscore.
+//
+// ```
+// fn some_function(x: i32$0) {}
+// ```
+// ->
+// ```
+// fn some_function(x: i32) {
+//     let _ = x;
+// }
+// ```
+pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let param: ast::Param = ctx.find_node_at_offset()?;
+
+    let ident_pat = match param.pat()? {
+        ast::Pat::IdentPat(it) => it,
+        _ => return None,
+    };
+
+    let func = param.syntax().ancestors().find_map(ast::Fn::cast)?;
+    if is_trait_impl(&func) {
+        cov_mark::hit!(trait_impl);
+        return None;
+    }
+
+    let param_def = {
+        let local = ctx.sema.to_def(&ident_pat)?;
+        Definition::Local(local)
+    };
+    if param_def.usages(&ctx.sema).at_least_one() {
+        cov_mark::hit!(keep_used);
+        return None;
+    }
+
+    let line_index = ctx.db().line_index(ctx.file_id());
+
+    let mut indent = func.indent_level();
+    indent.0 += 1;
+    let mut text = format!("\n{indent}let _ = {ident_pat};");
+
+    let stmt_list = func.body()?.stmt_list()?;
+    let l_curly_range = stmt_list.l_curly_token()?.text_range();
+    let r_curly_range = stmt_list.r_curly_token()?.text_range();
+    let left_line = line_index.line_col(l_curly_range.end()).line;
+    let right_line = line_index.line_col(r_curly_range.start()).line;
+
+    if left_line == right_line {
+        text.push('\n');
+    }
+
+    acc.add(
+        AssistId("bind_unused_param", AssistKind::Refactor),
+        &format!("Bind as `let _ = {};`", &ident_pat),
+        param.syntax().text_range(),
+        |builder| {
+            builder.insert(l_curly_range.end(), text);
+        },
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_assist;
+
+    use super::*;
+
+    #[test]
+    fn bind_unused_empty_block() {
+        check_assist(
+            bind_unused_param,
+            r#"
+fn foo($0y: i32) {}
+"#,
+            r#"
+fn foo(y: i32) {
+    let _ = y;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn bind_unused_empty_block_with_newline() {
+        check_assist(
+            bind_unused_param,
+            r#"
+fn foo($0y: i32) {
+}
+"#,
+            r#"
+fn foo(y: i32) {
+    let _ = y;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn bind_unused_generic() {
+        check_assist(
+            bind_unused_param,
+            r#"
+fn foo<T>($0y: T)
+where T : Default {
+}
+"#,
+            r#"
+fn foo<T>(y: T)
+where T : Default {
+    let _ = y;
+}
+"#,
+        );
+    }
+}
diff --git a/crates/ide-assists/src/handlers/remove_unused_param.rs b/crates/ide-assists/src/handlers/remove_unused_param.rs
index 0772b168d49..aa1002ee393 100644
--- a/crates/ide-assists/src/handlers/remove_unused_param.rs
+++ b/crates/ide-assists/src/handlers/remove_unused_param.rs
@@ -42,13 +42,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) ->
         param.syntax().parent()?.children().find_map(ast::SelfParam::cast).is_some();
 
     // check if fn is in impl Trait for ..
-    if func
-        .syntax()
-        .parent() // AssocItemList
-        .and_then(|x| x.parent())
-        .and_then(ast::Impl::cast)
-        .map_or(false, |imp| imp.trait_().is_some())
-    {
+    if is_trait_impl(&func) {
         cov_mark::hit!(trait_impl);
         return None;
     }
@@ -87,6 +81,14 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) ->
     )
 }
 
+pub(crate) fn is_trait_impl(func: &ast::Fn) -> bool {
+    func.syntax()
+        .parent() // AssocItemList
+        .and_then(|x| x.parent())
+        .and_then(ast::Impl::cast)
+        .map_or(false, |imp| imp.trait_().is_some())
+}
+
 fn process_usages(
     ctx: &AssistContext<'_>,
     builder: &mut SourceChangeBuilder,
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index 2ebb5ef9b19..30a0ce40e30 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -114,6 +114,7 @@ mod handlers {
     mod add_turbo_fish;
     mod apply_demorgan;
     mod auto_import;
+    mod bind_unused_param;
     mod change_visibility;
     mod convert_bool_then;
     mod convert_comment_block;
@@ -224,6 +225,7 @@ mod handlers {
             add_turbo_fish::add_turbo_fish,
             apply_demorgan::apply_demorgan,
             auto_import::auto_import,
+            bind_unused_param::bind_unused_param,
             change_visibility::change_visibility,
             convert_bool_then::convert_bool_then_to_if,
             convert_bool_then::convert_if_to_bool_then,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 6eadc3dbcbc..0a30d6537dd 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -266,6 +266,21 @@ pub mod std { pub mod collections { pub struct HashMap { } } }
 }
 
 #[test]
+fn doctest_bind_unused_param() {
+    check_doc_test(
+        "bind_unused_param",
+        r#####"
+fn some_function(x: i32$0) {}
+"#####,
+        r#####"
+fn some_function(x: i32) {
+    let _ = x;
+}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_change_visibility() {
     check_doc_test(
         "change_visibility",