Repository: rust-analyzer/rust-analyzer
Pull request #8168 (2021-03-23 15:23:34)

correct `convert to guard return` let_stmt
close https://github.com/rust-analyzer/rust-analyzer/issues/8074

hi-rustin: I don't think this is the right way to do it, I tried checking mut_token but can't seem to find the token, so I can't tell whether to use ident_pat or ident_mut_pat.

@Veykril do you have any suggestions?


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 7f666ca618..19ee689cb4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -35,9 +35,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.39"
+version = "1.0.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81cddc5f91628367664cc7c69714ff08deee8a3efc54623011c772544d7b2767"
+checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
 
 [[package]]
 name = "anymap"
@@ -111,9 +111,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
 [[package]]
 name = "camino"
-version = "1.0.4"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4648c6d00a709aa069a236adcaae4f605a6241c72bf5bee79331a4b625921a9"
+checksum = "cd065703998b183ed0b348a22555691373a9345a1431141e5778b48bb17e4703"
 dependencies = [
  "serde",
 ]
@@ -794,9 +794,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.91"
+version = "0.2.88"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7"
+checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a"
 
 [[package]]
 name = "libloading"
@@ -946,9 +946,9 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "0.7.11"
+version = "0.7.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956"
+checksum = "a5dede4e2065b3842b8b0af444119f3aa331cc7cc2dd20388bfb0f5d5a38823a"
 dependencies = [
  "libc",
  "log",
@@ -959,10 +959,11 @@ dependencies = [
 
 [[package]]
 name = "miow"
-version = "0.3.7"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
+checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
 dependencies = [
+ "socket2",
  "winapi",
 ]
 
@@ -1298,9 +1299,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.4.5"
+version = "1.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
+checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
 dependencies = [
  "regex-syntax",
 ]
@@ -1317,9 +1318,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.23"
+version = "0.6.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
+checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
 
 [[package]]
 name = "rowan"
@@ -1484,18 +1485,18 @@ dependencies = [
 
 [[package]]
 name = "serde"
-version = "1.0.125"
+version = "1.0.124"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
+checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.125"
+version = "1.0.124"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
+checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1564,6 +1565,17 @@ version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dc725476a1398f0480d56cd0ad381f6f32acf2642704456f8f59a35df464b59a"
 
+[[package]]
+name = "socket2"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "winapi",
+]
+
 [[package]]
 name = "stdx"
 version = "0.0.0"
@@ -1574,9 +1586,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.64"
+version = "1.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
+checksum = "8fd9bc7ccc2688b3344c2f48b9b546648b25ce0b20fc717ee7fa7981a8ca9717"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1706,9 +1718,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-attributes"
-version = "0.1.15"
+version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2"
+checksum = "a8a9bd1db7706f2373a190b0d067146caa39350c486f3d455b0e33b431f94c07"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1747,9 +1759,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-subscriber"
-version = "0.2.17"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "705096c6f83bf68ea5d357a6aa01829ddbdac531b357b45abeca842938085baa"
+checksum = "8ab8966ac3ca27126141f7999361cc97dd6fb4b71da04c02044fa9045d98bb96"
 dependencies = [
  "ansi_term",
  "chrono",
@@ -1855,9 +1867,9 @@ dependencies = [
 
 [[package]]
 name = "version_check"
-version = "0.9.3"
+version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
+checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
 
 [[package]]
 name = "vfs"
@@ -1884,9 +1896,9 @@ dependencies = [
 
 [[package]]
 name = "walkdir"
-version = "2.3.2"
+version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
 dependencies = [
  "same-file",
  "winapi",
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index eb7865c842..5da6a03404 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -852,7 +852,6 @@ impl Function {
             })
             .collect()
     }
-
     pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> {
         if self.self_param(db).is_none() {
             return None;
@@ -910,7 +909,7 @@ impl From<hir_ty::Mutability> for Access {
     }
 }
 
-#[derive(Clone, Debug)]
+#[derive(Debug)]
 pub struct Param {
     func: Function,
     /// The index in parameter list, including self parameter.
@@ -923,25 +922,13 @@ impl Param {
         &self.ty
     }
 
-    pub fn as_local(&self, db: &dyn HirDatabase) -> Local {
-        let parent = DefWithBodyId::FunctionId(self.func.into());
-        let body = db.body(parent);
-        Local { parent, pat_id: body.params[self.idx] }
-    }
-
     pub fn pattern_source(&self, db: &dyn HirDatabase) -> Option<ast::Pat> {
-        self.source(db).and_then(|p| p.value.pat())
-    }
-
-    pub fn source(&self, db: &dyn HirDatabase) -> Option<InFile<ast::Param>> {
-        let InFile { file_id, value } = self.func.source(db)?;
-        let params = value.param_list()?;
+        let params = self.func.source(db)?.value.param_list()?;
         if params.self_param().is_some() {
-            params.params().nth(self.idx.checked_sub(1)?)
+            params.params().nth(self.idx.checked_sub(1)?)?.pat()
         } else {
-            params.params().nth(self.idx)
+            params.params().nth(self.idx)?.pat()
         }
-        .map(|value| InFile { file_id, value })
     }
 }
 
@@ -973,14 +960,6 @@ impl SelfParam {
             Access::Owned => "self",
         }
     }
-
-    pub fn source(&self, db: &dyn HirDatabase) -> Option<InFile<ast::SelfParam>> {
-        let InFile { file_id, value } = Function::from(self.func).source(db)?;
-        value
-            .param_list()
-            .and_then(|params| params.self_param())
-            .map(|value| InFile { file_id, value })
-    }
 }
 
 impl HasVisibility for Function {
@@ -1356,13 +1335,6 @@ impl Local {
         }
     }
 
-    pub fn as_self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
-        match self.parent {
-            DefWithBodyId::FunctionId(func) if self.is_self(db) => Some(SelfParam { func }),
-            _ => None,
-        }
-    }
-
     // FIXME: why is this an option? It shouldn't be?
     pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
         let body = db.body(self.parent);
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index d8fabe49ba..28b73c3a18 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -91,6 +91,7 @@ pub(super) fn collect_defs(
         resolved_imports: Vec::new(),
 
         unexpanded_macros: Vec::new(),
+        unexpanded_attribute_macros: Vec::new(),
         mod_dirs: FxHashMap::default(),
         cfg_options,
         proc_macros,
@@ -201,14 +202,15 @@ struct ImportDirective {
 #[derive(Clone, Debug, Eq, PartialEq)]
 struct MacroDirective {
     module_id: LocalModuleId,
+    ast_id: AstIdWithPath<ast::MacroCall>,
+    legacy: Option<MacroCallId>,
     depth: usize,
-    kind: MacroDirectiveKind,
 }
 
 #[derive(Clone, Debug, Eq, PartialEq)]
-enum MacroDirectiveKind {
-    FnLike { ast_id: AstIdWithPath<ast::MacroCall>, legacy: Option<MacroCallId> },
-    Derive { ast_id: AstIdWithPath<ast::Item> },
+struct DeriveDirective {
+    module_id: LocalModuleId,
+    ast_id: AstIdWithPath<ast::Item>,
 }
 
 struct DefData<'a> {
@@ -226,6 +228,7 @@ struct DefCollector<'a> {
     unresolved_imports: Vec<ImportDirective>,
     resolved_imports: Vec<ImportDirective>,
     unexpanded_macros: Vec<MacroDirective>,
+    unexpanded_attribute_macros: Vec<DeriveDirective>,
     mod_dirs: FxHashMap<LocalModuleId, ModDir>,
     cfg_options: &'a CfgOptions,
     /// List of procedural macros defined by this crate. This is read from the dynamic library
@@ -779,58 +782,60 @@ impl DefCollector<'_> {
 
     fn resolve_macros(&mut self) -> ReachedFixedPoint {
         let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new());
+        let mut attribute_macros =
+            std::mem::replace(&mut self.unexpanded_attribute_macros, Vec::new());
         let mut resolved = Vec::new();
         let mut res = ReachedFixedPoint::Yes;
         macros.retain(|directive| {
-            match &directive.kind {
-                MacroDirectiveKind::FnLike { ast_id, legacy } => {
-                    if let Some(call_id) = legacy {
-                        res = ReachedFixedPoint::No;
-                        resolved.push((directive.module_id, *call_id, directive.depth));
-                        return false;
-                    }
+            if let Some(call_id) = directive.legacy {
+                res = ReachedFixedPoint::No;
+                resolved.push((directive.module_id, call_id, directive.depth));
+                return false;
+            }
 
-                    match macro_call_as_call_id(
-                        ast_id,
+            match macro_call_as_call_id(
+                &directive.ast_id,
+                self.db,
+                self.def_map.krate,
+                |path| {
+                    let resolved_res = self.def_map.resolve_path_fp_with_macro(
                         self.db,
-                        self.def_map.krate,
-                        |path| {
-                            let resolved_res = self.def_map.resolve_path_fp_with_macro(
-                                self.db,
-                                ResolveMode::Other,
-                                directive.module_id,
-                                &path,
-                                BuiltinShadowMode::Module,
-                            );
-                            resolved_res.resolved_def.take_macros()
-                        },
-                        &mut |_err| (),
-                    ) {
-                        Ok(Ok(call_id)) => {
-                            resolved.push((directive.module_id, call_id, directive.depth));
-                            res = ReachedFixedPoint::No;
-                            return false;
-                        }
-                        Err(UnresolvedMacro) | Ok(Err(_)) => {}
-                    }
+                        ResolveMode::Other,
+                        directive.module_id,
+                        &path,
+                        BuiltinShadowMode::Module,
+                    );
+                    resolved_res.resolved_def.take_macros()
+                },
+                &mut |_err| (),
+            ) {
+                Ok(Ok(call_id)) => {
+                    resolved.push((directive.module_id, call_id, directive.depth));
+                    res = ReachedFixedPoint::No;
+                    return false;
                 }
-                MacroDirectiveKind::Derive { ast_id } => {
-                    match derive_macro_as_call_id(ast_id, self.db, self.def_map.krate, |path| {
-                        self.resolve_derive_macro(directive.module_id, &path)
-                    }) {
-                        Ok(call_id) => {
-                            resolved.push((directive.module_id, call_id, 0));
-                            res = ReachedFixedPoint::No;
-                            return false;
-                        }
-                        Err(UnresolvedMacro) => (),
-                    }
+                Err(UnresolvedMacro) | Ok(Err(_)) => {}
+            }
+
+            true
+        });
+        attribute_macros.retain(|directive| {
+            match derive_macro_as_call_id(&directive.ast_id, self.db, self.def_map.krate, |path| {
+                self.resolve_derive_macro(&directive, &path)
+            }) {
+                Ok(call_id) => {
+                    resolved.push((directive.module_id, call_id, 0));
+                    res = ReachedFixedPoint::No;
+                    return false;
                 }
+                Err(UnresolvedMacro) => (),
             }
 
             true
         });
+
         self.unexpanded_macros = macros;
+        self.unexpanded_attribute_macros = attribute_macros;
 
         for (module_id, macro_call_id, depth) in resolved {
             self.collect_macro_expansion(module_id, macro_call_id, depth);
@@ -839,11 +844,15 @@ impl DefCollector<'_> {
         res
     }
 
-    fn resolve_derive_macro(&self, module: LocalModuleId, path: &ModPath) -> Option<MacroDefId> {
+    fn resolve_derive_macro(
+        &self,
+        directive: &DeriveDirective,
+        path: &ModPath,
+    ) -> Option<MacroDefId> {
         let resolved_res = self.def_map.resolve_path_fp_with_macro(
             self.db,
             ResolveMode::Other,
-            module,
+            directive.module_id,
             &path,
             BuiltinShadowMode::Module,
         );
@@ -903,35 +912,33 @@ impl DefCollector<'_> {
         // Emit diagnostics for all remaining unexpanded macros.
 
         for directive in &self.unexpanded_macros {
-            match &directive.kind {
-                MacroDirectiveKind::FnLike { ast_id, .. } => match macro_call_as_call_id(
-                    ast_id,
-                    self.db,
-                    self.def_map.krate,
-                    |path| {
-                        let resolved_res = self.def_map.resolve_path_fp_with_macro(
-                            self.db,
-                            ResolveMode::Other,
-                            directive.module_id,
-                            &path,
-                            BuiltinShadowMode::Module,
-                        );
-                        resolved_res.resolved_def.take_macros()
-                    },
-                    &mut |_| (),
-                ) {
-                    Ok(_) => (),
-                    Err(UnresolvedMacro) => {
-                        self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
-                            directive.module_id,
-                            ast_id.ast_id,
-                        ));
-                    }
+            let mut error = None;
+            match macro_call_as_call_id(
+                &directive.ast_id,
+                self.db,
+                self.def_map.krate,
+                |path| {
+                    let resolved_res = self.def_map.resolve_path_fp_with_macro(
+                        self.db,
+                        ResolveMode::Other,
+                        directive.module_id,
+                        &path,
+                        BuiltinShadowMode::Module,
+                    );
+                    resolved_res.resolved_def.take_macros()
+                },
+                &mut |e| {
+                    error.get_or_insert(e);
                 },
-                MacroDirectiveKind::Derive { .. } => {
-                    // FIXME: we might want to diagnose this too
+            ) {
+                Ok(_) => (),
+                Err(UnresolvedMacro) => {
+                    self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
+                        directive.module_id,
+                        directive.ast_id.ast_id,
+                    ));
                 }
-            }
+            };
         }
 
         // Emit diagnostics for all remaining unresolved imports.
@@ -1373,11 +1380,9 @@ impl ModCollector<'_, '_> {
                 Some(derive_macros) => {
                     for path in derive_macros {
                         let ast_id = AstIdWithPath::new(self.file_id, ast_id, path);
-                        self.def_collector.unexpanded_macros.push(MacroDirective {
-                            module_id: self.module_id,
-                            depth: self.macro_depth + 1,
-                            kind: MacroDirectiveKind::Derive { ast_id },
-                        });
+                        self.def_collector
+                            .unexpanded_attribute_macros
+                            .push(DeriveDirective { module_id: self.module_id, ast_id });
                     }
                 }
                 None => {
@@ -1492,8 +1497,9 @@ impl ModCollector<'_, '_> {
 
         self.def_collector.unexpanded_macros.push(MacroDirective {
             module_id: self.module_id,
+            ast_id,
+            legacy: None,
             depth: self.macro_depth + 1,
-            kind: MacroDirectiveKind::FnLike { ast_id, legacy: None },
         });
     }
 
@@ -1536,6 +1542,7 @@ mod tests {
             unresolved_imports: Vec::new(),
             resolved_imports: Vec::new(),
             unexpanded_macros: Vec::new(),
+            unexpanded_attribute_macros: Vec::new(),
             mod_dirs: FxHashMap::default(),
             cfg_options: &CfgOptions::default(),
             proc_macros: Default::default(),
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index 5cd186565e..96021f677f 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -50,16 +50,16 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
     for (file_id, references) in refs.references {
         let file = sema.parse(file_id);
         let file = file.syntax();
-        for (relative_range, token) in references
-            .into_iter()
-            .filter_map(|(range, _)| Some(range).zip(file.token_at_offset(range.start()).next()))
-        {
+        for (r_range, _) in references {
+            let token = file.token_at_offset(r_range.start()).next()?;
             let token = sema.descend_into_macros(token);
             // This target is the containing function
             if let Some(nav) = token.ancestors().find_map(|node| {
-                let def = ast::Fn::cast(node).and_then(|fn_| sema.to_def(&fn_))?;
+                let fn_ = ast::Fn::cast(node)?;
+                let def = sema.to_def(&fn_)?;
                 def.try_to_nav(sema.db)
             }) {
+                let relative_range = r_range;
                 calls.add(&nav, relative_range);
             }
         }
@@ -87,6 +87,7 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
             let name_ref = call_node.name_ref()?;
             let func_target = match call_node {
                 FnCallNode::CallExpr(expr) => {
+                    //FIXME: Type::as_callable is broken
                     let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?;
                     match callable.kind() {
                         hir::CallableKind::Function(it) => it.try_to_nav(db),
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 0cee741acf..c7c1f4fee9 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -1,4 +1,4 @@
-//! Extracts, resolves and rewrites links and intra-doc links in markdown documentation.
+//! Resolves and rewrites links in markdown documentation.
 
 use std::{convert::TryFrom, iter::once, ops::Range};
 
@@ -15,10 +15,7 @@ use ide_db::{
     defs::{Definition, NameClass, NameRefClass},
     RootDatabase,
 };
-use syntax::{
-    ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxNode, SyntaxToken, TextRange, TextSize,
-    TokenAtOffset, T,
-};
+use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
 
 use crate::{FilePosition, Semantics};
 
@@ -63,6 +60,29 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Defi
     out
 }
 
+pub(crate) fn extract_definitions_from_markdown(
+    markdown: &str,
+) -> Vec<(String, Option<hir::Namespace>, Range<usize>)> {
+    let mut res = vec![];
+    let mut cb = |link: BrokenLink| {
+        // These allocations are actually unnecessary but the lifetimes on BrokenLinkCallback are wrong
+        // this is fixed in the repo but not on the crates.io release yet
+        Some((
+            /*url*/ link.reference.to_owned().into(),
+            /*title*/ link.reference.to_owned().into(),
+        ))
+    };
+    let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb));
+    for (event, range) in doc.into_offset_iter() {
+        if let Event::Start(Tag::Link(_, target, title)) = event {
+            let link = if target.is_empty() { title } else { target };
+            let (link, ns) = parse_link(&link);
+            res.push((link.to_string(), ns, range));
+        }
+    }
+    res
+}
+
 /// Remove all links in markdown documentation.
 pub(crate) fn remove_links(markdown: &str) -> String {
     let mut drop_link = false;
@@ -98,105 +118,6 @@ pub(crate) fn remove_links(markdown: &str) -> String {
     out
 }
 
-pub(crate) fn extract_definitions_from_markdown(
-    markdown: &str,
-) -> Vec<(Range<usize>, String, Option<hir::Namespace>)> {
-    let mut res = vec![];
-    let mut cb = |link: BrokenLink| {
-        // These allocations are actually unnecessary but the lifetimes on BrokenLinkCallback are wrong
-        // this is fixed in the repo but not on the crates.io release yet
-        Some((
-            /*url*/ link.reference.to_owned().into(),
-            /*title*/ link.reference.to_owned().into(),
-        ))
-    };
-    let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb));
-    for (event, range) in doc.into_offset_iter() {
-        if let Event::Start(Tag::Link(_, target, title)) = event {
-            let link = if target.is_empty() { title } else { target };
-            let (link, ns) = parse_intra_doc_link(&link);
-            res.push((range, link.to_string(), ns));
-        }
-    }
-    res
-}
-
-/// Extracts a link from a comment at the given position returning the spanning range, link and
-/// optionally it's namespace.
-pub(crate) fn extract_positioned_link_from_comment(
-    position: TextSize,
-    comment: &ast::Comment,
-) -> Option<(TextRange, String, Option<hir::Namespace>)> {
-    let doc_comment = comment.doc_comment()?;
-    let comment_start =
-        comment.syntax().text_range().start() + TextSize::from(comment.prefix().len() as u32);
-    let def_links = extract_definitions_from_markdown(doc_comment);
-    let (range, def_link, ns) =
-        def_links.into_iter().find_map(|(Range { start, end }, def_link, ns)| {
-            let range = TextRange::at(
-                comment_start + TextSize::from(start as u32),
-                TextSize::from((end - start) as u32),
-            );
-            range.contains(position).then(|| (range, def_link, ns))
-        })?;
-    Some((range, def_link, ns))
-}
-
-/// Turns a syntax node into it's [`Definition`] if it can hold docs.
-pub(crate) fn doc_owner_to_def(
-    sema: &Semantics<RootDatabase>,
-    item: &SyntaxNode,
-) -> Option<Definition> {
-    let res: hir::ModuleDef = match_ast! {
-        match item {
-            ast::SourceFile(_it) => sema.scope(item).module()?.into(),
-            ast::Fn(it) => sema.to_def(&it)?.into(),
-            ast::Struct(it) => sema.to_def(&it)?.into(),
-            ast::Enum(it) => sema.to_def(&it)?.into(),
-            ast::Union(it) => sema.to_def(&it)?.into(),
-            ast::Trait(it) => sema.to_def(&it)?.into(),
-            ast::Const(it) => sema.to_def(&it)?.into(),
-            ast::Static(it) => sema.to_def(&it)?.into(),
-            ast::TypeAlias(it) => sema.to_def(&it)?.into(),
-            ast::Variant(it) => sema.to_def(&it)?.into(),
-            ast::Trait(it) => sema.to_def(&it)?.into(),
-            ast::Impl(it) => return sema.to_def(&it).map(Definition::SelfType),
-            ast::MacroRules(it) => return sema.to_def(&it).map(Definition::Macro),
-            ast::TupleField(it) => return sema.to_def(&it).map(Definition::Field),
-            ast::RecordField(it) => return sema.to_def(&it).map(Definition::Field),
-            _ => return None,
-        }
-    };
-    Some(Definition::ModuleDef(res))
-}
-
-pub(crate) fn resolve_doc_path_for_def(
-    db: &dyn HirDatabase,
-    def: Definition,
-    link: &str,
-    ns: Option<hir::Namespace>,
-) -> Option<hir::ModuleDef> {
-    match def {
-        Definition::ModuleDef(def) => match def {
-            ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns),
-            ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns),
-            ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns),
-            ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns),
-            ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns),
-            ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns),
-            ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns),
-            ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns),
-            ModuleDef::BuiltinType(_) => None,
-        },
-        Definition::Macro(it) => it.resolve_doc_path(db, &link, ns),
-        Definition::Field(it) => it.resolve_doc_path(db, &link, ns),
-        Definition::SelfType(_)
-        | Definition::Local(_)
-        | Definition::GenericParam(_)
-        | Definition::Label(_) => None,
-    }
-}
-
 // FIXME:
 // BUG: For Option::Some
 // Returns https://doc.rust-lang.org/nightly/core/prelude/v1/enum.Option.html#variant.Some
@@ -276,8 +197,26 @@ fn rewrite_intra_doc_link(
     title: &str,
 ) -> Option<(String, String)> {
     let link = if target.is_empty() { title } else { target };
-    let (link, ns) = parse_intra_doc_link(link);
-    let resolved = resolve_doc_path_for_def(db, def, link, ns)?;
+    let (link, ns) = parse_link(link);
+    let resolved = match def {
+        Definition::ModuleDef(def) => match def {
+            ModuleDef::Module(it) => it.resolve_doc_path(db, link, ns),
+            ModuleDef::Function(it) => it.resolve_doc_path(db, link, ns),
+            ModuleDef::Adt(it) => it.resolve_doc_path(db, link, ns),
+            ModuleDef::Variant(it) => it.resolve_doc_path(db, link, ns),
+            ModuleDef::Const(it) => it.resolve_doc_path(db, link, ns),
+            ModuleDef::Static(it) => it.resolve_doc_path(db, link, ns),
+            ModuleDef::Trait(it) => it.resolve_doc_path(db, link, ns),
+            ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
+            ModuleDef::BuiltinType(_) => return None,
+        },
+        Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
+        Definition::Field(it) => it.resolve_doc_path(db, link, ns),
+        Definition::SelfType(_)
+        | Definition::Local(_)
+        | Definition::GenericParam(_)
+        | Definition::Label(_) => return None,
+    }?;
     let krate = resolved.module(db)?.krate();
     let canonical_path = resolved.canonical_path(db)?;
     let mut new_url = get_doc_url(db, &krate)?
@@ -289,23 +228,24 @@ fn rewrite_intra_doc_link(
         .ok()?;
 
     if let ModuleDef::Trait(t) = resolved {
-        if let Some(assoc_item) = t.items(db).into_iter().find_map(|assoc_item| {
+        let items = t.items(db);
+        if let Some(field_or_assoc_item) = items.iter().find_map(|assoc_item| {
             if let Some(name) = assoc_item.name(db) {
                 if *link == format!("{}::{}", canonical_path, name) {
-                    return Some(assoc_item);
+                    return Some(FieldOrAssocItem::AssocItem(*assoc_item));
                 }
             }
             None
         }) {
-            if let Some(fragment) =
-                get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(assoc_item))
-            {
+            if let Some(fragment) = get_symbol_fragment(db, &field_or_assoc_item) {
                 new_url = new_url.join(&fragment).ok()?;
             }
         };
     }
 
-    Some((new_url.into_string(), strip_prefixes_suffixes(title).to_string()))
+    let new_target = new_url.into_string();
+    let new_title = strip_prefixes_suffixes(title);
+    Some((new_target, new_title.to_string()))
 }
 
 /// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`).
@@ -382,61 +322,73 @@ fn map_links<'e>(
     })
 }
 
-const TYPES: ([&str; 9], [&str; 0]) =
-    (["type", "struct", "enum", "mod", "trait", "union", "module", "prim", "primitive"], []);
-const VALUES: ([&str; 8], [&str; 1]) =
+fn parse_link(s: &str) -> (&str, Option<hir::Namespace>) {
+    let path = strip_prefixes_suffixes(s);
+    let ns = ns_from_intra_spec(s);
+    (path, ns)
+}
+
+/// Strip prefixes, suffixes, and inline code marks from the given string.
+fn strip_prefixes_suffixes(mut s: &str) -> &str {
+    s = s.trim_matches('`');
+
+    [
+        (TYPES.0.iter(), TYPES.1.iter()),
+        (VALUES.0.iter(), VALUES.1.iter()),
+        (MACROS.0.iter(), MACROS.1.iter()),
+    ]
+    .iter()
+    .for_each(|(prefixes, suffixes)| {
+        prefixes.clone().for_each(|prefix| s = s.trim_start_matches(*prefix));
+        suffixes.clone().for_each(|suffix| s = s.trim_end_matches(*suffix));
+    });
+    s.trim_start_matches('@').trim()
+}
+
+static TYPES: ([&str; 7], [&str; 0]) =
+    (["type", "struct", "enum", "mod", "trait", "union", "module"], []);
+static VALUES: ([&str; 8], [&str; 1]) =
     (["value", "function", "fn", "method", "const", "static", "mod", "module"], ["()"]);
-const MACROS: ([&str; 2], [&str; 1]) = (["macro", "derive"], ["!"]);
+static MACROS: ([&str; 1], [&str; 1]) = (["macro"], ["!"]);
 
 /// Extract the specified namespace from an intra-doc-link if one exists.
 ///
 /// # Examples
 ///
-/// * `struct MyStruct` -> ("MyStruct", `Namespace::Types`)
-/// * `panic!` -> ("panic", `Namespace::Macros`)
-/// * `fn@from_intra_spec` -> ("from_intra_spec", `Namespace::Values`)
-fn parse_intra_doc_link(s: &str) -> (&str, Option<hir::Namespace>) {
-    let s = s.trim_matches('`');
-
+/// * `struct MyStruct` -> `Namespace::Types`
+/// * `panic!` -> `Namespace::Macros`
+/// * `fn@from_intra_spec` -> `Namespace::Values`
+fn ns_from_intra_spec(s: &str) -> Option<hir::Namespace> {
     [
         (hir::Namespace::Types, (TYPES.0.iter(), TYPES.1.iter())),
         (hir::Namespace::Values, (VALUES.0.iter(), VALUES.1.iter())),
         (hir::Namespace::Macros, (MACROS.0.iter(), MACROS.1.iter())),
     ]
     .iter()
-    .cloned()
-    .find_map(|(ns, (mut prefixes, mut suffixes))| {
-        if let Some(prefix) = prefixes.find(|&&prefix| {
-            s.starts_with(prefix)
-                && s.chars().nth(prefix.len()).map_or(false, |c| c == '@' || c == ' ')
-        }) {
-            Some((&s[prefix.len() + 1..], ns))
-        } else {
-            suffixes.find_map(|&suffix| s.strip_suffix(suffix).zip(Some(ns)))
-        }
-    })
-    .map_or((s, None), |(s, ns)| (s, Some(ns)))
-}
-
-fn strip_prefixes_suffixes(s: &str) -> &str {
-    [
-        (TYPES.0.iter(), TYPES.1.iter()),
-        (VALUES.0.iter(), VALUES.1.iter()),
-        (MACROS.0.iter(), MACROS.1.iter()),
-    ]
-    .iter()
-    .cloned()
-    .find_map(|(mut prefixes, mut suffixes)| {
-        if let Some(prefix) = prefixes.find(|&&prefix| {
-            s.starts_with(prefix)
-                && s.chars().nth(prefix.len()).map_or(false, |c| c == '@' || c == ' ')
-        }) {
-            Some(&s[prefix.len() + 1..])
-        } else {
-            suffixes.find_map(|&suffix| s.strip_suffix(suffix))
-        }
+    .filter(|(_ns, (prefixes, suffixes))| {
+        prefixes
+            .clone()
+            .map(|prefix| {
+                s.starts_with(*prefix)
+                    && s.chars()
+                        .nth(prefix.len() + 1)
+                        .map(|c| c == '@' || c == ' ')
+                        .unwrap_or(false)
+            })
+            .any(|cond| cond)
+            || suffixes
+                .clone()
+                .map(|suffix| {
+                    s.starts_with(*suffix)
+                        && s.chars()
+                            .nth(suffix.len() + 1)
+                            .map(|c| c == '@' || c == ' ')
+                            .unwrap_or(false)
+                })
+                .any(|cond| cond)
     })
-    .unwrap_or(s)
+    .map(|(ns, (_, _))| *ns)
+    .next()
 }
 
 /// Get the root URL for the documentation of a crate.
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index a2c97061f2..473d48c2fc 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,14 +1,18 @@
+use std::ops::Range;
+
 use either::Either;
-use hir::Semantics;
+use hir::{HasAttrs, ModuleDef, Semantics};
 use ide_db::{
-    defs::{NameClass, NameRefClass},
+    defs::{Definition, NameClass, NameRefClass},
     RootDatabase,
 };
-use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
+use syntax::{
+    ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, TextSize,
+    TokenAtOffset, T,
+};
 
 use crate::{
-    display::TryToNav,
-    doc_links::{doc_owner_to_def, extract_positioned_link_from_comment, resolve_doc_path_for_def},
+    display::TryToNav, doc_links::extract_definitions_from_markdown, runnables::doc_owner_to_def,
     FilePosition, NavigationTarget, RangeInfo,
 };
 
@@ -31,9 +35,7 @@ pub(crate) fn goto_definition(
     let token = sema.descend_into_macros(original_token.clone());
     let parent = token.parent()?;
     if let Some(comment) = ast::Comment::cast(token) {
-        let (_, link, ns) = extract_positioned_link_from_comment(position.offset, &comment)?;
-        let def = doc_owner_to_def(&sema, &parent)?;
-        let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?;
+        let nav = def_for_doc_comment(&sema, position, &comment)?.try_to_nav(db)?;
         return Some(RangeInfo::new(original_token.text_range(), vec![nav]));
     }
 
@@ -59,6 +61,54 @@ pub(crate) fn goto_definition(
     Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect()))
 }
 
+fn def_for_doc_comment(
+    sema: &Semantics<RootDatabase>,
+    position: FilePosition,
+    doc_comment: &ast::Comment,
+) -> Option<hir::ModuleDef> {
+    let parent = doc_comment.syntax().parent()?;
+    let (link, ns) = extract_positioned_link_from_comment(position, doc_comment)?;
+
+    let def = doc_owner_to_def(sema, parent)?;
+    match def {
+        Definition::ModuleDef(def) => match def {
+            ModuleDef::Module(it) => it.resolve_doc_path(sema.db, &link, ns),
+            ModuleDef::Function(it) => it.resolve_doc_path(sema.db, &link, ns),
+            ModuleDef::Adt(it) => it.resolve_doc_path(sema.db, &link, ns),
+            ModuleDef::Variant(it) => it.resolve_doc_path(sema.db, &link, ns),
+            ModuleDef::Const(it) => it.resolve_doc_path(sema.db, &link, ns),
+            ModuleDef::Static(it) => it.resolve_doc_path(sema.db, &link, ns),
+            ModuleDef::Trait(it) => it.resolve_doc_path(sema.db, &link, ns),
+            ModuleDef::TypeAlias(it) => it.resolve_doc_path(sema.db, &link, ns),
+            ModuleDef::BuiltinType(_) => return None,
+        },
+        Definition::Macro(it) => it.resolve_doc_path(sema.db, &link, ns),
+        Definition::Field(it) => it.resolve_doc_path(sema.db, &link, ns),
+        Definition::SelfType(_)
+        | Definition::Local(_)
+        | Definition::GenericParam(_)
+        | Definition::Label(_) => return None,
+    }
+}
+
+fn extract_positioned_link_from_comment(
+    position: FilePosition,
+    comment: &ast::Comment,
+) -> Option<(String, Option<hir::Namespace>)> {
+    let doc_comment = comment.doc_comment()?;
+    let comment_start =
+        comment.syntax().text_range().start() + TextSize::from(comment.prefix().len() as u32);
+    let def_links = extract_definitions_from_markdown(doc_comment);
+    let (def_link, ns, _) = def_links.into_iter().find(|&(_, _, Range { start, end })| {
+        TextRange::at(
+            comment_start + TextSize::from(start as u32),
+            TextSize::from((end - start) as u32),
+        )
+        .contains(position.offset)
+    })?;
+    Some((def_link, ns))
+}
+
 fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
     return tokens.max_by_key(priority);
     fn priority(n: &SyntaxToken) -> usize {
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index c430894765..a3fb17c0a6 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -11,14 +11,11 @@ use ide_db::{
 };
 use itertools::Itertools;
 use stdx::format_to;
-use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
+use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
 
 use crate::{
     display::{macro_label, TryToNav},
-    doc_links::{
-        doc_owner_to_def, extract_positioned_link_from_comment, remove_links,
-        resolve_doc_path_for_def, rewrite_links,
-    },
+    doc_links::{remove_links, rewrite_links},
     markdown_remove::remove_markdown,
     markup::Markup,
     runnables::{runnable_fn, runnable_mod},
@@ -96,35 +93,20 @@ pub(crate) fn hover(
     let mut res = HoverResult::default();
 
     let node = token.parent()?;
-    let mut range = None;
     let definition = match_ast! {
         match node {
             // we don't use NameClass::referenced_or_defined here as we do not want to resolve
             // field pattern shorthands to their definition
             ast::Name(name) => NameClass::classify(&sema, &name).and_then(|class| match class {
                 NameClass::ConstReference(def) => Some(def),
-                def => def.defined(db),
+                def => def.defined(sema.db),
             }),
-            ast::NameRef(name_ref) => {
-                NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(db))
-            },
-            ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime).map_or_else(
-                || NameRefClass::classify_lifetime(&sema, &lifetime).map(|d| d.referenced(db)),
-                |d| d.defined(db),
-            ),
-
-            _ => ast::Comment::cast(token.clone())
-                .and_then(|comment| {
-                    let (idl_range, link, ns) =
-                        extract_positioned_link_from_comment(position.offset, &comment)?;
-                    range = Some(idl_range);
-                    let def = doc_owner_to_def(&sema, &node)?;
-                    resolve_doc_path_for_def(db, def, &link, ns)
-                })
-                .map(Definition::ModuleDef),
+            ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)),
+            ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime)
+                .map_or_else(|| NameRefClass::classify_lifetime(&sema, &lifetime).map(|d| d.referenced(sema.db)), |d| d.defined(sema.db)),
+            _ => None,
         }
     };
-
     if let Some(definition) = definition {
         let famous_defs = match &definition {
             Definition::ModuleDef(ModuleDef::BuiltinType(_)) => {
@@ -146,16 +128,15 @@ pub(crate) fn hover(
                 res.actions.push(action);
             }
 
-            let range = range.unwrap_or_else(|| sema.original_range(&node).range);
+            let range = sema.original_range(&node).range;
             return Some(RangeInfo::new(range, res));
         }
     }
 
     if token.kind() == syntax::SyntaxKind::COMMENT {
-        cov_mark::hit!(no_highlight_on_comment_hover);
+        // don't highlight the entire parent node on comment hover
         return None;
     }
-
     if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) {
         return res;
     }
@@ -3502,7 +3483,6 @@ fn foo$0() {}
 
     #[test]
     fn hover_comments_dont_highlight_parent() {
-        cov_mark::check!(no_highlight_on_comment_hover);
         check_hover_no_result(
             r#"
 fn no_hover() {
@@ -3775,29 +3755,4 @@ fn main() {
             "#]],
         )
     }
-
-    #[test]
-    fn hover_intra_doc_links() {
-        check(
-            r#"
-/// This is the [`foo`](foo$0) function.
-fn foo() {}
-"#,
-            expect![[r#"
-                *[`foo`](foo)*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                fn foo()
-                ```
-
-                ---
-
-                This is the [`foo`](https://docs.rs/test/*/test/fn.foo.html) function.
-            "#]],
-        );
-    }
 }
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 26d6dc9c9f..5340b638a0 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -1,11 +1,8 @@
-//! Renaming functionality
-//!
-//! All reference and file rename requests go through here where the corresponding [`SourceChange`]s
-//! will be calculated.
+//! FIXME: write short doc here
 use std::fmt::{self, Display};
 
 use either::Either;
-use hir::{AsAssocItem, InFile, Module, ModuleDef, ModuleSource, Semantics};
+use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics};
 use ide_db::{
     base_db::{AnchoredPathBuf, FileId},
     defs::{Definition, NameClass, NameRefClass},
@@ -199,7 +196,7 @@ fn rename_mod(
                 file_id,
                 TextEdit::replace(name.syntax().text_range(), new_name.to_string()),
             ),
-            _ => never!("Module source node is missing a name"),
+            _ => unreachable!(),
         }
     }
     let def = Definition::ModuleDef(ModuleDef::Module(module));
@@ -219,44 +216,40 @@ fn rename_reference(
 ) -> RenameResult<SourceChange> {
     let ident_kind = check_identifier(new_name)?;
 
-    if matches!(
-        def, // is target a lifetime?
+    let def_is_lbl_or_lt = matches!(
+        def,
         Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_)
-    ) {
-        match ident_kind {
-            IdentifierKind::Ident | IdentifierKind::ToSelf | IdentifierKind::Underscore => {
-                cov_mark::hit!(rename_not_a_lifetime_ident_ref);
-                bail!("Invalid name `{}`: not a lifetime identifier", new_name);
-            }
-            IdentifierKind::Lifetime => cov_mark::hit!(rename_lifetime),
+    );
+    match (ident_kind, def) {
+        (IdentifierKind::ToSelf, _)
+        | (IdentifierKind::Underscore, _)
+        | (IdentifierKind::Ident, _)
+            if def_is_lbl_or_lt =>
+        {
+            cov_mark::hit!(rename_not_a_lifetime_ident_ref);
+            bail!("Invalid name `{}`: not a lifetime identifier", new_name)
         }
-    } else {
-        match (ident_kind, def) {
-            (IdentifierKind::Lifetime, _) => {
-                cov_mark::hit!(rename_not_an_ident_ref);
-                bail!("Invalid name `{}`: not an identifier", new_name);
-            }
-            (IdentifierKind::ToSelf, Definition::Local(local)) => {
-                if local.is_self(sema.db) {
-                    // no-op
-                    cov_mark::hit!(rename_self_to_self);
-                    return Ok(SourceChange::default());
-                } else {
-                    cov_mark::hit!(rename_to_self);
-                    return rename_to_self(sema, local);
-                }
-            }
-            (ident_kind, Definition::Local(local)) => {
-                if let Some(self_param) = local.as_self_param(sema.db) {
-                    cov_mark::hit!(rename_self_to_param);
-                    return rename_self_to_param(sema, local, self_param, new_name, ident_kind);
-                } else {
-                    cov_mark::hit!(rename_local);
-                }
-            }
-            (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name),
-            (IdentifierKind::Ident, _) => cov_mark::hit!(rename_non_local),
-            (IdentifierKind::Underscore, _) => (),
+        (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => cov_mark::hit!(rename_lifetime),
+        (IdentifierKind::Lifetime, _) => {
+            cov_mark::hit!(rename_not_an_ident_ref);
+            bail!("Invalid name `{}`: not an identifier", new_name)
+        }
+        (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => {
+            // no-op
+            cov_mark::hit!(rename_self_to_self);
+            return Ok(SourceChange::default());
+        }
+        (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => {
+            cov_mark::hit!(rename_self_to_param);
+            return rename_self_to_param(sema, local, new_name, ident_kind);
+        }
+        (IdentifierKind::ToSelf, Definition::Local(local)) => {
+            cov_mark::hit!(rename_to_self);
+            return rename_to_self(sema, local);
+        }
+        (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name),
+        (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => {
+            cov_mark::hit!(rename_ident)
         }
     }
 
@@ -282,32 +275,46 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
 
     let fn_def = match local.parent(sema.db) {
         hir::DefWithBody::Function(func) => func,
-        _ => bail!("Cannot rename local to self outside of function"),
+        _ => bail!("Cannot rename non-param local to self"),
     };
 
-    if let Some(_) = fn_def.self_param(sema.db) {
+    // FIXME: reimplement this on the hir instead
+    // as of the time of this writing params in hir don't keep their names
+    let fn_ast = fn_def
+        .source(sema.db)
+        .ok_or_else(|| format_err!("Cannot rename non-param local to self"))?
+        .value;
+
+    let first_param_range = fn_ast
+        .param_list()
+        .and_then(|p| p.params().next())
+        .ok_or_else(|| format_err!("Method has no parameters"))?
+        .syntax()
+        .text_range();
+    let InFile { file_id, value: local_source } = local.source(sema.db);
+    match local_source {
+        either::Either::Left(pat)
+            if !first_param_range.contains_range(pat.syntax().text_range()) =>
+        {
+            bail!("Only the first parameter can be self");
+        }
+        _ => (),
+    }
+
+    let impl_block = fn_ast
+        .syntax()
+        .ancestors()
+        .find_map(|node| ast::Impl::cast(node))
+        .and_then(|def| sema.to_def(&def))
+        .ok_or_else(|| format_err!("No impl block found for function"))?;
+    if fn_def.self_param(sema.db).is_some() {
         bail!("Method already has a self parameter");
     }
 
     let params = fn_def.assoc_fn_params(sema.db);
-    let first_param = params
-        .first()
-        .ok_or_else(|| format_err!("Cannot rename local to self unless it is a parameter"))?;
-    if first_param.as_local(sema.db) != local {
-        bail!("Only the first parameter may be renamed to self");
-    }
-
-    let assoc_item = fn_def
-        .as_assoc_item(sema.db)
-        .ok_or_else(|| format_err!("Cannot rename parameter to self for free function"))?;
-    let impl_ = match assoc_item.container(sema.db) {
-        hir::AssocItemContainer::Trait(_) => {
-            bail!("Cannot rename parameter to self for trait functions");
-        }
-        hir::AssocItemContainer::Impl(impl_) => impl_,
-    };
+    let first_param = params.first().ok_or_else(|| format_err!("Method has no parameters"))?;
     let first_param_ty = first_param.ty();
-    let impl_ty = impl_.target_ty(sema.db);
+    let impl_ty = impl_block.target_ty(sema.db);
     let (ty, self_param) = if impl_ty.remove_ref().is_some() {
         // if the impl is a ref to the type we can just match the `&T` with self directly
         (first_param_ty.clone(), "self")
@@ -321,9 +328,6 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
         bail!("Parameter type differs from impl block type");
     }
 
-    let InFile { file_id, value: param_source } =
-        first_param.source(sema.db).ok_or_else(|| format_err!("No source for parameter found"))?;
-
     let def = Definition::Local(local);
     let usages = def.usages(sema).all();
     let mut source_change = SourceChange::default();
@@ -332,20 +336,25 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
     }));
     source_change.insert_source_edit(
         file_id.original_file(sema.db),
-        TextEdit::replace(param_source.syntax().text_range(), String::from(self_param)),
+        TextEdit::replace(first_param_range, String::from(self_param)),
     );
+
     Ok(source_change)
 }
 
 fn rename_self_to_param(
     sema: &Semantics<RootDatabase>,
     local: hir::Local,
-    self_param: hir::SelfParam,
     new_name: &str,
     identifier_kind: IdentifierKind,
 ) -> RenameResult<SourceChange> {
-    let InFile { file_id, value: self_param } =
-        self_param.source(sema.db).ok_or_else(|| format_err!("cannot find function source"))?;
+    let (file_id, self_param) = match local.source(sema.db) {
+        InFile { file_id, value: Either::Right(self_param) } => (file_id, self_param),
+        _ => {
+            never!(true, "rename_self_to_param invoked on a non-self local");
+            bail!("rename_self_to_param invoked on a non-self local");
+        }
+    };
 
     let def = Definition::Local(local);
     let usages = def.usages(sema).all();
@@ -701,7 +710,7 @@ foo!(Foo$0);",
 
     #[test]
     fn test_rename_for_local() {
-        cov_mark::check!(rename_local);
+        cov_mark::check!(rename_ident);
         check(
             "k",
             r#"
@@ -1242,7 +1251,6 @@ pub mod foo$0;
 
     #[test]
     fn test_enum_variant_from_module_1() {
-        cov_mark::check!(rename_non_local);
         check(
             "Baz",
             r#"
@@ -1353,7 +1361,7 @@ fn f(foo$0: &mut Foo) -> i32 {
     foo.i
 }
 "#,
-            "error: Cannot rename parameter to self for free function",
+            "error: No impl block found for function",
         );
         check(
             "self",
@@ -1383,7 +1391,7 @@ impl Foo {
     }
 }
 "#,
-            "error: Only the first parameter may be renamed to self",
+            "error: Only the first parameter can be self",
         );
     }
 
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 5b488e2c58..bea020b061 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -7,13 +7,17 @@ use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
 use ide_assists::utils::test_related_attribute;
 use ide_db::{
     base_db::{FilePosition, FileRange},
+    defs::Definition,
     helpers::visit_file_defs,
     search::SearchScope,
     RootDatabase, SymbolKind,
 };
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
-use syntax::ast::{self, AstNode, AttrsOwner};
+use syntax::{
+    ast::{self, AstNode, AttrsOwner},
+    match_ast, SyntaxNode,
+};
 
 use crate::{
     display::{ToNav, TryToNav},
@@ -267,6 +271,28 @@ pub(crate) fn runnable_mod(sema: &Semantics<RootDatabase>, def: hir::Module) ->
     Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg })
 }
 
+// FIXME: figure out a proper API here.
+pub(crate) fn doc_owner_to_def(
+    sema: &Semantics<RootDatabase>,
+    item: SyntaxNode,
+) -> Option<Definition> {
+    let res: hir::ModuleDef = match_ast! {
+        match item {
+            ast::SourceFile(_it) => sema.scope(&item).module()?.into(),
+            ast::Fn(it) => sema.to_def(&it)?.into(),
+            ast::Struct(it) => sema.to_def(&it)?.into(),
+            ast::Enum(it) => sema.to_def(&it)?.into(),
+            ast::Union(it) => sema.to_def(&it)?.into(),
+            ast::Trait(it) => sema.to_def(&it)?.into(),
+            ast::Const(it) => sema.to_def(&it)?.into(),
+            ast::Static(it) => sema.to_def(&it)?.into(),
+            ast::TypeAlias(it) => sema.to_def(&it)?.into(),
+            _ => return None,
+        }
+    };
+    Some(Definition::ModuleDef(res))
+}
+
 fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Option<Runnable> {
     let attrs = match def {
         hir::ModuleDef::Module(it) => it.attrs(sema.db),
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 38bf493489..8e09401840 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -190,10 +190,10 @@ pub(super) fn doc_comment(
                     intra_doc_links.extend(
                         extract_definitions_from_markdown(line)
                             .into_iter()
-                            .filter_map(|(range, link, ns)| {
-                                Some(range).zip(validate_intra_doc_link(sema.db, &def, &link, ns))
+                            .filter_map(|(link, ns, range)| {
+                                validate_intra_doc_link(sema.db, &def, &link, ns).zip(Some(range))
                             })
-                            .map(|(Range { start, end }, def)| {
+                            .map(|(def, Range { start, end })| {
                                 (
                                     def,
                                     TextRange::at(
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs
index 1482d37f84..bba6c08e02 100644
--- a/crates/ide_assists/src/assist_context.rs
+++ b/crates/ide_assists/src/assist_context.rs
@@ -14,8 +14,8 @@ use ide_db::{
 };
 use syntax::{
     algo::{self, find_node_at_offset, SyntaxRewriter},
-    AstNode, AstToken, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxNodePtr,
-    SyntaxToken, TextRange, TextSize, TokenAtOffset,
+    AstNode, AstToken, SourceFile, SyntaxElement, SyntaxKind, SyntaxToken, TextRange, TextSize,
+    TokenAtOffset,
 };
 use text_edit::{TextEdit, TextEditBuilder};
 
@@ -180,19 +180,11 @@ pub(crate) struct AssistBuilder {
     edit: TextEditBuilder,
     file_id: FileId,
     source_change: SourceChange,
-
-    /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin.
-    mutated_tree: Option<(SyntaxNode, SyntaxNode)>,
 }
 
 impl AssistBuilder {
     pub(crate) fn new(file_id: FileId) -> AssistBuilder {
-        AssistBuilder {
-            edit: TextEdit::builder(),
-            file_id,
-            source_change: SourceChange::default(),
-            mutated_tree: None,
-        }
+        AssistBuilder { edit: TextEdit::builder(), file_id, source_change: SourceChange::default() }
     }
 
     pub(crate) fn edit_file(&mut self, file_id: FileId) {
@@ -201,42 +193,12 @@ impl AssistBuilder {
     }
 
     fn commit(&mut self) {
-        if let Some((old, new)) = self.mutated_tree.take() {
-            algo::diff(&old, &new).into_text_edit(&mut self.edit)
-        }
-
         let edit = mem::take(&mut self.edit).finish();
         if !edit.is_empty() {
             self.source_change.insert_source_edit(self.file_id, edit);
         }
     }
 
-    pub(crate) fn make_ast_mut<N: AstNode>(&mut self, node: N) -> N {
-        N::cast(self.make_mut(node.syntax().clone())).unwrap()
-    }
-    /// Returns a copy of the `node`, suitable for mutation.
-    ///
-    /// Syntax trees in rust-analyzer are typically immutable, and mutating
-    /// operations panic at runtime. However, it is possible to make a copy of
-    /// the tree and mutate the copy freely. Mutation is based on interior
-    /// mutability, and different nodes in the same tree see the same mutations.
-    ///
-    /// The typical pattern for an assist is to find specific nodes in the read
-    /// phase, and then get their mutable couterparts using `make_mut` in the
-    /// mutable state.
-    pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode {
-        let root = &self
-            .mutated_tree
-            .get_or_insert_with(|| {
-                let immutable = node.ancestors().last().unwrap();
-                let mutable = immutable.clone_for_update();
-                (immutable, mutable)
-            })
-            .1;
-        let ptr = SyntaxNodePtr::new(&&node);
-        ptr.to_node(root)
-    }
-
     /// Remove specified `range` of text.
     pub(crate) fn delete(&mut self, range: TextRange) {
         self.edit.delete(range)
diff --git a/crates/ide_assists/src/handlers/add_explicit_type.rs b/crates/ide_assists/src/handlers/add_explicit_type.rs
index 62db319524..cb1548cef7 100644
--- a/crates/ide_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ide_assists/src/handlers/add_explicit_type.rs
@@ -34,33 +34,26 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
     // The binding must have a name
     let name = pat.name()?;
     let name_range = name.syntax().text_range();
-
+    let stmt_range = let_stmt.syntax().text_range();
+    let eq_range = let_stmt.eq_token()?.text_range();
     // Assist should only be applicable if cursor is between 'let' and '='
-    let cursor_in_range = {
-        let stmt_range = let_stmt.syntax().text_range();
-        let eq_range = let_stmt.eq_token()?.text_range();
-        let let_range = TextRange::new(stmt_range.start(), eq_range.start());
-        let_range.contains_range(ctx.frange.range)
-    };
+    let let_range = TextRange::new(stmt_range.start(), eq_range.start());
+    let cursor_in_range = let_range.contains_range(ctx.frange.range);
     if !cursor_in_range {
-        cov_mark::hit!(add_explicit_type_not_applicable_if_cursor_after_equals);
         return None;
     }
-
     // Assist not applicable if the type has already been specified
     // and it has no placeholders
     let ascribed_ty = let_stmt.ty();
     if let Some(ty) = &ascribed_ty {
         if ty.syntax().descendants().find_map(ast::InferType::cast).is_none() {
-            cov_mark::hit!(add_explicit_type_not_applicable_if_ty_already_specified);
             return None;
         }
     }
-
     // Infer type
     let ty = ctx.sema.type_of_expr(&expr)?;
+
     if ty.contains_unknown() || ty.is_closure() {
-        cov_mark::hit!(add_explicit_type_not_applicable_if_ty_not_inferred);
         return None;
     }
 
@@ -88,25 +81,17 @@ mod tests {
 
     #[test]
     fn add_explicit_type_target() {
-        check_assist_target(add_explicit_type, r#"fn f() { let a$0 = 1; }"#, "a");
+        check_assist_target(add_explicit_type, "fn f() { let a$0 = 1; }", "a");
     }
 
     #[test]
     fn add_explicit_type_works_for_simple_expr() {
-        check_assist(
-            add_explicit_type,
-            r#"fn f() { let a$0 = 1; }"#,
-            r#"fn f() { let a: i32 = 1; }"#,
-        );
+        check_assist(add_explicit_type, "fn f() { let a$0 = 1; }", "fn f() { let a: i32 = 1; }");
     }
 
     #[test]
     fn add_explicit_type_works_for_underscore() {
-        check_assist(
-            add_explicit_type,
-            r#"fn f() { let a$0: _ = 1; }"#,
-            r#"fn f() { let a: i32 = 1; }"#,
-        );
+        check_assist(add_explicit_type, "fn f() { let a$0: _ = 1; }", "fn f() { let a: i32 = 1; }");
     }
 
     #[test]
@@ -114,19 +99,23 @@ mod tests {
         check_assist(
             add_explicit_type,
             r#"
-enum Option<T> { Some(T), None }
+            enum Option<T> {
+                Some(T),
+                None
+            }
 
-fn f() {
-    let a$0: Option<_> = Option::Some(1);
-}
-"#,
+            fn f() {
+                let a$0: Option<_> = Option::Some(1);
+            }"#,
             r#"
-enum Option<T> { Some(T), None }
+            enum Option<T> {
+                Some(T),
+                None
+            }
 
-fn f() {
-    let a: Option<i32> = Option::Some(1);
-}
-"#,
+            fn f() {
+                let a: Option<i32> = Option::Some(1);
+            }"#,
         );
     }
 
@@ -150,30 +139,24 @@ fn f() {
 
     #[test]
     fn add_explicit_type_not_applicable_if_ty_not_inferred() {
-        cov_mark::check!(add_explicit_type_not_applicable_if_ty_not_inferred);
-        check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0 = None; }"#);
+        check_assist_not_applicable(add_explicit_type, "fn f() { let a$0 = None; }");
     }
 
     #[test]
     fn add_explicit_type_not_applicable_if_ty_already_specified() {
-        cov_mark::check!(add_explicit_type_not_applicable_if_ty_already_specified);
-        check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0: i32 = 1; }"#);
+        check_assist_not_applicable(add_explicit_type, "fn f() { let a$0: i32 = 1; }");
     }
 
     #[test]
     fn add_explicit_type_not_applicable_if_specified_ty_is_tuple() {
-        check_assist_not_applicable(
-            add_explicit_type,
-            r#"fn f() { let a$0: (i32, i32) = (3, 4); }"#,
-        );
+        check_assist_not_applicable(add_explicit_type, "fn f() { let a$0: (i32, i32) = (3, 4); }");
     }
 
     #[test]
     fn add_explicit_type_not_applicable_if_cursor_after_equals() {
-        cov_mark::check!(add_explicit_type_not_applicable_if_cursor_after_equals);
         check_assist_not_applicable(
             add_explicit_type,
-            r#"fn f() {let a =$0 match 1 {2 => 3, 3 => 5};}"#,
+            "fn f() {let a =$0 match 1 {2 => 3, 3 => 5};}",
         )
     }
 
@@ -181,7 +164,7 @@ fn f() {
     fn add_explicit_type_not_applicable_if_cursor_before_let() {
         check_assist_not_applicable(
             add_explicit_type,
-            r#"fn f() $0{let a = match 1 {2 => 3, 3 => 5};}"#,
+            "fn f() $0{let a = match 1 {2 => 3, 3 => 5};}",
         )
     }
 
@@ -193,8 +176,7 @@ fn f() {
 fn main() {
     let multiply_by_two$0 = |i| i * 3;
     let six = multiply_by_two(2);
-}
-"#,
+}"#,
         )
     }
 
@@ -203,19 +185,23 @@ fn main() {
         check_assist(
             add_explicit_type,
             r#"
-struct Test<K, T = u8> { k: K, t: T }
+struct Test<K, T = u8> {
+    k: K,
+    t: T,
+}
 
 fn main() {
     let test$0 = Test { t: 23u8, k: 33 };
-}
-"#,
+}"#,
             r#"
-struct Test<K, T = u8> { k: K, t: T }
+struct Test<K, T = u8> {
+    k: K,
+    t: T,
+}
 
 fn main() {
     let test: Test<i32> = Test { t: 23u8, k: 33 };
-}
-"#,
+}"#,
         );
     }
 }
diff --git a/crates/ide_assists/src/handlers/add_lifetime_to_type.rs b/crates/ide_assists/src/handlers/add_lifetime_to_type.rs
index 7030d0f978..844928754f 100644
--- a/crates/ide_assists/src/handlers/add_lifetime_to_type.rs
+++ b/crates/ide_assists/src/handlers/add_lifetime_to_type.rs
@@ -1,4 +1,5 @@
-use syntax::ast::{self, AstNode, GenericParamsOwner, NameOwner};
+use ast::FieldList;
+use syntax::ast::{self, AstNode, GenericParamsOwner, NameOwner, RefType, Type};
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
 
@@ -64,8 +65,8 @@ pub(crate) fn add_lifetime_to_type(acc: &mut Assists, ctx: &AssistContext) -> Op
     )
 }
 
-fn fetch_borrowed_types(node: &ast::Adt) -> Option<Vec<ast::RefType>> {
-    let ref_types: Vec<ast::RefType> = match node {
+fn fetch_borrowed_types(node: &ast::Adt) -> Option<Vec<RefType>> {
+    let ref_types: Vec<RefType> = match node {
         ast::Adt::Enum(enum_) => {
             let variant_list = enum_.variant_list()?;
             variant_list
@@ -87,7 +88,7 @@ fn fetch_borrowed_types(node: &ast::Adt) -> Option<Vec<ast::RefType>> {
             record_field_list
                 .fields()
                 .filter_map(|r_field| {
-                    if let ast::Type::RefType(ref_type) = r_field.ty()? {
+                    if let Type::RefType(ref_type) = r_field.ty()? {
                         if ref_type.lifetime().is_none() {
                             return Some(ref_type);
                         }
@@ -106,12 +107,12 @@ fn fetch_borrowed_types(node: &ast::Adt) -> Option<Vec<ast::RefType>> {
     }
 }
 
-fn find_ref_types_from_field_list(field_list: &ast::FieldList) -> Option<Vec<ast::RefType>> {
-    let ref_types: Vec<ast::RefType> = match field_list {
+fn find_ref_types_from_field_list(field_list: &FieldList) -> Option<Vec<RefType>> {
+    let ref_types: Vec<RefType> = match field_list {
         ast::FieldList::RecordFieldList(record_list) => record_list
             .fields()
             .filter_map(|f| {
-                if let ast::Type::RefType(ref_type) = f.ty()? {
+                if let Type::RefType(ref_type) = f.ty()? {
                     if ref_type.lifetime().is_none() {
                         return Some(ref_type);
                     }
@@ -123,7 +124,7 @@ fn find_ref_types_from_field_list(field_list: &ast::FieldList) -> Option<Vec<ast
         ast::FieldList::TupleFieldList(tuple_field_list) => tuple_field_list
             .fields()
             .filter_map(|f| {
-                if let ast::Type::RefType(ref_type) = f.ty()? {
+                if let Type::RefType(ref_type) = f.ty()? {
                     if ref_type.lifetime().is_none() {
                         return Some(ref_type);
                     }
@@ -151,79 +152,76 @@ mod tests {
     fn add_lifetime_to_struct() {
         check_assist(
             add_lifetime_to_type,
-            r#"struct Foo { a: &$0i32 }"#,
-            r#"struct Foo<'a> { a: &'a i32 }"#,
+            "struct Foo { a: &$0i32 }",
+            "struct Foo<'a> { a: &'a i32 }",
         );
 
         check_assist(
             add_lifetime_to_type,
-            r#"struct Foo { a: &$0i32, b: &usize }"#,
-            r#"struct Foo<'a> { a: &'a i32, b: &'a usize }"#,
+            "struct Foo { a: &$0i32, b: &usize }",
+            "struct Foo<'a> { a: &'a i32, b: &'a usize }",
         );
 
         check_assist(
             add_lifetime_to_type,
-            r#"struct Foo { a: &$0i32, b: usize }"#,
-            r#"struct Foo<'a> { a: &'a i32, b: usize }"#,
+            "struct Foo { a: &$0i32, b: usize }",
+            "struct Foo<'a> { a: &'a i32, b: usize }",
         );
 
         check_assist(
             add_lifetime_to_type,
-            r#"struct Foo<T> { a: &$0T, b: usize }"#,
-            r#"struct Foo<'a, T> { a: &'a T, b: usize }"#,
+            "struct Foo<T> { a: &$0T, b: usize }",
+            "struct Foo<'a, T> { a: &'a T, b: usize }",
         );
 
-        check_assist_not_applicable(add_lifetime_to_type, r#"struct Foo<'a> { a: &$0'a i32 }"#);
-        check_assist_not_applicable(add_lifetime_to_type, r#"struct Foo { a: &'a$0 i32 }"#);
+        check_assist_not_applicable(add_lifetime_to_type, "struct Foo<'a> { a: &$0'a i32 }");
+        check_assist_not_applicable(add_lifetime_to_type, "struct Foo { a: &'a$0 i32 }");
     }
 
     #[test]
     fn add_lifetime_to_enum() {
         check_assist(
             add_lifetime_to_type,
-            r#"enum Foo { Bar { a: i32 }, Other, Tuple(u32, &$0u32)}"#,
-            r#"enum Foo<'a> { Bar { a: i32 }, Other, Tuple(u32, &'a u32)}"#,
+            "enum Foo { Bar { a: i32 }, Other, Tuple(u32, &$0u32)}",
+            "enum Foo<'a> { Bar { a: i32 }, Other, Tuple(u32, &'a u32)}",
         );
 
         check_assist(
             add_lifetime_to_type,
-            r#"enum Foo { Bar { a: &$0i32 }}"#,
-            r#"enum Foo<'a> { Bar { a: &'a i32 }}"#,
+            "enum Foo { Bar { a: &$0i32 }}",
+            "enum Foo<'a> { Bar { a: &'a i32 }}",
         );
 
         check_assist(
             add_lifetime_to_type,
-            r#"enum Foo<T> { Bar { a: &$0i32, b: &T }}"#,
-            r#"enum Foo<'a, T> { Bar { a: &'a i32, b: &'a T }}"#,
+            "enum Foo<T> { Bar { a: &$0i32, b: &T }}",
+            "enum Foo<'a, T> { Bar { a: &'a i32, b: &'a T }}",
         );
 
-        check_assist_not_applicable(
-            add_lifetime_to_type,
-            r#"enum Foo<'a> { Bar { a: &$0'a i32 }}"#,
-        );
-        check_assist_not_applicable(add_lifetime_to_type, r#"enum Foo { Bar, $0Misc }"#);
+        check_assist_not_applicable(add_lifetime_to_type, "enum Foo<'a> { Bar { a: &$0'a i32 }}");
+        check_assist_not_applicable(add_lifetime_to_type, "enum Foo { Bar, $0Misc }");
     }
 
     #[test]
     fn add_lifetime_to_union() {
         check_assist(
             add_lifetime_to_type,
-            r#"union Foo { a: &$0i32 }"#,
-            r#"union Foo<'a> { a: &'a i32 }"#,
+            "union Foo { a: &$0i32 }",
+            "union Foo<'a> { a: &'a i32 }",
         );
 
         check_assist(
             add_lifetime_to_type,
-            r#"union Foo { a: &$0i32, b: &usize }"#,
-            r#"union Foo<'a> { a: &'a i32, b: &'a usize }"#,
+            "union Foo { a: &$0i32, b: &usize }",
+            "union Foo<'a> { a: &'a i32, b: &'a usize }",
         );
 
         check_assist(
             add_lifetime_to_type,
-            r#"union Foo<T> { a: &$0T, b: usize }"#,
-            r#"union Foo<'a, T> { a: &'a T, b: usize }"#,
+            "union Foo<T> { a: &$0T, b: usize }",
+            "union Foo<'a, T> { a: &'a T, b: usize }",
         );
 
-        check_assist_not_applicable(add_lifetime_to_type, r#"struct Foo<'a> { a: &'a $0i32 }"#);
+        check_assist_not_applicable(add_lifetime_to_type, "struct Foo<'a> { a: &'a $0i32 }");
     }
 }
diff --git a/crates/ide_assists/src/handlers/add_missing_impl_members.rs b/crates/ide_assists/src/handlers/add_missing_impl_members.rs
index 0148635f9e..63cea754da 100644
--- a/crates/ide_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide_assists/src/handlers/add_missing_impl_members.rs
@@ -3,9 +3,9 @@ use syntax::ast::{self, AstNode};
 
 use crate::{
     assist_context::{AssistContext, Assists},
-    utils::{
-        add_trait_assoc_items_to_impl, filter_assoc_items, render_snippet, Cursor, DefaultMethods,
-    },
+    utils::add_trait_assoc_items_to_impl,
+    utils::DefaultMethods,
+    utils::{filter_assoc_items, render_snippet, Cursor},
     AssistId, AssistKind,
 };
 
diff --git a/crates/ide_assists/src/handlers/convert_comment_block.rs b/crates/ide_assists/src/handlers/convert_comment_block.rs
index d202a85f95..9dc3ee28fd 100644
--- a/crates/ide_assists/src/handlers/convert_comment_block.rs
+++ b/crates/ide_assists/src/handlers/convert_comment_block.rs
@@ -1,6 +1,13 @@
 use itertools::Itertools;
 use syntax::{
-    ast::{self, edit::IndentLevel, Comment, CommentKind, CommentShape, Whitespace},
+    ast::{
+        self,
+        edit::IndentLevel,
+        Comment, CommentKind,
+        CommentPlacement::{Inner, Outer},
+        CommentShape::{self, Block, Line},
+        Whitespace,
+    },
     AstToken, Direction, SyntaxElement, TextRange,
 };
 
@@ -22,18 +29,21 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 ///   */
 /// ```
 pub(crate) fn convert_comment_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    let comment = ctx.find_token_at_offset::<ast::Comment>()?;
-    // Only allow comments which are alone on their line
-    if let Some(prev) = comment.syntax().prev_token() {
-        if Whitespace::cast(prev).filter(|w| w.text().contains('\n')).is_none() {
-            return None;
+    if let Some(comment) = ctx.find_token_at_offset::<ast::Comment>() {
+        // Only allow comments which are alone on their line
+        if let Some(prev) = comment.syntax().prev_token() {
+            if Whitespace::cast(prev).filter(|w| w.text().contains('\n')).is_none() {
+                return None;
+            }
         }
-    }
 
-    match comment.kind().shape {
-        ast::CommentShape::Block => block_to_line(acc, comment),
-        ast::CommentShape::Line => line_to_block(acc, comment),
+        return match comment.kind().shape {
+            ast::CommentShape::Block => block_to_line(acc, comment),
+            ast::CommentShape::Line => line_to_block(acc, comment),
+        };
     }
+
+    return None;
 }
 
 fn block_to_line(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
@@ -45,7 +55,8 @@ fn block_to_line(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
         target,
         |edit| {
             let indentation = IndentLevel::from_token(comment.syntax());
-            let line_prefix = CommentKind { shape: CommentShape::Line, ..comment.kind() }.prefix();
+            let line_prefix =
+                comment_kind_prefix(CommentKind { shape: CommentShape::Line, ..comment.kind() });
 
             let text = comment.text();
             let text = &text[comment.prefix().len()..(text.len() - "*/".len())].trim();
@@ -94,7 +105,7 @@ fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
                 comments.into_iter().map(|c| line_comment_text(indentation, c)).join("\n");
 
             let block_prefix =
-                CommentKind { shape: CommentShape::Block, ..comment.kind() }.prefix();
+                comment_kind_prefix(CommentKind { shape: CommentShape::Block, ..comment.kind() });
 
             let output =
                 format!("{}\n{}\n{}*/", block_prefix, block_comment_body, indentation.to_string());
@@ -171,6 +182,17 @@ fn line_comment_text(indentation: IndentLevel, comm: ast::Comment) -> String {
     }
 }
 
+fn comment_kind_prefix(ck: ast::CommentKind) -> &'static str {
+    match (ck.shape, ck.doc) {
+        (Line, Some(Inner)) => "//!",
+        (Line, Some(Outer)) => "///",
+        (Line, None) => "//",
+        (Block, Some(Inner)) => "/*!",
+        (Block, Some(Outer)) => "/**",
+        (Block, None) => "/*",
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use crate::tests::{check_assist, check_assist_not_applicable};
diff --git a/crates/ide_assists/src/handlers/early_return.rs b/crates/ide_assists/src/handlers/early_return.rs
index 9e0918477b..2148df7c81 100644
--- a/crates/ide_assists/src/handlers/early_return.rs
+++ b/crates/ide_assists/src/handlers/early_return.rs
@@ -56,7 +56,11 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
             match path.qualifier() {
                 None => {
                     let bound_ident = pat.fields().next().unwrap();
-                    Some((path, bound_ident))
+                    if ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
+                        Some((path, bound_ident))
+                    } else {
+                        return None;
+                    }
                 }
                 Some(_) => return None,
             }
@@ -143,10 +147,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
                         make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
                     };
 
-                    let let_stmt = make::let_stmt(
-                        make::ident_pat(make::name(&bound_ident.syntax().to_string())).into(),
-                        Some(match_expr),
-                    );
+                    let let_stmt = make::let_stmt(bound_ident, Some(match_expr));
                     let let_stmt = let_stmt.indent(if_indent_level);
                     replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
                 }
@@ -284,7 +285,7 @@ mod tests {
             r#"
             fn main(n: Option<String>) {
                 bar();
-                if$0 let Ok(n) = n {
+                if$0 let Some(n) = n {
                     foo(n);
 
                     //comment
@@ -296,7 +297,69 @@ mod tests {
             fn main(n: Option<String>) {
                 bar();
                 let n = match n {
-                    Ok(it) => it,
+                    Some(it) => it,
+                    _ => return,
+                };
+                foo(n);
+
+                //comment
+                bar();
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn convert_let_mut_ok_inside_fn() {
+        check_assist(
+            convert_to_guarded_return,
+            r#"
+            fn main(n: Option<String>) {
+                bar();
+                if$0 let Some(mut n) = n {
+                    foo(n);
+
+                    //comment
+                    bar();
+                }
+            }
+            "#,
+            r#"
+            fn main(n: Option<String>) {
+                bar();
+                let mut n = match n {
+                    Some(it) => it,
+                    _ => return,
+                };
+                foo(n);
+
+                //comment
+                bar();
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn convert_let_ref_ok_inside_fn() {
+        check_assist(
+            convert_to_guarded_return,
+            r#"
+            fn main(n: Option<&str>) {
+                bar();
+                if$0 let Some(ref n) = n {
+                    foo(n);
+
+                    //comment
+                    bar();
+                }
+            }
+            "#,
+            r#"
+            fn main(n: Option<&str>) {
+                bar();
+                let ref n = match n {
+                    Some(it) => it,
                     _ => return,
                 };
                 foo(n);
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs
index 8e07942180..cfc472a325 100644
--- a/crates/ide_assists/src/handlers/merge_imports.rs
+++ b/crates/ide_assists/src/handlers/merge_imports.rs
@@ -21,6 +21,12 @@ use crate::{
 // ```
 pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let tree: ast::UseTree = ctx.find_node_at_offset()?;
+    let original_parent = tree.syntax().ancestors().last()?;
+
+    let tree = tree.clone_for_update();
+    let new_parent = tree.syntax().ancestors().last()?;
+
+    let mut offset = ctx.offset();
 
     let mut imports = None;
     let mut uses = None;
@@ -47,20 +53,22 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()
         target,
         |builder| {
             if let Some((to_replace, replacement, to_remove)) = imports {
-                let to_replace = builder.make_ast_mut(to_replace);
-                let to_remove = builder.make_ast_mut(to_remove);
-
-                ted::replace(to_replace.syntax(), replacement.syntax());
+                if to_remove.syntax().text_range().end() < offset {
+                    offset -= to_remove.syntax().text_range().len();
+                }
+                ted::replace(to_replace.syntax().clone(), replacement.syntax().clone());
                 to_remove.remove();
             }
 
             if let Some((to_replace, replacement, to_remove)) = uses {
-                let to_replace = builder.make_ast_mut(to_replace);
-                let to_remove = builder.make_ast_mut(to_remove);
-
-                ted::replace(to_replace.syntax(), replacement.syntax());
+                if to_remove.syntax().text_range().end() < offset {
+                    offset -= to_remove.syntax().text_range().len();
+                }
+                ted::replace(to_replace.syntax().clone(), replacement.syntax().clone());
                 to_remove.remove()
             }
+
+            builder.replace(original_parent.text_range(), new_parent.to_string())
         },
     )
 }
diff --git a/crates/ide_assists/src/handlers/move_bounds.rs b/crates/ide_assists/src/handlers/move_bounds.rs
index 011a28d446..b5dec80147 100644
--- a/crates/ide_assists/src/handlers/move_bounds.rs
+++ b/crates/ide_assists/src/handlers/move_bounds.rs
@@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 // }
 // ```
 pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    let type_param_list = ctx.find_node_at_offset::<ast::GenericParamList>()?;
+    let type_param_list = ctx.find_node_at_offset::<ast::GenericParamList>()?.clone_for_update();
 
     let mut type_params = type_param_list.type_params();
     if type_params.all(|p| p.type_bound_list().is_none()) {
@@ -29,6 +29,7 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext
     }
 
     let parent = type_param_list.syntax().parent()?;
+    let original_parent_range = parent.text_range();
 
     let target = type_param_list.syntax().text_range();
     acc.add(
@@ -36,9 +37,6 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext
         "Move to where clause",
         target,
         |edit| {
-            let type_param_list = edit.make_ast_mut(type_param_list);
-            let parent = edit.make_mut(parent);
-
             let where_clause: ast::WhereClause = match_ast! {
                 match parent {
                     ast::Fn(it) => it.get_or_create_where_clause(),
@@ -58,6 +56,8 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext
                     tbl.remove()
                 }
             }
+
+            edit.replace(original_parent_range, parent.to_string())
         },
     )
 }
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs
index 383ca6c473..794c893236 100644
--- a/crates/ide_assists/src/handlers/reorder_fields.rs
+++ b/crates/ide_assists/src/handlers/reorder_fields.rs
@@ -1,6 +1,9 @@
+use itertools::Itertools;
 use rustc_hash::FxHashMap;
 
-use syntax::{algo, ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode};
+use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
+use ide_db::RootDatabase;
+use syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode};
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
 
@@ -20,39 +23,26 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 // ```
 //
 pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    let record = ctx
-        .find_node_at_offset::<ast::RecordExpr>()
-        .map(|it| it.syntax().clone())
-        .or_else(|| ctx.find_node_at_offset::<ast::RecordPat>().map(|it| it.syntax().clone()))?;
+    reorder::<ast::RecordExpr>(acc, ctx).or_else(|| reorder::<ast::RecordPat>(acc, ctx))
+}
 
-    let path = record.children().find_map(ast::Path::cast)?;
+fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+    let record = ctx.find_node_at_offset::<R>()?;
+    let path = record.syntax().children().find_map(ast::Path::cast)?;
 
     let ranks = compute_fields_ranks(&path, &ctx)?;
 
-    let fields: Vec<SyntaxNode> = {
-        let field_kind = match record.kind() {
-            RECORD_EXPR => RECORD_EXPR_FIELD,
-            RECORD_PAT => RECORD_PAT_FIELD,
-            _ => {
-                stdx::never!();
-                return None;
-            }
-        };
-        record.children().flat_map(|n| n.children()).filter(|n| n.kind() == field_kind).collect()
-    };
-
-    let sorted_fields = {
-        let mut fields = fields.clone();
-        fields.sort_by_key(|node| *ranks.get(&get_field_name(node)).unwrap_or(&usize::max_value()));
-        fields
-    };
+    let fields = get_fields(&record.syntax());
+    let sorted_fields = sorted_by_rank(&fields, |node| {
+        *ranks.get(&get_field_name(node)).unwrap_or(&usize::max_value())
+    });
 
     if sorted_fields == fields {
         cov_mark::hit!(reorder_sorted_fields);
         return None;
     }
 
-    let target = record.text_range();
+    let target = record.syntax().text_range();
     acc.add(
         AssistId("reorder_fields", AssistKind::RefactorRewrite),
         "Reorder record fields",
@@ -67,6 +57,14 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<(
     )
 }
 
+fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> {
+    match node.kind() {
+        RECORD_EXPR => vec![RECORD_EXPR_FIELD],
+        RECORD_PAT => vec![RECORD_PAT_FIELD, IDENT_PAT],
+        _ => vec![],
+    }
+}
+
 fn get_field_name(node: &SyntaxNode) -> String {
     let res = match_ast! {
         match node {
@@ -78,20 +76,34 @@ fn get_field_name(node: &SyntaxNode) -> String {
     res.unwrap_or_default()
 }
 
-fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> {
-    let strukt = match ctx.sema.resolve_path(path) {
-        Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Struct(it)))) => it,
-        _ => return None,
-    };
+fn get_fields(record: &SyntaxNode) -> Vec<SyntaxNode> {
+    let kinds = get_fields_kind(record);
+    record.children().flat_map(|n| n.children()).filter(|n| kinds.contains(&n.kind())).collect()
+}
 
-    let res = strukt
-        .fields(ctx.db())
-        .iter()
-        .enumerate()
-        .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx))
-        .collect();
+fn sorted_by_rank(
+    fields: &[SyntaxNode],
+    get_rank: impl Fn(&SyntaxNode) -> usize,
+) -> Vec<SyntaxNode> {
+    fields.iter().cloned().sorted_by_key(get_rank).collect()
+}
 
-    Some(res)
+fn struct_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option<Struct> {
+    match sema.resolve_path(path) {
+        Some(PathResolution::Def(ModuleDef::Adt(Adt::Struct(s)))) => Some(s),
+        _ => None,
+    }
+}
+
+fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> {
+    Some(
+        struct_definition(path, &ctx.sema)?
+            .fields(ctx.db())
+            .iter()
+            .enumerate()
+            .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx))
+            .collect(),
+    )
 }
 
 #[cfg(test)]
@@ -106,7 +118,11 @@ mod tests {
         check_assist_not_applicable(
             reorder_fields,
             r#"
-struct Foo { foo: i32, bar: i32 }
+struct Foo {
+    foo: i32,
+    bar: i32,
+}
+
 const test: Foo = $0Foo { foo: 0, bar: 0 };
 "#,
         )
@@ -117,8 +133,8 @@ const test: Foo = $0Foo { foo: 0, bar: 0 };
         check_assist_not_applicable(
             reorder_fields,
             r#"
-struct Foo {}
-const test: Foo = $0Foo {};
+struct Foo {};
+const test: Foo = $0Foo {}
 "#,
         )
     }
@@ -128,12 +144,12 @@ const test: Foo = $0Foo {};
         check_assist(
             reorder_fields,
             r#"
-struct Foo { foo: i32, bar: i32 }
-const test: Foo = $0Foo { bar: 0, foo: 1 };
+struct Foo {foo: i32, bar: i32};
+const test: Foo = $0Foo {bar: 0, foo: 1}
 "#,
             r#"
-struct Foo { foo: i32, bar: i32 }
-const test: Foo = Foo { foo: 1, bar: 0 };
+struct Foo {foo: i32, bar: i32};
+const test: Foo = Foo {foo: 1, bar: 0}
 "#,
         )
     }
@@ -170,7 +186,10 @@ fn f(f: Foo) -> {
         check_assist(
             reorder_fields,
             r#"
-struct Foo { foo: String, bar: String }
+struct Foo {
+    foo: String,
+    bar: String,
+}
 
 impl Foo {
     fn new() -> Foo {
@@ -184,7 +203,10 @@ impl Foo {
 }
 "#,
             r#"
-struct Foo { foo: String, bar: String }
+struct Foo {
+    foo: String,
+    bar: String,
+}
 
 impl Foo {
     fn new() -> Foo {
diff --git a/crates/proc_macro_api/src/msg.rs b/crates/proc_macro_api/src/msg.rs
index f525df1525..970f165edc 100644
--- a/crates/proc_macro_api/src/msg.rs
+++ b/crates/proc_macro_api/src/msg.rs
@@ -55,8 +55,8 @@ pub enum ErrorCode {
 }
 
 pub trait Message: Serialize + DeserializeOwned {
-    fn read(inp: &mut impl BufRead, buf: &mut String) -> io::Result<Option<Self>> {
-        Ok(match read_json(inp, buf)? {
+    fn read(inp: &mut impl BufRead) -> io::Result<Option<Self>> {
+        Ok(match read_json(inp)? {
             None => None,
             Some(text) => {
                 let mut deserializer = serde_json::Deserializer::from_str(&text);
@@ -76,29 +76,14 @@ pub trait Message: Serialize + DeserializeOwned {
 impl Message for Request {}
 impl Message for Response {}
 
-fn read_json<'a>(
-    inp: &mut impl BufRead,
-    mut buf: &'a mut String,
-) -> io::Result<Option<&'a String>> {
-    loop {
-        buf.clear();
-
-        inp.read_line(&mut buf)?;
-        buf.pop(); // Remove trailing '\n'
-
-        if buf.is_empty() {
-            return Ok(None);
-        }
-
-        // Some ill behaved macro try to use stdout for debugging
-        // We ignore it here
-        if !buf.starts_with("{") {
-            log::error!("proc-macro tried to print : {}", buf);
-            continue;
-        }
-
-        return Ok(Some(buf));
-    }
+fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> {
+    let mut buf = String::new();
+    inp.read_line(&mut buf)?;
+    buf.pop(); // Remove trailing '\n'
+    Ok(match buf.len() {
+        0 => None,
+        _ => Some(buf),
+    })
 }
 
 fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> {
diff --git a/crates/proc_macro_api/src/process.rs b/crates/proc_macro_api/src/process.rs
index 99d05aef37..30bb1b687b 100644
--- a/crates/proc_macro_api/src/process.rs
+++ b/crates/proc_macro_api/src/process.rs
@@ -90,10 +90,8 @@ impl ProcMacroProcessSrv {
 fn client_loop(task_rx: Receiver<Task>, mut process: Process) {
     let (mut stdin, mut stdout) = process.stdio().expect("couldn't access child stdio");
 
-    let mut buf = String::new();
-
     for Task { req, result_tx } in task_rx {
-        match send_request(&mut stdin, &mut stdout, req, &mut buf) {
+        match send_request(&mut stdin, &mut stdout, req) {
             Ok(res) => result_tx.send(res).unwrap(),
             Err(err) => {
                 log::error!(
@@ -154,8 +152,7 @@ fn send_request(
     mut writer: &mut impl Write,
     mut reader: &mut impl BufRead,
     req: Request,
-    buf: &mut String,
 ) -> io::Result<Option<Response>> {
     req.write(&mut writer)?;
-    Response::read(&mut reader, buf)
+    Response::read(&mut reader)
 }
diff --git a/crates/proc_macro_srv/src/cli.rs b/crates/proc_macro_srv/src/cli.rs
index bc48f1c436..d428b95675 100644
--- a/crates/proc_macro_srv/src/cli.rs
+++ b/crates/proc_macro_srv/src/cli.rs
@@ -6,9 +6,8 @@ use std::io;
 
 pub fn run() -> io::Result<()> {
     let mut srv = ProcMacroSrv::default();
-    let mut buf = String::new();
 
-    while let Some(req) = read_request(&mut buf)? {
+    while let Some(req) = read_request()? {
         let res = match req {
             msg::Request::ListMacro(task) => srv.list_macros(&task).map(msg::Response::ListMacro),
             msg::Request::ExpansionMacro(task) => {
@@ -31,8 +30,8 @@ pub fn run() -> io::Result<()> {
     Ok(())
 }
 
-fn read_request(buf: &mut String) -> io::Result<Option<msg::Request>> {
-    msg::Request::read(&mut io::stdin().lock(), buf)
+fn read_request() -> io::Result<Option<msg::Request>> {
+    msg::Request::read(&mut io::stdin().lock())
 }
 
 fn write_response(msg: msg::Response) -> io::Result<()> {
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index 29d25a58a3..090282d280 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -102,9 +102,8 @@ impl CommentKind {
         kind
     }
 
-    pub fn prefix(&self) -> &'static str {
-        let &(prefix, _) =
-            CommentKind::BY_PREFIX.iter().rev().find(|(_, kind)| kind == self).unwrap();
+    fn prefix(&self) -> &'static str {
+        let &(prefix, _) = CommentKind::BY_PREFIX.iter().find(|(_, kind)| kind == self).unwrap();
         prefix
     }
 }


Test patch:
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs
index 4442cbff68..19516de7b9 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/main.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs
@@ -712,10 +712,6 @@ pub fn foo(_input: TokenStream) -> TokenStream {
     // We hard code the output here for preventing to use any deps
     let mut res = TokenStream::new();
 
-    // ill behaved proc-macro will use the stdout
-    // we should ignore it
-    println!("I am bad guy");
-
     // impl Bar for Foo { fn bar() {} }
     let mut tokens = vec![t!("impl"), t!("Bar"), t!("for"), t!("Foo")];
     let mut fn_stream = TokenStream::new();