diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index d0a49d1f37a..39c58615429 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -1332,9 +1332,71 @@ fn foo(foo: Foo) {
 struct Foo { baz: i32 }
 
 fn foo(foo: Foo) {
-    let Foo { ref baz @ qux } = foo;
+    let Foo { baz: ref baz @ qux } = foo;
     let _ = qux;
 }
+"#,
+        );
+        check(
+            "baz",
+            r#"
+struct Foo { i$0: i32 }
+
+fn foo(foo: Foo) {
+    let Foo { i: ref baz } = foo;
+    let _ = qux;
+}
+"#,
+            r#"
+struct Foo { baz: i32 }
+
+fn foo(foo: Foo) {
+    let Foo { ref baz } = foo;
+    let _ = qux;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_struct_local_pat_into_shorthand() {
+        cov_mark::check!(test_rename_local_put_init_shorthand_pat);
+        check(
+            "field",
+            r#"
+struct Foo { field: i32 }
+
+fn foo(foo: Foo) {
+    let Foo { field: qux$0 } = foo;
+    let _ = qux;
+}
+"#,
+            r#"
+struct Foo { field: i32 }
+
+fn foo(foo: Foo) {
+    let Foo { field } = foo;
+    let _ = field;
+}
+"#,
+        );
+        check(
+            "field",
+            r#"
+struct Foo { field: i32 }
+
+fn foo(foo: Foo) {
+    let Foo { field: x @ qux$0 } = foo;
+    let _ = qux;
+}
+"#,
+            r#"
+struct Foo { field: i32 }
+
+fn foo(foo: Foo) {
+    let Foo { field: x @ field } = foo;
+    let _ = field;
+}
 "#,
         );
     }
@@ -1390,7 +1452,7 @@ struct Foo {
     i: i32
 }
 
-fn foo(Foo { i }: foo) -> i32 {
+fn foo(Foo { i }: Foo) -> i32 {
     i$0
 }
 "#,
@@ -1399,7 +1461,7 @@ struct Foo {
     i: i32
 }
 
-fn foo(Foo { i: bar }: foo) -> i32 {
+fn foo(Foo { i: bar }: Foo) -> i32 {
     bar
 }
 "#,
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 3433398ebab..a672c109174 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -120,11 +120,11 @@ impl ImportAssets {
     }
 
     pub fn for_ident_pat(pat: &ast::IdentPat, sema: &Semantics<RootDatabase>) -> Option<Self> {
-        let name = pat.name()?;
-        let candidate_node = pat.syntax().clone();
         if !pat.is_simple_ident() {
             return None;
         }
+        let name = pat.name()?;
+        let candidate_node = pat.syntax().clone();
         Some(Self {
             import_candidate: ImportCandidate::for_name(sema, &name)?,
             module_with_candidate: sema.scope(&candidate_node).module()?,
diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs
index 4ca3260877c..6d8d8a2b8fb 100644
--- a/crates/ide_db/src/rename.rs
+++ b/crates/ide_db/src/rename.rs
@@ -320,6 +320,7 @@ pub fn source_edit_from_references(
         .unwrap_or_else(|| (reference.range, new_name.to_string()));
         edit.replace(range, replacement);
     }
+
     edit.finish()
 }
 
@@ -334,6 +335,7 @@ fn source_edit_from_name(name: &ast::Name, new_name: &str) -> Option<(TextRange,
             ));
         }
     }
+
     None
 }
 
@@ -387,7 +389,9 @@ fn source_edit_from_name_ref(
         let rcf_pat = record_field.pat();
         match (rcf_name_ref, rcf_pat) {
             // field: rename
-            (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => {
+            (Some(field_name), Some(ast::Pat::IdentPat(pat)))
+                if field_name == *name_ref && pat.at_token().is_none() =>
+            {
                 // field name is being renamed
                 if pat.name().map_or(false, |it| it.text() == new_name) {
                     cov_mark::hit!(test_rename_field_put_init_shorthand_pat);
@@ -412,32 +416,52 @@ fn source_edit_from_def(
     def: Definition,
     new_name: &str,
 ) -> Result<(FileId, TextEdit)> {
-    let frange = def
+    let FileRange { file_id, range } = def
         .range_for_rename(sema)
         .ok_or_else(|| format_err!("No identifier available to rename"))?;
 
-    let mut replacement_text = String::new();
-    let mut repl_range = frange.range;
+    let mut edit = TextEdit::builder();
     if let Definition::Local(local) = def {
         if let Either::Left(pat) = local.source(sema.db).value {
-            if matches!(
-                pat.syntax().parent().and_then(ast::RecordPatField::cast),
-                Some(pat_field) if pat_field.name_ref().is_none()
-            ) {
-                replacement_text.push_str(": ");
-                replacement_text.push_str(new_name);
-                repl_range = TextRange::new(
-                    pat.syntax().text_range().end(),
-                    pat.syntax().text_range().end(),
-                );
+            // special cases required for renaming fields/locals in Record patterns
+            if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) {
+                let name_range = pat.name().unwrap().syntax().text_range();
+                if let Some(name_ref) = pat_field.name_ref() {
+                    if new_name == name_ref.text() && pat.at_token().is_none() {
+                        // Foo { field: ref mut local } -> Foo { ref mut field }
+                        //       ^^^^^^ delete this
+                        //                      ^^^^^ replace this with `field`
+                        cov_mark::hit!(test_rename_local_put_init_shorthand_pat);
+                        edit.delete(
+                            name_ref
+                                .syntax()
+                                .text_range()
+                                .cover_offset(pat.syntax().text_range().start()),
+                        );
+                        edit.replace(name_range, name_ref.text().to_string());
+                    } else {
+                        // Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 }
+                        // Foo { field: ref mut local } -> Foo { field: ref mut new_name }
+                        //                      ^^^^^ replace this with `new_name`
+                        edit.replace(name_range, new_name.to_string());
+                    }
+                } else {
+                    // Foo { ref mut field } -> Foo { field: ref mut new_name }
+                    //      ^ insert `field: `
+                    //               ^^^^^ replace this with `new_name`
+                    edit.insert(
+                        pat.syntax().text_range().start(),
+                        format!("{}: ", pat_field.field_name().unwrap()),
+                    );
+                    edit.replace(name_range, new_name.to_string());
+                }
             }
         }
     }
-    if replacement_text.is_empty() {
-        replacement_text.push_str(new_name);
+    if edit.is_empty() {
+        edit.replace(range, new_name.to_string());
     }
-    let edit = TextEdit::replace(repl_range, replacement_text);
-    Ok((frange.file_id, edit))
+    Ok((file_id, edit.finish()))
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
diff --git a/crates/text_edit/src/lib.rs b/crates/text_edit/src/lib.rs
index 685039c4bec..a43ffe202ff 100644
--- a/crates/text_edit/src/lib.rs
+++ b/crates/text_edit/src/lib.rs
@@ -159,6 +159,9 @@ impl<'a> IntoIterator for &'a TextEdit {
 }
 
 impl TextEditBuilder {
+    pub fn is_empty(&self) -> bool {
+        self.indels.is_empty()
+    }
     pub fn replace(&mut self, range: TextRange, replace_with: String) {
         self.indel(Indel::replace(range, replace_with))
     }