Repository: rust-analyzer/rust-analyzer
Pull request #3231 (2020-02-19 11:49:37)

Add `remove_mut` assist


bors r+
🤖


Patch:
diff --git a/crates/ra_assists/src/handlers/remove_mut.rs b/crates/ra_assists/src/handlers/remove_mut.rs
new file mode 100644
index 0000000000..6884830eb6
--- /dev/null
+++ b/crates/ra_assists/src/handlers/remove_mut.rs
@@ -0,0 +1,32 @@
+use ra_syntax::{SyntaxKind, TextRange, T};
+
+use crate::{Assist, AssistCtx, AssistId};
+
+// Assist: remove_mut
+//
+// Removes the `mut` keyword.
+//
+// ```
+// impl Walrus {
+//     fn feed(&mut<|> self, amount: u32) {}
+// }
+// ```
+// ->
+// ```
+// impl Walrus {
+//     fn feed(&self, amount: u32) {}
+// }
+// ```
+pub(crate) fn remove_mut(ctx: AssistCtx) -> Option<Assist> {
+    let mut_token = ctx.find_token_at_offset(T![mut])?;
+    let delete_from = mut_token.text_range().start();
+    let delete_to = match mut_token.next_token() {
+        Some(it) if it.kind() == SyntaxKind::WHITESPACE => it.text_range().end(),
+        _ => mut_token.text_range().end(),
+    };
+
+    ctx.add_assist(AssistId("remove_mut"), "Remove `mut` keyword", |edit| {
+        edit.set_cursor(delete_from);
+        edit.delete(TextRange::from_to(delete_from, delete_to));
+    })
+}
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index f4a7497dbe..d7998b0d1a 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -108,6 +108,7 @@ mod handlers {
     mod introduce_variable;
     mod inline_local_variable;
     mod raw_string;
+    mod remove_mut;
     mod replace_if_let_with_match;
     mod split_import;
     mod remove_dbg;
@@ -147,6 +148,7 @@ mod handlers {
             raw_string::make_raw_string,
             raw_string::make_usual_string,
             raw_string::remove_hash,
+            remove_mut::remove_mut,
             early_return::convert_to_guarded_return,
             auto_import::auto_import,
         ]
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 6d4416c0bb..1de3cb5796 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -13,7 +13,7 @@ use ra_syntax::{
 
 use crate::{
     display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
-    expand::{descend_into_macros, original_range},
+    expand::descend_into_macros,
     references::{classify_name, classify_name_ref, NameKind, NameKind::*},
     FilePosition, FileRange, RangeInfo,
 };
@@ -148,18 +148,17 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
     let mut res = HoverResult::new();
 
     let mut sb = SourceBinder::new(db);
-    if let Some((node, name_kind)) = match_ast! {
+    if let Some((range, name_kind)) = match_ast! {
         match (token.value.parent()) {
             ast::NameRef(name_ref) => {
-                classify_name_ref(&mut sb, token.with_value(&name_ref)).map(|d| (name_ref.syntax().clone(), d.kind))
+                classify_name_ref(&mut sb, token.with_value(&name_ref)).map(|d| (name_ref.syntax().text_range(), d.kind))
             },
             ast::Name(name) => {
-                classify_name(&mut sb, token.with_value(&name)).map(|d| (name.syntax().clone(), d.kind))
+                classify_name(&mut sb, token.with_value(&name)).map(|d| (name.syntax().text_range(), d.kind))
             },
             _ => None,
         }
     } {
-        let range = original_range(db, token.with_value(&node)).range;
         res.extend(hover_text_from_name_kind(db, name_kind));
 
         if !res.is_empty() {
@@ -172,7 +171,8 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
         .ancestors()
         .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
 
-    let frange = original_range(db, token.with_value(&node));
+    // The following logic will not work if token is coming from a macro
+    let frange = FileRange { file_id: position.file_id, range: node.text_range() };
     res.extend(type_of(db, frange).map(rust_code_markup));
     if res.is_empty() {
         return None;
@@ -220,7 +220,6 @@ mod tests {
     use crate::mock_analysis::{
         analysis_and_position, single_file_with_position, single_file_with_range,
     };
-    use ra_db::FileLoader;
     use ra_syntax::TextRange;
 
     fn trim_markup(s: &str) -> &str {
@@ -231,7 +230,7 @@ mod tests {
         s.map(trim_markup)
     }
 
-    fn check_hover_result(fixture: &str, expected: &[&str]) -> String {
+    fn check_hover_result(fixture: &str, expected: &[&str]) {
         let (analysis, position) = analysis_and_position(fixture);
         let hover = analysis.hover(position).unwrap().unwrap();
         let mut results = Vec::from(hover.info.results());
@@ -244,9 +243,6 @@ mod tests {
         }
 
         assert_eq!(hover.info.len(), expected.len());
-
-        let content = analysis.db.file_text(position.file_id);
-        content[hover.range].to_string()
     }
 
     #[test]
@@ -715,7 +711,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
 
     #[test]
     fn test_hover_through_macro() {
-        let hover_on = check_hover_result(
+        check_hover_result(
             "
             //- /lib.rs
             macro_rules! id {
@@ -730,13 +726,11 @@ fn func(foo: i32) { if true { <|>foo; }; }
             ",
             &["fn foo()"],
         );
-
-        assert_eq!(hover_on, "foo")
     }
 
     #[test]
     fn test_hover_through_expr_in_macro() {
-        let hover_on = check_hover_result(
+        check_hover_result(
             "
             //- /lib.rs
             macro_rules! id {
@@ -748,8 +742,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
             ",
             &["u32"],
         );
-
-        assert_eq!(hover_on, "bar")
     }
 
     #[test]
diff --git a/docs/user/assists.md b/docs/user/assists.md
index f737a2fa49..9861332af8 100644
--- a/docs/user/assists.md
+++ b/docs/user/assists.md
@@ -527,6 +527,22 @@ fn main() {
 }
 ```
 
+## `remove_mut`
+
+Removes the `mut` keyword.
+
+```rust
+// BEFORE
+impl Walrus {
+    fn feed(&mut┃ self, amount: u32) {}
+}
+
+// AFTER
+impl Walrus {
+    fn feed(&self, amount: u32) {}
+}
+```
+
 ## `replace_if_let_with_match`
 
 Replaces `if let` with an else branch with a `match` expression.


Test patch:
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs
index 4ab09b167b..3f56dd5084 100644
--- a/crates/ra_assists/src/doc_tests/generated.rs
+++ b/crates/ra_assists/src/doc_tests/generated.rs
@@ -548,6 +548,23 @@ fn main() {
     )
 }
 
+#[test]
+fn doctest_remove_mut() {
+    check(
+        "remove_mut",
+        r#####"
+impl Walrus {
+    fn feed(&mut<|> self, amount: u32) {}
+}
+"#####,
+        r#####"
+impl Walrus {
+    fn feed(&self, amount: u32) {}
+}
+"#####,
+    )
+}
+
 #[test]
 fn doctest_replace_if_let_with_match() {
     check(