Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #19692 from vishruth-thimmaiah/remove_underscore_for_used_var
feat: adds an assist to remove underscores from used variables
Chayim Refael Friedman 2025-04-27
parent 94fa31e · parent 47c22bf · commit d8887c0
-rw-r--r--crates/ide-assists/src/handlers/remove_underscore.rs191
-rw-r--r--crates/ide-assists/src/lib.rs2
-rw-r--r--crates/ide-assists/src/tests/generated.rs19
3 files changed, 212 insertions, 0 deletions
diff --git a/crates/ide-assists/src/handlers/remove_underscore.rs b/crates/ide-assists/src/handlers/remove_underscore.rs
new file mode 100644
index 0000000000..912e1936b5
--- /dev/null
+++ b/crates/ide-assists/src/handlers/remove_underscore.rs
@@ -0,0 +1,191 @@
+use ide_db::{
+ assists::AssistId,
+ defs::{Definition, NameClass, NameRefClass},
+};
+use syntax::{AstNode, ast};
+
+use crate::{AssistContext, Assists};
+
+// Assist: remove_underscore_from_used_variables
+//
+// Removes underscore from used variables.
+//
+// ```
+// fn main() {
+// let mut _$0foo = 1;
+// _foo = 2;
+// }
+// ```
+// ->
+// ```
+// fn main() {
+// let mut foo = 1;
+// foo = 2;
+// }
+// ```
+pub(crate) fn remove_underscore(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let (text, text_range, def) = if let Some(name_ref) = ctx.find_node_at_offset::<ast::Name>() {
+ let text = name_ref.text();
+ if !text.starts_with('_') {
+ return None;
+ }
+
+ let def = match NameClass::classify(&ctx.sema, &name_ref)? {
+ NameClass::Definition(def @ Definition::Local(_)) => def,
+ NameClass::PatFieldShorthand { local_def, .. } => Definition::Local(local_def),
+ _ => return None,
+ };
+ (text.to_owned(), name_ref.syntax().text_range(), def)
+ } else if let Some(name_ref) = ctx.find_node_at_offset::<ast::NameRef>() {
+ let text = name_ref.text();
+ if !text.starts_with('_') {
+ return None;
+ }
+ let def = match NameRefClass::classify(&ctx.sema, &name_ref)? {
+ NameRefClass::Definition(def @ Definition::Local(_), _) => def,
+ NameRefClass::FieldShorthand { local_ref, .. } => Definition::Local(local_ref),
+ _ => return None,
+ };
+ (text.to_owned(), name_ref.syntax().text_range(), def)
+ } else {
+ return None;
+ };
+
+ if !def.usages(&ctx.sema).at_least_one() {
+ return None;
+ }
+
+ let new_name = text.trim_start_matches('_');
+ acc.add(
+ AssistId::refactor("remove_underscore_from_used_variables"),
+ "Remove underscore from a used variable",
+ text_range,
+ |builder| {
+ let changes = def.rename(&ctx.sema, new_name).unwrap();
+ builder.source_change = changes;
+ },
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::{check_assist, check_assist_not_applicable};
+
+ use super::*;
+
+ #[test]
+ fn remove_underscore_from_used_variable() {
+ check_assist(
+ remove_underscore,
+ r#"
+fn main() {
+ let mut _$0foo = 1;
+ _foo = 2;
+}
+"#,
+ r#"
+fn main() {
+ let mut foo = 1;
+ foo = 2;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn not_applicable_for_unused() {
+ check_assist_not_applicable(
+ remove_underscore,
+ r#"
+fn main() {
+ let _$0unused = 1;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn not_applicable_for_no_underscore() {
+ check_assist_not_applicable(
+ remove_underscore,
+ r#"
+fn main() {
+ let f$0oo = 1;
+ foo = 2;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn remove_multiple_underscores() {
+ check_assist(
+ remove_underscore,
+ r#"
+fn main() {
+ let mut _$0_foo = 1;
+ __foo = 2;
+}
+"#,
+ r#"
+fn main() {
+ let mut foo = 1;
+ foo = 2;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn remove_underscore_on_usage() {
+ check_assist(
+ remove_underscore,
+ r#"
+fn main() {
+ let mut _foo = 1;
+ _$0foo = 2;
+}
+"#,
+ r#"
+fn main() {
+ let mut foo = 1;
+ foo = 2;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn remove_underscore_in_function_parameter_usage() {
+ check_assist(
+ remove_underscore,
+ r#"
+fn foo(_foo: i32) {
+ let bar = _$0foo + 1;
+}
+"#,
+ r#"
+fn foo(foo: i32) {
+ let bar = foo + 1;
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn remove_underscore_in_function_parameter() {
+ check_assist(
+ remove_underscore,
+ r#"
+fn foo(_$0foo: i32) {
+ let bar = _foo + 1;
+}
+"#,
+ r#"
+fn foo(foo: i32) {
+ let bar = foo + 1;
+}
+"#,
+ )
+ }
+}
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index fee5a9dd73..a157483a44 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -200,6 +200,7 @@ mod handlers {
mod remove_dbg;
mod remove_mut;
mod remove_parentheses;
+ mod remove_underscore;
mod remove_unused_imports;
mod remove_unused_param;
mod reorder_fields;
@@ -335,6 +336,7 @@ mod handlers {
remove_dbg::remove_dbg,
remove_mut::remove_mut,
remove_parentheses::remove_parentheses,
+ remove_underscore::remove_underscore,
remove_unused_imports::remove_unused_imports,
remove_unused_param::remove_unused_param,
reorder_fields::reorder_fields,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 359de438ed..00a9d35c31 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -2749,6 +2749,25 @@ fn main() {
}
#[test]
+fn doctest_remove_underscore_from_used_variables() {
+ check_doc_test(
+ "remove_underscore_from_used_variables",
+ r#####"
+fn main() {
+ let mut _$0foo = 1;
+ _foo = 2;
+}
+"#####,
+ r#####"
+fn main() {
+ let mut foo = 1;
+ foo = 2;
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_remove_unused_imports() {
check_doc_test(
"remove_unused_imports",