Repository: rust-analyzer/rust-analyzer
Pull request #8882 (2021-05-19 13:19:52)

internal: resolve attributes in name resolution (minimal version)
Closes https://github.com/rust-analyzer/rust-analyzer/pull/7049

This should not have any observable effect, since we don't attempt to expand attribute macros yet, and I have implemented a fallback that treats items with unresolved attributes as if the attribute wasn't there.

Derive helpers are not yet resolved. `#![register_{attr,tool}]` are not yet supported.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 76a26ea4ec..f9c34547ed 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -437,18 +437,18 @@ dependencies = [
 
 [[package]]
 name = "fsevent-sys"
-version = "3.1.0"
+version = "3.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca6f5e6817058771c10f0eb0f05ddf1e35844266f972004fe8e4b21fda295bd5"
+checksum = "77a29c77f1ca394c3e73a9a5d24cfcabb734682d9634fc398f2204a63c994120"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "fst"
-version = "0.4.6"
+version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e398fae362f4124bbe630d99519fb2d68a03e2e3a23b441028cdcdc4f4895687"
+checksum = "d79238883cf0307100b90aba4a755d8051a3182305dfe7f649a1e9dc0517006f"
 
 [[package]]
 name = "gimli"
@@ -939,10 +939,11 @@ dependencies = [
 
 [[package]]
 name = "notify"
-version = "5.0.0-pre.8"
+version = "5.0.0-pre.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46bbbcd078f1f00ddb7a9abe70b96e91229b44b0b3afdec610f8e5137f8f014b"
+checksum = "1ebe7699a0f8c5759450716ee03d231685c22b4fe8f406c42c22e0ad94d40ce7"
 dependencies = [
+ "anymap",
  "bitflags",
  "crossbeam-channel",
  "filetime",
@@ -1294,9 +1295,9 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
 
 [[package]]
 name = "rowan"
-version = "0.13.0-pre.6"
+version = "0.13.0-pre.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82ccc04e145e9a5ab51b9c12a81d77c4a8250d87a407ab02ac650451141ff00d"
+checksum = "32a5fc82ed0b7e7fba157331f0d8f64abd73bced6e7ac2a4dfa0c4cf0ab584e8"
 dependencies = [
  "countme",
  "hashbrown",
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index 93cf6a3d68..1682d8bde2 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -215,7 +215,6 @@ impl FlycheckActor {
             } => {
                 let mut cmd = Command::new(toolchain::cargo());
                 cmd.arg(command);
-                cmd.current_dir(&self.workspace_root);
                 cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
                     .arg(self.workspace_root.join("Cargo.toml"));
 
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index d443b124c4..c9ef4b4205 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -52,9 +52,7 @@ use hir_def::{
 };
 use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind};
 use hir_ty::{
-    autoderef,
-    consteval::ConstExt,
-    could_unify,
+    autoderef, could_unify,
     method_resolution::{self, def_crates, TyFingerprint},
     primitive::UintTy,
     subst_prefix,
@@ -875,10 +873,6 @@ impl Function {
         db.function_data(self.id).is_unsafe()
     }
 
-    pub fn is_async(self, db: &dyn HirDatabase) -> bool {
-        db.function_data(self.id).is_async()
-    }
-
     pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
         let krate = self.module(db).id.krate();
         hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink);
@@ -1916,7 +1910,6 @@ impl Type {
                     substs.iter(&Interner).filter_map(|a| a.ty(&Interner)).any(go)
                 }
 
-                TyKind::Array(_ty, len) if len.is_unknown() => true,
                 TyKind::Array(ty, _)
                 | TyKind::Slice(ty)
                 | TyKind::Raw(_, ty)
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index 98b485b602..8360426f13 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -21,6 +21,8 @@ use profile::Count;
 use rustc_hash::FxHashMap;
 use syntax::{ast, AstNode, AstPtr};
 
+pub use lower::LowerCtx;
+
 use crate::{
     attr::{Attrs, RawAttrs},
     db::DefDatabase,
@@ -33,8 +35,6 @@ use crate::{
     UnresolvedMacro,
 };
 
-pub use lower::LowerCtx;
-
 /// A subset of Expander that only deals with cfg attributes. We only need it to
 /// avoid cyclic queries in crate def map during enum processing.
 #[derive(Debug)]
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 2a7e0205f5..9f278d35b7 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -1006,27 +1006,23 @@ impl From<ast::BinOp> for BinaryOp {
 impl From<ast::LiteralKind> for Literal {
     fn from(ast_lit_kind: ast::LiteralKind) -> Self {
         match ast_lit_kind {
-            // FIXME: these should have actual values filled in, but unsure on perf impact
             LiteralKind::IntNumber(lit) => {
                 if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
                     return Literal::Float(Default::default(), builtin);
                 } else if let builtin @ Some(_) =
                     lit.suffix().and_then(|it| BuiltinInt::from_suffix(&it))
                 {
-                    Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
+                    Literal::Int(Default::default(), builtin)
                 } else {
                     let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(&it));
-                    Literal::Uint(lit.value().unwrap_or(0), builtin)
+                    Literal::Uint(Default::default(), builtin)
                 }
             }
             LiteralKind::FloatNumber(lit) => {
                 let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(&it));
                 Literal::Float(Default::default(), ty)
             }
-            LiteralKind::ByteString(bs) => {
-                let text = bs.value().map(Vec::from).unwrap_or_else(Default::default);
-                Literal::ByteString(text)
-            }
+            LiteralKind::ByteString(_) => Literal::ByteString(Default::default()),
             LiteralKind::String(_) => Literal::String(Default::default()),
             LiteralKind::Byte => Literal::Uint(Default::default(), Some(BuiltinUint::U8)),
             LiteralKind::Bool(val) => Literal::Bool(val),
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index 2ba619d234..0c3b410802 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -43,8 +43,8 @@ pub enum Literal {
     ByteString(Vec<u8>),
     Char(char),
     Bool(bool),
-    Int(i128, Option<BuiltinInt>),
-    Uint(u128, Option<BuiltinUint>),
+    Int(u64, Option<BuiltinInt>),
+    Uint(u64, Option<BuiltinUint>),
     Float(u64, Option<BuiltinFloat>), // FIXME: f64 is not Eq
 }
 
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index ee52794aa8..858e880381 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -5,10 +5,10 @@ use std::iter;
 use hir_expand::name::{known, AsName, Name};
 use rustc_hash::FxHashSet;
 
+use crate::nameres::DefMap;
 use crate::{
     db::DefDatabase,
     item_scope::ItemInNs,
-    nameres::DefMap,
     path::{ModPath, PathKind},
     visibility::Visibility,
     ModuleDefId, ModuleId,
@@ -134,16 +134,7 @@ fn find_path_inner(
     for (name, def_id) in root_def_map.extern_prelude() {
         if item == ItemInNs::Types(*def_id) {
             let name = scope_name.unwrap_or_else(|| name.clone());
-
-            let name_already_occupied_in_type_ns = def_map
-                .with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
-                    def_map[local_id].scope.get(&name).take_types().filter(|&id| id != *def_id)
-                })
-                .is_some();
-            return Some(ModPath::from_segments(
-                if name_already_occupied_in_type_ns { PathKind::Abs } else { PathKind::Plain },
-                vec![name],
-            ));
+            return Some(ModPath::from_segments(PathKind::Plain, vec![name]));
         }
     }
 
diff --git a/crates/hir_def/src/intern.rs b/crates/hir_def/src/intern.rs
index abc304ef0d..5cc7f2df65 100644
--- a/crates/hir_def/src/intern.rs
+++ b/crates/hir_def/src/intern.rs
@@ -4,7 +4,7 @@
 
 use std::{
     collections::HashMap,
-    fmt::{self, Debug},
+    fmt::{self, Debug, Display},
     hash::{BuildHasherDefault, Hash, Hasher},
     ops::Deref,
     sync::Arc,
@@ -171,6 +171,12 @@ impl<T: Debug + Internable + ?Sized> Debug for Interned<T> {
     }
 }
 
+impl<T: Display + Internable + ?Sized> Display for Interned<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        (*self.arc).fmt(f)
+    }
+}
+
 pub struct InternStorage<T: ?Sized> {
     map: OnceCell<InternMap<T>>,
 }
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index d4840be2fb..66d9396aad 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -21,6 +21,7 @@ use syntax::ast;
 
 use crate::{
     attr::{AttrId, Attrs},
+    builtin_attr,
     db::DefDatabase,
     derive_macro_as_call_id,
     intern::Interned,
@@ -99,6 +100,7 @@ pub(super) fn collect_defs(
         proc_macros,
         exports_proc_macros: false,
         from_glob_import: Default::default(),
+        ignore_attrs_on: FxHashSet::default(),
     };
     match block {
         Some(block) => {
@@ -217,6 +219,7 @@ struct MacroDirective {
 enum MacroDirectiveKind {
     FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind },
     Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId },
+    Attr { ast_id: AstIdWithPath<ast::Item>, attr: AttrId, mod_item: ModItem },
 }
 
 struct DefData<'a> {
@@ -243,6 +246,7 @@ struct DefCollector<'a> {
     proc_macros: Vec<(Name, ProcMacroExpander)>,
     exports_proc_macros: bool,
     from_glob_import: PerNsGlobImports,
+    ignore_attrs_on: FxHashSet<ModItem>,
 }
 
 impl DefCollector<'_> {
@@ -292,16 +296,26 @@ impl DefCollector<'_> {
     fn collect(&mut self) {
         // main name resolution fixed-point loop.
         let mut i = 0;
-        loop {
-            self.db.check_canceled();
-            self.resolve_imports();
+        'outer: loop {
+            loop {
+                self.db.check_canceled();
+                loop {
+                    if self.resolve_imports() == ReachedFixedPoint::Yes {
+                        break;
+                    }
+                }
+                if self.resolve_macros() == ReachedFixedPoint::Yes {
+                    break;
+                }
 
-            match self.resolve_macros() {
-                ReachedFixedPoint::Yes => break,
-                ReachedFixedPoint::No => i += 1,
+                i += 1;
+                if i == FIXED_POINT_LIMIT {
+                    log::error!("name resolution is stuck");
+                    break 'outer;
+                }
             }
-            if i == FIXED_POINT_LIMIT {
-                log::error!("name resolution is stuck");
+
+            if self.reseed_with_unresolved_attributes() == ReachedFixedPoint::Yes {
                 break;
             }
         }
@@ -343,6 +357,50 @@ impl DefCollector<'_> {
         }
     }
 
+    /// When the fixed-point loop reaches a stable state, we might still have some unresolved
+    /// attributes (or unexpanded attribute proc macros) left over. This takes them, and feeds the
+    /// item they're applied to back into name resolution.
+    ///
+    /// This effectively ignores the fact that the macro is there and just treats the items as
+    /// normal code.
+    ///
+    /// This improves UX when proc macros are turned off or don't work, and replicates the behavior
+    /// before we supported proc. attribute macros.
+    fn reseed_with_unresolved_attributes(&mut self) -> ReachedFixedPoint {
+        let mut added_items = false;
+        let unexpanded_macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new());
+        for directive in &unexpanded_macros {
+            if let MacroDirectiveKind::Attr { mod_item, .. } = &directive.kind {
+                // Make sure to only add such items once.
+                if !self.ignore_attrs_on.insert(*mod_item) {
+                    continue;
+                }
+
+                let file_id = self.def_map[directive.module_id].definition_source(self.db).file_id;
+                let item_tree = self.db.file_item_tree(file_id);
+                let mod_dir = self.mod_dirs[&directive.module_id].clone();
+                ModCollector {
+                    def_collector: &mut *self,
+                    macro_depth: directive.depth,
+                    module_id: directive.module_id,
+                    file_id,
+                    item_tree: &item_tree,
+                    mod_dir,
+                }
+                .collect(&[*mod_item]);
+                added_items = true;
+            }
+        }
+        self.unexpanded_macros = unexpanded_macros;
+
+        if added_items {
+            // Continue name resolution with the new data.
+            ReachedFixedPoint::No
+        } else {
+            ReachedFixedPoint::Yes
+        }
+    }
+
     /// Adds a definition of procedural macro `name` to the root module.
     ///
     /// # Notes on procedural macro resolution
@@ -481,11 +539,6 @@ impl DefCollector<'_> {
         let res = self.def_map.resolve_name_in_extern_prelude(self.db, &extern_crate.name);
 
         if let Some(ModuleDefId::ModuleId(m)) = res.take_types() {
-            if m == self.def_map.module_id(current_module_id) {
-                cov_mark::hit!(ignore_macro_use_extern_crate_self);
-                return;
-            }
-
             cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
             self.import_all_macros_exported(current_module_id, m.krate);
         }
@@ -504,35 +557,35 @@ impl DefCollector<'_> {
         }
     }
 
-    /// Import resolution
-    ///
-    /// This is a fix point algorithm. We resolve imports until no forward
-    /// progress in resolving imports is made
-    fn resolve_imports(&mut self) {
-        let mut n_previous_unresolved = self.unresolved_imports.len() + 1;
-
-        while self.unresolved_imports.len() < n_previous_unresolved {
-            n_previous_unresolved = self.unresolved_imports.len();
-            let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
-            for mut directive in imports {
+    /// Tries to resolve every currently unresolved import.
+    fn resolve_imports(&mut self) -> ReachedFixedPoint {
+        let mut res = ReachedFixedPoint::Yes;
+        let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
+        let imports = imports
+            .into_iter()
+            .filter_map(|mut directive| {
                 directive.status = self.resolve_import(directive.module_id, &directive.import);
                 match directive.status {
                     PartialResolvedImport::Indeterminate(_) => {
                         self.record_resolved_import(&directive);
                         // FIXME: For avoid performance regression,
                         // we consider an imported resolved if it is indeterminate (i.e not all namespace resolved)
-                        self.resolved_imports.push(directive)
+                        self.resolved_imports.push(directive);
+                        res = ReachedFixedPoint::No;
+                        None
                     }
                     PartialResolvedImport::Resolved(_) => {
                         self.record_resolved_import(&directive);
-                        self.resolved_imports.push(directive)
-                    }
-                    PartialResolvedImport::Unresolved => {
-                        self.unresolved_imports.push(directive);
+                        self.resolved_imports.push(directive);
+                        res = ReachedFixedPoint::No;
+                        None
                     }
+                    PartialResolvedImport::Unresolved => Some(directive),
                 }
-            }
-        }
+            })
+            .collect();
+        self.unresolved_imports = imports;
+        res
     }
 
     fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
@@ -811,17 +864,6 @@ impl DefCollector<'_> {
         let mut resolved = Vec::new();
         let mut res = ReachedFixedPoint::Yes;
         macros.retain(|directive| {
-            let resolver = |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()
-            };
-
             match &directive.kind {
                 MacroDirectiveKind::FnLike { ast_id, fragment } => {
                     match macro_call_as_call_id(
@@ -829,7 +871,16 @@ impl DefCollector<'_> {
                         *fragment,
                         self.db,
                         self.def_map.krate,
-                        &resolver,
+                        |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)) => {
@@ -846,7 +897,7 @@ impl DefCollector<'_> {
                         *derive_attr,
                         self.db,
                         self.def_map.krate,
-                        &resolver,
+                        |path| self.resolve_derive_macro(directive.module_id, &path),
                     ) {
                         Ok(call_id) => {
                             resolved.push((directive.module_id, call_id, directive.depth));
@@ -856,6 +907,9 @@ impl DefCollector<'_> {
                         Err(UnresolvedMacro { .. }) => (),
                     }
                 }
+                MacroDirectiveKind::Attr { .. } => {
+                    // not yet :)
+                }
             }
 
             true
@@ -869,6 +923,18 @@ impl DefCollector<'_> {
         res
     }
 
+    fn resolve_derive_macro(&self, module: LocalModuleId, path: &ModPath) -> Option<MacroDefId> {
+        let resolved_res = self.def_map.resolve_path_fp_with_macro(
+            self.db,
+            ResolveMode::Other,
+            module,
+            &path,
+            BuiltinShadowMode::Module,
+        );
+
+        resolved_res.resolved_def.take_macros()
+    }
+
     fn collect_macro_expansion(
         &mut self,
         module_id: LocalModuleId,
@@ -948,7 +1014,7 @@ impl DefCollector<'_> {
                         ));
                     }
                 },
-                MacroDirectiveKind::Derive { .. } => {
+                MacroDirectiveKind::Derive { .. } | MacroDirectiveKind::Attr { .. } => {
                     // FIXME: we might want to diagnose this too
                 }
             }
@@ -1056,6 +1122,14 @@ impl ModCollector<'_, '_> {
                     continue;
                 }
             }
+
+            if let Err(()) = self.resolve_attributes(&attrs, item) {
+                // Do not process the item. It has at least one non-builtin attribute, which *must*
+                // resolve to a proc macro (or fail to resolve), so we'll never see this item during
+                // normal name resolution.
+                continue;
+            }
+
             let module = self.def_collector.def_map.module_id(self.module_id);
 
             let mut def = None;
@@ -1362,6 +1436,62 @@ impl ModCollector<'_, '_> {
         res
     }
 
+    /// Resolves attributes on an item.
+    ///
+    /// Returns `Err` when some attributes could not be resolved to builtins and have been
+    /// registered as unresolved.
+    fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> {
+        fn is_builtin_attr(path: &ModPath) -> bool {
+            if path.kind == PathKind::Plain {
+                if let Some(tool_module) = path.segments().first() {
+                    let tool_module = tool_module.to_string();
+                    if builtin_attr::TOOL_MODULES.iter().any(|m| tool_module == *m) {
+                        return true;
+                    }
+                }
+
+                if let Some(name) = path.as_ident() {
+                    let name = name.to_string();
+                    if builtin_attr::INERT_ATTRIBUTES
+                        .iter()
+                        .chain(builtin_attr::EXTRA_ATTRIBUTES)
+                        .any(|attr| name == *attr)
+                    {
+                        return true;
+                    }
+                }
+            }
+
+            false
+        }
+
+        // We failed to resolve an attribute on this item earlier, and are falling back to treating
+        // the item as-is.
+        if self.def_collector.ignore_attrs_on.contains(&mod_item) {
+            return Ok(());
+        }
+
+        match attrs.iter().find(|attr| !is_builtin_attr(&attr.path)) {
+            Some(non_builtin_attr) => {
+                log::debug!("non-builtin attribute {}", non_builtin_attr.path);
+
+                let ast_id = AstIdWithPath::new(
+                    self.file_id,
+                    mod_item.ast_id(self.item_tree),
+                    non_builtin_attr.path.as_ref().clone(),
+                );
+                self.def_collector.unexpanded_macros.push(MacroDirective {
+                    module_id: self.module_id,
+                    depth: self.macro_depth + 1,
+                    kind: MacroDirectiveKind::Attr { ast_id, attr: non_builtin_attr.id, mod_item },
+                });
+
+                Err(())
+            }
+            None => Ok(()),
+        }
+    }
+
     fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) {
         for derive in attrs.by_key("derive").attrs() {
             match derive.parse_derive() {
@@ -1594,6 +1724,7 @@ mod tests {
             proc_macros: Default::default(),
             exports_proc_macros: false,
             from_glob_import: Default::default(),
+            ignore_attrs_on: FxHashSet::default(),
         };
         collector.seed_with_top_level();
         collector.collect();
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs
index 9e44547cb0..ea29da5daa 100644
--- a/crates/hir_def/src/type_ref.rs
+++ b/crates/hir_def/src/type_ref.rs
@@ -2,7 +2,6 @@
 //! be directly created from an ast::TypeRef, without further queries.
 
 use hir_expand::{name::Name, AstId, InFile};
-use std::convert::TryInto;
 use syntax::ast;
 
 use crate::{body::LowerCtx, path::Path};
@@ -80,9 +79,7 @@ pub enum TypeRef {
     Path(Path),
     RawPtr(Box<TypeRef>, Mutability),
     Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
-    // FIXME: for full const generics, the latter element (length) here is going to have to be an
-    // expression that is further lowered later in hir_ty.
-    Array(Box<TypeRef>, ConstScalar),
+    Array(Box<TypeRef> /*, Expr*/),
     Slice(Box<TypeRef>),
     /// A fn pointer. Last element of the vector is the return type.
     Fn(Vec<TypeRef>, bool /*varargs*/),
@@ -143,16 +140,7 @@ impl TypeRef {
                 TypeRef::RawPtr(Box::new(inner_ty), mutability)
             }
             ast::Type::ArrayType(inner) => {
-                // FIXME: This is a hack. We should probably reuse the machinery of
-                // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
-                // `hir_ty` level, which would allow knowing the type of:
-                // let v: [u8; 2 + 2] = [0u8; 4];
-                let len = inner
-                    .expr()
-                    .map(ConstScalar::usize_from_literal_expr)
-                    .unwrap_or(ConstScalar::Unknown);
-
-                TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())), len)
+                TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())))
             }
             ast::Type::SliceType(inner) => {
                 TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())))
@@ -224,7 +212,7 @@ impl TypeRef {
                 }
                 TypeRef::RawPtr(type_ref, _)
                 | TypeRef::Reference(type_ref, ..)
-                | TypeRef::Array(type_ref, _)
+                | TypeRef::Array(type_ref)
                 | TypeRef::Slice(type_ref) => go(&type_ref, f),
                 TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
                     for bound in bounds {
@@ -310,58 +298,3 @@ impl TypeBound {
         }
     }
 }
-
-/// A concrete constant value
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum ConstScalar {
-    // for now, we only support the trivial case of constant evaluating the length of an array
-    // Note that this is u64 because the target usize may be bigger than our usize
-    Usize(u64),
-
-    /// Case of an unknown value that rustc might know but we don't
-    // FIXME: this is a hack to get around chalk not being able to represent unevaluatable
-    // constants
-    // https://github.com/rust-analyzer/rust-analyzer/pull/8813#issuecomment-840679177
-    // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348
-    Unknown,
-}
-
-impl std::fmt::Display for ConstScalar {
-    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
-        match self {
-            ConstScalar::Usize(us) => write!(fmt, "{}", us),
-            ConstScalar::Unknown => write!(fmt, "_"),
-        }
-    }
-}
-
-impl ConstScalar {
-    /// Gets a target usize out of the ConstScalar
-    pub fn as_usize(&self) -> Option<u64> {
-        match self {
-            &ConstScalar::Usize(us) => Some(us),
-            _ => None,
-        }
-    }
-
-    // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
-    // parse stage.
-    fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar {
-        match expr {
-            ast::Expr::Literal(lit) => {
-                let lkind = lit.kind();
-                match lkind {
-                    ast::LiteralKind::IntNumber(num)
-                        if num.suffix() == None || num.suffix() == Some("usize") =>
-                    {
-                        num.value().and_then(|v| v.try_into().ok())
-                    }
-                    _ => None,
-                }
-            }
-            _ => None,
-        }
-        .map(ConstScalar::Usize)
-        .unwrap_or(ConstScalar::Unknown)
-    }
-}
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 280c25f111..af98021441 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -118,7 +118,6 @@ register_builtin! {
     EAGER:
     (compile_error, CompileError) => compile_error_expand,
     (concat, Concat) => concat_expand,
-    (concat_idents, ConcatIdents) => concat_idents_expand,
     (include, Include) => include_expand,
     (include_bytes, IncludeBytes) => include_bytes_expand,
     (include_str, IncludeStr) => include_str_expand,
@@ -374,28 +373,6 @@ fn concat_expand(
     ExpandResult { value: Some(ExpandedEager::new(quote!(#text), FragmentKind::Expr)), err }
 }
 
-fn concat_idents_expand(
-    _db: &dyn AstDatabase,
-    _arg_id: EagerMacroId,
-    tt: &tt::Subtree,
-) -> ExpandResult<Option<ExpandedEager>> {
-    let mut err = None;
-    let mut ident = String::new();
-    for (i, t) in tt.token_trees.iter().enumerate() {
-        match t {
-            tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => {
-                ident.push_str(id.text.as_str());
-            }
-            tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
-            _ => {
-                err.get_or_insert(mbe::ExpandError::UnexpectedToken);
-            }
-        }
-    }
-    let ident = tt::Ident { text: ident.into(), id: tt::TokenId::unspecified() };
-    ExpandResult { value: Some(ExpandedEager::new(quote!(#ident), FragmentKind::Expr)), err }
-}
-
 fn relative_file(
     db: &dyn AstDatabase,
     call_id: MacroCallId,
@@ -817,16 +794,4 @@ mod tests {
             expect![[r#""foor0bar\nfalse""#]],
         );
     }
-
-    #[test]
-    fn test_concat_idents_expand() {
-        check_expansion(
-            r##"
-            #[rustc_builtin_macro]
-            macro_rules! concat_idents {}
-            concat_idents!(foo, bar);
-            "##,
-            expect![[r#"foobar"#]],
-        );
-    }
 }
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 5a5dc9afdb..bcfd3e5242 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -212,7 +212,6 @@ pub mod known {
         std_panic,
         stringify,
         concat,
-        concat_idents,
         include,
         include_bytes,
         include_str,
diff --git a/crates/hir_ty/src/consteval.rs b/crates/hir_ty/src/consteval.rs
deleted file mode 100644
index e3ceb3d626..0000000000
--- a/crates/hir_ty/src/consteval.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-//! Constant evaluation details
-
-use std::convert::TryInto;
-
-use hir_def::{
-    builtin_type::BuiltinUint,
-    expr::{Expr, Literal},
-    type_ref::ConstScalar,
-};
-
-use crate::{Const, ConstData, ConstValue, Interner, TyKind};
-
-/// Extension trait for [`Const`]
-pub trait ConstExt {
-    /// Is a [`Const`] unknown?
-    fn is_unknown(&self) -> bool;
-}
-
-impl ConstExt for Const {
-    fn is_unknown(&self) -> bool {
-        match self.data(&Interner).value {
-            // interned Unknown
-            chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst {
-                interned: ConstScalar::Unknown,
-            }) => true,
-
-            // interned concrete anything else
-            chalk_ir::ConstValue::Concrete(..) => false,
-
-            _ => {
-                log::error!("is_unknown was called on a non-concrete constant value! {:?}", self);
-                true
-            }
-        }
-    }
-}
-
-// FIXME: support more than just evaluating literals
-pub fn eval_usize(expr: &Expr) -> Option<u64> {
-    match expr {
-        Expr::Literal(Literal::Uint(v, None))
-        | Expr::Literal(Literal::Uint(v, Some(BuiltinUint::Usize))) => (*v).try_into().ok(),
-        _ => None,
-    }
-}
-
-/// Interns a possibly-unknown target usize
-pub fn usize_const(value: Option<u64>) -> Const {
-    ConstData {
-        ty: TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(&Interner),
-        value: ConstValue::Concrete(chalk_ir::ConcreteConst {
-            interned: value.map(|value| ConstScalar::Usize(value)).unwrap_or(ConstScalar::Unknown),
-        }),
-    }
-    .intern(&Interner)
-}
diff --git a/crates/hir_ty/src/consts.rs b/crates/hir_ty/src/consts.rs
new file mode 100644
index 0000000000..0044b1cffc
--- /dev/null
+++ b/crates/hir_ty/src/consts.rs
@@ -0,0 +1,21 @@
+//! Handling of concrete const values
+
+/// A concrete constant value
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum ConstScalar {
+    // for now, we only support the trivial case of constant evaluating the length of an array
+    // Note that this is u64 because the target usize may be bigger than our usize
+    Usize(u64),
+
+    /// Case of an unknown value that rustc might know but we don't
+    Unknown,
+}
+
+impl std::fmt::Display for ConstScalar {
+    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+        match self {
+            ConstScalar::Usize(us) => write!(fmt, "{}", us),
+            ConstScalar::Unknown => write!(fmt, "_"),
+        }
+    }
+}
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index 6ee0529c62..e9762622f3 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -1119,7 +1119,6 @@ fn main() {
         (true, false, true) => (),
         (true) => (),
     }
-    match (true, false) { (true,) => {} }
     match (0) { () => () }
     match Unresolved::Bar { Unresolved::Baz => () }
 }
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index 7bbd1a1f7d..8a42966973 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -962,10 +962,11 @@ impl HirDisplay for TypeRef {
                 write!(f, "{}", mutability)?;
                 inner.hir_fmt(f)?;
             }
-            TypeRef::Array(inner, len) => {
+            TypeRef::Array(inner) => {
                 write!(f, "[")?;
                 inner.hir_fmt(f)?;
-                write!(f, "; {}]", len)?;
+                // FIXME: Array length?
+                write!(f, "; _]")?;
             }
             TypeRef::Slice(inner) => {
                 write!(f, "[")?;
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index b6b5a1b750..2178ffd077 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -3,7 +3,7 @@
 use std::iter::{repeat, repeat_with};
 use std::{mem, sync::Arc};
 
-use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
+use chalk_ir::{cast::Cast, fold::Shift, ConstData, Mutability, TyVariableKind};
 use hir_def::{
     expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
     path::{GenericArg, GenericArgs},
@@ -15,7 +15,9 @@ use stdx::always;
 use syntax::ast::RangeOp;
 
 use crate::{
-    autoderef, consteval,
+    autoderef,
+    consts::ConstScalar,
+    dummy_usize_const,
     lower::lower_to_chalk_mutability,
     mapping::from_chalk,
     method_resolution, op,
@@ -23,7 +25,7 @@ use crate::{
     static_lifetime, to_chalk_trait_id,
     traits::FnTrait,
     utils::{generics, Generics},
-    AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner,
+    AdtId, Binders, CallableDefId, ConstValue, FnPointer, FnSig, FnSubst, InEnvironment, Interner,
     ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
 };
 
@@ -722,7 +724,7 @@ impl<'a> InferenceContext<'a> {
                         for expr in items.iter() {
                             self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone()));
                         }
-                        Some(items.len() as u64)
+                        Some(items.len())
                     }
                     Array::Repeat { initializer, repeat } => {
                         self.infer_expr_coerce(
@@ -735,13 +737,20 @@ impl<'a> InferenceContext<'a> {
                                 TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner),
                             ),
                         );
-
-                        let repeat_expr = &self.body.exprs[*repeat];
-                        consteval::eval_usize(repeat_expr)
+                        // FIXME: support length for Repeat array expressions
+                        None
                     }
                 };
 
-                TyKind::Array(elem_ty, consteval::usize_const(len)).intern(&Interner)
+                let cd = ConstData {
+                    ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner),
+                    value: ConstValue::Concrete(chalk_ir::ConcreteConst {
+                        interned: len
+                            .map(|len| ConstScalar::Usize(len as u64))
+                            .unwrap_or(ConstScalar::Unknown),
+                    }),
+                };
+                TyKind::Array(elem_ty, cd.intern(&Interner)).intern(&Interner)
             }
             Expr::Literal(lit) => match lit {
                 Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner),
@@ -749,12 +758,11 @@ impl<'a> InferenceContext<'a> {
                     TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(&Interner))
                         .intern(&Interner)
                 }
-                Literal::ByteString(bs) => {
+                Literal::ByteString(..) => {
                     let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner);
 
-                    let len = consteval::usize_const(Some(bs.len() as u64));
-
-                    let array_type = TyKind::Array(byte_type, len).intern(&Interner);
+                    let array_type =
+                        TyKind::Array(byte_type, dummy_usize_const()).intern(&Interner);
                     TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner)
                 }
                 Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(&Interner),
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index 60b94a6422..aea354cdec 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -126,12 +126,11 @@ impl<'a> InferenceContext<'a> {
                     _ => &[],
                 };
 
-                let ((pre, post), n_uncovered_patterns) = match ellipsis {
-                    Some(idx) => {
-                        (args.split_at(idx), expectations.len().saturating_sub(args.len()))
-                    }
-                    None => ((&args[..], &[][..]), 0),
+                let (pre, post) = match ellipsis {
+                    Some(idx) => args.split_at(idx),
+                    None => (&args[..], &[][..]),
                 };
+                let n_uncovered_patterns = expectations.len().saturating_sub(args.len());
                 let err_ty = self.err_ty();
                 let mut expectations_iter =
                     expectations.iter().map(|a| a.assert_ty_ref(&Interner)).chain(repeat(&err_ty));
diff --git a/crates/hir_ty/src/interner.rs b/crates/hir_ty/src/interner.rs
index 7b41197477..4cbc9cd4fa 100644
--- a/crates/hir_ty/src/interner.rs
+++ b/crates/hir_ty/src/interner.rs
@@ -1,12 +1,11 @@
 //! Implementation of the Chalk `Interner` trait, which allows customizing the
 //! representation of the various objects Chalk deals with (types, goals etc.).
 
-use crate::{chalk_db, tls, GenericArg};
+use crate::{chalk_db, consts::ConstScalar, tls, GenericArg};
 use base_db::salsa::InternId;
 use chalk_ir::{Goal, GoalData};
 use hir_def::{
     intern::{impl_internable, InternStorage, Internable, Interned},
-    type_ref::ConstScalar,
     TypeAliasId,
 };
 use smallvec::SmallVec;
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index 15b61bedc1..d23eff513b 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -10,9 +10,9 @@ mod autoderef;
 mod builder;
 mod chalk_db;
 mod chalk_ext;
-pub mod consteval;
 mod infer;
 mod interner;
+mod consts;
 mod lower;
 mod mapping;
 mod op;
@@ -38,13 +38,9 @@ use chalk_ir::{
     interner::HasInterner,
     UintTy,
 };
-use hir_def::{
-    expr::ExprId,
-    type_ref::{ConstScalar, Rawness},
-    TypeParamId,
-};
+use hir_def::{expr::ExprId, type_ref::Rawness, TypeParamId};
 
-use crate::{db::HirDatabase, display::HirDisplay, utils::generics};
+use crate::{consts::ConstScalar, db::HirDatabase, display::HirDisplay, utils::generics};
 
 pub use autoderef::autoderef;
 pub use builder::TyBuilder;
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index bd8bb60286..9751b45e46 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -29,8 +29,8 @@ use stdx::impl_from;
 use syntax::ast;
 
 use crate::{
-    consteval,
     db::HirDatabase,
+    dummy_usize_const,
     mapping::ToChalk,
     static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
     utils::{
@@ -172,12 +172,11 @@ impl<'a> TyLoweringContext<'a> {
                 let inner_ty = self.lower_ty(inner);
                 TyKind::Raw(lower_to_chalk_mutability(*mutability), inner_ty).intern(&Interner)
             }
-            TypeRef::Array(inner, len) => {
+            TypeRef::Array(inner) => {
                 let inner_ty = self.lower_ty(inner);
-
-                let const_len = consteval::usize_const(len.as_usize());
-
-                TyKind::Array(inner_ty, const_len).intern(&Interner)
+                // FIXME: we don't have length info here because we don't store an expression for
+                // the length
+                TyKind::Array(inner_ty, dummy_usize_const()).intern(&Interner)
             }
             TypeRef::Slice(inner) => {
                 let inner_ty = self.lower_ty(inner);
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 27d347dbd9..273d8cfbb5 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -28,7 +28,7 @@ use unlinked_file::UnlinkedFile;
 
 use crate::{Assist, AssistId, AssistKind, FileId, Label, SourceChange};
 
-use self::fixes::DiagnosticWithFixes;
+use self::fixes::DiagnosticWithFix;
 
 #[derive(Debug)]
 pub struct Diagnostic {
@@ -36,14 +36,14 @@ pub struct Diagnostic {
     pub message: String,
     pub range: TextRange,
     pub severity: Severity,
-    pub fixes: Option<Vec<Assist>>,
+    pub fix: Option<Assist>,
     pub unused: bool,
     pub code: Option<DiagnosticCode>,
 }
 
 impl Diagnostic {
     fn error(range: TextRange, message: String) -> Self {
-        Self { message, range, severity: Severity::Error, fixes: None, unused: false, code: None }
+        Self { message, range, severity: Severity::Error, fix: None, unused: false, code: None }
     }
 
     fn hint(range: TextRange, message: String) -> Self {
@@ -51,14 +51,14 @@ impl Diagnostic {
             message,
             range,
             severity: Severity::WeakWarning,
-            fixes: None,
+            fix: None,
             unused: false,
             code: None,
         }
     }
 
-    fn with_fixes(self, fixes: Option<Vec<Assist>>) -> Self {
-        Self { fixes, ..self }
+    fn with_fix(self, fix: Option<Assist>) -> Self {
+        Self { fix, ..self }
     }
 
     fn with_unused(self, unused: bool) -> Self {
@@ -154,7 +154,7 @@ pub(crate) fn diagnostics(
             // Override severity and mark as unused.
             res.borrow_mut().push(
                 Diagnostic::hint(range, d.message())
-                    .with_fixes(d.fixes(&sema, resolve))
+                    .with_fix(d.fix(&sema, resolve))
                     .with_code(Some(d.code())),
             );
         })
@@ -210,23 +210,23 @@ pub(crate) fn diagnostics(
     res.into_inner()
 }
 
-fn diagnostic_with_fix<D: DiagnosticWithFixes>(
+fn diagnostic_with_fix<D: DiagnosticWithFix>(
     d: &D,
     sema: &Semantics<RootDatabase>,
     resolve: &AssistResolveStrategy,
 ) -> Diagnostic {
     Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message())
-        .with_fixes(d.fixes(&sema, resolve))
+        .with_fix(d.fix(&sema, resolve))
         .with_code(Some(d.code()))
 }
 
-fn warning_with_fix<D: DiagnosticWithFixes>(
+fn warning_with_fix<D: DiagnosticWithFix>(
     d: &D,
     sema: &Semantics<RootDatabase>,
     resolve: &AssistResolveStrategy,
 ) -> Diagnostic {
     Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message())
-        .with_fixes(d.fixes(&sema, resolve))
+        .with_fix(d.fix(&sema, resolve))
         .with_code(Some(d.code()))
 }
 
@@ -256,12 +256,12 @@ fn check_unnecessary_braces_in_use_statement(
 
         acc.push(
             Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string())
-                .with_fixes(Some(vec![fix(
+                .with_fix(Some(fix(
                     "remove_braces",
                     "Remove unnecessary braces",
                     SourceChange::from_text_edit(file_id, edit),
                     use_range,
-                )])),
+                ))),
         );
     }
 
@@ -309,23 +309,9 @@ mod tests {
     /// Takes a multi-file input fixture with annotated cursor positions,
     /// and checks that:
     ///  * a diagnostic is produced
-    ///  * the first diagnostic fix trigger range touches the input cursor position
+    ///  * this diagnostic fix trigger range touches the input cursor position
     ///  * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
     pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
-        check_nth_fix(0, ra_fixture_before, ra_fixture_after);
-    }
-    /// Takes a multi-file input fixture with annotated cursor positions,
-    /// and checks that:
-    ///  * a diagnostic is produced
-    ///  * every diagnostic fixes trigger range touches the input cursor position
-    ///  * that the contents of the file containing the cursor match `after` after each diagnostic fix is applied
-    pub(crate) fn check_fixes(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>) {
-        for (i, ra_fixture_after) in ra_fixtures_after.iter().enumerate() {
-            check_nth_fix(i, ra_fixture_before, ra_fixture_after)
-        }
-    }
-
-    fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
         let after = trim_indent(ra_fixture_after);
 
         let (analysis, file_position) = fixture::position(ra_fixture_before);
@@ -338,9 +324,9 @@ mod tests {
             .unwrap()
             .pop()
             .unwrap();
-        let fix = &diagnostic.fixes.unwrap()[nth];
+        let fix = diagnostic.fix.unwrap();
         let actual = {
-            let source_change = fix.source_change.as_ref().unwrap();
+            let source_change = fix.source_change.unwrap();
             let file_id = *source_change.source_file_edits.keys().next().unwrap();
             let mut actual = analysis.file_text(file_id).unwrap().to_string();
 
@@ -358,6 +344,7 @@ mod tests {
             file_position.offset
         );
     }
+
     /// Checks that there's a diagnostic *without* fix at `$0`.
     fn check_no_fix(ra_fixture: &str) {
         let (analysis, file_position) = fixture::position(ra_fixture);
@@ -370,7 +357,7 @@ mod tests {
             .unwrap()
             .pop()
             .unwrap();
-        assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {:?}", diagnostic);
+        assert!(diagnostic.fix.is_none(), "got a fix when none was expected: {:?}", diagnostic);
     }
 
     /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
@@ -388,7 +375,7 @@ mod tests {
         assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
     }
 
-    pub(crate) fn check_expect(ra_fixture: &str, expect: Expect) {
+    fn check_expect(ra_fixture: &str, expect: Expect) {
         let (analysis, file_id) = fixture::file(ra_fixture);
         let diagnostics = analysis
             .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
@@ -396,6 +383,374 @@ mod tests {
         expect.assert_debug_eq(&diagnostics)
     }
 
+    #[test]
+    fn test_wrap_return_type_option() {
+        check_fix(
+            r#"
+//- /main.rs crate:main deps:core
+use core::option::Option::{self, Some, None};
+
+fn div(x: i32, y: i32) -> Option<i32> {
+    if y == 0 {
+        return None;
+    }
+    x / y$0
+}
+//- /core/lib.rs crate:core
+pub mod result {
+    pub enum Result<T, E> { Ok(T), Err(E) }
+}
+pub mod option {
+    pub enum Option<T> { Some(T), None }
+}
+"#,
+            r#"
+use core::option::Option::{self, Some, None};
+
+fn div(x: i32, y: i32) -> Option<i32> {
+    if y == 0 {
+        return None;
+    }
+    Some(x / y)
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_wrap_return_type() {
+        check_fix(
+            r#"
+//- /main.rs crate:main deps:core
+use core::result::Result::{self, Ok, Err};
+
+fn div(x: i32, y: i32) -> Result<i32, ()> {
+    if y == 0 {
+        return Err(());
+    }
+    x / y$0
+}
+//- /core/lib.rs crate:core
+pub mod result {
+    pub enum Result<T, E> { Ok(T), Err(E) }
+}
+pub mod option {
+    pub enum Option<T> { Some(T), None }
+}
+"#,
+            r#"
+use core::result::Result::{self, Ok, Err};
+
+fn div(x: i32, y: i32) -> Result<i32, ()> {
+    if y == 0 {
+        return Err(());
+    }
+    Ok(x / y)
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_wrap_return_type_handles_generic_functions() {
+        check_fix(
+            r#"
+//- /main.rs crate:main deps:core
+use core::result::Result::{self, Ok, Err};
+
+fn div<T>(x: T) -> Result<T, i32> {
+    if x == 0 {
+        return Err(7);
+    }
+    $0x
+}
+//- /core/lib.rs crate:core
+pub mod result {
+    pub enum Result<T, E> { Ok(T), Err(E) }
+}
+pub mod option {
+    pub enum Option<T> { Some(T), None }
+}
+"#,
+            r#"
+use core::result::Result::{self, Ok, Err};
+
+fn div<T>(x: T) -> Result<T, i32> {
+    if x == 0 {
+        return Err(7);
+    }
+    Ok(x)
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_wrap_return_type_handles_type_aliases() {
+        check_fix(
+            r#"
+//- /main.rs crate:main deps:core
+use core::result::Result::{self, Ok, Err};
+
+type MyResult<T> = Result<T, ()>;
+
+fn div(x: i32, y: i32) -> MyResult<i32> {
+    if y == 0 {
+        return Err(());
+    }
+    x $0/ y
+}
+//- /core/lib.rs crate:core
+pub mod result {
+    pub enum Result<T, E> { Ok(T), Err(E) }
+}
+pub mod option {
+    pub enum Option<T> { Some(T), None }
+}
+"#,
+            r#"
+use core::result::Result::{self, Ok, Err};
+
+type MyResult<T> = Result<T, ()>;
+
+fn div(x: i32, y: i32) -> MyResult<i32> {
+    if y == 0 {
+        return Err(());
+    }
+    Ok(x / y)
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
+        check_no_diagnostics(
+            r#"
+//- /main.rs crate:main deps:core
+use core::result::Result::{self, Ok, Err};
+
+fn foo() -> Result<(), i32> { 0 }
+
+//- /core/lib.rs crate:core
+pub mod result {
+    pub enum Result<T, E> { Ok(T), Err(E) }
+}
+pub mod option {
+    pub enum Option<T> { Some(T), None }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
+        check_no_diagnostics(
+            r#"
+//- /main.rs crate:main deps:core
+use core::result::Result::{self, Ok, Err};
+
+enum SomeOtherEnum { Ok(i32), Err(String) }
+
+fn foo() -> SomeOtherEnum { 0 }
+
+//- /core/lib.rs crate:core
+pub mod result {
+    pub enum Result<T, E> { Ok(T), Err(E) }
+}
+pub mod option {
+    pub enum Option<T> { Some(T), None }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_fill_struct_fields_empty() {
+        check_fix(
+            r#"
+struct TestStruct { one: i32, two: i64 }
+
+fn test_fn() {
+    let s = TestStruct {$0};
+}
+"#,
+            r#"
+struct TestStruct { one: i32, two: i64 }
+
+fn test_fn() {
+    let s = TestStruct { one: (), two: ()};
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_fill_struct_fields_self() {
+        check_fix(
+            r#"
+struct TestStruct { one: i32 }
+
+impl TestStruct {
+    fn test_fn() { let s = Self {$0}; }
+}
+"#,
+            r#"
+struct TestStruct { one: i32 }
+
+impl TestStruct {
+    fn test_fn() { let s = Self { one: ()}; }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_fill_struct_fields_enum() {
+        check_fix(
+            r#"
+enum Expr {
+    Bin { lhs: Box<Expr>, rhs: Box<Expr> }
+}
+
+impl Expr {
+    fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
+        Expr::Bin {$0 }
+    }
+}
+"#,
+            r#"
+enum Expr {
+    Bin { lhs: Box<Expr>, rhs: Box<Expr> }
+}
+
+impl Expr {
+    fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
+        Expr::Bin { lhs: (), rhs: () }
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_fill_struct_fields_partial() {
+        check_fix(
+            r#"
+struct TestStruct { one: i32, two: i64 }
+
+fn test_fn() {
+    let s = TestStruct{ two: 2$0 };
+}
+"#,
+            r"
+struct TestStruct { one: i32, two: i64 }
+
+fn test_fn() {
+    let s = TestStruct{ two: 2, one: () };
+}
+",
+        );
+    }
+
+    #[test]
+    fn test_fill_struct_fields_raw_ident() {
+        check_fix(
+            r#"
+struct TestStruct { r#type: u8 }
+
+fn test_fn() {
+    TestStruct { $0 };
+}
+"#,
+            r"
+struct TestStruct { r#type: u8 }
+
+fn test_fn() {
+    TestStruct { r#type: ()  };
+}
+",
+        );
+    }
+
+    #[test]
+    fn test_fill_struct_fields_no_diagnostic() {
+        check_no_diagnostics(
+            r"
+            struct TestStruct { one: i32, two: i64 }
+
+            fn test_fn() {
+                let one = 1;
+                let s = TestStruct{ one, two: 2 };
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn test_fill_struct_fields_no_diagnostic_on_spread() {
+        check_no_diagnostics(
+            r"
+            struct TestStruct { one: i32, two: i64 }
+
+            fn test_fn() {
+                let one = 1;
+                let s = TestStruct{ ..a };
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn test_unresolved_module_diagnostic() {
+        check_expect(
+            r#"mod foo;"#,
+            expect![[r#"
+                [
+                    Diagnostic {
+                        message: "unresolved module",
+                        range: 0..8,
+                        severity: Error,
+                        fix: Some(
+                            Assist {
+                                id: AssistId(
+                                    "create_module",
+                                    QuickFix,
+                                ),
+                                label: "Create module",
+                                group: None,
+                                target: 0..8,
+                                source_change: Some(
+                                    SourceChange {
+                                        source_file_edits: {},
+                                        file_system_edits: [
+                                            CreateFile {
+                                                dst: AnchoredPathBuf {
+                                                    anchor: FileId(
+                                                        0,
+                                                    ),
+                                                    path: "foo.rs",
+                                                },
+                                                initial_contents: "",
+                                            },
+                                        ],
+                                        is_snippet: false,
+                                    },
+                                ),
+                            },
+                        ),
+                        unused: false,
+                        code: Some(
+                            DiagnosticCode(
+                                "unresolved-module",
+                            ),
+                        ),
+                    },
+                ]
+            "#]],
+        );
+    }
+
     #[test]
     fn test_unresolved_macro_range() {
         check_expect(
@@ -406,7 +761,7 @@ mod tests {
                         message: "unresolved macro `foo::bar!`",
                         range: 5..8,
                         severity: Error,
-                        fixes: None,
+                        fix: None,
                         unused: false,
                         code: Some(
                             DiagnosticCode(
@@ -437,7 +792,7 @@ fn main() {
 pub struct Foo { pub a: i32, pub b: i32 }
 "#,
             r#"
-fn some(, b: () ) {}
+fn some(, b: ()) {}
 fn items() {}
 fn here() {}
 
@@ -535,6 +890,53 @@ mod a {
         );
     }
 
+    #[test]
+    fn test_add_field_from_usage() {
+        check_fix(
+            r"
+fn main() {
+    Foo { bar: 3, baz$0: false};
+}
+struct Foo {
+    bar: i32
+}
+",
+            r"
+fn main() {
+    Foo { bar: 3, baz: false};
+}
+struct Foo {
+    bar: i32,
+    baz: bool
+}
+",
+        )
+    }
+
+    #[test]
+    fn test_add_field_in_other_file_from_usage() {
+        check_fix(
+            r#"
+//- /main.rs
+mod foo;
+
+fn main() {
+    foo::Foo { bar: 3, $0baz: false};
+}
+//- /foo.rs
+struct Foo {
+    bar: i32
+}
+"#,
+            r#"
+struct Foo {
+    bar: i32,
+    pub(crate) baz: bool
+}
+"#,
+        )
+    }
+
     #[test]
     fn test_disabled_diagnostics() {
         let mut config = DiagnosticsConfig::default();
@@ -552,29 +954,135 @@ mod a {
         assert!(!diagnostics.is_empty());
     }
 
+    #[test]
+    fn test_rename_incorrect_case() {
+        check_fix(
+            r#"
+pub struct test_struct$0 { one: i32 }
+
+pub fn some_fn(val: test_struct) -> test_struct {
+    test_struct { one: val.one + 1 }
+}
+"#,
+            r#"
+pub struct TestStruct { one: i32 }
+
+pub fn some_fn(val: TestStruct) -> TestStruct {
+    TestStruct { one: val.one + 1 }
+}
+"#,
+        );
+
+        check_fix(
+            r#"
+pub fn some_fn(NonSnakeCase$0: u8) -> u8 {
+    NonSnakeCase
+}
+"#,
+            r#"
+pub fn some_fn(non_snake_case: u8) -> u8 {
+    non_snake_case
+}
+"#,
+        );
+
+        check_fix(
+            r#"
+pub fn SomeFn$0(val: u8) -> u8 {
+    if val != 0 { SomeFn(val - 1) } else { val }
+}
+"#,
+            r#"
+pub fn some_fn(val: u8) -> u8 {
+    if val != 0 { some_fn(val - 1) } else { val }
+}
+"#,
+        );
+
+        check_fix(
+            r#"
+fn some_fn() {
+    let whatAWeird_Formatting$0 = 10;
+    another_func(whatAWeird_Formatting);
+}
+"#,
+            r#"
+fn some_fn() {
+    let what_a_weird_formatting = 10;
+    another_func(what_a_weird_formatting);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_uppercase_const_no_diagnostics() {
+        check_no_diagnostics(
+            r#"
+fn foo() {
+    const ANOTHER_ITEM$0: &str = "some_item";
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_rename_incorrect_case_struct_method() {
+        check_fix(
+            r#"
+pub struct TestStruct;
+
+impl TestStruct {
+    pub fn SomeFn$0() -> TestStruct {
+        TestStruct
+    }
+}
+"#,
+            r#"
+pub struct TestStruct;
+
+impl TestStruct {
+    pub fn some_fn() -> TestStruct {
+        TestStruct
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() {
+        let input = r#"fn FOO$0() {}"#;
+        let expected = r#"fn foo() {}"#;
+
+        let (analysis, file_position) = fixture::position(input);
+        let diagnostics = analysis
+            .diagnostics(
+                &DiagnosticsConfig::default(),
+                AssistResolveStrategy::All,
+                file_position.file_id,
+            )
+            .unwrap();
+        assert_eq!(diagnostics.len(), 1);
+
+        check_fix(input, expected);
+    }
+
     #[test]
     fn unlinked_file_prepend_first_item() {
         cov_mark::check!(unlinked_file_prepend_before_first_item);
-        // Only tests the first one for `pub mod` since the rest are the same
-        check_fixes(
+        check_fix(
             r#"
 //- /main.rs
 fn f() {}
 //- /foo.rs
 $0
 "#,
-            vec![
-                r#"
+            r#"
 mod foo;
 
 fn f() {}
 "#,
-                r#"
-pub mod foo;
-
-fn f() {}
-"#,
-            ],
         );
     }
 
diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide/src/diagnostics/field_shorthand.rs
index 01bd2dba67..2b1787f9b1 100644
--- a/crates/ide/src/diagnostics/field_shorthand.rs
+++ b/crates/ide/src/diagnostics/field_shorthand.rs
@@ -46,13 +46,14 @@ fn check_expr_field_shorthand(
 
         let field_range = record_field.syntax().text_range();
         acc.push(
-            Diagnostic::hint(field_range, "Shorthand struct initialization".to_string())
-                .with_fixes(Some(vec![fix(
+            Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()).with_fix(
+                Some(fix(
                     "use_expr_field_shorthand",
                     "Use struct shorthand initialization",
                     SourceChange::from_text_edit(file_id, edit),
                     field_range,
-                )])),
+                )),
+            ),
         );
     }
 }
@@ -85,13 +86,13 @@ fn check_pat_field_shorthand(
         let edit = edit_builder.finish();
 
         let field_range = record_pat_field.syntax().text_range();
-        acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fixes(
-            Some(vec![fix(
+        acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fix(
+            Some(fix(
                 "use_pat_field_shorthand",
                 "Use struct field shorthand",
                 SourceChange::from_text_edit(file_id, edit),
                 field_range,
-            )]),
+            )),
         ));
     }
 }
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index 258ac69744..15821500f1 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -1,31 +1,297 @@
 //! Provides a way to attach fixes to the diagnostics.
 //! The same module also has all curret custom fixes for the diagnostics implemented.
-mod change_case;
-mod create_field;
-mod fill_missing_fields;
-mod remove_semicolon;
-mod replace_with_find_map;
-mod unresolved_module;
-mod wrap_tail_expr;
-
-use hir::{diagnostics::Diagnostic, Semantics};
+use hir::{
+    db::AstDatabase,
+    diagnostics::{
+        Diagnostic, IncorrectCase, MissingFields, MissingOkOrSomeInTailExpr, NoSuchField,
+        RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, UnresolvedModule,
+    },
+    HasSource, HirDisplay, InFile, Semantics, VariantDef,
+};
 use ide_assists::AssistResolveStrategy;
-use ide_db::RootDatabase;
+use ide_db::{
+    base_db::{AnchoredPathBuf, FileId},
+    source_change::{FileSystemEdit, SourceChange},
+    RootDatabase,
+};
+use syntax::{
+    algo,
+    ast::{self, edit::IndentLevel, make, ArgListOwner},
+    AstNode, TextRange,
+};
+use text_edit::TextEdit;
 
-use crate::Assist;
+use crate::{
+    diagnostics::{fix, unresolved_fix},
+    references::rename::rename_with_semantics,
+    Assist, FilePosition,
+};
 
-/// A [Diagnostic] that potentially has some fixes available.
+/// A [Diagnostic] that potentially has a fix available.
 ///
 /// [Diagnostic]: hir::diagnostics::Diagnostic
-pub(crate) trait DiagnosticWithFixes: Diagnostic {
+pub(crate) trait DiagnosticWithFix: Diagnostic {
     /// `resolve` determines if the diagnostic should fill in the `edit` field
     /// of the assist.
     ///
     /// If `resolve` is false, the edit will be computed later, on demand, and
     /// can be omitted.
-    fn fixes(
+    fn fix(
         &self,
         sema: &Semantics<RootDatabase>,
         _resolve: &AssistResolveStrategy,
-    ) -> Option<Vec<Assist>>;
+    ) -> Option<Assist>;
+}
+
+impl DiagnosticWithFix for UnresolvedModule {
+    fn fix(
+        &self,
+        sema: &Semantics<RootDatabase>,
+        _resolve: &AssistResolveStrategy,
+    ) -> Option<Assist> {
+        let root = sema.db.parse_or_expand(self.file)?;
+        let unresolved_module = self.decl.to_node(&root);
+        Some(fix(
+            "create_module",
+            "Create module",
+            FileSystemEdit::CreateFile {
+                dst: AnchoredPathBuf {
+                    anchor: self.file.original_file(sema.db),
+                    path: self.candidate.clone(),
+                },
+                initial_contents: "".to_string(),
+            }
+            .into(),
+            unresolved_module.syntax().text_range(),
+        ))
+    }
+}
+
+impl DiagnosticWithFix for NoSuchField {
+    fn fix(
+        &self,
+        sema: &Semantics<RootDatabase>,
+        _resolve: &AssistResolveStrategy,
+    ) -> Option<Assist> {
+        let root = sema.db.parse_or_expand(self.file)?;
+        missing_record_expr_field_fix(
+            &sema,
+            self.file.original_file(sema.db),
+            &self.field.to_node(&root),
+        )
+    }
+}
+
+impl DiagnosticWithFix for MissingFields {
+    fn fix(
+        &self,
+        sema: &Semantics<RootDatabase>,
+        _resolve: &AssistResolveStrategy,
+    ) -> Option<Assist> {
+        // Note that although we could add a diagnostics to
+        // fill the missing tuple field, e.g :
+        // `struct A(usize);`
+        // `let a = A { 0: () }`
+        // but it is uncommon usage and it should not be encouraged.
+        if self.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
+            return None;
+        }
+
+        let root = sema.db.parse_or_expand(self.file)?;
+        let field_list_parent = self.field_list_parent.to_node(&root);
+        let old_field_list = field_list_parent.record_expr_field_list()?;
+        let mut new_field_list = old_field_list.clone();
+        for f in self.missed_fields.iter() {
+            let field =
+                make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit()));
+            new_field_list = new_field_list.append_field(&field);
+        }
+
+        let edit = {
+            let mut builder = TextEdit::builder();
+            algo::diff(&old_field_list.syntax(), &new_field_list.syntax())
+                .into_text_edit(&mut builder);
+            builder.finish()
+        };
+        Some(fix(
+            "fill_missing_fields",
+            "Fill struct fields",
+            SourceChange::from_text_edit(self.file.original_file(sema.db), edit),
+            sema.original_range(&field_list_parent.syntax()).range,
+        ))
+    }
+}
+
+impl DiagnosticWithFix for MissingOkOrSomeInTailExpr {
+    fn fix(
+        &self,
+        sema: &Semantics<RootDatabase>,
+        _resolve: &AssistResolveStrategy,
+    ) -> Option<Assist> {
+        let root = sema.db.parse_or_expand(self.file)?;
+        let tail_expr = self.expr.to_node(&root);
+        let tail_expr_range = tail_expr.syntax().text_range();
+        let replacement = format!("{}({})", self.required, tail_expr.syntax());
+        let edit = TextEdit::replace(tail_expr_range, replacement);
+        let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);
+        let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" };
+        Some(fix("wrap_tail_expr", name, source_change, tail_expr_range))
+    }
+}
+
+impl DiagnosticWithFix for RemoveThisSemicolon {
+    fn fix(
+        &self,
+        sema: &Semantics<RootDatabase>,
+        _resolve: &AssistResolveStrategy,
+    ) -> Option<Assist> {
+        let root = sema.db.parse_or_expand(self.file)?;
+
+        let semicolon = self
+            .expr
+            .to_node(&root)
+            .syntax()
+            .parent()
+            .and_then(ast::ExprStmt::cast)
+            .and_then(|expr| expr.semicolon_token())?
+            .text_range();
+
+        let edit = TextEdit::delete(semicolon);
+        let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);
+
+        Some(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon))
+    }
+}
+
+impl DiagnosticWithFix for IncorrectCase {
+    fn fix(
+        &self,
+        sema: &Semantics<RootDatabase>,
+        resolve: &AssistResolveStrategy,
+    ) -> Option<Assist> {
+        let root = sema.db.parse_or_expand(self.file)?;
+        let name_node = self.ident.to_node(&root);
+
+        let name_node = InFile::new(self.file, name_node.syntax());
+        let frange = name_node.original_file_range(sema.db);
+        let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
+
+        let label = format!("Rename to {}", self.suggested_text);
+        let mut res = unresolved_fix("change_case", &label, frange.range);
+        if resolve.should_resolve(&res.id) {
+            let source_change = rename_with_semantics(sema, file_position, &self.suggested_text);
+            res.source_change = Some(source_change.ok().unwrap_or_default());
+        }
+
+        Some(res)
+    }
+}
+
+impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap {
+    fn fix(
+        &self,
+        sema: &Semantics<RootDatabase>,
+        _resolve: &AssistResolveStrategy,
+    ) -> Option<Assist> {
+        let root = sema.db.parse_or_expand(self.file)?;
+        let next_expr = self.next_expr.to_node(&root);
+        let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?;
+
+        let filter_map_call = ast::MethodCallExpr::cast(next_call.receiver()?.syntax().clone())?;
+        let filter_map_name_range = filter_map_call.name_ref()?.ident_token()?.text_range();
+        let filter_map_args = filter_map_call.arg_list()?;
+
+        let range_to_replace =
+            TextRange::new(filter_map_name_range.start(), next_expr.syntax().text_range().end());
+        let replacement = format!("find_map{}", filter_map_args.syntax().text());
+        let trigger_range = next_expr.syntax().text_range();
+
+        let edit = TextEdit::replace(range_to_replace, replacement);
+
+        let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);
+
+        Some(fix(
+            "replace_with_find_map",
+            "Replace filter_map(..).next() with find_map()",
+            source_change,
+            trigger_range,
+        ))
+    }
+}
+
+fn missing_record_expr_field_fix(
+    sema: &Semantics<RootDatabase>,
+    usage_file_id: FileId,
+    record_expr_field: &ast::RecordExprField,
+) -> Option<Assist> {
+    let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?;
+    let def_id = sema.resolve_variant(record_lit)?;
+    let module;
+    let def_file_id;
+    let record_fields = match def_id {
+        VariantDef::Struct(s) => {
+            module = s.module(sema.db);
+            let source = s.source(sema.db)?;
+            def_file_id = source.file_id;
+            let fields = source.value.field_list()?;
+            record_field_list(fields)?
+        }
+        VariantDef::Union(u) => {
+            module = u.module(sema.db);
+            let source = u.source(sema.db)?;
+            def_file_id = source.file_id;
+            source.value.record_field_list()?
+        }
+        VariantDef::Variant(e) => {
+            module = e.module(sema.db);
+            let source = e.source(sema.db)?;
+            def_file_id = source.file_id;
+            let fields = source.value.field_list()?;
+            record_field_list(fields)?
+        }
+    };
+    let def_file_id = def_file_id.original_file(sema.db);
+
+    let new_field_type = sema.type_of_expr(&record_expr_field.expr()?)?;
+    if new_field_type.is_unknown() {
+        return None;
+    }
+    let new_field = make::record_field(
+        None,
+        make::name(&record_expr_field.field_name()?.text()),
+        make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?),
+    );
+
+    let last_field = record_fields.fields().last()?;
+    let last_field_syntax = last_field.syntax();
+    let indent = IndentLevel::from_node(last_field_syntax);
+
+    let mut new_field = new_field.to_string();
+    if usage_file_id != def_file_id {
+        new_field = format!("pub(crate) {}", new_field);
+    }
+    new_field = format!("\n{}{}", indent, new_field);
+
+    let needs_comma = !last_field_syntax.to_string().ends_with(',');
+    if needs_comma {
+        new_field = format!(",{}", new_field);
+    }
+
+    let source_change = SourceChange::from_text_edit(
+        def_file_id,
+        TextEdit::insert(last_field_syntax.text_range().end(), new_field),
+    );
+    return Some(fix(
+        "create_field",
+        "Create field",
+        source_change,
+        record_expr_field.syntax().text_range(),
+    ));
+
+    fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> {
+        match field_def_list {
+            ast::FieldList::RecordFieldList(it) => Some(it),
+            ast::FieldList::TupleFieldList(_) => None,
+        }
+    }
 }
diff --git a/crates/ide/src/diagnostics/fixes/change_case.rs b/crates/ide/src/diagnostics/fixes/change_case.rs
deleted file mode 100644
index 42be3375f3..0000000000
--- a/crates/ide/src/diagnostics/fixes/change_case.rs
+++ /dev/null
@@ -1,155 +0,0 @@
-use hir::{db::AstDatabase, diagnostics::IncorrectCase, InFile, Semantics};
-use ide_assists::{Assist, AssistResolveStrategy};
-use ide_db::{base_db::FilePosition, RootDatabase};
-use syntax::AstNode;
-
-use crate::{
-    diagnostics::{unresolved_fix, DiagnosticWithFixes},
-    references::rename::rename_with_semantics,
-};
-
-impl DiagnosticWithFixes for IncorrectCase {
-    fn fixes(
-        &self,
-        sema: &Semantics<RootDatabase>,
-        resolve: &AssistResolveStrategy,
-    ) -> Option<Vec<Assist>> {
-        let root = sema.db.parse_or_expand(self.file)?;
-        let name_node = self.ident.to_node(&root);
-
-        let name_node = InFile::new(self.file, name_node.syntax());
-        let frange = name_node.original_file_range(sema.db);
-        let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
-
-        let label = format!("Rename to {}", self.suggested_text);
-        let mut res = unresolved_fix("change_case", &label, frange.range);
-        if resolve.should_resolve(&res.id) {
-            let source_change = rename_with_semantics(sema, file_position, &self.suggested_text);
-            res.source_change = Some(source_change.ok().unwrap_or_default());
-        }
-
-        Some(vec![res])
-    }
-}
-
-#[cfg(test)]
-mod change_case {
-    use crate::{
-        diagnostics::tests::{check_fix, check_no_diagnostics},
-        fixture, AssistResolveStrategy, DiagnosticsConfig,
-    };
-
-    #[test]
-    fn test_rename_incorrect_case() {
-        check_fix(
-            r#"
-pub struct test_struct$0 { one: i32 }
-
-pub fn some_fn(val: test_struct) -> test_struct {
-    test_struct { one: val.one + 1 }
-}
-"#,
-            r#"
-pub struct TestStruct { one: i32 }
-
-pub fn some_fn(val: TestStruct) -> TestStruct {
-    TestStruct { one: val.one + 1 }
-}
-"#,
-        );
-
-        check_fix(
-            r#"
-pub fn some_fn(NonSnakeCase$0: u8) -> u8 {
-    NonSnakeCase
-}
-"#,
-            r#"
-pub fn some_fn(non_snake_case: u8) -> u8 {
-    non_snake_case
-}
-"#,
-        );
-
-        check_fix(
-            r#"
-pub fn SomeFn$0(val: u8) -> u8 {
-    if val != 0 { SomeFn(val - 1) } else { val }
-}
-"#,
-            r#"
-pub fn some_fn(val: u8) -> u8 {
-    if val != 0 { some_fn(val - 1) } else { val }
-}
-"#,
-        );
-
-        check_fix(
-            r#"
-fn some_fn() {
-    let whatAWeird_Formatting$0 = 10;
-    another_func(whatAWeird_Formatting);
-}
-"#,
-            r#"
-fn some_fn() {
-    let what_a_weird_formatting = 10;
-    another_func(what_a_weird_formatting);
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_uppercase_const_no_diagnostics() {
-        check_no_diagnostics(
-            r#"
-fn foo() {
-    const ANOTHER_ITEM$0: &str = "some_item";
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_rename_incorrect_case_struct_method() {
-        check_fix(
-            r#"
-pub struct TestStruct;
-
-impl TestStruct {
-    pub fn SomeFn$0() -> TestStruct {
-        TestStruct
-    }
-}
-"#,
-            r#"
-pub struct TestStruct;
-
-impl TestStruct {
-    pub fn some_fn() -> TestStruct {
-        TestStruct
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() {
-        let input = r#"fn FOO$0() {}"#;
-        let expected = r#"fn foo() {}"#;
-
-        let (analysis, file_position) = fixture::position(input);
-        let diagnostics = analysis
-            .diagnostics(
-                &DiagnosticsConfig::default(),
-                AssistResolveStrategy::All,
-                file_position.file_id,
-            )
-            .unwrap();
-        assert_eq!(diagnostics.len(), 1);
-
-        check_fix(input, expected);
-    }
-}
diff --git a/crates/ide/src/diagnostics/fixes/create_field.rs b/crates/ide/src/diagnostics/fixes/create_field.rs
deleted file mode 100644
index a5f457dcea..0000000000
--- a/crates/ide/src/diagnostics/fixes/create_field.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-use hir::{db::AstDatabase, diagnostics::NoSuchField, HasSource, HirDisplay, Semantics};
-use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase};
-use syntax::{
-    ast::{self, edit::IndentLevel, make},
-    AstNode,
-};
-use text_edit::TextEdit;
-
-use crate::{
-    diagnostics::{fix, DiagnosticWithFixes},
-    Assist, AssistResolveStrategy,
-};
-impl DiagnosticWithFixes for NoSuchField {
-    fn fixes(
-        &self,
-        sema: &Semantics<RootDatabase>,
-        _resolve: &AssistResolveStrategy,
-    ) -> Option<Vec<Assist>> {
-        let root = sema.db.parse_or_expand(self.file)?;
-        missing_record_expr_field_fixes(
-            &sema,
-            self.file.original_file(sema.db),
-            &self.field.to_node(&root),
-        )
-    }
-}
-
-fn missing_record_expr_field_fixes(
-    sema: &Semantics<RootDatabase>,
-    usage_file_id: FileId,
-    record_expr_field: &ast::RecordExprField,
-) -> Option<Vec<Assist>> {
-    let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?;
-    let def_id = sema.resolve_variant(record_lit)?;
-    let module;
-    let def_file_id;
-    let record_fields = match def_id {
-        hir::VariantDef::Struct(s) => {
-            module = s.module(sema.db);
-            let source = s.source(sema.db)?;
-            def_file_id = source.file_id;
-            let fields = source.value.field_list()?;
-            record_field_list(fields)?
-        }
-        hir::VariantDef::Union(u) => {
-            module = u.module(sema.db);
-            let source = u.source(sema.db)?;
-            def_file_id = source.file_id;
-            source.value.record_field_list()?
-        }
-        hir::VariantDef::Variant(e) => {
-            module = e.module(sema.db);
-            let source = e.source(sema.db)?;
-            def_file_id = source.file_id;
-            let fields = source.value.field_list()?;
-            record_field_list(fields)?
-        }
-    };
-    let def_file_id = def_file_id.original_file(sema.db);
-
-    let new_field_type = sema.type_of_expr(&record_expr_field.expr()?)?;
-    if new_field_type.is_unknown() {
-        return None;
-    }
-    let new_field = make::record_field(
-        None,
-        make::name(&record_expr_field.field_name()?.text()),
-        make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?),
-    );
-
-    let last_field = record_fields.fields().last()?;
-    let last_field_syntax = last_field.syntax();
-    let indent = IndentLevel::from_node(last_field_syntax);
-
-    let mut new_field = new_field.to_string();
-    if usage_file_id != def_file_id {
-        new_field = format!("pub(crate) {}", new_field);
-    }
-    new_field = format!("\n{}{}", indent, new_field);
-
-    let needs_comma = !last_field_syntax.to_string().ends_with(',');
-    if needs_comma {
-        new_field = format!(",{}", new_field);
-    }
-
-    let source_change = SourceChange::from_text_edit(
-        def_file_id,
-        TextEdit::insert(last_field_syntax.text_range().end(), new_field),
-    );
-
-    return Some(vec![fix(
-        "create_field",
-        "Create field",
-        source_change,
-        record_expr_field.syntax().text_range(),
-    )]);
-
-    fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> {
-        match field_def_list {
-            ast::FieldList::RecordFieldList(it) => Some(it),
-            ast::FieldList::TupleFieldList(_) => None,
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::diagnostics::tests::check_fix;
-
-    #[test]
-    fn test_add_field_from_usage() {
-        check_fix(
-            r"
-fn main() {
-    Foo { bar: 3, baz$0: false};
-}
-struct Foo {
-    bar: i32
-}
-",
-            r"
-fn main() {
-    Foo { bar: 3, baz: false};
-}
-struct Foo {
-    bar: i32,
-    baz: bool
-}
-",
-        )
-    }
-
-    #[test]
-    fn test_add_field_in_other_file_from_usage() {
-        check_fix(
-            r#"
-//- /main.rs
-mod foo;
-
-fn main() {
-    foo::Foo { bar: 3, $0baz: false};
-}
-//- /foo.rs
-struct Foo {
-    bar: i32
-}
-"#,
-            r#"
-struct Foo {
-    bar: i32,
-    pub(crate) baz: bool
-}
-"#,
-        )
-    }
-}
diff --git a/crates/ide/src/diagnostics/fixes/fill_missing_fields.rs b/crates/ide/src/diagnostics/fixes/fill_missing_fields.rs
deleted file mode 100644
index b5dd64c08c..0000000000
--- a/crates/ide/src/diagnostics/fixes/fill_missing_fields.rs
+++ /dev/null
@@ -1,217 +0,0 @@
-use hir::{db::AstDatabase, diagnostics::MissingFields, Semantics};
-use ide_assists::AssistResolveStrategy;
-use ide_db::{source_change::SourceChange, RootDatabase};
-use syntax::{algo, ast::make, AstNode};
-use text_edit::TextEdit;
-
-use crate::{
-    diagnostics::{fix, fixes::DiagnosticWithFixes},
-    Assist,
-};
-
-impl DiagnosticWithFixes for MissingFields {
-    fn fixes(
-        &self,
-        sema: &Semantics<RootDatabase>,
-        _resolve: &AssistResolveStrategy,
-    ) -> Option<Vec<Assist>> {
-        // Note that although we could add a diagnostics to
-        // fill the missing tuple field, e.g :
-        // `struct A(usize);`
-        // `let a = A { 0: () }`
-        // but it is uncommon usage and it should not be encouraged.
-        if self.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
-            return None;
-        }
-
-        let root = sema.db.parse_or_expand(self.file)?;
-        let field_list_parent = self.field_list_parent.to_node(&root);
-        let old_field_list = field_list_parent.record_expr_field_list()?;
-        let new_field_list = old_field_list.clone_for_update();
-        for f in self.missed_fields.iter() {
-            let field =
-                make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit()))
-                    .clone_for_update();
-            new_field_list.add_field(field);
-        }
-
-        let edit = {
-            let mut builder = TextEdit::builder();
-            algo::diff(&old_field_list.syntax(), &new_field_list.syntax())
-                .into_text_edit(&mut builder);
-            builder.finish()
-        };
-        Some(vec![fix(
-            "fill_missing_fields",
-            "Fill struct fields",
-            SourceChange::from_text_edit(self.file.original_file(sema.db), edit),
-            sema.original_range(&field_list_parent.syntax()).range,
-        )])
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::diagnostics::tests::{check_fix, check_no_diagnostics};
-
-    #[test]
-    fn test_fill_struct_fields_empty() {
-        check_fix(
-            r#"
-struct TestStruct { one: i32, two: i64 }
-
-fn test_fn() {
-    let s = TestStruct {$0};
-}
-"#,
-            r#"
-struct TestStruct { one: i32, two: i64 }
-
-fn test_fn() {
-    let s = TestStruct { one: (), two: () };
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_fill_struct_fields_self() {
-        check_fix(
-            r#"
-struct TestStruct { one: i32 }
-
-impl TestStruct {
-    fn test_fn() { let s = Self {$0}; }
-}
-"#,
-            r#"
-struct TestStruct { one: i32 }
-
-impl TestStruct {
-    fn test_fn() { let s = Self { one: () }; }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_fill_struct_fields_enum() {
-        check_fix(
-            r#"
-enum Expr {
-    Bin { lhs: Box<Expr>, rhs: Box<Expr> }
-}
-
-impl Expr {
-    fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
-        Expr::Bin {$0 }
-    }
-}
-"#,
-            r#"
-enum Expr {
-    Bin { lhs: Box<Expr>, rhs: Box<Expr> }
-}
-
-impl Expr {
-    fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
-        Expr::Bin { lhs: (), rhs: () }
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_fill_struct_fields_partial() {
-        check_fix(
-            r#"
-struct TestStruct { one: i32, two: i64 }
-
-fn test_fn() {
-    let s = TestStruct{ two: 2$0 };
-}
-"#,
-            r"
-struct TestStruct { one: i32, two: i64 }
-
-fn test_fn() {
-    let s = TestStruct{ two: 2, one: () };
-}
-",
-        );
-    }
-
-    #[test]
-    fn test_fill_struct_fields_raw_ident() {
-        check_fix(
-            r#"
-struct TestStruct { r#type: u8 }
-
-fn test_fn() {
-    TestStruct { $0 };
-}
-"#,
-            r"
-struct TestStruct { r#type: u8 }
-
-fn test_fn() {
-    TestStruct { r#type: ()  };
-}
-",
-        );
-    }
-
-    #[test]
-    fn test_fill_struct_fields_no_diagnostic() {
-        check_no_diagnostics(
-            r#"
-struct TestStruct { one: i32, two: i64 }
-
-fn test_fn() {
-    let one = 1;
-    let s = TestStruct{ one, two: 2 };
-}
-        "#,
-        );
-    }
-
-    #[test]
-    fn test_fill_struct_fields_no_diagnostic_on_spread() {
-        check_no_diagnostics(
-            r#"
-struct TestStruct { one: i32, two: i64 }
-
-fn test_fn() {
-    let one = 1;
-    let s = TestStruct{ ..a };
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_fill_struct_fields_blank_line() {
-        check_fix(
-            r#"
-struct S { a: (), b: () }
-
-fn f() {
-    S {
-        $0
-    };
-}
-"#,
-            r#"
-struct S { a: (), b: () }
-
-fn f() {
-    S {
-        a: (),
-        b: (),
-    };
-}
-"#,
-        );
-    }
-}
diff --git a/crates/ide/src/diagnostics/fixes/remove_semicolon.rs b/crates/ide/src/diagnostics/fixes/remove_semicolon.rs
deleted file mode 100644
index f1724d4791..0000000000
--- a/crates/ide/src/diagnostics/fixes/remove_semicolon.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use hir::{db::AstDatabase, diagnostics::RemoveThisSemicolon, Semantics};
-use ide_assists::{Assist, AssistResolveStrategy};
-use ide_db::{source_change::SourceChange, RootDatabase};
-use syntax::{ast, AstNode};
-use text_edit::TextEdit;
-
-use crate::diagnostics::{fix, DiagnosticWithFixes};
-
-impl DiagnosticWithFixes for RemoveThisSemicolon {
-    fn fixes(
-        &self,
-        sema: &Semantics<RootDatabase>,
-        _resolve: &AssistResolveStrategy,
-    ) -> Option<Vec<Assist>> {
-        let root = sema.db.parse_or_expand(self.file)?;
-
-        let semicolon = self
-            .expr
-            .to_node(&root)
-            .syntax()
-            .parent()
-            .and_then(ast::ExprStmt::cast)
-            .and_then(|expr| expr.semicolon_token())?
-            .text_range();
-
-        let edit = TextEdit::delete(semicolon);
-        let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);
-
-        Some(vec![fix("remove_semicolon", "Remove this semicolon", source_change, semicolon)])
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::diagnostics::tests::check_fix;
-
-    #[test]
-    fn remove_semicolon() {
-        check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);
-    }
-}
diff --git a/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs b/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs
deleted file mode 100644
index 444bf563b5..0000000000
--- a/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs
+++ /dev/null
@@ -1,84 +0,0 @@
-use hir::{db::AstDatabase, diagnostics::ReplaceFilterMapNextWithFindMap, Semantics};
-use ide_assists::{Assist, AssistResolveStrategy};
-use ide_db::{source_change::SourceChange, RootDatabase};
-use syntax::{
-    ast::{self, ArgListOwner},
-    AstNode, TextRange,
-};
-use text_edit::TextEdit;
-
-use crate::diagnostics::{fix, DiagnosticWithFixes};
-
-impl DiagnosticWithFixes for ReplaceFilterMapNextWithFindMap {
-    fn fixes(
-        &self,
-        sema: &Semantics<RootDatabase>,
-        _resolve: &AssistResolveStrategy,
-    ) -> Option<Vec<Assist>> {
-        let root = sema.db.parse_or_expand(self.file)?;
-        let next_expr = self.next_expr.to_node(&root);
-        let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?;
-
-        let filter_map_call = ast::MethodCallExpr::cast(next_call.receiver()?.syntax().clone())?;
-        let filter_map_name_range = filter_map_call.name_ref()?.ident_token()?.text_range();
-        let filter_map_args = filter_map_call.arg_list()?;
-
-        let range_to_replace =
-            TextRange::new(filter_map_name_range.start(), next_expr.syntax().text_range().end());
-        let replacement = format!("find_map{}", filter_map_args.syntax().text());
-        let trigger_range = next_expr.syntax().text_range();
-
-        let edit = TextEdit::replace(range_to_replace, replacement);
-
-        let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);
-
-        Some(vec![fix(
-            "replace_with_find_map",
-            "Replace filter_map(..).next() with find_map()",
-            source_change,
-            trigger_range,
-        )])
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::diagnostics::tests::check_fix;
-
-    #[test]
-    fn replace_with_wind_map() {
-        check_fix(
-            r#"
-//- /main.rs crate:main deps:core
-use core::iter::Iterator;
-use core::option::Option::{self, Some, None};
-fn foo() {
-    let m = [1, 2, 3].iter().$0filter_map(|x| if *x == 2 { Some (4) } else { None }).next();
-}
-//- /core/lib.rs crate:core
-pub mod option {
-    pub enum Option<T> { Some(T), None }
-}
-pub mod iter {
-    pub trait Iterator {
-        type Item;
-        fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap }
-        fn next(&mut self) -> Option<Self::Item>;
-    }
-    pub struct FilterMap {}
-    impl Iterator for FilterMap {
-        type Item = i32;
-        fn next(&mut self) -> i32 { 7 }
-    }
-}
-"#,
-            r#"
-use core::iter::Iterator;
-use core::option::Option::{self, Some, None};
-fn foo() {
-    let m = [1, 2, 3].iter().find_map(|x| if *x == 2 { Some (4) } else { None });
-}
-"#,
-        )
-    }
-}
diff --git a/crates/ide/src/diagnostics/fixes/unresolved_module.rs b/crates/ide/src/diagnostics/fixes/unresolved_module.rs
deleted file mode 100644
index b3d0283bbf..0000000000
--- a/crates/ide/src/diagnostics/fixes/unresolved_module.rs
+++ /dev/null
@@ -1,89 +0,0 @@
-use hir::{db::AstDatabase, diagnostics::UnresolvedModule, Semantics};
-use ide_assists::{Assist, AssistResolveStrategy};
-use ide_db::{base_db::AnchoredPathBuf, source_change::FileSystemEdit, RootDatabase};
-use syntax::AstNode;
-
-use crate::diagnostics::{fix, DiagnosticWithFixes};
-
-impl DiagnosticWithFixes for UnresolvedModule {
-    fn fixes(
-        &self,
-        sema: &Semantics<RootDatabase>,
-        _resolve: &AssistResolveStrategy,
-    ) -> Option<Vec<Assist>> {
-        let root = sema.db.parse_or_expand(self.file)?;
-        let unresolved_module = self.decl.to_node(&root);
-        Some(vec![fix(
-            "create_module",
-            "Create module",
-            FileSystemEdit::CreateFile {
-                dst: AnchoredPathBuf {
-                    anchor: self.file.original_file(sema.db),
-                    path: self.candidate.clone(),
-                },
-                initial_contents: "".to_string(),
-            }
-            .into(),
-            unresolved_module.syntax().text_range(),
-        )])
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use expect_test::expect;
-
-    use crate::diagnostics::tests::check_expect;
-
-    #[test]
-    fn test_unresolved_module_diagnostic() {
-        check_expect(
-            r#"mod foo;"#,
-            expect![[r#"
-                [
-                    Diagnostic {
-                        message: "unresolved module",
-                        range: 0..8,
-                        severity: Error,
-                        fixes: Some(
-                            [
-                                Assist {
-                                    id: AssistId(
-                                        "create_module",
-                                        QuickFix,
-                                    ),
-                                    label: "Create module",
-                                    group: None,
-                                    target: 0..8,
-                                    source_change: Some(
-                                        SourceChange {
-                                            source_file_edits: {},
-                                            file_system_edits: [
-                                                CreateFile {
-                                                    dst: AnchoredPathBuf {
-                                                        anchor: FileId(
-                                                            0,
-                                                        ),
-                                                        path: "foo.rs",
-                                                    },
-                                                    initial_contents: "",
-                                                },
-                                            ],
-                                            is_snippet: false,
-                                        },
-                                    ),
-                                },
-                            ],
-                        ),
-                        unused: false,
-                        code: Some(
-                            DiagnosticCode(
-                                "unresolved-module",
-                            ),
-                        ),
-                    },
-                ]
-            "#]],
-        );
-    }
-}
diff --git a/crates/ide/src/diagnostics/fixes/wrap_tail_expr.rs b/crates/ide/src/diagnostics/fixes/wrap_tail_expr.rs
deleted file mode 100644
index 715a403b99..0000000000
--- a/crates/ide/src/diagnostics/fixes/wrap_tail_expr.rs
+++ /dev/null
@@ -1,211 +0,0 @@
-use hir::{db::AstDatabase, diagnostics::MissingOkOrSomeInTailExpr, Semantics};
-use ide_assists::{Assist, AssistResolveStrategy};
-use ide_db::{source_change::SourceChange, RootDatabase};
-use syntax::AstNode;
-use text_edit::TextEdit;
-
-use crate::diagnostics::{fix, DiagnosticWithFixes};
-
-impl DiagnosticWithFixes for MissingOkOrSomeInTailExpr {
-    fn fixes(
-        &self,
-        sema: &Semantics<RootDatabase>,
-        _resolve: &AssistResolveStrategy,
-    ) -> Option<Vec<Assist>> {
-        let root = sema.db.parse_or_expand(self.file)?;
-        let tail_expr = self.expr.to_node(&root);
-        let tail_expr_range = tail_expr.syntax().text_range();
-        let replacement = format!("{}({})", self.required, tail_expr.syntax());
-        let edit = TextEdit::replace(tail_expr_range, replacement);
-        let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);
-        let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" };
-        Some(vec![fix("wrap_tail_expr", name, source_change, tail_expr_range)])
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::diagnostics::tests::{check_fix, check_no_diagnostics};
-
-    #[test]
-    fn test_wrap_return_type_option() {
-        check_fix(
-            r#"
-//- /main.rs crate:main deps:core
-use core::option::Option::{self, Some, None};
-
-fn div(x: i32, y: i32) -> Option<i32> {
-    if y == 0 {
-        return None;
-    }
-    x / y$0
-}
-//- /core/lib.rs crate:core
-pub mod result {
-    pub enum Result<T, E> { Ok(T), Err(E) }
-}
-pub mod option {
-    pub enum Option<T> { Some(T), None }
-}
-"#,
-            r#"
-use core::option::Option::{self, Some, None};
-
-fn div(x: i32, y: i32) -> Option<i32> {
-    if y == 0 {
-        return None;
-    }
-    Some(x / y)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_wrap_return_type() {
-        check_fix(
-            r#"
-//- /main.rs crate:main deps:core
-use core::result::Result::{self, Ok, Err};
-
-fn div(x: i32, y: i32) -> Result<i32, ()> {
-    if y == 0 {
-        return Err(());
-    }
-    x / y$0
-}
-//- /core/lib.rs crate:core
-pub mod result {
-    pub enum Result<T, E> { Ok(T), Err(E) }
-}
-pub mod option {
-    pub enum Option<T> { Some(T), None }
-}
-"#,
-            r#"
-use core::result::Result::{self, Ok, Err};
-
-fn div(x: i32, y: i32) -> Result<i32, ()> {
-    if y == 0 {
-        return Err(());
-    }
-    Ok(x / y)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_wrap_return_type_handles_generic_functions() {
-        check_fix(
-            r#"
-//- /main.rs crate:main deps:core
-use core::result::Result::{self, Ok, Err};
-
-fn div<T>(x: T) -> Result<T, i32> {
-    if x == 0 {
-        return Err(7);
-    }
-    $0x
-}
-//- /core/lib.rs crate:core
-pub mod result {
-    pub enum Result<T, E> { Ok(T), Err(E) }
-}
-pub mod option {
-    pub enum Option<T> { Some(T), None }
-}
-"#,
-            r#"
-use core::result::Result::{self, Ok, Err};
-
-fn div<T>(x: T) -> Result<T, i32> {
-    if x == 0 {
-        return Err(7);
-    }
-    Ok(x)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_wrap_return_type_handles_type_aliases() {
-        check_fix(
-            r#"
-//- /main.rs crate:main deps:core
-use core::result::Result::{self, Ok, Err};
-
-type MyResult<T> = Result<T, ()>;
-
-fn div(x: i32, y: i32) -> MyResult<i32> {
-    if y == 0 {
-        return Err(());
-    }
-    x $0/ y
-}
-//- /core/lib.rs crate:core
-pub mod result {
-    pub enum Result<T, E> { Ok(T), Err(E) }
-}
-pub mod option {
-    pub enum Option<T> { Some(T), None }
-}
-"#,
-            r#"
-use core::result::Result::{self, Ok, Err};
-
-type MyResult<T> = Result<T, ()>;
-
-fn div(x: i32, y: i32) -> MyResult<i32> {
-    if y == 0 {
-        return Err(());
-    }
-    Ok(x / y)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
-        check_no_diagnostics(
-            r#"
-//- /main.rs crate:main deps:core
-use core::result::Result::{self, Ok, Err};
-
-fn foo() -> Result<(), i32> { 0 }
-
-//- /core/lib.rs crate:core
-pub mod result {
-    pub enum Result<T, E> { Ok(T), Err(E) }
-}
-pub mod option {
-    pub enum Option<T> { Some(T), None }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
-        check_no_diagnostics(
-            r#"
-//- /main.rs crate:main deps:core
-use core::result::Result::{self, Ok, Err};
-
-enum SomeOtherEnum { Ok(i32), Err(String) }
-
-fn foo() -> SomeOtherEnum { 0 }
-
-//- /core/lib.rs crate:core
-pub mod result {
-    pub enum Result<T, E> { Ok(T), Err(E) }
-}
-pub mod option {
-    pub enum Option<T> { Some(T), None }
-}
-"#,
-        );
-    }
-}
diff --git a/crates/ide/src/diagnostics/unlinked_file.rs b/crates/ide/src/diagnostics/unlinked_file.rs
index 51fe0f360e..93fd25dea4 100644
--- a/crates/ide/src/diagnostics/unlinked_file.rs
+++ b/crates/ide/src/diagnostics/unlinked_file.rs
@@ -18,7 +18,7 @@ use syntax::{
 use text_edit::TextEdit;
 
 use crate::{
-    diagnostics::{fix, fixes::DiagnosticWithFixes},
+    diagnostics::{fix, fixes::DiagnosticWithFix},
     Assist,
 };
 
@@ -50,13 +50,13 @@ impl Diagnostic for UnlinkedFile {
     }
 }
 
-impl DiagnosticWithFixes for UnlinkedFile {
-    fn fixes(
+impl DiagnosticWithFix for UnlinkedFile {
+    fn fix(
         &self,
         sema: &hir::Semantics<RootDatabase>,
         _resolve: &AssistResolveStrategy,
-    ) -> Option<Vec<Assist>> {
-        // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file,
+    ) -> Option<Assist> {
+        // If there's an existing module that could add a `mod` item to include the unlinked file,
         // suggest that as a fix.
 
         let source_root = sema.db.source_root(sema.db.file_source_root(self.file_id));
@@ -90,7 +90,7 @@ impl DiagnosticWithFixes for UnlinkedFile {
                         }
 
                         if module.origin.file_id() == Some(*parent_id) {
-                            return make_fixes(sema.db, *parent_id, module_name, self.file_id);
+                            return make_fix(sema.db, *parent_id, module_name, self.file_id);
                         }
                     }
                 }
@@ -101,23 +101,20 @@ impl DiagnosticWithFixes for UnlinkedFile {
     }
 }
 
-fn make_fixes(
+fn make_fix(
     db: &RootDatabase,
     parent_file_id: FileId,
     new_mod_name: &str,
     added_file_id: FileId,
-) -> Option<Vec<Assist>> {
+) -> Option<Assist> {
     fn is_outline_mod(item: &ast::Item) -> bool {
         matches!(item, ast::Item::Module(m) if m.item_list().is_none())
     }
 
     let mod_decl = format!("mod {};", new_mod_name);
-    let pub_mod_decl = format!("pub mod {};", new_mod_name);
-
     let ast: ast::SourceFile = db.parse(parent_file_id).tree();
 
-    let mut mod_decl_builder = TextEdit::builder();
-    let mut pub_mod_decl_builder = TextEdit::builder();
+    let mut builder = TextEdit::builder();
 
     // If there's an existing `mod m;` statement matching the new one, don't emit a fix (it's
     // probably `#[cfg]`d out).
@@ -141,43 +138,30 @@ fn make_fixes(
     {
         Some(last) => {
             cov_mark::hit!(unlinked_file_append_to_existing_mods);
-            let offset = last.syntax().text_range().end();
-            mod_decl_builder.insert(offset, format!("\n{}", mod_decl));
-            pub_mod_decl_builder.insert(offset, format!("\n{}", pub_mod_decl));
+            builder.insert(last.syntax().text_range().end(), format!("\n{}", mod_decl));
         }
         None => {
             // Prepend before the first item in the file.
             match ast.items().next() {
                 Some(item) => {
                     cov_mark::hit!(unlinked_file_prepend_before_first_item);
-                    let offset = item.syntax().text_range().start();
-                    mod_decl_builder.insert(offset, format!("{}\n\n", mod_decl));
-                    pub_mod_decl_builder.insert(offset, format!("{}\n\n", pub_mod_decl));
+                    builder.insert(item.syntax().text_range().start(), format!("{}\n\n", mod_decl));
                 }
                 None => {
                     // No items in the file, so just append at the end.
                     cov_mark::hit!(unlinked_file_empty_file);
-                    let offset = ast.syntax().text_range().end();
-                    mod_decl_builder.insert(offset, format!("{}\n", mod_decl));
-                    pub_mod_decl_builder.insert(offset, format!("{}\n", pub_mod_decl));
+                    builder.insert(ast.syntax().text_range().end(), format!("{}\n", mod_decl));
                 }
             }
         }
     }
 
+    let edit = builder.finish();
     let trigger_range = db.parse(added_file_id).tree().syntax().text_range();
-    Some(vec![
-        fix(
-            "add_mod_declaration",
-            &format!("Insert `{}`", mod_decl),
-            SourceChange::from_text_edit(parent_file_id, mod_decl_builder.finish()),
-            trigger_range,
-        ),
-        fix(
-            "add_pub_mod_declaration",
-            &format!("Insert `{}`", pub_mod_decl),
-            SourceChange::from_text_edit(parent_file_id, pub_mod_decl_builder.finish()),
-            trigger_range,
-        ),
-    ])
+    Some(fix(
+        "add_mod_declaration",
+        &format!("Insert `{}`", mod_decl),
+        SourceChange::from_text_edit(parent_file_id, edit),
+        trigger_range,
+    ))
 }
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 320694a17a..cb5a8e19a9 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -29,8 +29,7 @@ pub(crate) type DocumentationLink = String;
 /// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs)
 pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition) -> String {
     let mut cb = broken_link_clone_cb;
-    let doc =
-        Parser::new_with_broken_link_callback(markdown, Options::ENABLE_TASKLISTS, Some(&mut cb));
+    let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb));
 
     let doc = map_links(doc, |target, title: &str| {
         // This check is imperfect, there's some overlap between valid intra-doc links
@@ -65,7 +64,8 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Defi
 pub(crate) fn remove_links(markdown: &str) -> String {
     let mut drop_link = false;
 
-    let opts = Options::ENABLE_TASKLISTS | Options::ENABLE_FOOTNOTES;
+    let mut opts = Options::empty();
+    opts.insert(Options::ENABLE_FOOTNOTES);
 
     let mut cb = |_: BrokenLink| {
         let empty = InlineStr::try_from("").unwrap();
@@ -123,7 +123,7 @@ pub(crate) fn extract_definitions_from_markdown(
 ) -> Vec<(TextRange, String, Option<hir::Namespace>)> {
     Parser::new_with_broken_link_callback(
         markdown,
-        Options::ENABLE_TASKLISTS,
+        Options::empty(),
         Some(&mut broken_link_clone_cb),
     )
     .into_offset_iter()
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index c67ccd1a9f..61dcbb399d 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -4,7 +4,7 @@ use ide_assists::utils::extract_trivial_expression;
 use itertools::Itertools;
 use syntax::{
     algo::non_trivia_sibling,
-    ast::{self, AstNode, AstToken, IsString},
+    ast::{self, AstNode, AstToken},
     Direction, NodeOrToken, SourceFile,
     SyntaxKind::{self, USE_TREE, WHITESPACE},
     SyntaxNode, SyntaxToken, TextRange, TextSize, T,
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index f4b90db3a0..db08547d1d 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -565,7 +565,7 @@ impl Analysis {
             let diagnostic_assists = if include_fixes {
                 diagnostics::diagnostics(db, diagnostics_config, &resolve, frange.file_id)
                     .into_iter()
-                    .flat_map(|it| it.fixes.unwrap_or_default())
+                    .filter_map(|it| it.fix)
                     .filter(|it| it.target.intersect(frange.range).is_some())
                     .collect()
             } else {
diff --git a/crates/ide/src/matching_brace.rs b/crates/ide/src/matching_brace.rs
index 011c8cc553..261dcc2559 100644
--- a/crates/ide/src/matching_brace.rs
+++ b/crates/ide/src/matching_brace.rs
@@ -19,10 +19,14 @@ use syntax::{
 pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> {
     const BRACES: &[SyntaxKind] =
         &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]];
-    let (brace_token, brace_idx) = file.syntax().token_at_offset(offset).find_map(|node| {
-        let idx = BRACES.iter().position(|&brace| brace == node.kind())?;
-        Some((node, idx))
-    })?;
+    let (brace_token, brace_idx) = file
+        .syntax()
+        .token_at_offset(offset)
+        .filter_map(|node| {
+            let idx = BRACES.iter().position(|&brace| brace == node.kind())?;
+            Some((node, idx))
+        })
+        .next()?;
     let parent = brace_token.parent()?;
     if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) {
         cov_mark::hit!(pipes_not_braces);
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs
index 9b1f480446..99365c8a73 100644
--- a/crates/ide/src/parent_module.rs
+++ b/crates/ide/src/parent_module.rs
@@ -1,8 +1,6 @@
 use hir::Semantics;
-use ide_db::{
-    base_db::{CrateId, FileId, FilePosition},
-    RootDatabase,
-};
+use ide_db::base_db::{CrateId, FileId, FilePosition};
+use ide_db::RootDatabase;
 use itertools::Itertools;
 use syntax::{
     algo::find_node_at_offset,
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 01fe3a1a16..2bf9533052 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -55,14 +55,12 @@ pub(crate) fn prepare_rename(
     match def {
         Definition::SelfType(_) => bail!("Cannot rename `Self`"),
         Definition::ModuleDef(ModuleDef::BuiltinType(_)) => bail!("Cannot rename builtin type"),
-        Definition::ModuleDef(ModuleDef::Module(_)) => (),
-        _ => {
-            let nav = def
-                .try_to_nav(sema.db)
-                .ok_or_else(|| format_err!("No references found at position"))?;
-            nav.focus_range.ok_or_else(|| format_err!("No identifier available to rename"))?;
-        }
+        _ => {}
     };
+    let nav =
+        def.try_to_nav(sema.db).ok_or_else(|| format_err!("No references found at position"))?;
+    nav.focus_range.ok_or_else(|| format_err!("No identifier available to rename"))?;
+
     let name_like = sema
         .find_node_at_offset_with_descend(&syntax, position.offset)
         .ok_or_else(|| format_err!("No references found at position"))?;
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index baed8e2170..b586dcc179 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -227,8 +227,8 @@ pub(super) fn element(
         k if k.is_keyword() => {
             let h = Highlight::new(HlTag::Keyword);
             match k {
-                T![await] => h | HlMod::Async | HlMod::ControlFlow,
-                T![break]
+                T![await]
+                | T![break]
                 | T![continue]
                 | T![else]
                 | T![if]
@@ -255,7 +255,6 @@ pub(super) fn element(
                     })
                     .map(|modifier| h | modifier)
                     .unwrap_or(h),
-                T![async] => h | HlMod::Async,
                 _ => h,
             }
         }
@@ -311,9 +310,6 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
                 if func.is_unsafe(db) {
                     h |= HlMod::Unsafe;
                 }
-                if func.is_async(db) {
-                    h |= HlMod::Async;
-                }
                 return h;
             }
             hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HlTag::Symbol(SymbolKind::Struct),
@@ -413,9 +409,6 @@ fn highlight_method_call(
     if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) {
         h |= HlMod::Unsafe;
     }
-    if func.is_async(sema.db) {
-        h |= HlMod::Async;
-    }
     if func.as_assoc_item(sema.db).and_then(|it| it.containing_trait(sema.db)).is_some() {
         h |= HlMod::Trait
     }
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 4269d339eb..bc221d5996 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -6,7 +6,7 @@ use either::Either;
 use hir::{InFile, Semantics};
 use ide_db::{call_info::ActiveParameter, helpers::rust_doc::is_rust_fence, SymbolKind};
 use syntax::{
-    ast::{self, AstNode, IsString},
+    ast::{self, AstNode},
     AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize,
 };
 
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index f4a2e7506a..a304b32506 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -65,8 +65,6 @@ pub enum HlMod {
     Static,
     /// Used for items in traits and trait impls.
     Trait,
-    /// Used with keywords like `async` and `await`.
-    Async,
     // Keep this last!
     /// Used for unsafe functions, mutable statics, union accesses and unsafe operations.
     Unsafe,
@@ -188,7 +186,6 @@ impl HlMod {
         HlMod::Mutable,
         HlMod::Static,
         HlMod::Trait,
-        HlMod::Async,
         HlMod::Unsafe,
     ];
 
@@ -206,7 +203,6 @@ impl HlMod {
             HlMod::Mutable => "mutable",
             HlMod::Static => "static",
             HlMod::Trait => "trait",
-            HlMod::Async => "async",
             HlMod::Unsafe => "unsafe",
         }
     }
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs
index 20754a02ad..682f0ff5e1 100644
--- a/crates/ide_assists/src/assist_context.rs
+++ b/crates/ide_assists/src/assist_context.rs
@@ -238,8 +238,8 @@ impl AssistBuilder {
         }
     }
 
-    pub(crate) fn make_mut<N: AstNode>(&mut self, node: N) -> N {
-        self.mutated_tree.get_or_insert_with(|| TreeMutator::new(node.syntax())).make_mut(&node)
+    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.
     ///
@@ -251,7 +251,7 @@ impl AssistBuilder {
     /// 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_syntax_mut(&mut self, node: SyntaxNode) -> SyntaxNode {
+    pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode {
         self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node)
     }
 
diff --git a/crates/ide_assists/src/handlers/add_explicit_type.rs b/crates/ide_assists/src/handlers/add_explicit_type.rs
index 36589203d5..62db319524 100644
--- a/crates/ide_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ide_assists/src/handlers/add_explicit_type.rs
@@ -198,34 +198,6 @@ fn main() {
         )
     }
 
-    /// https://github.com/rust-analyzer/rust-analyzer/issues/2922
-    #[test]
-    fn regression_issue_2922() {
-        check_assist(
-            add_explicit_type,
-            r#"
-fn main() {
-    let $0v = [0.0; 2];
-}
-"#,
-            r#"
-fn main() {
-    let v: [f64; 2] = [0.0; 2];
-}
-"#,
-        );
-        // note: this may break later if we add more consteval. it just needs to be something that our
-        // consteval engine doesn't understand
-        check_assist_not_applicable(
-            add_explicit_type,
-            r#"
-fn main() {
-    let $0l = [0.0; 2+2];
-}
-"#,
-        );
-    }
-
     #[test]
     fn default_generics_should_not_be_added() {
         check_assist(
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 8225ae22c6..0148635f9e 100644
--- a/crates/ide_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide_assists/src/handlers/add_missing_impl_members.rs
@@ -64,6 +64,7 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -
 // impl Trait for () {
 //     type X = ();
 //     fn foo(&self) {}$0
+//
 // }
 // ```
 // ->
@@ -194,7 +195,6 @@ impl Foo for S {
     fn baz(&self) {
         todo!()
     }
-
 }"#,
         );
     }
@@ -231,7 +231,6 @@ impl Foo for S {
     fn foo(&self) {
         ${0:todo!()}
     }
-
 }"#,
         );
     }
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index dda5a6631c..a454a2af32 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -102,8 +102,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
             range,
             |builder| {
                 let scope = match scope.clone() {
-                    ImportScope::File(it) => ImportScope::File(builder.make_mut(it)),
-                    ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)),
+                    ImportScope::File(it) => ImportScope::File(builder.make_ast_mut(it)),
+                    ImportScope::Module(it) => ImportScope::Module(builder.make_ast_mut(it)),
                 };
                 insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use);
             },
@@ -966,32 +966,6 @@ mod bar {
         println!("Hallo");
     }
 }
-"#,
-        );
-    }
-
-    #[test]
-    fn uses_abs_path_with_extern_crate_clash() {
-        check_assist(
-            auto_import,
-            r#"
-//- /main.rs crate:main deps:foo
-mod foo {}
-
-const _: () = {
-    Foo$0
-};
-//- /foo.rs crate:foo
-pub struct Foo
-"#,
-            r#"
-use ::foo::Foo;
-
-mod foo {}
-
-const _: () = {
-    Foo
-};
 "#,
         );
     }
diff --git a/crates/ide_assists/src/handlers/expand_glob_import.rs b/crates/ide_assists/src/handlers/expand_glob_import.rs
index 79cb08d690..da8d245e5f 100644
--- a/crates/ide_assists/src/handlers/expand_glob_import.rs
+++ b/crates/ide_assists/src/handlers/expand_glob_import.rs
@@ -61,7 +61,7 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Opti
         "Expand glob import",
         target.text_range(),
         |builder| {
-            let use_tree = builder.make_mut(use_tree);
+            let use_tree = builder.make_ast_mut(use_tree);
 
             let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs);
             let expanded = make::use_tree_list(names_to_import.iter().map(|n| {
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index a2dba915ce..6311afc1f5 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -642,10 +642,6 @@ fn vars_used_in_body(ctx: &AssistContext, body: &FunctionBody) -> Vec<Local> {
         .collect()
 }
 
-fn body_contains_await(body: &FunctionBody) -> bool {
-    body.descendants().any(|d| matches!(d.kind(), SyntaxKind::AWAIT_EXPR))
-}
-
 /// find `self` param, that was not defined inside `body`
 ///
 /// It should skip `self` params from impls inside `body`
@@ -1127,10 +1123,9 @@ fn format_function(
     let params = make_param_list(ctx, module, fun);
     let ret_ty = make_ret_ty(ctx, module, fun);
     let body = make_body(ctx, old_indent, new_indent, fun);
-    let async_kw = if body_contains_await(&fun.body) { "async " } else { "" };
     match ctx.config.snippet_cap {
-        Some(_) => format_to!(fn_def, "\n\n{}{}fn $0{}{}", new_indent, async_kw, fun.name, params),
-        None => format_to!(fn_def, "\n\n{}{}fn {}{}", new_indent, async_kw, fun.name, params),
+        Some(_) => format_to!(fn_def, "\n\n{}fn $0{}{}", new_indent, fun.name, params),
+        None => format_to!(fn_def, "\n\n{}fn {}{}", new_indent, fun.name, params),
     }
     if let Some(ret_ty) = ret_ty {
         format_to!(fn_def, " {}", ret_ty);
@@ -3570,60 +3565,4 @@ fn $0fun_name(n: i32) -> i32 {
 }",
         );
     }
-
-    #[test]
-    fn extract_with_await() {
-        check_assist(
-            extract_function,
-            r#"fn main() {
-    $0some_function().await;$0
-}
-
-async fn some_function() {
-
-}
-"#,
-            r#"
-fn main() {
-    fun_name();
-}
-
-async fn $0fun_name() {
-    some_function().await;
-}
-
-async fn some_function() {
-
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn extract_with_await_in_args() {
-        check_assist(
-            extract_function,
-            r#"fn main() {
-    $0function_call("a", some_function().await);$0
-}
-
-async fn some_function() {
-
-}
-"#,
-            r#"
-fn main() {
-    fun_name();
-}
-
-async fn $0fun_name() {
-    function_call("a", some_function().await);
-}
-
-async fn some_function() {
-
-}
-"#,
-        );
-    }
 }
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
index 007aba23d2..8e21783919 100644
--- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -70,7 +70,7 @@ pub(crate) fn extract_struct_from_enum_variant(
                     continue;
                 }
                 builder.edit_file(file_id);
-                let source_file = builder.make_mut(ctx.sema.parse(file_id));
+                let source_file = builder.make_ast_mut(ctx.sema.parse(file_id));
                 let processed = process_references(
                     ctx,
                     &mut visited_modules_set,
@@ -84,8 +84,8 @@ pub(crate) fn extract_struct_from_enum_variant(
                 });
             }
             builder.edit_file(ctx.frange.file_id);
-            let source_file = builder.make_mut(ctx.sema.parse(ctx.frange.file_id));
-            let variant = builder.make_mut(variant.clone());
+            let source_file = builder.make_ast_mut(ctx.sema.parse(ctx.frange.file_id));
+            let variant = builder.make_ast_mut(variant.clone());
             if let Some(references) = def_file_references {
                 let processed = process_references(
                     ctx,
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
index 58b0010505..be927cc1c4 100644
--- a/crates/ide_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ide_assists/src/handlers/fill_match_arms.rs
@@ -71,7 +71,6 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
             .filter_map(|variant| build_pat(ctx.db(), module, variant))
             .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
             .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
-            .map(|it| it.clone_for_update())
             .collect::<Vec<_>>();
         if Some(enum_def)
             == FamousDefs(&ctx.sema, Some(module.krate()))
@@ -100,7 +99,6 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
             })
             .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
             .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
-            .map(|it| it.clone_for_update())
             .collect()
     } else {
         return None;
@@ -116,20 +114,10 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
         "Fill match arms",
         target,
         |builder| {
-            let new_match_arm_list = match_arm_list.clone_for_update();
-
-            let catch_all_arm = new_match_arm_list
-                .arms()
-                .find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_))));
-            if let Some(arm) = catch_all_arm {
-                arm.remove()
-            }
-            let mut first_new_arm = None;
-            for arm in missing_arms {
-                first_new_arm.get_or_insert_with(|| arm.clone());
-                new_match_arm_list.add_arm(arm);
-            }
-
+            let new_arm_list = match_arm_list.remove_placeholder();
+            let n_old_arms = new_arm_list.arms().count();
+            let new_arm_list = new_arm_list.append_arms(missing_arms);
+            let first_new_arm = new_arm_list.arms().nth(n_old_arms);
             let old_range = ctx.sema.original_range(match_arm_list.syntax()).range;
             match (first_new_arm, ctx.config.snippet_cap) {
                 (Some(first_new_arm), Some(cap)) => {
@@ -143,10 +131,10 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
                             }
                             None => Cursor::Before(first_new_arm.syntax()),
                         };
-                    let snippet = render_snippet(cap, new_match_arm_list.syntax(), cursor);
+                    let snippet = render_snippet(cap, new_arm_list.syntax(), cursor);
                     builder.replace_snippet(cap, old_range, snippet);
                 }
-                _ => builder.replace(old_range, new_match_arm_list.to_string()),
+                _ => builder.replace(old_range, new_arm_list.to_string()),
             }
         },
     )
@@ -275,18 +263,18 @@ mod tests {
         check_assist_not_applicable(
             fill_match_arms,
             r#"
-enum A {
-    As,
-    Bs{x:i32, y:Option<i32>},
-    Cs(i32, Option<i32>),
-}
-fn main() {
-    match A::As$0 {
-        A::As,
-        A::Bs{x,y:Some(_)} => {}
-        A::Cs(_, Some(_)) => {}
-    }
-}
+            enum A {
+                As,
+                Bs{x:i32, y:Option<i32>},
+                Cs(i32, Option<i32>),
+            }
+            fn main() {
+                match A::As$0 {
+                    A::As,
+                    A::Bs{x,y:Some(_)} => {}
+                    A::Cs(_, Some(_)) => {}
+                }
+            }
             "#,
         );
     }
@@ -296,13 +284,13 @@ fn main() {
         check_assist_not_applicable(
             fill_match_arms,
             r#"
-fn foo(a: bool) {
-    match a$0 {
-        true => {}
-        false => {}
-    }
-}
-"#,
+            fn foo(a: bool) {
+                match a$0 {
+                    true => {}
+                    false => {}
+                }
+            }
+            "#,
         )
     }
 
@@ -313,11 +301,11 @@ fn foo(a: bool) {
         check_assist_not_applicable(
             fill_match_arms,
             r#"
-fn main() {
-    match (0, false)$0 {
-    }
-}
-"#,
+            fn main() {
+                match (0, false)$0 {
+                }
+            }
+            "#,
         );
     }
 
@@ -326,19 +314,19 @@ fn main() {
         check_assist(
             fill_match_arms,
             r#"
-fn foo(a: bool) {
-    match a$0 {
-    }
-}
-"#,
+            fn foo(a: bool) {
+                match a$0 {
+                }
+            }
+            "#,
             r#"
-fn foo(a: bool) {
-    match a {
-        $0true => {}
-        false => {}
-    }
-}
-"#,
+            fn foo(a: bool) {
+                match a {
+                    $0true => {}
+                    false => {}
+                }
+            }
+            "#,
         )
     }
 
@@ -347,20 +335,20 @@ fn foo(a: bool) {
         check_assist(
             fill_match_arms,
             r#"
-fn foo(a: bool) {
-    match a$0 {
-        true => {}
-    }
-}
-"#,
+            fn foo(a: bool) {
+                match a$0 {
+                    true => {}
+                }
+            }
+            "#,
             r#"
-fn foo(a: bool) {
-    match a {
-        true => {}
-        $0false => {}
-    }
-}
-"#,
+            fn foo(a: bool) {
+                match a {
+                    true => {}
+                    $0false => {}
+                }
+            }
+            "#,
         )
     }
 
@@ -369,15 +357,15 @@ fn foo(a: bool) {
         check_assist_not_applicable(
             fill_match_arms,
             r#"
-fn foo(a: bool) {
-    match (a, a)$0 {
-        (true, true) => {}
-        (true, false) => {}
-        (false, true) => {}
-        (false, false) => {}
-    }
-}
-"#,
+            fn foo(a: bool) {
+                match (a, a)$0 {
+                    (true, true) => {}
+                    (true, false) => {}
+                    (false, true) => {}
+                    (false, false) => {}
+                }
+            }
+            "#,
         )
     }
 
@@ -386,21 +374,21 @@ fn foo(a: bool) {
         check_assist(
             fill_match_arms,
             r#"
-fn foo(a: bool) {
-    match (a, a)$0 {
-    }
-}
-"#,
+            fn foo(a: bool) {
+                match (a, a)$0 {
+                }
+            }
+            "#,
             r#"
-fn foo(a: bool) {
-    match (a, a) {
-        $0(true, true) => {}
-        (true, false) => {}
-        (false, true) => {}
-        (false, false) => {}
-    }
-}
-"#,
+            fn foo(a: bool) {
+                match (a, a) {
+                    $0(true, true) => {}
+                    (true, false) => {}
+                    (false, true) => {}
+                    (false, false) => {}
+                }
+            }
+            "#,
         )
     }
 
@@ -409,22 +397,22 @@ fn foo(a: bool) {
         check_assist(
             fill_match_arms,
             r#"
-fn foo(a: bool) {
-    match (a, a)$0 {
-        (false, true) => {}
-    }
-}
-"#,
+            fn foo(a: bool) {
+                match (a, a)$0 {
+                    (false, true) => {}
+                }
+            }
+            "#,
             r#"
-fn foo(a: bool) {
-    match (a, a) {
-        (false, true) => {}
-        $0(true, true) => {}
-        (true, false) => {}
-        (false, false) => {}
-    }
-}
-"#,
+            fn foo(a: bool) {
+                match (a, a) {
+                    (false, true) => {}
+                    $0(true, true) => {}
+                    (true, false) => {}
+                    (false, false) => {}
+                }
+            }
+            "#,
         )
     }
 
@@ -433,32 +421,32 @@ fn foo(a: bool) {
         check_assist(
             fill_match_arms,
             r#"
-enum A {
-    As,
-    Bs { x: i32, y: Option<i32> },
-    Cs(i32, Option<i32>),
-}
-fn main() {
-    match A::As$0 {
-        A::Bs { x, y: Some(_) } => {}
-        A::Cs(_, Some(_)) => {}
-    }
-}
-"#,
+            enum A {
+                As,
+                Bs { x: i32, y: Option<i32> },
+                Cs(i32, Option<i32>),
+            }
+            fn main() {
+                match A::As$0 {
+                    A::Bs { x, y: Some(_) } => {}
+                    A::Cs(_, Some(_)) => {}
+                }
+            }
+            "#,
             r#"
-enum A {
-    As,
-    Bs { x: i32, y: Option<i32> },
-    Cs(i32, Option<i32>),
-}
-fn main() {
-    match A::As {
-        A::Bs { x, y: Some(_) } => {}
-        A::Cs(_, Some(_)) => {}
-        $0A::As => {}
-    }
-}
-"#,
+            enum A {
+                As,
+                Bs { x: i32, y: Option<i32> },
+                Cs(i32, Option<i32>),
+            }
+            fn main() {
+                match A::As {
+                    A::Bs { x, y: Some(_) } => {}
+                    A::Cs(_, Some(_)) => {}
+                    $0A::As => {}
+                }
+            }
+            "#,
         );
     }
 
@@ -605,30 +593,30 @@ fn main() {
         check_assist(
             fill_match_arms,
             r#"
-enum A { One, Two }
-enum B { One, Two }
+            enum A { One, Two }
+            enum B { One, Two }
 
-fn main() {
-    let a = A::One;
-    let b = B::One;
-    match (a$0, b) {}
-}
-"#,
+            fn main() {
+                let a = A::One;
+                let b = B::One;
+                match (a$0, b) {}
+            }
+            "#,
             r#"
-enum A { One, Two }
-enum B { One, Two }
-
-fn main() {
-    let a = A::One;
-    let b = B::One;
-    match (a, b) {
-        $0(A::One, B::One) => {}
-        (A::One, B::Two) => {}
-        (A::Two, B::One) => {}
-        (A::Two, B::Two) => {}
-    }
-}
-"#,
+            enum A { One, Two }
+            enum B { One, Two }
+
+            fn main() {
+                let a = A::One;
+                let b = B::One;
+                match (a, b) {
+                    $0(A::One, B::One) => {}
+                    (A::One, B::Two) => {}
+                    (A::Two, B::One) => {}
+                    (A::Two, B::Two) => {}
+                }
+            }
+            "#,
         );
     }
 
@@ -637,30 +625,30 @@ fn main() {
         check_assist(
             fill_match_arms,
             r#"
-enum A { One, Two }
-enum B { One, Two }
+            enum A { One, Two }
+            enum B { One, Two }
 
-fn main() {
-    let a = A::One;
-    let b = B::One;
-    match (&a$0, &b) {}
-}
-"#,
+            fn main() {
+                let a = A::One;
+                let b = B::One;
+                match (&a$0, &b) {}
+            }
+            "#,
             r#"
-enum A { One, Two }
-enum B { One, Two }
-
-fn main() {
-    let a = A::One;
-    let b = B::One;
-    match (&a, &b) {
-        $0(A::One, B::One) => {}
-        (A::One, B::Two) => {}
-        (A::Two, B::One) => {}
-        (A::Two, B::Two) => {}
-    }
-}
-"#,
+            enum A { One, Two }
+            enum B { One, Two }
+
+            fn main() {
+                let a = A::One;
+                let b = B::One;
+                match (&a, &b) {
+                    $0(A::One, B::One) => {}
+                    (A::One, B::Two) => {}
+                    (A::Two, B::One) => {}
+                    (A::Two, B::Two) => {}
+                }
+            }
+            "#,
         );
     }
 
@@ -749,20 +737,20 @@ fn main() {
         check_assist_not_applicable(
             fill_match_arms,
             r#"
-enum A { One, Two }
-enum B { One, Two }
-
-fn main() {
-    let a = A::One;
-    let b = B::One;
-    match (a$0, b) {
-        (A::Two, B::One) => {}
-        (A::One, B::One) => {}
-        (A::One, B::Two) => {}
-        (A::Two, B::Two) => {}
-    }
-}
-"#,
+            enum A { One, Two }
+            enum B { One, Two }
+
+            fn main() {
+                let a = A::One;
+                let b = B::One;
+                match (a$0, b) {
+                    (A::Two, B::One) => {}
+                    (A::One, B::One) => {}
+                    (A::One, B::Two) => {}
+                    (A::Two, B::Two) => {}
+                }
+            }
+            "#,
         );
     }
 
@@ -771,25 +759,25 @@ fn main() {
         check_assist(
             fill_match_arms,
             r#"
-enum A { One, Two }
+            enum A { One, Two }
 
-fn main() {
-    let a = A::One;
-    match (a$0, ) {
-    }
-}
-"#,
+            fn main() {
+                let a = A::One;
+                match (a$0, ) {
+                }
+            }
+            "#,
             r#"
-enum A { One, Two }
+            enum A { One, Two }
 
-fn main() {
-    let a = A::One;
-    match (a, ) {
-        $0(A::One,) => {}
-        (A::Two,) => {}
-    }
-}
-"#,
+            fn main() {
+                let a = A::One;
+                match (a, ) {
+                    $0(A::One,) => {}
+                    (A::Two,) => {}
+                }
+            }
+            "#,
         );
     }
 
@@ -798,47 +786,47 @@ fn main() {
         check_assist(
             fill_match_arms,
             r#"
-enum A { As }
+            enum A { As }
 
-fn foo(a: &A) {
-    match a$0 {
-    }
-}
-"#,
+            fn foo(a: &A) {
+                match a$0 {
+                }
+            }
+            "#,
             r#"
-enum A { As }
+            enum A { As }
 
-fn foo(a: &A) {
-    match a {
-        $0A::As => {}
-    }
-}
-"#,
+            fn foo(a: &A) {
+                match a {
+                    $0A::As => {}
+                }
+            }
+            "#,
         );
 
         check_assist(
             fill_match_arms,
             r#"
-enum A {
-    Es { x: usize, y: usize }
-}
+            enum A {
+                Es { x: usize, y: usize }
+            }
 
-fn foo(a: &mut A) {
-    match a$0 {
-    }
-}
-"#,
+            fn foo(a: &mut A) {
+                match a$0 {
+                }
+            }
+            "#,
             r#"
-enum A {
-    Es { x: usize, y: usize }
-}
+            enum A {
+                Es { x: usize, y: usize }
+            }
 
-fn foo(a: &mut A) {
-    match a {
-        $0A::Es { x, y } => {}
-    }
-}
-"#,
+            fn foo(a: &mut A) {
+                match a {
+                    $0A::Es { x, y } => {}
+                }
+            }
+            "#,
         );
     }
 
@@ -847,12 +835,12 @@ fn foo(a: &mut A) {
         check_assist_target(
             fill_match_arms,
             r#"
-enum E { X, Y }
+            enum E { X, Y }
 
-fn main() {
-    match E::X$0 {}
-}
-"#,
+            fn main() {
+                match E::X$0 {}
+            }
+            "#,
             "match E::X {}",
         );
     }
@@ -862,24 +850,24 @@ fn main() {
         check_assist(
             fill_match_arms,
             r#"
-enum E { X, Y }
+            enum E { X, Y }
 
-fn main() {
-    match E::X {
-        $0_ => {}
-    }
-}
-"#,
+            fn main() {
+                match E::X {
+                    $0_ => {}
+                }
+            }
+            "#,
             r#"
-enum E { X, Y }
+            enum E { X, Y }
 
-fn main() {
-    match E::X {
-        $0E::X => {}
-        E::Y => {}
-    }
-}
-"#,
+            fn main() {
+                match E::X {
+                    $0E::X => {}
+                    E::Y => {}
+                }
+            }
+            "#,
         );
     }
 
@@ -888,26 +876,26 @@ fn main() {
         check_assist(
             fill_match_arms,
             r#"
-mod foo { pub enum E { X, Y } }
-use foo::E::X;
+            mod foo { pub enum E { X, Y } }
+            use foo::E::X;
 
-fn main() {
-    match X {
-        $0
-    }
-}
-"#,
+            fn main() {
+                match X {
+                    $0
+                }
+            }
+            "#,
             r#"
-mod foo { pub enum E { X, Y } }
-use foo::E::X;
+            mod foo { pub enum E { X, Y } }
+            use foo::E::X;
 
-fn main() {
-    match X {
-        $0X => {}
-        foo::E::Y => {}
-    }
-}
-"#,
+            fn main() {
+                match X {
+                    $0X => {}
+                    foo::E::Y => {}
+                }
+            }
+            "#,
         );
     }
 
@@ -916,26 +904,26 @@ fn main() {
         check_assist(
             fill_match_arms,
             r#"
-enum A { One, Two }
-fn foo(a: A) {
-    match a {
-        // foo bar baz$0
-        A::One => {}
-        // This is where the rest should be
-    }
-}
-"#,
+            enum A { One, Two }
+            fn foo(a: A) {
+                match a {
+                    // foo bar baz$0
+                    A::One => {}
+                    // This is where the rest should be
+                }
+            }
+            "#,
             r#"
-enum A { One, Two }
-fn foo(a: A) {
-    match a {
-        // foo bar baz
-        A::One => {}
-        $0A::Two => {}
-        // This is where the rest should be
-    }
-}
-"#,
+            enum A { One, Two }
+            fn foo(a: A) {
+                match a {
+                    // foo bar baz
+                    A::One => {}
+                    // This is where the rest should be
+                    $0A::Two => {}
+                }
+            }
+            "#,
         );
     }
 
@@ -944,23 +932,23 @@ fn foo(a: A) {
         check_assist(
             fill_match_arms,
             r#"
-enum A { One, Two }
-fn foo(a: A) {
-    match a {
-        // foo bar baz$0
-    }
-}
-"#,
+            enum A { One, Two }
+            fn foo(a: A) {
+                match a {
+                    // foo bar baz$0
+                }
+            }
+            "#,
             r#"
-enum A { One, Two }
-fn foo(a: A) {
-    match a {
-        $0A::One => {}
-        A::Two => {}
-        // foo bar baz
-    }
-}
-"#,
+            enum A { One, Two }
+            fn foo(a: A) {
+                match a {
+                    // foo bar baz
+                    $0A::One => {}
+                    A::Two => {}
+                }
+            }
+            "#,
         );
     }
 
@@ -969,22 +957,22 @@ fn foo(a: A) {
         check_assist(
             fill_match_arms,
             r#"
-enum A { One, Two, }
-fn foo(a: A) {
-    match a$0 {
-        _ => (),
-    }
-}
-"#,
+            enum A { One, Two, }
+            fn foo(a: A) {
+                match a$0 {
+                    _ => (),
+                }
+            }
+            "#,
             r#"
-enum A { One, Two, }
-fn foo(a: A) {
-    match a {
-        $0A::One => {}
-        A::Two => {}
-    }
-}
-"#,
+            enum A { One, Two, }
+            fn foo(a: A) {
+                match a {
+                    $0A::One => {}
+                    A::Two => {}
+                }
+            }
+            "#,
         );
     }
 
@@ -1028,8 +1016,7 @@ enum Test {
 fn foo(t: Test) {
     m!(match t$0 {});
 }"#,
-            r#"
-macro_rules! m { ($expr:expr) => {$expr}}
+            r#"macro_rules! m { ($expr:expr) => {$expr}}
 enum Test {
     A,
     B,
diff --git a/crates/ide_assists/src/handlers/generate_default_from_new.rs b/crates/ide_assists/src/handlers/generate_default_from_new.rs
index bad8263669..dc14552d60 100644
--- a/crates/ide_assists/src/handlers/generate_default_from_new.rs
+++ b/crates/ide_assists/src/handlers/generate_default_from_new.rs
@@ -3,10 +3,8 @@ use crate::{
     AssistId,
 };
 use ide_db::helpers::FamousDefs;
-use itertools::Itertools;
-use stdx::format_to;
 use syntax::{
-    ast::{self, GenericParamsOwner, Impl, NameOwner, TypeBoundsOwner},
+    ast::{self, Impl, NameOwner},
     AstNode,
 };
 
@@ -67,56 +65,23 @@ pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext)
         "Generate a Default impl from a new fn",
         insert_location,
         move |builder| {
-            let default_code = "    fn default() -> Self {
-        Self::new()
-    }";
-            let code = generate_trait_impl_text_from_impl(&impl_, "Default", default_code);
+            let code = default_fn_node_for_new(impl_);
             builder.insert(insert_location.end(), code);
         },
     )
 }
 
-fn generate_trait_impl_text_from_impl(impl_: &ast::Impl, trait_text: &str, code: &str) -> String {
-    let generic_params = impl_.generic_param_list();
-    let mut buf = String::with_capacity(code.len());
-    buf.push_str("\n\n");
-    buf.push_str("impl");
-
-    if let Some(generic_params) = &generic_params {
-        let lifetimes = generic_params.lifetime_params().map(|lt| format!("{}", lt.syntax()));
-        let type_params = generic_params.type_params().map(|type_param| {
-            let mut buf = String::new();
-            if let Some(it) = type_param.name() {
-                format_to!(buf, "{}", it.syntax());
-            }
-            if let Some(it) = type_param.colon_token() {
-                format_to!(buf, "{} ", it);
-            }
-            if let Some(it) = type_param.type_bound_list() {
-                format_to!(buf, "{}", it.syntax());
-            }
-            buf
-        });
-        let const_params = generic_params.const_params().map(|t| t.syntax().to_string());
-        let generics = lifetimes.chain(type_params).chain(const_params).format(", ");
-        format_to!(buf, "<{}>", generics);
-    }
-
-    buf.push(' ');
-    buf.push_str(trait_text);
-    buf.push_str(" for ");
-    buf.push_str(&impl_.self_ty().unwrap().syntax().text().to_string());
-
-    match impl_.where_clause() {
-        Some(where_clause) => {
-            format_to!(buf, "\n{}\n{{\n{}\n}}", where_clause, code);
-        }
-        None => {
-            format_to!(buf, " {{\n{}\n}}", code);
-        }
-    }
+fn default_fn_node_for_new(impl_: Impl) -> String {
+    format!(
+        "
 
-    buf
+impl Default for {} {{
+    fn default() -> Self {{
+        Self::new()
+    }}
+}}",
+        impl_.self_ty().unwrap().syntax().text()
+    )
 }
 
 fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool {
@@ -210,234 +175,6 @@ impl Default for Test {
         );
     }
 
-    #[test]
-    fn new_function_with_generic() {
-        check_pass(
-            r#"
-pub struct Foo<T> {
-    _bar: *mut T,
-}
-
-impl<T> Foo<T> {
-    pub fn ne$0w() -> Self {
-        unimplemented!()
-    }
-}
-"#,
-            r#"
-pub struct Foo<T> {
-    _bar: *mut T,
-}
-
-impl<T> Foo<T> {
-    pub fn new() -> Self {
-        unimplemented!()
-    }
-}
-
-impl<T> Default for Foo<T> {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn new_function_with_generics() {
-        check_pass(
-            r#"
-pub struct Foo<T, B> {
-    _tars: *mut T,
-    _bar: *mut B,
-}
-
-impl<T, B> Foo<T, B> {
-    pub fn ne$0w() -> Self {
-        unimplemented!()
-    }
-}
-"#,
-            r#"
-pub struct Foo<T, B> {
-    _tars: *mut T,
-    _bar: *mut B,
-}
-
-impl<T, B> Foo<T, B> {
-    pub fn new() -> Self {
-        unimplemented!()
-    }
-}
-
-impl<T, B> Default for Foo<T, B> {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn new_function_with_generic_and_bound() {
-        check_pass(
-            r#"
-pub struct Foo<T> {
-    t: T,
-}
-
-impl<T: From<i32>> Foo<T> {
-    pub fn ne$0w() -> Self {
-        Foo { t: 0.into() }
-    }
-}
-"#,
-            r#"
-pub struct Foo<T> {
-    t: T,
-}
-
-impl<T: From<i32>> Foo<T> {
-    pub fn new() -> Self {
-        Foo { t: 0.into() }
-    }
-}
-
-impl<T: From<i32>> Default for Foo<T> {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn new_function_with_generics_and_bounds() {
-        check_pass(
-            r#"
-pub struct Foo<T, B> {
-    _tars: T,
-    _bar: B,
-}
-
-impl<T: From<i32>, B: From<i64>> Foo<T, B> {
-    pub fn ne$0w() -> Self {
-        unimplemented!()
-    }
-}
-"#,
-            r#"
-pub struct Foo<T, B> {
-    _tars: T,
-    _bar: B,
-}
-
-impl<T: From<i32>, B: From<i64>> Foo<T, B> {
-    pub fn new() -> Self {
-        unimplemented!()
-    }
-}
-
-impl<T: From<i32>, B: From<i64>> Default for Foo<T, B> {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn new_function_with_generic_and_where() {
-        check_pass(
-            r#"
-pub struct Foo<T> {
-    t: T,
-}
-
-impl<T: From<i32>> Foo<T>
-where
-    Option<T>: Debug
-{
-    pub fn ne$0w() -> Self {
-        Foo { t: 0.into() }
-    }
-}
-"#,
-            r#"
-pub struct Foo<T> {
-    t: T,
-}
-
-impl<T: From<i32>> Foo<T>
-where
-    Option<T>: Debug
-{
-    pub fn new() -> Self {
-        Foo { t: 0.into() }
-    }
-}
-
-impl<T: From<i32>> Default for Foo<T>
-where
-    Option<T>: Debug
-{
-    fn default() -> Self {
-        Self::new()
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn new_function_with_generics_and_wheres() {
-        check_pass(
-            r#"
-pub struct Foo<T, B> {
-    _tars: T,
-    _bar: B,
-}
-
-impl<T: From<i32>, B: From<i64>> Foo<T, B>
-where
-    Option<T>: Debug, Option<B>: Debug,
-{
-    pub fn ne$0w() -> Self {
-        unimplemented!()
-    }
-}
-"#,
-            r#"
-pub struct Foo<T, B> {
-    _tars: T,
-    _bar: B,
-}
-
-impl<T: From<i32>, B: From<i64>> Foo<T, B>
-where
-    Option<T>: Debug, Option<B>: Debug,
-{
-    pub fn new() -> Self {
-        unimplemented!()
-    }
-}
-
-impl<T: From<i32>, B: From<i64>> Default for Foo<T, B>
-where
-    Option<T>: Debug, Option<B>: Debug,
-{
-    fn default() -> Self {
-        Self::new()
-    }
-}
-"#,
-        );
-    }
-
     #[test]
     fn new_function_with_parameters() {
         cov_mark::check!(new_function_with_parameters);
diff --git a/crates/ide_assists/src/handlers/generate_new.rs b/crates/ide_assists/src/handlers/generate_new.rs
index 959a1f86cb..8ce5930b77 100644
--- a/crates/ide_assists/src/handlers/generate_new.rs
+++ b/crates/ide_assists/src/handlers/generate_new.rs
@@ -1,3 +1,4 @@
+use ast::Adt;
 use itertools::Itertools;
 use stdx::format_to;
 use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner};
@@ -36,7 +37,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
     };
 
     // Return early if we've found an existing new fn
-    let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), "new")?;
+    let impl_def = find_struct_impl(&ctx, &Adt::Struct(strukt.clone()), "new")?;
 
     let target = strukt.syntax().text_range();
     acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
@@ -59,7 +60,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
         let start_offset = impl_def
             .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf))
             .unwrap_or_else(|| {
-                buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
+                buf = generate_impl_text(&Adt::Struct(strukt.clone()), &buf);
                 strukt.syntax().text_range().end()
             });
 
@@ -80,132 +81,101 @@ mod tests {
     use super::*;
 
     #[test]
+    #[rustfmt::skip]
     fn test_generate_new() {
+        // Check output of generation
         check_assist(
             generate_new,
-            r#"
-struct Foo {$0}
-"#,
-            r#"
-struct Foo {}
+"struct Foo {$0}",
+"struct Foo {}
 
 impl Foo {
     fn $0new() -> Self { Self {  } }
-}
-"#,
+}",
         );
         check_assist(
             generate_new,
-            r#"
-struct Foo<T: Clone> {$0}
-"#,
-            r#"
-struct Foo<T: Clone> {}
+"struct Foo<T: Clone> {$0}",
+"struct Foo<T: Clone> {}
 
 impl<T: Clone> Foo<T> {
     fn $0new() -> Self { Self {  } }
-}
-"#,
+}",
         );
         check_assist(
             generate_new,
-            r#"
-struct Foo<'a, T: Foo<'a>> {$0}
-"#,
-            r#"
-struct Foo<'a, T: Foo<'a>> {}
+"struct Foo<'a, T: Foo<'a>> {$0}",
+"struct Foo<'a, T: Foo<'a>> {}
 
 impl<'a, T: Foo<'a>> Foo<'a, T> {
     fn $0new() -> Self { Self {  } }
-}
-"#,
+}",
         );
         check_assist(
             generate_new,
-            r#"
-struct Foo { baz: String $0}
-"#,
-            r#"
-struct Foo { baz: String }
+"struct Foo { baz: String $0}",
+"struct Foo { baz: String }
 
 impl Foo {
     fn $0new(baz: String) -> Self { Self { baz } }
-}
-"#,
+}",
         );
         check_assist(
             generate_new,
-            r#"
-struct Foo { baz: String, qux: Vec<i32> $0}
-"#,
-            r#"
-struct Foo { baz: String, qux: Vec<i32> }
+"struct Foo { baz: String, qux: Vec<i32> $0}",
+"struct Foo { baz: String, qux: Vec<i32> }
 
 impl Foo {
     fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
-}
-"#,
+}",
         );
-    }
 
-    #[test]
-    fn check_that_visibility_modifiers_dont_get_brought_in() {
+        // Check that visibility modifiers don't get brought in for fields
         check_assist(
             generate_new,
-            r#"
-struct Foo { pub baz: String, pub qux: Vec<i32> $0}
-"#,
-            r#"
-struct Foo { pub baz: String, pub qux: Vec<i32> }
+"struct Foo { pub baz: String, pub qux: Vec<i32> $0}",
+"struct Foo { pub baz: String, pub qux: Vec<i32> }
 
 impl Foo {
     fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
-}
-"#,
+}",
         );
-    }
 
-    #[test]
-    fn check_it_reuses_existing_impls() {
+        // Check that it reuses existing impls
         check_assist(
             generate_new,
-            r#"
-struct Foo {$0}
+"struct Foo {$0}
 
 impl Foo {}
-"#,
-            r#"
-struct Foo {}
+",
+"struct Foo {}
 
 impl Foo {
     fn $0new() -> Self { Self {  } }
 }
-"#,
+",
         );
         check_assist(
             generate_new,
-            r#"
-struct Foo {$0}
+"struct Foo {$0}
 
 impl Foo {
     fn qux(&self) {}
 }
-"#,
-            r#"
-struct Foo {}
+",
+"struct Foo {}
 
 impl Foo {
     fn $0new() -> Self { Self {  } }
 
     fn qux(&self) {}
 }
-"#,
+",
         );
 
         check_assist(
             generate_new,
-            r#"
-struct Foo {$0}
+"struct Foo {$0}
 
 impl Foo {
     fn qux(&self) {}
@@ -213,9 +183,8 @@ impl Foo {
         5
     }
 }
-"#,
-            r#"
-struct Foo {}
+",
+"struct Foo {}
 
 impl Foo {
     fn $0new() -> Self { Self {  } }
@@ -225,37 +194,27 @@ impl Foo {
         5
     }
 }
-"#,
+",
         );
-    }
 
-    #[test]
-    fn check_visibility_of_new_fn_based_on_struct() {
+        // Check visibility of new fn based on struct
         check_assist(
             generate_new,
-            r#"
-pub struct Foo {$0}
-"#,
-            r#"
-pub struct Foo {}
+"pub struct Foo {$0}",
+"pub struct Foo {}
 
 impl Foo {
     pub fn $0new() -> Self { Self {  } }
-}
-"#,
+}",
         );
         check_assist(
             generate_new,
-            r#"
-pub(crate) struct Foo {$0}
-"#,
-            r#"
-pub(crate) struct Foo {}
+"pub(crate) struct Foo {$0}",
+"pub(crate) struct Foo {}
 
 impl Foo {
     pub(crate) fn $0new() -> Self { Self {  } }
-}
-"#,
+}",
         );
     }
 
@@ -263,28 +222,26 @@ impl Foo {
     fn generate_new_not_applicable_if_fn_exists() {
         check_assist_not_applicable(
             generate_new,
-            r#"
+            "
 struct Foo {$0}
 
 impl Foo {
     fn new() -> Self {
         Self
     }
-}
-"#,
+}",
         );
 
         check_assist_not_applicable(
             generate_new,
-            r#"
+            "
 struct Foo {$0}
 
 impl Foo {
     fn New() -> Self {
         Self
     }
-}
-"#,
+}",
         );
     }
 
@@ -292,12 +249,12 @@ impl Foo {
     fn generate_new_target() {
         check_assist_target(
             generate_new,
-            r#"
+            "
 struct SomeThingIrrelevant;
 /// Has a lifetime parameter
 struct Foo<'a, T: Foo<'a>> {$0}
 struct EvenMoreIrrelevant;
-"#,
+",
             "/// Has a lifetime parameter
 struct Foo<'a, T: Foo<'a>> {}",
         );
@@ -307,7 +264,7 @@ struct Foo<'a, T: Foo<'a>> {}",
     fn test_unrelated_new() {
         check_assist(
             generate_new,
-            r#"
+            r##"
 pub struct AstId<N: AstNode> {
     file_id: HirFileId,
     file_ast_id: FileAstId<N>,
@@ -328,9 +285,8 @@ impl<T> Source<T> {
     pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
         Source { file_id: self.file_id, ast: f(self.ast) }
     }
-}
-"#,
-            r#"
+}"##,
+            r##"
 pub struct AstId<N: AstNode> {
     file_id: HirFileId,
     file_ast_id: FileAstId<N>,
@@ -353,8 +309,7 @@ impl<T> Source<T> {
     pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
         Source { file_id: self.file_id, ast: f(self.ast) }
     }
-}
-"#,
+}"##,
         );
     }
 }
diff --git a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
index 16f8f9d70b..68bc15120b 100644
--- a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
+++ b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
@@ -84,8 +84,8 @@ fn generate_fn_def_assist(
         }
     };
     acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| {
-        let fn_def = builder.make_mut(fn_def);
-        let lifetime = builder.make_mut(lifetime);
+        let fn_def = builder.make_ast_mut(fn_def);
+        let lifetime = builder.make_ast_mut(lifetime);
         let loc_needing_lifetime =
             loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position());
 
@@ -107,8 +107,8 @@ fn generate_impl_def_assist(
 ) -> Option<()> {
     let new_lifetime_param = generate_unique_lifetime_param_name(impl_def.generic_param_list())?;
     acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| {
-        let impl_def = builder.make_mut(impl_def);
-        let lifetime = builder.make_mut(lifetime);
+        let impl_def = builder.make_ast_mut(impl_def);
+        let lifetime = builder.make_ast_mut(lifetime);
 
         impl_def.get_or_create_generic_param_list().add_generic_param(
             make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(),
@@ -141,8 +141,8 @@ enum NeedsLifetime {
 impl NeedsLifetime {
     fn make_mut(self, builder: &mut AssistBuilder) -> Self {
         match self {
-            Self::SelfParam(it) => Self::SelfParam(builder.make_mut(it)),
-            Self::RefType(it) => Self::RefType(builder.make_mut(it)),
+            Self::SelfParam(it) => Self::SelfParam(builder.make_ast_mut(it)),
+            Self::RefType(it) => Self::RefType(builder.make_ast_mut(it)),
         }
     }
 
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs
index 31854840c2..3cd090737e 100644
--- a/crates/ide_assists/src/handlers/merge_imports.rs
+++ b/crates/ide_assists/src/handlers/merge_imports.rs
@@ -47,16 +47,16 @@ 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_mut(to_replace);
-                let to_remove = builder.make_mut(to_remove);
+                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());
                 to_remove.remove();
             }
 
             if let Some((to_replace, replacement, to_remove)) = uses {
-                let to_replace = builder.make_mut(to_replace);
-                let to_remove = builder.make_mut(to_remove);
+                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());
                 to_remove.remove()
diff --git a/crates/ide_assists/src/handlers/move_bounds.rs b/crates/ide_assists/src/handlers/move_bounds.rs
index d89d11bdf4..fa3f766093 100644
--- a/crates/ide_assists/src/handlers/move_bounds.rs
+++ b/crates/ide_assists/src/handlers/move_bounds.rs
@@ -36,8 +36,8 @@ 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_mut(type_param_list);
-            let parent = edit.make_syntax_mut(parent);
+            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 {
diff --git a/crates/ide_assists/src/handlers/move_module_to_file.rs b/crates/ide_assists/src/handlers/move_module_to_file.rs
index 93f702c556..6e685b4b29 100644
--- a/crates/ide_assists/src/handlers/move_module_to_file.rs
+++ b/crates/ide_assists/src/handlers/move_module_to_file.rs
@@ -1,4 +1,4 @@
-use ast::edit::IndentLevel;
+use ast::{edit::IndentLevel, VisibilityOwner};
 use ide_db::base_db::AnchoredPathBuf;
 use stdx::format_to;
 use syntax::{
@@ -60,18 +60,12 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Opt
             };
 
             let mut buf = String::new();
+            if let Some(v) = module_ast.visibility() {
+                format_to!(buf, "{} ", v);
+            }
             format_to!(buf, "mod {};", module_name);
 
-            let replacement_start = if let Some(mod_token) = module_ast.mod_token() {
-                mod_token.text_range().start()
-            } else {
-                module_ast.syntax().text_range().start()
-            };
-
-            builder.replace(
-                TextRange::new(replacement_start, module_ast.syntax().text_range().end()),
-                buf,
-            );
+            builder.replace(module_ast.syntax().text_range(), buf);
 
             let dst = AnchoredPathBuf { anchor: ctx.frange.file_id, path };
             builder.create_file(dst, contents);
@@ -190,26 +184,4 @@ pub(crate) mod tests;
         cov_mark::check!(available_before_curly);
         check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#);
     }
-
-    #[test]
-    fn keep_outer_comments_and_attributes() {
-        check_assist(
-            move_module_to_file,
-            r#"
-/// doc comment
-#[attribute]
-mod $0tests {
-    #[test] fn t() {}
-}
-"#,
-            r#"
-//- /main.rs
-/// doc comment
-#[attribute]
-mod tests;
-//- /tests.rs
-#[test] fn t() {}
-"#,
-        );
-    }
 }
diff --git a/crates/ide_assists/src/handlers/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs
index f07b8a6c0d..3128faa68b 100644
--- a/crates/ide_assists/src/handlers/pull_assignment_up.rs
+++ b/crates/ide_assists/src/handlers/pull_assignment_up.rs
@@ -74,10 +74,10 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Opti
             let assignments: Vec<_> = collector
                 .assignments
                 .into_iter()
-                .map(|(stmt, rhs)| (edit.make_mut(stmt), rhs.clone_for_update()))
+                .map(|(stmt, rhs)| (edit.make_ast_mut(stmt), rhs.clone_for_update()))
                 .collect();
 
-            let tgt = edit.make_mut(tgt);
+            let tgt = edit.make_ast_mut(tgt);
 
             for (stmt, rhs) in assignments {
                 let mut stmt = stmt.syntax().clone();
diff --git a/crates/ide_assists/src/handlers/raw_string.rs b/crates/ide_assists/src/handlers/raw_string.rs
index d98a55ae4a..d0f1613f37 100644
--- a/crates/ide_assists/src/handlers/raw_string.rs
+++ b/crates/ide_assists/src/handlers/raw_string.rs
@@ -1,6 +1,6 @@
 use std::borrow::Cow;
 
-use syntax::{ast, ast::IsString, AstToken, TextRange, TextSize};
+use syntax::{ast, AstToken, TextRange, TextSize};
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
 
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs
index 933acead1b..e90bbdbcf2 100644
--- a/crates/ide_assists/src/handlers/reorder_fields.rs
+++ b/crates/ide_assists/src/handlers/reorder_fields.rs
@@ -70,10 +70,10 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<(
         target,
         |builder| match fields {
             Either::Left((sorted, field_list)) => {
-                replace(builder.make_mut(field_list).fields(), sorted)
+                replace(builder.make_ast_mut(field_list).fields(), sorted)
             }
             Either::Right((sorted, field_list)) => {
-                replace(builder.make_mut(field_list).fields(), sorted)
+                replace(builder.make_ast_mut(field_list).fields(), sorted)
             }
         },
     )
diff --git a/crates/ide_assists/src/handlers/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs
index 5a6a9f158e..54a9a468ec 100644
--- a/crates/ide_assists/src/handlers/reorder_impl.rs
+++ b/crates/ide_assists/src/handlers/reorder_impl.rs
@@ -79,7 +79,8 @@ pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
         "Sort methods",
         target,
         |builder| {
-            let methods = methods.into_iter().map(|fn_| builder.make_mut(fn_)).collect::<Vec<_>>();
+            let methods =
+                methods.into_iter().map(|fn_| builder.make_ast_mut(fn_)).collect::<Vec<_>>();
             methods
                 .into_iter()
                 .zip(sorted)
diff --git a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs
index 540a905cc0..15420aedfb 100644
--- a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs
+++ b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs
@@ -32,8 +32,8 @@ pub(crate) fn replace_impl_trait_with_generic(
         "Replace impl trait with generic",
         target,
         |edit| {
-            let impl_trait_type = edit.make_mut(impl_trait_type);
-            let fn_ = edit.make_mut(fn_);
+            let impl_trait_type = edit.make_ast_mut(impl_trait_type);
+            let fn_ = edit.make_ast_mut(fn_);
 
             let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type);
 
diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
index 39f5eb4ff4..99ba798606 100644
--- a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
@@ -40,7 +40,7 @@ pub(crate) fn replace_qualified_name_with_use(
         |builder| {
             // Now that we've brought the name into scope, re-qualify all paths that could be
             // affected (that is, all paths inside the node we added the `use` to).
-            let syntax = builder.make_syntax_mut(syntax.clone());
+            let syntax = builder.make_mut(syntax.clone());
             if let Some(ref import_scope) = ImportScope::from(syntax.clone()) {
                 shorten_paths(&syntax, &path.clone_for_update());
                 insert_use(import_scope, path, ctx.config.insert_use);
diff --git a/crates/ide_assists/src/handlers/replace_string_with_char.rs b/crates/ide_assists/src/handlers/replace_string_with_char.rs
index 0800d291e4..634b9c0b79 100644
--- a/crates/ide_assists/src/handlers/replace_string_with_char.rs
+++ b/crates/ide_assists/src/handlers/replace_string_with_char.rs
@@ -1,4 +1,4 @@
-use syntax::{ast, ast::IsString, AstToken, SyntaxKind::STRING};
+use syntax::{ast, AstToken, SyntaxKind::STRING};
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
 
diff --git a/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs
index a3bfa221c2..0fec961b43 100644
--- a/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs
+++ b/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs
@@ -17,7 +17,7 @@ use ide_db::ty_filter::TryEnum;
 
 // Assist: replace_unwrap_with_match
 //
-// Replaces `unwrap` with a `match` expression. Works for Result and Option.
+// Replaces `unwrap` a `match` expression. Works for Result and Option.
 //
 // ```
 // enum Result<T, E> { Ok(T), Err(E) }
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index fc7caee04b..0dcf20c618 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -17,7 +17,7 @@ use syntax::{
     ast::AttrsOwner,
     ast::NameOwner,
     ast::{self, edit, make, ArgListOwner, GenericParamsOwner},
-    ted, AstNode, Direction, SmolStr,
+    AstNode, Direction, SmolStr,
     SyntaxKind::*,
     SyntaxNode, TextSize, T,
 };
@@ -128,43 +128,43 @@ pub fn add_trait_assoc_items_to_impl(
     sema: &hir::Semantics<ide_db::RootDatabase>,
     items: Vec<ast::AssocItem>,
     trait_: hir::Trait,
-    impl_: ast::Impl,
+    impl_def: ast::Impl,
     target_scope: hir::SemanticsScope,
 ) -> (ast::Impl, ast::AssocItem) {
+    let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list);
+
+    let n_existing_items = impl_item_list.assoc_items().count();
     let source_scope = sema.scope_for_def(trait_);
     let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
-        .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_.clone()));
+        .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone()));
 
     let items = items
         .into_iter()
         .map(|it| it.clone_for_update())
         .inspect(|it| ast_transform::apply(&*ast_transform, it))
-        .map(|it| edit::remove_attrs_and_docs(&it).clone_subtree().clone_for_update());
-
-    let res = impl_.clone_for_update();
-
-    let assoc_item_list = res.get_or_create_assoc_item_list();
-    let mut first_item = None;
-    for item in items {
-        first_item.get_or_insert_with(|| item.clone());
-        match &item {
-            ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
+        .map(|it| match it {
+            ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)),
+            ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()),
+            _ => it,
+        })
+        .map(|it| edit::remove_attrs_and_docs(&it));
+
+    let new_impl_item_list = impl_item_list.append_items(items);
+    let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list);
+    let first_new_item =
+        new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap();
+    return (new_impl_def, first_new_item);
+
+    fn add_body(fn_def: ast::Fn) -> ast::Fn {
+        match fn_def.body() {
+            Some(_) => fn_def,
+            None => {
                 let body = make::block_expr(None, Some(make::ext::expr_todo()))
                     .indent(edit::IndentLevel(1));
-                ted::replace(fn_.get_or_create_body().syntax(), body.clone_for_update().syntax())
+                fn_def.with_body(body)
             }
-            ast::AssocItem::TypeAlias(type_alias) => {
-                if let Some(type_bound_list) = type_alias.type_bound_list() {
-                    type_bound_list.remove()
-                }
-            }
-            _ => {}
         }
-
-        assoc_item_list.add_item(item)
     }
-
-    (res, first_item.unwrap())
 }
 
 #[derive(Clone, Copy, Debug)]
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 61b6671044..b635e0ca35 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -9,21 +9,22 @@ use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKin
 pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
     // complete keyword "crate" in use stmt
     let source_range = ctx.source_range();
-    let kw_completion = move |text: &str| {
-        let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, text);
-        item.kind(CompletionItemKind::Keyword).insert_text(text);
-        item
-    };
 
     if ctx.use_item_syntax.is_some() {
         if ctx.path_qual.is_none() {
-            kw_completion("crate::").add_to(acc);
+            let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "crate::");
+            item.kind(CompletionItemKind::Keyword).insert_text("crate::");
+            item.add_to(acc);
         }
-        kw_completion("self").add_to(acc);
+        let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "self");
+        item.kind(CompletionItemKind::Keyword);
+        item.add_to(acc);
         if iter::successors(ctx.path_qual.clone(), |p| p.qualifier())
             .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
         {
-            kw_completion("super::").add_to(acc);
+            let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "super::");
+            item.kind(CompletionItemKind::Keyword).insert_text("super::");
+            item.add_to(acc);
         }
     }
 
@@ -31,8 +32,9 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
     if let Some(receiver) = &ctx.dot_receiver {
         if let Some(ty) = ctx.sema.type_of_expr(receiver) {
             if ty.impls_future(ctx.db) {
-                let mut item = kw_completion("await");
-                item.detail("expr.await");
+                let mut item =
+                    CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await");
+                item.kind(CompletionItemKind::Keyword).detail("expr.await").insert_text("await");
                 item.add_to(acc);
             }
         };
diff --git a/crates/ide_db/src/helpers/merge_imports.rs b/crates/ide_db/src/helpers/merge_imports.rs
index 8fb40e8371..af2a51a4d7 100644
--- a/crates/ide_db/src/helpers/merge_imports.rs
+++ b/crates/ide_db/src/helpers/merge_imports.rs
@@ -2,9 +2,8 @@
 use std::cmp::Ordering;
 
 use itertools::{EitherOrBoth, Itertools};
-use syntax::{
-    ast::{self, make, AstNode, AttrsOwner, PathSegmentKind, VisibilityOwner},
-    ted,
+use syntax::ast::{
+    self, edit::AstNodeEdit, make, AstNode, AttrsOwner, PathSegmentKind, VisibilityOwner,
 };
 
 /// What type of merges are allowed.
@@ -42,12 +41,10 @@ pub fn try_merge_imports(
         return None;
     }
 
-    let lhs = lhs.clone_subtree().clone_for_update();
     let lhs_tree = lhs.use_tree()?;
     let rhs_tree = rhs.use_tree()?;
     let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behavior)?;
-    ted::replace(lhs_tree.syntax(), merged.syntax());
-    Some(lhs)
+    Some(lhs.with_use_tree(merged).clone_for_update())
 }
 
 pub fn try_merge_trees(
@@ -68,7 +65,7 @@ pub fn try_merge_trees(
     } else {
         (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix))
     };
-    recursive_merge(&lhs, &rhs, merge).map(|it| it.clone_for_update())
+    recursive_merge(&lhs, &rhs, merge)
 }
 
 /// Recursively "zips" together lhs and rhs.
@@ -81,8 +78,7 @@ fn recursive_merge(
         .use_tree_list()
         .into_iter()
         .flat_map(|list| list.use_trees())
-        // We use Option here to early return from this function(this is not the
-        // same as a `filter` op).
+        // we use Option here to early return from this function(this is not the same as a `filter` op)
         .map(|tree| match merge.is_tree_allowed(&tree) {
             true => Some(tree),
             false => None,
@@ -115,10 +111,8 @@ fn recursive_merge(
                     let tree_is_self = |tree: ast::UseTree| {
                         tree.path().as_ref().map(path_is_self).unwrap_or(false)
                     };
-                    // Check if only one of the two trees has a tree list, and
-                    // whether that then contains `self` or not. If this is the
-                    // case we can skip this iteration since the path without
-                    // the list is already included in the other one via `self`.
+                    // check if only one of the two trees has a tree list, and whether that then contains `self` or not.
+                    // If this is the case we can skip this iteration since the path without the list is already included in the other one via `self`
                     let tree_contains_self = |tree: &ast::UseTree| {
                         tree.use_tree_list()
                             .map(|tree_list| tree_list.use_trees().any(tree_is_self))
@@ -133,11 +127,9 @@ fn recursive_merge(
                         _ => (),
                     }
 
-                    // Glob imports aren't part of the use-tree lists so we need
-                    // to special handle them here as well this special handling
-                    // is only required for when we merge a module import into a
-                    // glob import of said module see the `merge_self_glob` or
-                    // `merge_mod_into_glob` tests.
+                    // glob imports arent part of the use-tree lists so we need to special handle them here as well
+                    // this special handling is only required for when we merge a module import into a glob import of said module
+                    // see the `merge_self_glob` or `merge_mod_into_glob` tests
                     if lhs_t.star_token().is_some() || rhs_t.star_token().is_some() {
                         *lhs_t = make::use_tree(
                             make::path_unqualified(make::path_segment_self()),
@@ -173,11 +165,11 @@ fn recursive_merge(
         }
     }
 
-    let lhs = lhs.clone_subtree().clone_for_update();
-    if let Some(old) = lhs.use_tree_list() {
-        ted::replace(old.syntax(), make::use_tree_list(use_trees).syntax().clone_for_update());
-    }
-    ast::UseTree::cast(lhs.syntax().clone_subtree())
+    Some(if let Some(old) = lhs.use_tree_list() {
+        lhs.replace_descendant(old, make::use_tree_list(use_trees)).clone_for_update()
+    } else {
+        lhs.clone()
+    })
 }
 
 /// Traverses both paths until they differ, returning the common prefix of both.
diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs
index 0f5c4abc4b..da427d6868 100644
--- a/crates/ide_db/src/symbol_index.rs
+++ b/crates/ide_db/src/symbol_index.rs
@@ -161,11 +161,6 @@ impl<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> {
 // That is, `#` switches from "types" to all symbols, `*` switches from the current
 // workspace to dependencies.
 //
-// Note that filtering does not currently work in VSCode due to the editor never
-// sending the special symbols to the language server. Instead, you can configure
-// the filtering via the `rust-analyzer.workspace.symbol.search.scope` and
-// `rust-analyzer.workspace.symbol.search.kind` settings.
-//
 // |===
 // | Editor  | Shortcut
 //
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs
index 3aa546980c..7b88dca630 100644
--- a/crates/project_model/src/build_data.rs
+++ b/crates/project_model/src/build_data.rs
@@ -143,7 +143,6 @@ impl WorkspaceBuildData {
             cmd.env("RA_RUSTC_WRAPPER", "1");
         }
 
-        cmd.current_dir(cargo_toml.parent().unwrap());
         cmd.args(&["check", "--quiet", "--workspace", "--message-format=json", "--manifest-path"])
             .arg(cargo_toml.as_ref());
 
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index b2317618a8..3c87782f28 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -122,7 +122,6 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
             "runnables": {
                 "kinds": [ "cargo" ],
             },
-            "workspaceSymbolScopeKindFiltering": true,
         })),
     }
 }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 339014fd31..d83670bdad 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -24,8 +24,7 @@ use vfs::AbsPathBuf;
 
 use crate::{
     caps::completion_item_edit_resolve, diagnostics::DiagnosticsMapConfig,
-    line_index::OffsetEncoding, lsp_ext::supports_utf8, lsp_ext::WorkspaceSymbolSearchKind,
-    lsp_ext::WorkspaceSymbolSearchScope,
+    line_index::OffsetEncoding, lsp_ext::supports_utf8,
 };
 
 // Defines the server-side configuration of the rust-analyzer. We generate
@@ -125,13 +124,6 @@ config_data! {
         /// These directories will be ignored by rust-analyzer.
         files_excludeDirs: Vec<PathBuf> = "[]",
 
-        /// Use semantic tokens for strings.
-        ///
-        /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars.
-        /// By disabling semantic tokens for strings, other grammars can be used to highlight
-        /// their contents.
-        highlighting_strings: bool = "true",
-
         /// Whether to show `Debug` action. Only applies when
         /// `#rust-analyzer.hoverActions.enable#` is set.
         hoverActions_debug: bool           = "true",
@@ -216,11 +208,6 @@ config_data! {
         /// Advanced option, fully override the command rust-analyzer uses for
         /// formatting.
         rustfmt_overrideCommand: Option<Vec<String>> = "null",
-
-        /// Workspace symbol search scope.
-        workspace_symbol_search_scope: WorskpaceSymbolSearchScopeDef = "\"workspace\"",
-        /// Workspace symbol search kind.
-        workspace_symbol_search_kind: WorskpaceSymbolSearchKindDef = "\"only_types\"",
     }
 }
 
@@ -315,15 +302,6 @@ pub struct RunnablesConfig {
     pub cargo_extra_args: Vec<String>,
 }
 
-/// Configuration for workspace symbol search requests.
-#[derive(Debug, Clone)]
-pub struct WorkspaceSymbolConfig {
-    /// In what scope should the symbol be searched in.
-    pub search_scope: WorkspaceSymbolSearchScope,
-    /// What kind of symbol is being search for.
-    pub search_kind: WorkspaceSymbolSearchKind,
-}
-
 impl Config {
     pub fn new(root_path: AbsPathBuf, caps: ClientCapabilities) -> Self {
         Config { caps, data: ConfigData::default(), discovered_projects: None, root_path }
@@ -677,9 +655,6 @@ impl Config {
             refs: self.data.lens_enable && self.data.lens_references,
         }
     }
-    pub fn highlighting_strings(&self) -> bool {
-        self.data.highlighting_strings
-    }
     pub fn hover(&self) -> HoverConfig {
         HoverConfig {
             implementations: self.data.hoverActions_enable
@@ -702,22 +677,6 @@ impl Config {
             .contains(&MarkupKind::Markdown),
         }
     }
-
-    pub fn workspace_symbol(&self) -> WorkspaceSymbolConfig {
-        WorkspaceSymbolConfig {
-            search_scope: match self.data.workspace_symbol_search_scope {
-                WorskpaceSymbolSearchScopeDef::Workspace => WorkspaceSymbolSearchScope::Workspace,
-                WorskpaceSymbolSearchScopeDef::WorkspaceAndDependencies => {
-                    WorkspaceSymbolSearchScope::WorkspaceAndDependencies
-                }
-            },
-            search_kind: match self.data.workspace_symbol_search_kind {
-                WorskpaceSymbolSearchKindDef::OnlyTypes => WorkspaceSymbolSearchKind::OnlyTypes,
-                WorskpaceSymbolSearchKindDef::AllSymbols => WorkspaceSymbolSearchKind::AllSymbols,
-            },
-        }
-    }
-
     pub fn semantic_tokens_refresh(&self) -> bool {
         try_or!(self.caps.workspace.as_ref()?.semantic_tokens.as_ref()?.refresh_support?, false)
     }
@@ -764,20 +723,6 @@ enum ImportPrefixDef {
     ByCrate,
 }
 
-#[derive(Deserialize, Debug, Clone)]
-#[serde(rename_all = "snake_case")]
-enum WorskpaceSymbolSearchScopeDef {
-    Workspace,
-    WorkspaceAndDependencies,
-}
-
-#[derive(Deserialize, Debug, Clone)]
-#[serde(rename_all = "snake_case")]
-enum WorskpaceSymbolSearchKindDef {
-    OnlyTypes,
-    AllSymbols,
-}
-
 macro_rules! _config_data {
     (struct $name:ident {
         $(
@@ -948,22 +893,6 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
             "type": "array",
             "items": { "type": ["string", "object"] },
         },
-        "WorskpaceSymbolSearchScopeDef" => set! {
-            "type": "string",
-            "enum": ["workspace", "workspace_and_dependencies"],
-            "enumDescriptions": [
-                "Search in current workspace only",
-                "Search in current workspace and dependencies"
-            ],
-        },
-        "WorskpaceSymbolSearchKindDef" => set! {
-            "type": "string",
-            "enum": ["only_types", "all_symbols"],
-            "enumDescriptions": [
-                "Search for types only",
-                "Search for all symbols kinds"
-            ],
-        },
         _ => panic!("{}: {}", ty, default),
     }
 
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 51041d7a0c..551013aa9c 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -38,7 +38,7 @@ use crate::{
     from_proto,
     global_state::{GlobalState, GlobalStateSnapshot},
     line_index::LineEndings,
-    lsp_ext::{self, InlayHint, InlayHintsParams, WorkspaceSymbolParams},
+    lsp_ext::{self, InlayHint, InlayHintsParams},
     lsp_utils::all_edits_are_disjoint,
     to_proto, LspError, Result,
 };
@@ -380,12 +380,11 @@ pub(crate) fn handle_document_symbol(
 
 pub(crate) fn handle_workspace_symbol(
     snap: GlobalStateSnapshot,
-    params: WorkspaceSymbolParams,
+    params: lsp_types::WorkspaceSymbolParams,
 ) -> Result<Option<Vec<SymbolInformation>>> {
     let _p = profile::span("handle_workspace_symbol");
-
-    let (all_symbols, libs) = decide_search_scope_and_kind(&params, &snap);
-
+    let all_symbols = params.query.contains('#');
+    let libs = params.query.contains('*');
     let query = {
         let query: String = params.query.chars().filter(|&c| c != '#' && c != '*').collect();
         let mut q = Query::new(query);
@@ -407,45 +406,6 @@ pub(crate) fn handle_workspace_symbol(
 
     return Ok(Some(res));
 
-    fn decide_search_scope_and_kind(
-        params: &WorkspaceSymbolParams,
-        snap: &GlobalStateSnapshot,
-    ) -> (bool, bool) {
-        // Support old-style parsing of markers in the query.
-        let mut all_symbols = params.query.contains('#');
-        let mut libs = params.query.contains('*');
-
-        let config = snap.config.workspace_symbol();
-
-        // If no explicit marker was set, check request params. If that's also empty
-        // use global config.
-        if !all_symbols {
-            let search_kind = if let Some(ref search_kind) = params.search_kind {
-                search_kind
-            } else {
-                &config.search_kind
-            };
-            all_symbols = match search_kind {
-                lsp_ext::WorkspaceSymbolSearchKind::OnlyTypes => false,
-                lsp_ext::WorkspaceSymbolSearchKind::AllSymbols => true,
-            }
-        }
-
-        if !libs {
-            let search_scope = if let Some(ref search_scope) = params.search_scope {
-                search_scope
-            } else {
-                &config.search_scope
-            };
-            libs = match search_scope {
-                lsp_ext::WorkspaceSymbolSearchScope::Workspace => false,
-                lsp_ext::WorkspaceSymbolSearchScope::WorkspaceAndDependencies => true,
-            }
-        }
-
-        (all_symbols, libs)
-    }
-
     fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result<Vec<SymbolInformation>> {
         let mut res = Vec::new();
         for nav in snap.analysis.symbol_search(query)? {
@@ -1434,9 +1394,7 @@ pub(crate) fn handle_semantic_tokens_full(
     let line_index = snap.file_line_index(file_id)?;
 
     let highlights = snap.analysis.highlight(file_id)?;
-    let highlight_strings = snap.config.highlighting_strings();
-    let semantic_tokens =
-        to_proto::semantic_tokens(&text, &line_index, highlights, highlight_strings);
+    let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
 
     // Unconditionally cache the tokens
     snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens.clone());
@@ -1455,9 +1413,8 @@ pub(crate) fn handle_semantic_tokens_full_delta(
     let line_index = snap.file_line_index(file_id)?;
 
     let highlights = snap.analysis.highlight(file_id)?;
-    let highlight_strings = snap.config.highlighting_strings();
-    let semantic_tokens =
-        to_proto::semantic_tokens(&text, &line_index, highlights, highlight_strings);
+
+    let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
 
     let mut cache = snap.semantic_tokens_cache.lock();
     let cached_tokens = cache.entry(params.text_document.uri).or_default();
@@ -1486,9 +1443,7 @@ pub(crate) fn handle_semantic_tokens_range(
     let line_index = snap.file_line_index(frange.file_id)?;
 
     let highlights = snap.analysis.highlight_range(frange)?;
-    let highlight_strings = snap.config.highlighting_strings();
-    let semantic_tokens =
-        to_proto::semantic_tokens(&text, &line_index, highlights, highlight_strings);
+    let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
     Ok(Some(semantic_tokens.into()))
 }
 
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs
index ba2790acbd..56de9681c4 100644
--- a/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -123,7 +123,7 @@ fn integrated_completion_benchmark() {
     };
 
     {
-        let _p = profile::span("unqualified path completion");
+        let _it = stdx::timeit("unqualified path completion");
         let _span = profile::cpu_span();
         let analysis = host.analysis();
         let config = CompletionConfig {
@@ -156,7 +156,7 @@ fn integrated_completion_benchmark() {
     };
 
     {
-        let _p = profile::span("dot completion");
+        let _it = stdx::timeit("dot completion");
         let _span = profile::cpu_span();
         let analysis = host.analysis();
         let config = CompletionConfig {
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 34b53a7a80..3bd0980580 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -4,8 +4,7 @@ use std::{collections::HashMap, path::PathBuf};
 
 use lsp_types::request::Request;
 use lsp_types::{
-    notification::Notification, CodeActionKind, PartialResultParams, Position, Range,
-    TextDocumentIdentifier, WorkDoneProgressParams,
+    notification::Notification, CodeActionKind, Position, Range, TextDocumentIdentifier,
 };
 use serde::{Deserialize, Serialize};
 
@@ -439,42 +438,3 @@ pub enum MoveItemDirection {
     Up,
     Down,
 }
-
-#[derive(Debug)]
-pub enum WorkspaceSymbol {}
-
-impl Request for WorkspaceSymbol {
-    type Params = WorkspaceSymbolParams;
-    type Result = Option<Vec<lsp_types::SymbolInformation>>;
-    const METHOD: &'static str = "workspace/symbol";
-}
-
-#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
-pub struct WorkspaceSymbolParams {
-    #[serde(flatten)]
-    pub partial_result_params: PartialResultParams,
-
-    #[serde(flatten)]
-    pub work_done_progress_params: WorkDoneProgressParams,
-
-    /// A non-empty query string
-    pub query: String,
-
-    pub search_scope: Option<WorkspaceSymbolSearchScope>,
-
-    pub search_kind: Option<WorkspaceSymbolSearchKind>,
-}
-
-#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
-#[serde(rename_all = "camelCase")]
-pub enum WorkspaceSymbolSearchScope {
-    Workspace,
-    WorkspaceAndDependencies,
-}
-
-#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
-#[serde(rename_all = "camelCase")]
-pub enum WorkspaceSymbolSearchKind {
-    OnlyTypes,
-    AllSymbols,
-}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 4e07916113..c7bd7eee1a 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -525,9 +525,9 @@ impl GlobalState {
             .on::<lsp_ext::ExternalDocs>(handlers::handle_open_docs)
             .on::<lsp_ext::OpenCargoToml>(handlers::handle_open_cargo_toml)
             .on::<lsp_ext::MoveItem>(handlers::handle_move_item)
-            .on::<lsp_ext::WorkspaceSymbol>(handlers::handle_workspace_symbol)
             .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)
             .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)
+            .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)
             .on::<lsp_types::request::GotoDefinition>(handlers::handle_goto_definition)
             .on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)
             .on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 4fd576adbb..ecab89b2a8 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -91,7 +91,6 @@ define_semantic_token_modifiers![
     (INJECTED, "injected"),
     (MUTABLE, "mutable"),
     (CONSUMING, "consuming"),
-    (ASYNC, "async"),
     (UNSAFE, "unsafe"),
     (ATTRIBUTE_MODIFIER, "attribute"),
     (TRAIT_MODIFIER, "trait"),
@@ -184,8 +183,8 @@ pub(crate) fn diff_tokens(old: &[SemanticToken], new: &[SemanticToken]) -> Vec<S
     }
 }
 
-pub(crate) fn type_index(ty: SemanticTokenType) -> u32 {
-    SUPPORTED_TYPES.iter().position(|it| *it == ty).unwrap() as u32
+pub(crate) fn type_index(type_: SemanticTokenType) -> u32 {
+    SUPPORTED_TYPES.iter().position(|it| *it == type_).unwrap() as u32
 }
 
 #[cfg(test)]
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 9dec46c789..ecf6fd12fe 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -381,7 +381,6 @@ pub(crate) fn semantic_tokens(
     text: &str,
     line_index: &LineIndex,
     highlights: Vec<HlRange>,
-    highlight_strings: bool,
 ) -> lsp_types::SemanticTokens {
     let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string();
     let mut builder = semantic_tokens::SemanticTokensBuilder::new(id);
@@ -390,11 +389,8 @@ pub(crate) fn semantic_tokens(
         if highlight_range.highlight.is_empty() {
             continue;
         }
-        let (ty, mods) = semantic_token_type_and_modifiers(highlight_range.highlight);
-        if !highlight_strings && ty == lsp_types::SemanticTokenType::STRING {
-            continue;
-        }
-        let token_index = semantic_tokens::type_index(ty);
+        let (type_, mods) = semantic_token_type_and_modifiers(highlight_range.highlight);
+        let token_index = semantic_tokens::type_index(type_);
         let modifier_bitset = mods.0;
 
         for mut text_range in line_index.index.lines(highlight_range.range) {
@@ -426,7 +422,7 @@ fn semantic_token_type_and_modifiers(
     let type_ = match highlight.tag {
         HlTag::Symbol(symbol) => match symbol {
             SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE,
-            SymbolKind::Impl => semantic_tokens::TYPE_ALIAS,
+            SymbolKind::Impl => lsp_types::SemanticTokenType::TYPE,
             SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY,
             SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER,
             SymbolKind::ConstParam => semantic_tokens::CONST_PARAMETER,
@@ -500,7 +496,6 @@ fn semantic_token_type_and_modifiers(
             HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW,
             HlMod::Mutable => semantic_tokens::MUTABLE,
             HlMod::Consuming => semantic_tokens::CONSUMING,
-            HlMod::Async => semantic_tokens::ASYNC,
             HlMod::Unsafe => semantic_tokens::UNSAFE,
             HlMod::Callable => semantic_tokens::CALLABLE,
             HlMod::Static => lsp_types::SemanticTokenModifier::STATIC,
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs
similarity index 100%
rename from crates/rust-analyzer/tests/slow-tests/main.rs
rename to crates/rust-analyzer/tests/rust-analyzer/main.rs
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs
similarity index 100%
rename from crates/rust-analyzer/tests/slow-tests/support.rs
rename to crates/rust-analyzer/tests/rust-analyzer/support.rs
diff --git a/crates/rust-analyzer/tests/slow-tests/testdir.rs b/crates/rust-analyzer/tests/rust-analyzer/testdir.rs
similarity index 100%
rename from crates/rust-analyzer/tests/slow-tests/testdir.rs
rename to crates/rust-analyzer/tests/rust-analyzer/testdir.rs
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 747f0b9eb1..c0bc599188 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
 [dependencies]
 cov-mark = { version = "1.1", features = ["thread-local"] }
 itertools = "0.10.0"
-rowan = "=0.13.0-pre.6"
+rowan = "=0.13.0-pre.5"
 rustc_lexer = { version = "716.0.0", package = "rustc-ap-rustc_lexer" }
 rustc-hash = "1.1.0"
 arrayvec = "0.7"
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 241713c48e..3f9b84ab90 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -1,6 +1,6 @@
 //! FIXME: write short doc here
 
-use std::{hash::BuildHasherDefault, ops::RangeInclusive};
+use std::{fmt, hash::BuildHasherDefault, ops::RangeInclusive};
 
 use indexmap::IndexMap;
 use itertools::Itertools;
@@ -330,6 +330,137 @@ fn _replace_children(
     with_children(parent, new_children)
 }
 
+#[derive(Debug, PartialEq, Eq, Hash)]
+enum InsertPos {
+    FirstChildOf(SyntaxNode),
+    After(SyntaxElement),
+}
+
+#[derive(Default)]
+pub struct SyntaxRewriter<'a> {
+    //FIXME: add debug_assertions that all elements are in fact from the same file.
+    replacements: FxHashMap<SyntaxElement, Replacement>,
+    insertions: IndexMap<InsertPos, Vec<SyntaxElement>>,
+    _pd: std::marker::PhantomData<&'a ()>,
+}
+
+impl fmt::Debug for SyntaxRewriter<'_> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("SyntaxRewriter")
+            .field("replacements", &self.replacements)
+            .field("insertions", &self.insertions)
+            .finish()
+    }
+}
+
+impl SyntaxRewriter<'_> {
+    pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) {
+        let what = what.clone().into();
+        let replacement = Replacement::Single(with.clone().into());
+        self.replacements.insert(what, replacement);
+    }
+
+    pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode {
+        let _p = profile::span("rewrite");
+
+        if self.replacements.is_empty() && self.insertions.is_empty() {
+            return node.clone();
+        }
+        let green = self.rewrite_children(node);
+        with_green(node, green)
+    }
+
+    pub fn rewrite_ast<N: AstNode>(self, node: &N) -> N {
+        N::cast(self.rewrite(node.syntax())).unwrap()
+    }
+
+    /// Returns a node that encompasses all replacements to be done by this rewriter.
+    ///
+    /// Passing the returned node to `rewrite` will apply all replacements queued up in `self`.
+    ///
+    /// Returns `None` when there are no replacements.
+    pub fn rewrite_root(&self) -> Option<SyntaxNode> {
+        let _p = profile::span("rewrite_root");
+        fn element_to_node_or_parent(element: &SyntaxElement) -> Option<SyntaxNode> {
+            match element {
+                SyntaxElement::Node(it) => Some(it.clone()),
+                SyntaxElement::Token(it) => it.parent(),
+            }
+        }
+
+        self.replacements
+            .keys()
+            .filter_map(element_to_node_or_parent)
+            .chain(self.insertions.keys().filter_map(|pos| match pos {
+                InsertPos::FirstChildOf(it) => Some(it.clone()),
+                InsertPos::After(it) => element_to_node_or_parent(it),
+            }))
+            // If we only have one replacement/insertion, we must return its parent node, since `rewrite` does
+            // not replace the node passed to it.
+            .map(|it| it.parent().unwrap_or(it))
+            .fold1(|a, b| least_common_ancestor(&a, &b).unwrap())
+    }
+
+    fn replacement(&self, element: &SyntaxElement) -> Option<Replacement> {
+        self.replacements.get(element).cloned()
+    }
+
+    fn insertions(&self, pos: &InsertPos) -> Option<impl Iterator<Item = SyntaxElement> + '_> {
+        self.insertions.get(pos).map(|insertions| insertions.iter().cloned())
+    }
+
+    fn rewrite_children(&self, node: &SyntaxNode) -> rowan::GreenNode {
+        let _p = profile::span("rewrite_children");
+
+        //  FIXME: this could be made much faster.
+        let mut new_children = Vec::new();
+        if let Some(elements) = self.insertions(&InsertPos::FirstChildOf(node.clone())) {
+            new_children.extend(elements.map(element_to_green));
+        }
+        for child in node.children_with_tokens() {
+            self.rewrite_self(&mut new_children, &child);
+        }
+
+        rowan::GreenNode::new(rowan::SyntaxKind(node.kind() as u16), new_children)
+    }
+
+    fn rewrite_self(
+        &self,
+        acc: &mut Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>,
+        element: &SyntaxElement,
+    ) {
+        let _p = profile::span("rewrite_self");
+
+        if let Some(replacement) = self.replacement(&element) {
+            match replacement {
+                Replacement::Single(element) => acc.push(element_to_green(element)),
+            };
+        } else {
+            match element {
+                NodeOrToken::Token(it) => acc.push(NodeOrToken::Token(it.green().to_owned())),
+                NodeOrToken::Node(it) => {
+                    acc.push(NodeOrToken::Node(self.rewrite_children(it)));
+                }
+            }
+        }
+        if let Some(elements) = self.insertions(&InsertPos::After(element.clone())) {
+            acc.extend(elements.map(element_to_green));
+        }
+    }
+}
+
+fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> {
+    match element {
+        NodeOrToken::Node(it) => NodeOrToken::Node(it.green().into_owned()),
+        NodeOrToken::Token(it) => NodeOrToken::Token(it.green().to_owned()),
+    }
+}
+
+#[derive(Clone, Debug)]
+enum Replacement {
+    Single(SyntaxElement),
+}
+
 fn with_children(
     parent: &SyntaxNode,
     new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>,
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index a8071b51d2..7f472d4dbe 100644
--- a/crates/syntax/src/ast.rs
+++ b/crates/syntax/src/ast.rs
@@ -47,12 +47,6 @@ pub trait AstNode {
     {
         Self::cast(self.syntax().clone_for_update()).unwrap()
     }
-    fn clone_subtree(&self) -> Self
-    where
-        Self: Sized,
-    {
-        Self::cast(self.syntax().clone_subtree()).unwrap()
-    }
 }
 
 /// Like `AstNode`, but wraps tokens rather than interior nodes.
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 61952377f6..cbc75f9229 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -2,16 +2,20 @@
 //! immutable, all function here return a fresh copy of the tree, instead of
 //! doing an in-place modification.
 use std::{
-    fmt, iter,
+    array, fmt, iter,
     ops::{self, RangeInclusive},
 };
 
 use arrayvec::ArrayVec;
 
 use crate::{
-    algo,
-    ast::{self, make, AstNode},
-    ted, AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxKind,
+    algo::{self, SyntaxRewriter},
+    ast::{
+        self,
+        make::{self, tokens},
+        AstNode, GenericParamsOwner, NameOwner, TypeBoundsOwner,
+    },
+    AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind,
     SyntaxKind::{ATTR, COMMENT, WHITESPACE},
     SyntaxNode, SyntaxToken, T,
 };
@@ -25,6 +29,250 @@ impl ast::BinExpr {
     }
 }
 
+impl ast::Fn {
+    #[must_use]
+    pub fn with_body(&self, body: ast::BlockExpr) -> ast::Fn {
+        let mut to_insert: ArrayVec<SyntaxElement, 2> = ArrayVec::new();
+        let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.body() {
+            old_body.syntax().clone().into()
+        } else if let Some(semi) = self.semicolon_token() {
+            to_insert.push(make::tokens::single_space().into());
+            semi.into()
+        } else {
+            to_insert.push(make::tokens::single_space().into());
+            to_insert.push(body.syntax().clone().into());
+            return self.insert_children(InsertPosition::Last, to_insert);
+        };
+        to_insert.push(body.syntax().clone().into());
+        self.replace_children(single_node(old_body_or_semi), to_insert)
+    }
+
+    #[must_use]
+    pub fn with_generic_param_list(&self, generic_args: ast::GenericParamList) -> ast::Fn {
+        if let Some(old) = self.generic_param_list() {
+            return self.replace_descendant(old, generic_args);
+        }
+
+        let anchor = self.name().expect("The function must have a name").syntax().clone();
+
+        let to_insert = [generic_args.syntax().clone().into()];
+        self.insert_children(InsertPosition::After(anchor.into()), array::IntoIter::new(to_insert))
+    }
+}
+
+fn make_multiline<N>(node: N) -> N
+where
+    N: AstNode + Clone,
+{
+    let l_curly = match node.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) {
+        Some(it) => it,
+        None => return node,
+    };
+    let sibling = match l_curly.next_sibling_or_token() {
+        Some(it) => it,
+        None => return node,
+    };
+    let existing_ws = match sibling.as_token() {
+        None => None,
+        Some(tok) if tok.kind() != WHITESPACE => None,
+        Some(ws) => {
+            if ws.text().contains('\n') {
+                return node;
+            }
+            Some(ws.clone())
+        }
+    };
+
+    let indent = leading_indent(node.syntax()).unwrap_or_default();
+    let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
+    let to_insert = iter::once(ws.ws().into());
+    match existing_ws {
+        None => node.insert_children(InsertPosition::After(l_curly), to_insert),
+        Some(ws) => node.replace_children(single_node(ws), to_insert),
+    }
+}
+
+impl ast::Impl {
+    #[must_use]
+    pub fn with_assoc_item_list(&self, items: ast::AssocItemList) -> ast::Impl {
+        let mut to_insert: ArrayVec<SyntaxElement, 2> = ArrayVec::new();
+        if let Some(old_items) = self.assoc_item_list() {
+            let to_replace: SyntaxElement = old_items.syntax().clone().into();
+            to_insert.push(items.syntax().clone().into());
+            self.replace_children(single_node(to_replace), to_insert)
+        } else {
+            to_insert.push(make::tokens::single_space().into());
+            to_insert.push(items.syntax().clone().into());
+            self.insert_children(InsertPosition::Last, to_insert)
+        }
+    }
+}
+
+impl ast::AssocItemList {
+    #[must_use]
+    pub fn append_items(
+        &self,
+        items: impl IntoIterator<Item = ast::AssocItem>,
+    ) -> ast::AssocItemList {
+        let mut res = self.clone();
+        if !self.syntax().text().contains_char('\n') {
+            res = make_multiline(res);
+        }
+        items.into_iter().for_each(|it| res = res.append_item(it));
+        res.fixup_trailing_whitespace().unwrap_or(res)
+    }
+
+    #[must_use]
+    pub fn append_item(&self, item: ast::AssocItem) -> ast::AssocItemList {
+        let (indent, position, whitespace) = match self.assoc_items().last() {
+            Some(it) => (
+                leading_indent(it.syntax()).unwrap_or_default().to_string(),
+                InsertPosition::After(it.syntax().clone().into()),
+                "\n\n",
+            ),
+            None => match self.l_curly_token() {
+                Some(it) => (
+                    "    ".to_string() + &leading_indent(self.syntax()).unwrap_or_default(),
+                    InsertPosition::After(it.into()),
+                    "\n",
+                ),
+                None => return self.clone(),
+            },
+        };
+        let ws = tokens::WsBuilder::new(&format!("{}{}", whitespace, indent));
+        let to_insert: ArrayVec<SyntaxElement, 2> =
+            [ws.ws().into(), item.syntax().clone().into()].into();
+        self.insert_children(position, to_insert)
+    }
+
+    /// Remove extra whitespace between last item and closing curly brace.
+    fn fixup_trailing_whitespace(&self) -> Option<ast::AssocItemList> {
+        let first_token_after_items =
+            self.assoc_items().last()?.syntax().next_sibling_or_token()?;
+        let last_token_before_curly = self.r_curly_token()?.prev_sibling_or_token()?;
+        if last_token_before_curly != first_token_after_items {
+            // there is something more between last item and
+            // right curly than just whitespace - bail out
+            return None;
+        }
+        let whitespace =
+            last_token_before_curly.clone().into_token().and_then(ast::Whitespace::cast)?;
+        let text = whitespace.syntax().text();
+        let newline = text.rfind('\n')?;
+        let keep = tokens::WsBuilder::new(&text[newline..]);
+        Some(self.replace_children(
+            first_token_after_items..=last_token_before_curly,
+            std::iter::once(keep.ws().into()),
+        ))
+    }
+}
+
+impl ast::RecordExprFieldList {
+    #[must_use]
+    pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList {
+        self.insert_field(InsertPosition::Last, field)
+    }
+
+    #[must_use]
+    pub fn insert_field(
+        &self,
+        position: InsertPosition<&'_ ast::RecordExprField>,
+        field: &ast::RecordExprField,
+    ) -> ast::RecordExprFieldList {
+        let is_multiline = self.syntax().text().contains_char('\n');
+        let ws;
+        let space = if is_multiline {
+            ws = tokens::WsBuilder::new(&format!(
+                "\n{}    ",
+                leading_indent(self.syntax()).unwrap_or_default()
+            ));
+            ws.ws()
+        } else {
+            tokens::single_space()
+        };
+
+        let mut to_insert: ArrayVec<SyntaxElement, 4> = ArrayVec::new();
+        to_insert.push(space.into());
+        to_insert.push(field.syntax().clone().into());
+        to_insert.push(make::token(T![,]).into());
+
+        macro_rules! after_l_curly {
+            () => {{
+                let anchor = match self.l_curly_token() {
+                    Some(it) => it.into(),
+                    None => return self.clone(),
+                };
+                InsertPosition::After(anchor)
+            }};
+        }
+
+        macro_rules! after_field {
+            ($anchor:expr) => {
+                if let Some(comma) = $anchor
+                    .syntax()
+                    .siblings_with_tokens(Direction::Next)
+                    .find(|it| it.kind() == T![,])
+                {
+                    InsertPosition::After(comma)
+                } else {
+                    to_insert.insert(0, make::token(T![,]).into());
+                    InsertPosition::After($anchor.syntax().clone().into())
+                }
+            };
+        }
+
+        let position = match position {
+            InsertPosition::First => after_l_curly!(),
+            InsertPosition::Last => {
+                if !is_multiline {
+                    // don't insert comma before curly
+                    to_insert.pop();
+                }
+                match self.fields().last() {
+                    Some(it) => after_field!(it),
+                    None => after_l_curly!(),
+                }
+            }
+            InsertPosition::Before(anchor) => {
+                InsertPosition::Before(anchor.syntax().clone().into())
+            }
+            InsertPosition::After(anchor) => after_field!(anchor),
+        };
+
+        self.insert_children(position, to_insert)
+    }
+}
+
+impl ast::TypeAlias {
+    #[must_use]
+    pub fn remove_bounds(&self) -> ast::TypeAlias {
+        let colon = match self.colon_token() {
+            Some(it) => it,
+            None => return self.clone(),
+        };
+        let end = match self.type_bound_list() {
+            Some(it) => it.syntax().clone().into(),
+            None => colon.clone().into(),
+        };
+        self.replace_children(colon.into()..=end, iter::empty())
+    }
+}
+
+impl ast::TypeParam {
+    #[must_use]
+    pub fn remove_bounds(&self) -> ast::TypeParam {
+        let colon = match self.colon_token() {
+            Some(it) => it,
+            None => return self.clone(),
+        };
+        let end = match self.type_bound_list() {
+            Some(it) => it.syntax().clone().into(),
+            None => colon.clone().into(),
+        };
+        self.replace_children(colon.into()..=end, iter::empty())
+    }
+}
+
 impl ast::Path {
     #[must_use]
     pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path {
@@ -65,7 +313,33 @@ impl ast::PathSegment {
     }
 }
 
+impl ast::Use {
+    #[must_use]
+    pub fn with_use_tree(&self, use_tree: ast::UseTree) -> ast::Use {
+        if let Some(old) = self.use_tree() {
+            return self.replace_descendant(old, use_tree);
+        }
+        self.clone()
+    }
+}
+
 impl ast::UseTree {
+    #[must_use]
+    pub fn with_path(&self, path: ast::Path) -> ast::UseTree {
+        if let Some(old) = self.path() {
+            return self.replace_descendant(old, path);
+        }
+        self.clone()
+    }
+
+    #[must_use]
+    pub fn with_use_tree_list(&self, use_tree_list: ast::UseTreeList) -> ast::UseTree {
+        if let Some(old) = self.use_tree_list() {
+            return self.replace_descendant(old, use_tree_list);
+        }
+        self.clone()
+    }
+
     /// Splits off the given prefix, making it the path component of the use tree, appending the rest of the path to all UseTreeList items.
     #[must_use]
     pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree {
@@ -102,6 +376,134 @@ impl ast::UseTree {
     }
 }
 
+impl ast::MatchArmList {
+    #[must_use]
+    pub fn append_arms(&self, items: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
+        let mut res = self.clone();
+        res = res.strip_if_only_whitespace();
+        if !res.syntax().text().contains_char('\n') {
+            res = make_multiline(res);
+        }
+        items.into_iter().for_each(|it| res = res.append_arm(it));
+        res
+    }
+
+    fn strip_if_only_whitespace(&self) -> ast::MatchArmList {
+        let mut iter = self.syntax().children_with_tokens().skip_while(|it| it.kind() != T!['{']);
+        iter.next(); // Eat the curly
+        let mut inner = iter.take_while(|it| it.kind() != T!['}']);
+        if !inner.clone().all(|it| it.kind() == WHITESPACE) {
+            return self.clone();
+        }
+        let start = match inner.next() {
+            Some(s) => s,
+            None => return self.clone(),
+        };
+        let end = match inner.last() {
+            Some(s) => s,
+            None => start.clone(),
+        };
+        self.replace_children(start..=end, &mut iter::empty())
+    }
+
+    #[must_use]
+    pub fn remove_placeholder(&self) -> ast::MatchArmList {
+        let placeholder =
+            self.arms().find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_))));
+        if let Some(placeholder) = placeholder {
+            self.remove_arm(&placeholder)
+        } else {
+            self.clone()
+        }
+    }
+
+    #[must_use]
+    fn remove_arm(&self, arm: &ast::MatchArm) -> ast::MatchArmList {
+        let start = arm.syntax().clone();
+        let end = if let Some(comma) = start
+            .siblings_with_tokens(Direction::Next)
+            .skip(1)
+            .find(|it| !it.kind().is_trivia())
+            .filter(|it| it.kind() == T![,])
+        {
+            comma
+        } else {
+            start.clone().into()
+        };
+        self.replace_children(start.into()..=end, None)
+    }
+
+    #[must_use]
+    pub fn append_arm(&self, item: ast::MatchArm) -> ast::MatchArmList {
+        let r_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['}']) {
+            Some(t) => t,
+            None => return self.clone(),
+        };
+        let position = InsertPosition::Before(r_curly);
+        let arm_ws = tokens::WsBuilder::new("    ");
+        let match_indent = &leading_indent(self.syntax()).unwrap_or_default();
+        let match_ws = tokens::WsBuilder::new(&format!("\n{}", match_indent));
+        let to_insert: ArrayVec<SyntaxElement, 3> =
+            [arm_ws.ws().into(), item.syntax().clone().into(), match_ws.ws().into()].into();
+        self.insert_children(position, to_insert)
+    }
+}
+
+impl ast::GenericParamList {
+    #[must_use]
+    pub fn append_params(
+        &self,
+        params: impl IntoIterator<Item = ast::GenericParam>,
+    ) -> ast::GenericParamList {
+        let mut res = self.clone();
+        params.into_iter().for_each(|it| res = res.append_param(it));
+        res
+    }
+
+    #[must_use]
+    pub fn append_param(&self, item: ast::GenericParam) -> ast::GenericParamList {
+        let space = tokens::single_space();
+
+        let mut to_insert: ArrayVec<SyntaxElement, 4> = ArrayVec::new();
+        if self.generic_params().next().is_some() {
+            to_insert.push(space.into());
+        }
+        to_insert.push(item.syntax().clone().into());
+
+        macro_rules! after_l_angle {
+            () => {{
+                let anchor = match self.l_angle_token() {
+                    Some(it) => it.into(),
+                    None => return self.clone(),
+                };
+                InsertPosition::After(anchor)
+            }};
+        }
+
+        macro_rules! after_field {
+            ($anchor:expr) => {
+                if let Some(comma) = $anchor
+                    .syntax()
+                    .siblings_with_tokens(Direction::Next)
+                    .find(|it| it.kind() == T![,])
+                {
+                    InsertPosition::After(comma)
+                } else {
+                    to_insert.insert(0, make::token(T![,]).into());
+                    InsertPosition::After($anchor.syntax().clone().into())
+                }
+            };
+        }
+
+        let position = match self.generic_params().last() {
+            Some(it) => after_field!(it),
+            None => after_l_angle!(),
+        };
+
+        self.insert_children(position, to_insert)
+    }
+}
+
 #[must_use]
 pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
     N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap()
@@ -152,12 +554,6 @@ impl ops::Add<u8> for IndentLevel {
 }
 
 impl IndentLevel {
-    pub fn single() -> IndentLevel {
-        IndentLevel(0)
-    }
-    pub fn is_zero(&self) -> bool {
-        self.0 == 0
-    }
     pub fn from_element(element: &SyntaxElement) -> IndentLevel {
         match element {
             rowan::NodeOrToken::Node(it) => IndentLevel::from_node(it),
@@ -192,40 +588,54 @@ impl IndentLevel {
     /// ```
     /// if you indent the block, the `{` token would stay put.
     fn increase_indent(self, node: SyntaxNode) -> SyntaxNode {
-        let res = node.clone_subtree().clone_for_update();
-        let tokens = res.preorder_with_tokens().filter_map(|event| match event {
-            rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
-            _ => None,
-        });
-        for token in tokens {
-            if let Some(ws) = ast::Whitespace::cast(token) {
-                if ws.text().contains('\n') {
-                    let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self));
-                    ted::replace(ws.syntax(), &new_ws)
-                }
-            }
-        }
-        res.clone_subtree()
+        let mut rewriter = SyntaxRewriter::default();
+        node.descendants_with_tokens()
+            .filter_map(|el| el.into_token())
+            .filter_map(ast::Whitespace::cast)
+            .filter(|ws| {
+                let text = ws.syntax().text();
+                text.contains('\n')
+            })
+            .for_each(|ws| {
+                let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self,));
+                rewriter.replace(ws.syntax(), &new_ws)
+            });
+        rewriter.rewrite(&node)
     }
 
     fn decrease_indent(self, node: SyntaxNode) -> SyntaxNode {
-        let res = node.clone_subtree().clone_for_update();
-        let tokens = res.preorder_with_tokens().filter_map(|event| match event {
-            rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
-            _ => None,
-        });
-        for token in tokens {
-            if let Some(ws) = ast::Whitespace::cast(token) {
-                if ws.text().contains('\n') {
-                    let new_ws = make::tokens::whitespace(
-                        &ws.syntax().text().replace(&format!("\n{}", self), "\n"),
-                    );
-                    ted::replace(ws.syntax(), &new_ws)
-                }
+        let mut rewriter = SyntaxRewriter::default();
+        node.descendants_with_tokens()
+            .filter_map(|el| el.into_token())
+            .filter_map(ast::Whitespace::cast)
+            .filter(|ws| {
+                let text = ws.syntax().text();
+                text.contains('\n')
+            })
+            .for_each(|ws| {
+                let new_ws = make::tokens::whitespace(
+                    &ws.syntax().text().replace(&format!("\n{}", self), "\n"),
+                );
+                rewriter.replace(ws.syntax(), &new_ws)
+            });
+        rewriter.rewrite(&node)
+    }
+}
+
+// FIXME: replace usages with IndentLevel above
+fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> {
+    for token in prev_tokens(node.first_token()?) {
+        if let Some(ws) = ast::Whitespace::cast(token.clone()) {
+            let ws_text = ws.text();
+            if let Some(pos) = ws_text.rfind('\n') {
+                return Some(ws_text[pos + 1..].into());
             }
         }
-        res.clone_subtree()
+        if token.text().contains('\n') {
+            break;
+        }
     }
+    None
 }
 
 fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> {
@@ -252,6 +662,13 @@ pub trait AstNodeEdit: AstNode + Clone + Sized {
         let new_syntax = algo::replace_children(self.syntax(), to_replace, to_insert);
         Self::cast(new_syntax).unwrap()
     }
+
+    #[must_use]
+    fn replace_descendant<D: AstNode>(&self, old: D, new: D) -> Self {
+        let mut rewriter = SyntaxRewriter::default();
+        rewriter.replace(old.syntax(), new.syntax());
+        rewriter.rewrite_ast(self)
+    }
     fn indent_level(&self) -> IndentLevel {
         IndentLevel::from_node(self.syntax())
     }
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index 2676ed8c98..1683555557 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -2,18 +2,13 @@
 
 use std::iter::empty;
 
-use parser::{SyntaxKind, T};
-use rowan::SyntaxElement;
+use parser::T;
 
 use crate::{
     algo::neighbor,
-    ast::{
-        self,
-        edit::{AstNodeEdit, IndentLevel},
-        make, GenericParamsOwner,
-    },
+    ast::{self, edit::AstNodeEdit, make, GenericParamsOwner, WhereClause},
     ted::{self, Position},
-    AstNode, AstToken, Direction, SyntaxNode,
+    AstNode, AstToken, Direction,
 };
 
 use super::NameOwner;
@@ -42,7 +37,7 @@ impl GenericParamsOwnerEdit for ast::Fn {
         }
     }
 
-    fn get_or_create_where_clause(&self) -> ast::WhereClause {
+    fn get_or_create_where_clause(&self) -> WhereClause {
         if self.where_clause().is_none() {
             let position = if let Some(ty) = self.ret_type() {
                 Position::after(ty.syntax())
@@ -72,7 +67,7 @@ impl GenericParamsOwnerEdit for ast::Impl {
         }
     }
 
-    fn get_or_create_where_clause(&self) -> ast::WhereClause {
+    fn get_or_create_where_clause(&self) -> WhereClause {
         if self.where_clause().is_none() {
             let position = if let Some(items) = self.assoc_item_list() {
                 Position::before(items.syntax())
@@ -102,7 +97,7 @@ impl GenericParamsOwnerEdit for ast::Trait {
         }
     }
 
-    fn get_or_create_where_clause(&self) -> ast::WhereClause {
+    fn get_or_create_where_clause(&self) -> WhereClause {
         if self.where_clause().is_none() {
             let position = if let Some(items) = self.assoc_item_list() {
                 Position::before(items.syntax())
@@ -132,7 +127,7 @@ impl GenericParamsOwnerEdit for ast::Struct {
         }
     }
 
-    fn get_or_create_where_clause(&self) -> ast::WhereClause {
+    fn get_or_create_where_clause(&self) -> WhereClause {
         if self.where_clause().is_none() {
             let tfl = self.field_list().and_then(|fl| match fl {
                 ast::FieldList::RecordFieldList(_) => None,
@@ -170,7 +165,7 @@ impl GenericParamsOwnerEdit for ast::Enum {
         }
     }
 
-    fn get_or_create_where_clause(&self) -> ast::WhereClause {
+    fn get_or_create_where_clause(&self) -> WhereClause {
         if self.where_clause().is_none() {
             let position = if let Some(gpl) = self.generic_param_list() {
                 Position::after(gpl.syntax())
@@ -277,167 +272,6 @@ impl ast::Use {
     }
 }
 
-impl ast::Impl {
-    pub fn get_or_create_assoc_item_list(&self) -> ast::AssocItemList {
-        if self.assoc_item_list().is_none() {
-            let assoc_item_list = make::assoc_item_list().clone_for_update();
-            ted::append_child(self.syntax(), assoc_item_list.syntax());
-        }
-        self.assoc_item_list().unwrap()
-    }
-}
-
-impl ast::AssocItemList {
-    pub fn add_item(&self, item: ast::AssocItem) {
-        let (indent, position, whitespace) = match self.assoc_items().last() {
-            Some(last_item) => (
-                IndentLevel::from_node(last_item.syntax()),
-                Position::after(last_item.syntax()),
-                "\n\n",
-            ),
-            None => match self.l_curly_token() {
-                Some(l_curly) => {
-                    normalize_ws_between_braces(self.syntax());
-                    (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n")
-                }
-                None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"),
-            },
-        };
-        let elements: Vec<SyntaxElement<_>> = vec![
-            make::tokens::whitespace(&format!("{}{}", whitespace, indent)).into(),
-            item.syntax().clone().into(),
-        ];
-        ted::insert_all(position, elements);
-    }
-}
-
-impl ast::Fn {
-    pub fn get_or_create_body(&self) -> ast::BlockExpr {
-        if self.body().is_none() {
-            let body = make::ext::empty_block_expr().clone_for_update();
-            match self.semicolon_token() {
-                Some(semi) => {
-                    ted::replace(semi, body.syntax());
-                    ted::insert(Position::before(body.syntax), make::tokens::single_space());
-                }
-                None => ted::append_child(self.syntax(), body.syntax()),
-            }
-        }
-        self.body().unwrap()
-    }
-}
-
-impl ast::MatchArm {
-    pub fn remove(&self) {
-        if let Some(sibling) = self.syntax().prev_sibling_or_token() {
-            if sibling.kind() == SyntaxKind::WHITESPACE {
-                ted::remove(sibling);
-            }
-        }
-        if let Some(sibling) = self.syntax().next_sibling_or_token() {
-            if sibling.kind() == T![,] {
-                ted::remove(sibling);
-            }
-        }
-        ted::remove(self.syntax());
-    }
-}
-
-impl ast::MatchArmList {
-    pub fn add_arm(&self, arm: ast::MatchArm) {
-        normalize_ws_between_braces(self.syntax());
-        let position = match self.arms().last() {
-            Some(last_arm) => {
-                let curly = last_arm
-                    .syntax()
-                    .siblings_with_tokens(Direction::Next)
-                    .find(|it| it.kind() == T![,]);
-                Position::after(curly.unwrap_or_else(|| last_arm.syntax().clone().into()))
-            }
-            None => match self.l_curly_token() {
-                Some(it) => Position::after(it),
-                None => Position::last_child_of(self.syntax()),
-            },
-        };
-        let indent = IndentLevel::from_node(self.syntax()) + 1;
-        let elements = vec![
-            make::tokens::whitespace(&format!("\n{}", indent)).into(),
-            arm.syntax().clone().into(),
-        ];
-        ted::insert_all(position, elements);
-    }
-}
-
-impl ast::RecordExprFieldList {
-    pub fn add_field(&self, field: ast::RecordExprField) {
-        let is_multiline = self.syntax().text().contains_char('\n');
-        let whitespace = if is_multiline {
-            let indent = IndentLevel::from_node(self.syntax()) + 1;
-            make::tokens::whitespace(&format!("\n{}", indent))
-        } else {
-            make::tokens::single_space()
-        };
-
-        if is_multiline {
-            normalize_ws_between_braces(self.syntax());
-        }
-
-        let position = match self.fields().last() {
-            Some(last_field) => {
-                let comma = match last_field
-                    .syntax()
-                    .siblings_with_tokens(Direction::Next)
-                    .filter_map(|it| it.into_token())
-                    .find(|it| it.kind() == T![,])
-                {
-                    Some(it) => it,
-                    None => {
-                        let comma = ast::make::token(T![,]);
-                        ted::insert(Position::after(last_field.syntax()), &comma);
-                        comma
-                    }
-                };
-                Position::after(comma)
-            }
-            None => match self.l_curly_token() {
-                Some(it) => Position::after(it),
-                None => Position::last_child_of(self.syntax()),
-            },
-        };
-
-        ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]);
-        if is_multiline {
-            ted::insert(Position::after(field.syntax()), ast::make::token(T![,]));
-        }
-    }
-}
-
-fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
-    let l = node
-        .children_with_tokens()
-        .filter_map(|it| it.into_token())
-        .find(|it| it.kind() == T!['{'])?;
-    let r = node
-        .children_with_tokens()
-        .filter_map(|it| it.into_token())
-        .find(|it| it.kind() == T!['}'])?;
-
-    let indent = IndentLevel::from_node(node);
-
-    match l.next_sibling_or_token() {
-        Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => {
-            if ws.next_sibling_or_token()?.into_token()? == r {
-                ted::replace(ws, make::tokens::whitespace(&format!("\n{}", indent)));
-            }
-        }
-        Some(ws) if ws.kind() == T!['}'] => {
-            ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{}", indent)));
-        }
-        _ => (),
-    }
-    Some(())
-}
-
 #[cfg(test)]
 mod tests {
     use std::fmt;
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index d13926deda..1998ad1f65 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -12,7 +12,7 @@
 use itertools::Itertools;
 use stdx::{format_to, never};
 
-use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxToken};
+use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxToken};
 
 /// While the parent module defines basic atomic "constructors", the `ext`
 /// module defines shortcuts for common things.
@@ -99,7 +99,7 @@ fn ty_from_text(text: &str) -> ast::Type {
 }
 
 pub fn assoc_item_list() -> ast::AssocItemList {
-    ast_from_text("impl C for D {}")
+    ast_from_text("impl C for D {};")
 }
 
 pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl {
@@ -601,11 +601,17 @@ fn ast_from_text<N: AstNode>(text: &str) -> N {
             panic!("Failed to make ast node `{}` from text {}", std::any::type_name::<N>(), text)
         }
     };
-    let node = node.clone_subtree();
+    let node = node.syntax().clone();
+    let node = unroot(node);
+    let node = N::cast(node).unwrap();
     assert_eq!(node.syntax().text_range().start(), 0.into());
     node
 }
 
+fn unroot(n: SyntaxNode) -> SyntaxNode {
+    SyntaxNode::new_root(n.green().into())
+}
+
 pub fn token(kind: SyntaxKind) -> SyntaxToken {
     tokens::SOURCE_FILE
         .tree()
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index 4b1e1ccee2..29d25a58a3 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -143,30 +143,6 @@ impl QuoteOffsets {
     }
 }
 
-pub trait IsString: AstToken {
-    fn quote_offsets(&self) -> Option<QuoteOffsets> {
-        let text = self.text();
-        let offsets = QuoteOffsets::new(text)?;
-        let o = self.syntax().text_range().start();
-        let offsets = QuoteOffsets {
-            quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o),
-            contents: offsets.contents + o,
-        };
-        Some(offsets)
-    }
-    fn text_range_between_quotes(&self) -> Option<TextRange> {
-        self.quote_offsets().map(|it| it.contents)
-    }
-    fn open_quote_text_range(&self) -> Option<TextRange> {
-        self.quote_offsets().map(|it| it.quotes.0)
-    }
-    fn close_quote_text_range(&self) -> Option<TextRange> {
-        self.quote_offsets().map(|it| it.quotes.1)
-    }
-}
-
-impl IsString for ast::String {}
-
 impl ast::String {
     pub fn is_raw(&self) -> bool {
         self.text().starts_with('r')
@@ -211,49 +187,32 @@ impl ast::String {
             (false, false) => Some(Cow::Owned(buf)),
         }
     }
-}
 
-impl IsString for ast::ByteString {}
+    pub fn quote_offsets(&self) -> Option<QuoteOffsets> {
+        let text = self.text();
+        let offsets = QuoteOffsets::new(text)?;
+        let o = self.syntax().text_range().start();
+        let offsets = QuoteOffsets {
+            quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o),
+            contents: offsets.contents + o,
+        };
+        Some(offsets)
+    }
+    pub fn text_range_between_quotes(&self) -> Option<TextRange> {
+        self.quote_offsets().map(|it| it.contents)
+    }
+    pub fn open_quote_text_range(&self) -> Option<TextRange> {
+        self.quote_offsets().map(|it| it.quotes.0)
+    }
+    pub fn close_quote_text_range(&self) -> Option<TextRange> {
+        self.quote_offsets().map(|it| it.quotes.1)
+    }
+}
 
 impl ast::ByteString {
     pub fn is_raw(&self) -> bool {
         self.text().starts_with("br")
     }
-
-    pub fn value(&self) -> Option<Cow<'_, [u8]>> {
-        if self.is_raw() {
-            let text = self.text();
-            let text =
-                &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
-            return Some(Cow::Borrowed(text.as_bytes()));
-        }
-
-        let text = self.text();
-        let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
-
-        let mut buf: Vec<u8> = Vec::new();
-        let mut text_iter = text.chars();
-        let mut has_error = false;
-        unescape_literal(text, Mode::ByteStr, &mut |char_range, unescaped_char| match (
-            unescaped_char,
-            buf.capacity() == 0,
-        ) {
-            (Ok(c), false) => buf.push(c as u8),
-            (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (),
-            (Ok(c), true) => {
-                buf.reserve_exact(text.len());
-                buf.extend_from_slice(&text[..char_range.start].as_bytes());
-                buf.push(c as u8);
-            }
-            (Err(_), _) => has_error = true,
-        });
-
-        match (has_error, buf.capacity() == 0) {
-            (true, _) => None,
-            (false, true) => Some(Cow::Borrowed(text.as_bytes())),
-            (false, false) => Some(Cow::Owned(buf)),
-        }
-    }
 }
 
 #[derive(Debug)]
diff --git a/crates/syntax/src/parsing.rs b/crates/syntax/src/parsing.rs
index 431ed06999..333bde54ab 100644
--- a/crates/syntax/src/parsing.rs
+++ b/crates/syntax/src/parsing.rs
@@ -6,13 +6,14 @@ mod text_token_source;
 mod text_tree_sink;
 mod reparsing;
 
-use parser::SyntaxKind;
+use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode};
 use text_token_source::TextTokenSource;
 use text_tree_sink::TextTreeSink;
 
-use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode};
+pub(crate) use lexer::*;
 
-pub(crate) use crate::parsing::{lexer::*, reparsing::incremental_reparse};
+pub(crate) use self::reparsing::incremental_reparse;
+use parser::SyntaxKind;
 
 pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) {
     let (tokens, lexer_errors) = tokenize(&text);
diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs
index d63ec080b4..1934204eab 100644
--- a/crates/syntax/src/parsing/text_tree_sink.rs
+++ b/crates/syntax/src/parsing/text_tree_sink.rs
@@ -147,8 +147,8 @@ fn n_attached_trivias<'a>(
     trivias: impl Iterator<Item = (SyntaxKind, &'a str)>,
 ) -> usize {
     match kind {
-        CONST | ENUM | FN | IMPL | MACRO_CALL | MACRO_DEF | MACRO_RULES | MODULE | RECORD_FIELD
-        | STATIC | STRUCT | TRAIT | TUPLE_FIELD | TYPE_ALIAS | UNION | USE | VARIANT => {
+        MACRO_CALL | MACRO_RULES | MACRO_DEF | CONST | TYPE_ALIAS | STRUCT | UNION | ENUM
+        | VARIANT | FN | TRAIT | MODULE | RECORD_FIELD | STATIC | USE => {
             let mut res = 0;
             let mut trivias = trivias.enumerate().peekable();
 
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 3c4eacfeb5..8fcd72d5d5 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 <!---
-lsp_ext.rs hash: 10a8988e6893e6b2
+lsp_ext.rs hash: 6e57fc1b345b00e9
 
 If you need to change the above hash to make the test pass, please check if you
 need to adjust this doc as well and ping this issue:
@@ -650,37 +650,3 @@ export const enum Direction {
     Down = "Down"
 }
 ```
-
-## Workspace Symbols Filtering
-
-**Issue:** https://github.com/rust-analyzer/rust-analyzer/pull/7698
-
-**Experimental Server Capability:** `{ "workspaceSymbolScopeKindFiltering": boolean }`
-
-Extends the existing `workspace/symbol` request with ability to filter symbols by broad scope and kind of symbol.
-If this capability is set, `workspace/symbol` parameter gains two new optional fields:
-
-
-```typescript
-interface WorkspaceSymbolParams {
-    /**
-     * Return only the symbols defined in the specified scope.
-     */
-    searchScope?: WorkspaceSymbolSearchScope;
-    /**
-     * Return only the symbols of specified kinds.
-     */
-    searchKind?: WorkspaceSymbolSearchKind;
-    ...
-}
-
-const enum WorkspaceSymbolSearchScope {
-    Workspace = "workspace",
-    WorkspaceAndDependencies = "workspaceAndDependencies"
-}
-
-const enum WorkspaceSymbolSearchKind {
-    OnlyTypes = "onlyTypes",
-    AllSymbols = "allSymbols"
-}
-```
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 96dd684b32..d24a5952ed 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -636,10 +636,6 @@ use crate::{}
 
 // Finally, parent and child modules, but prefer `use crate::`.
 use super::{}
-
-// Re-exports are treated as item definitions rather than imports, so they go
-// after imports and modules. Use them sparingly.
-pub use crate::x::Z;
 ```
 
 **Rationale:** consistency.
@@ -698,9 +694,6 @@ Avoid local `use MyEnum::*` imports.
 Prefer `use crate::foo::bar` to `use super::bar` or `use self::bar::baz`.
 **Rationale:** consistency, this is the style which works in all cases.
 
-By default, avoid re-exports.
-**Rationale:** for non-library code, re-exports introduce two ways to use something and allow for inconsistency.
-
 ## Order of Items
 
 Optimize for the reader who sees the file for the first time, and wants to get a general idea about what's going on.
@@ -791,14 +784,13 @@ Many names in rust-analyzer conflict with keywords.
 We use mangled names instead of `r#ident` syntax:
 
 ```
+struct -> strukt
 crate  -> krate
-enum   -> enum_
-fn     -> func
 impl   -> imp
-mod    -> module
-struct -> strukt
 trait  -> trait_
-type   -> ty
+fn     -> func
+enum   -> enum_
+mod    -> module
 ```
 
 **Rationale:** consistency.
@@ -952,28 +944,6 @@ match p.current() {
 
 ## Documentation
 
-Style inline code comments as proper sentences.
-Start with a capital letter, end with a dot.
-
-```rust
-// GOOD
-
-// Only simple single segment paths are allowed.
-MergeBehavior::Last => {
-    tree.use_tree_list().is_none() && tree.path().map(path_len) <= Some(1)
-}
-
-// BAD
-
-// only simple single segment paths are allowed
-MergeBehavior::Last => {
-    tree.use_tree_list().is_none() && tree.path().map(path_len) <= Some(1)
-}
-```
-
-**Rationale:** writing a sentence (or maybe even a paragraph) rather just "a comment" creates a more appropriate frame of mind.
-It tricks you into writing down more of the context you keep in your head while coding.
-
 For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines.
 If the line is too long, you want to split the sentence in two :-)
 
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index b324118875..f705582007 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -179,15 +179,6 @@ Controls file watching implementation.
 --
 These directories will be ignored by rust-analyzer.
 --
-[[rust-analyzer.highlighting.strings]]rust-analyzer.highlighting.strings (default: `true`)::
-+
---
-Use semantic tokens for strings.
-
-In some editors (e.g. vscode) semantic tokens override other highlighting grammars.
-By disabling semantic tokens for strings, other grammars can be used to highlight
-their contents.
---
 [[rust-analyzer.hoverActions.debug]]rust-analyzer.hoverActions.debug (default: `true`)::
 +
 --
@@ -341,13 +332,3 @@ Additional arguments to `rustfmt`.
 Advanced option, fully override the command rust-analyzer uses for
 formatting.
 --
-[[rust-analyzer.workspace.symbol.search.scope]]rust-analyzer.workspace.symbol.search.scope (default: `"workspace"`)::
-+
---
-Workspace symbol search scope.
---
-[[rust-analyzer.workspace.symbol.search.kind]]rust-analyzer.workspace.symbol.search.kind (default: `"only_types"`)::
-+
---
-Workspace symbol search kind.
---
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index f96c09a79f..797af3f752 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -589,7 +589,6 @@ Here is a **non-exhaustive** list of ways to make rust-analyzer execute arbitrar
 
 * proc macros and build scripts are executed by default
 * `.cargo/config` can override `rustc` with an arbitrary executable
-* `rust-toolchain.toml` can override `rustc` with an arbitrary executable
 * VS Code plugin reads configuration from project directory, and that can be used to override paths to various executables, like `rustfmt` or `rust-analyzer` itself.
 * rust-analyzer's syntax trees library uses a lot of `unsafe` and hasn't been properly audited for memory safety.
 
diff --git a/editors/code/package.json b/editors/code/package.json
index 99223c4e8c..0f38a1673a 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -613,11 +613,6 @@
                         "type": "string"
                     }
                 },
-                "rust-analyzer.highlighting.strings": {
-                    "markdownDescription": "Use semantic tokens for strings.\n\nIn some editors (e.g. vscode) semantic tokens override other highlighting grammars.\nBy disabling semantic tokens for strings, other grammars can be used to highlight\ntheir contents.",
-                    "default": true,
-                    "type": "boolean"
-                },
                 "rust-analyzer.hoverActions.debug": {
                     "markdownDescription": "Whether to show `Debug` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.",
                     "default": true,
@@ -783,32 +778,6 @@
                         "type": "string"
                     }
                 },
-                "rust-analyzer.workspace.symbol.search.scope": {
-                    "markdownDescription": "Workspace symbol search scope.",
-                    "default": "workspace",
-                    "type": "string",
-                    "enum": [
-                        "workspace",
-                        "workspace_and_dependencies"
-                    ],
-                    "enumDescriptions": [
-                        "Search in current workspace only",
-                        "Search in current workspace and dependencies"
-                    ]
-                },
-                "rust-analyzer.workspace.symbol.search.kind": {
-                    "markdownDescription": "Workspace symbol search kind.",
-                    "default": "only_types",
-                    "type": "string",
-                    "enum": [
-                        "only_types",
-                        "all_symbols"
-                    ],
-                    "enumDescriptions": [
-                        "Search for types only",
-                        "Search for all symbols kinds"
-                    ]
-                },
                 "$generated-end": false
             }
         },
diff --git a/xtask/src/tidy.rs b/xtask/src/tidy.rs
index 6c55823eb0..c3c785eff3 100644
--- a/xtask/src/tidy.rs
+++ b/xtask/src/tidy.rs
@@ -347,8 +347,9 @@ struct TidyDocs {
 
 impl TidyDocs {
     fn visit(&mut self, path: &Path, text: &str) {
-        // Tests and diagnostic fixes don't need module level comments.
-        if is_exclude_dir(path, &["tests", "test_data", "fixes"]) {
+        // Test hopefully don't really need comments, and for assists we already
+        // have special comments which are source of doc tests and user docs.
+        if is_exclude_dir(path, &["tests", "test_data"]) {
             return;
         }
 


Test patch:
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index 9f652731d9..4f2e7a2f96 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -410,22 +410,6 @@ struct Arc;
     );
 }
 
-#[test]
-fn macro_use_extern_crate_self() {
-    cov_mark::check!(ignore_macro_use_extern_crate_self);
-    check(
-        r#"
-//- /main.rs crate:main
-#[macro_use]
-extern crate self as bla;
-"#,
-        expect![[r#"
-            crate
-            bla: t
-        "#]],
-    );
-}
-
 #[test]
 fn reexport_across_crates() {
     check(
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs
index 1904710691..aad3d610e2 100644
--- a/crates/hir_ty/src/tests/coercion.rs
+++ b/crates/hir_ty/src/tests/coercion.rs
@@ -64,42 +64,42 @@ fn coerce_places() {
             81..92 '{ loop {} }': T
             83..90 'loop {}': !
             88..90 '{}': ()
-            121..132 '{ loop {} }': *mut [T; 2]
+            121..132 '{ loop {} }': *mut [T; _]
             123..130 'loop {}': !
             128..130 '{}': ()
             159..172 '{     gen() }': *mut [U]
-            165..168 'gen': fn gen<U>() -> *mut [U; 2]
-            165..170 'gen()': *mut [U; 2]
+            165..168 'gen': fn gen<U>() -> *mut [U; _]
+            165..170 'gen()': *mut [U; _]
             185..419 '{     ...rr); }': ()
-            195..198 'arr': &[u8; 1]
+            195..198 'arr': &[u8; _]
             211..215 '&[1]': &[u8; 1]
             212..215 '[1]': [u8; 1]
             213..214 '1': u8
             226..227 'a': &[u8]
-            236..239 'arr': &[u8; 1]
+            236..239 'arr': &[u8; _]
             249..250 'b': u8
             253..254 'f': fn f<u8>(&[u8]) -> u8
             253..259 'f(arr)': u8
-            255..258 'arr': &[u8; 1]
+            255..258 'arr': &[u8; _]
             269..270 'c': &[u8]
             279..286 '{ arr }': &[u8]
-            281..284 'arr': &[u8; 1]
+            281..284 'arr': &[u8; _]
             296..297 'd': u8
             300..301 'g': fn g<u8>(S<&[u8]>) -> u8
             300..315 'g(S { a: arr })': u8
             302..314 'S { a: arr }': S<&[u8]>
-            309..312 'arr': &[u8; 1]
-            325..326 'e': [&[u8]; 1]
+            309..312 'arr': &[u8; _]
+            325..326 'e': [&[u8]; _]
             340..345 '[arr]': [&[u8]; 1]
-            341..344 'arr': &[u8; 1]
-            355..356 'f': [&[u8]; 2]
-            370..378 '[arr; 2]': [&[u8]; 2]
-            371..374 'arr': &[u8; 1]
+            341..344 'arr': &[u8; _]
+            355..356 'f': [&[u8]; _]
+            370..378 '[arr; 2]': [&[u8]; _]
+            371..374 'arr': &[u8; _]
             376..377 '2': usize
             388..389 'g': (&[u8], &[u8])
             406..416 '(arr, arr)': (&[u8], &[u8])
-            407..410 'arr': &[u8; 1]
-            412..415 'arr': &[u8; 1]
+            407..410 'arr': &[u8; _]
+            412..415 'arr': &[u8; _]
         "#]],
     );
 }
@@ -159,7 +159,7 @@ fn infer_custom_coerce_unsized() {
         impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
         impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
         "#,
-        expect![[r#"
+        expect![[r"
             257..258 'x': A<[T]>
             278..283 '{ x }': A<[T]>
             280..281 'x': A<[T]>
@@ -169,23 +169,23 @@ fn infer_custom_coerce_unsized() {
             333..334 'x': C<[T]>
             354..359 '{ x }': C<[T]>
             356..357 'x': C<[T]>
-            369..370 'a': A<[u8; 2]>
-            384..385 'b': B<[u8; 2]>
-            399..400 'c': C<[u8; 2]>
+            369..370 'a': A<[u8; _]>
+            384..385 'b': B<[u8; _]>
+            399..400 'c': C<[u8; _]>
             414..480 '{     ...(c); }': ()
             424..425 'd': A<[{unknown}]>
             428..432 'foo1': fn foo1<{unknown}>(A<[{unknown}]>) -> A<[{unknown}]>
             428..435 'foo1(a)': A<[{unknown}]>
-            433..434 'a': A<[u8; 2]>
+            433..434 'a': A<[u8; _]>
             445..446 'e': B<[u8]>
             449..453 'foo2': fn foo2<u8>(B<[u8]>) -> B<[u8]>
             449..456 'foo2(b)': B<[u8]>
-            454..455 'b': B<[u8; 2]>
+            454..455 'b': B<[u8; _]>
             466..467 'f': C<[u8]>
             470..474 'foo3': fn foo3<u8>(C<[u8]>) -> C<[u8]>
             470..477 'foo3(c)': C<[u8]>
-            475..476 'c': C<[u8; 2]>
-        "#]],
+            475..476 'c': C<[u8; _]>
+        "]],
     );
 }
 
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index 787647e9f3..33305f208f 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -345,19 +345,19 @@ fn infer_pattern_match_arr() {
         "#,
         expect![[r#"
             10..179 '{     ...   } }': ()
-            20..23 'arr': [f64; 2]
+            20..23 'arr': [f64; _]
             36..46 '[0.0, 1.0]': [f64; 2]
             37..40 '0.0': f64
             42..45 '1.0': f64
             52..177 'match ...     }': ()
-            58..61 'arr': [f64; 2]
-            72..80 '[1.0, a]': [f64; 2]
+            58..61 'arr': [f64; _]
+            72..80 '[1.0, a]': [f64; _]
             73..76 '1.0': f64
             73..76 '1.0': f64
             78..79 'a': f64
             84..110 '{     ...     }': ()
             98..99 'a': f64
-            120..126 '[b, c]': [f64; 2]
+            120..126 '[b, c]': [f64; _]
             121..122 'b': f64
             124..125 'c': f64
             130..171 '{     ...     }': ()
@@ -732,7 +732,7 @@ fn foo(tuple: (u8, i16, f32)) {
             111..112 'a': u8
             114..115 'b': i16
             124..126 '{}': ()
-            136..142 '(a, b)': (u8, i16)
+            136..142 '(a, b)': (u8, i16, f32)
             137..138 'a': u8
             140..141 'b': i16
             146..161 '{/*too short*/}': ()
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index a9cd42186e..8b09f2e4a0 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -488,34 +488,23 @@ fn infer_literals() {
                 mod foo {}
             "#;
             br#"yolo"#;
-            let a = b"a\x20b\
-            c";
-            let b = br"g\
-h";
-            let c = br#"x"\"yb"#;
         }
         "##,
         expect![[r##"
-            18..478 '{     ...     }': ()
-            32..36 '5i32': i32
-            50..54 '5f32': f32
-            68..72 '5f64': f64
-            86..93 '"hello"': &str
-            107..115 'b"bytes"': &[u8; 5]
-            129..132 ''c'': char
-            146..150 'b'b'': u8
-            164..168 '3.14': f64
-            182..186 '5000': i32
-            200..205 'false': bool
-            219..223 'true': bool
-            237..333 'r#"   ...    "#': &str
-            347..357 'br#"yolo"#': &[u8; 4]
-            375..376 'a': &[u8; 4]
-            379..403 'b"a\x2...    c"': &[u8; 4]
-            421..422 'b': &[u8; 4]
-            425..433 'br"g\ h"': &[u8; 4]
-            451..452 'c': &[u8; 6]
-            455..467 'br#"x"\"yb"#': &[u8; 6]
+            10..216 '{     ...o"#; }': ()
+            16..20 '5i32': i32
+            26..30 '5f32': f32
+            36..40 '5f64': f64
+            46..53 '"hello"': &str
+            59..67 'b"bytes"': &[u8; _]
+            73..76 ''c'': char
+            82..86 'b'b'': u8
+            92..96 '3.14': f64
+            102..106 '5000': i32
+            112..117 'false': bool
+            123..127 'true': bool
+            133..197 'r#"   ...    "#': &str
+            203..213 'br#"yolo"#': &[u8; _]
         "##]],
     );
 }
@@ -1271,14 +1260,12 @@ fn infer_array() {
 
             let b = [a, ["b"]];
             let x: [u8; 0] = [];
-            // FIXME: requires const evaluation/taking type from rhs somehow
-            let y: [u8; 2+2] = [1,2,3,4];
         }
         "#,
         expect![[r#"
             8..9 'x': &str
             17..18 'y': isize
-            27..395 '{     ...,4]; }': ()
+            27..292 '{     ... []; }': ()
             37..38 'a': [&str; 1]
             41..44 '[x]': [&str; 1]
             42..43 'x': &str
@@ -1326,14 +1313,8 @@ fn infer_array() {
             255..256 'a': [&str; 1]
             258..263 '["b"]': [&str; 1]
             259..262 '"b"': &str
-            274..275 'x': [u8; 0]
+            274..275 'x': [u8; _]
             287..289 '[]': [u8; 0]
-            368..369 'y': [u8; _]
-            383..392 '[1,2,3,4]': [u8; 4]
-            384..385 '1': u8
-            386..387 '2': u8
-            388..389 '3': u8
-            390..391 '4': u8
         "#]],
     );
 }
@@ -2428,38 +2409,38 @@ fn infer_operator_overload() {
             320..422 '{     ...     }': V2
             334..335 'x': f32
             338..342 'self': V2
-            338..344 'self.0': [f32; 2]
+            338..344 'self.0': [f32; _]
             338..347 'self.0[0]': {unknown}
             338..358 'self.0...s.0[0]': f32
             345..346 '0': i32
             350..353 'rhs': V2
-            350..355 'rhs.0': [f32; 2]
+            350..355 'rhs.0': [f32; _]
             350..358 'rhs.0[0]': {unknown}
             356..357 '0': i32
             372..373 'y': f32
             376..380 'self': V2
-            376..382 'self.0': [f32; 2]
+            376..382 'self.0': [f32; _]
             376..385 'self.0[1]': {unknown}
             376..396 'self.0...s.0[1]': f32
             383..384 '1': i32
             388..391 'rhs': V2
-            388..393 'rhs.0': [f32; 2]
+            388..393 'rhs.0': [f32; _]
             388..396 'rhs.0[1]': {unknown}
             394..395 '1': i32
-            406..408 'V2': V2([f32; 2]) -> V2
+            406..408 'V2': V2([f32; _]) -> V2
             406..416 'V2([x, y])': V2
             409..415 '[x, y]': [f32; 2]
             410..411 'x': f32
             413..414 'y': f32
             436..519 '{     ... vb; }': ()
             446..448 'va': V2
-            451..453 'V2': V2([f32; 2]) -> V2
+            451..453 'V2': V2([f32; _]) -> V2
             451..465 'V2([0.0, 1.0])': V2
             454..464 '[0.0, 1.0]': [f32; 2]
             455..458 '0.0': f32
             460..463 '1.0': f32
             475..477 'vb': V2
-            480..482 'V2': V2([f32; 2]) -> V2
+            480..482 'V2': V2([f32; _]) -> V2
             480..494 'V2([0.0, 1.0])': V2
             483..493 '[0.0, 1.0]': [f32; 2]
             484..487 '0.0': f32
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index f80cf98798..47a1455fd0 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -3474,100 +3474,3 @@ fn main(){
         "#]],
     )
 }
-
-#[test]
-fn array_length() {
-    check_infer(
-        r#"
-trait T {
-    type Output;
-    fn do_thing(&self) -> Self::Output;
-}
-
-impl T for [u8; 4] {
-    type Output = usize;
-    fn do_thing(&self) -> Self::Output {
-        2
-    }
-}
-
-impl T for [u8; 2] {
-    type Output = u8;
-    fn do_thing(&self) -> Self::Output {
-        2
-    }
-}
-
-fn main() {
-    let v = [0u8; 2];
-    let v2 = v.do_thing();
-    let v3 = [0u8; 4];
-    let v4 = v3.do_thing();
-}
-"#,
-        expect![[r#"
-            44..48 'self': &Self
-            133..137 'self': &[u8; 4]
-            155..172 '{     ...     }': usize
-            165..166 '2': usize
-            236..240 'self': &[u8; 2]
-            258..275 '{     ...     }': u8
-            268..269 '2': u8
-            289..392 '{     ...g(); }': ()
-            299..300 'v': [u8; 2]
-            303..311 '[0u8; 2]': [u8; 2]
-            304..307 '0u8': u8
-            309..310 '2': usize
-            321..323 'v2': u8
-            326..327 'v': [u8; 2]
-            326..338 'v.do_thing()': u8
-            348..350 'v3': [u8; 4]
-            353..361 '[0u8; 4]': [u8; 4]
-            354..357 '0u8': u8
-            359..360 '4': usize
-            371..373 'v4': usize
-            376..378 'v3': [u8; 4]
-            376..389 'v3.do_thing()': usize
-        "#]],
-    )
-}
-
-// FIXME: We should infer the length of the returned array :)
-#[test]
-fn const_generics() {
-    check_infer(
-        r#"
-trait T {
-    type Output;
-    fn do_thing(&self) -> Self::Output;
-}
-
-impl<const L: usize> T for [u8; L] {
-    type Output = [u8; L];
-    fn do_thing(&self) -> Self::Output {
-        *self
-    }
-}
-
-fn main() {
-    let v = [0u8; 2];
-    let v2 = v.do_thing();
-}
-"#,
-        expect![[r#"
-            44..48 'self': &Self
-            151..155 'self': &[u8; _]
-            173..194 '{     ...     }': [u8; _]
-            183..188 '*self': [u8; _]
-            184..188 'self': &[u8; _]
-            208..260 '{     ...g(); }': ()
-            218..219 'v': [u8; 2]
-            222..230 '[0u8; 2]': [u8; 2]
-            223..226 '0u8': u8
-            228..229 '2': usize
-            240..242 'v2': [u8; _]
-            245..246 'v': [u8; 2]
-            245..257 'v.do_thing()': [u8; _]
-        "#]],
-    )
-}
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 921a956e66..8d83ba2065 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -37,25 +37,13 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="comment documentation">//! This is a module to test doc injection.</span>
-<span class="comment documentation">//! ```</span>
-<span class="comment documentation">//! </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">test</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span>
-<span class="comment documentation">//! ```</span>
-
-<span class="comment documentation">/// ```</span>
+<pre><code><span class="comment documentation">/// ```</span>
 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"early doctests should not go boom"</span><span class="semicolon injected">;</span>
 <span class="comment documentation">/// ```</span>
 <span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="brace">{</span>
     <span class="field declaration">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span><span class="comma">,</span>
 <span class="brace">}</span>
 
-<span class="comment documentation">/// This is an impl with a code block.</span>
-<span class="comment documentation">///</span>
-<span class="comment documentation">/// ```</span>
-<span class="comment documentation">/// </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span>
-<span class="comment documentation">///</span>
-<span class="comment documentation">/// </span><span class="brace injected">}</span>
-<span class="comment documentation">/// ```</span>
 <span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span>
     <span class="comment documentation">/// ```</span>
     <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Call me</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 0d325f3f3d..df41921949 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -66,11 +66,11 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword">pub</span> <span class="field declaration">y</span><span class="colon">:</span> <span class="builtin_type">i32</span><span class="comma">,</span>
 <span class="brace">}</span>
 
-<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="keyword">where</span> <span class="type_param">Self</span><span class="colon">:</span> <span class="brace">{</span>
+<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span>
     <span class="keyword">fn</span> <span class="function associated declaration trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
-<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="keyword">where</span> <span class="self_type">Self</span><span class="colon">:</span> <span class="brace">{</span>
+<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span>
     <span class="keyword">fn</span> <span class="function associated declaration trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
         <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
     <span class="brace">}</span>
@@ -234,15 +234,4 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
             <span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="comma">,</span>
         <span class="brace">}</span>
     <span class="brace">}</span>
-<span class="brace">}</span>
-
-<span class="keyword async">async</span> <span class="keyword">fn</span> <span class="function declaration async">learn_and_sing</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
-    <span class="keyword">let</span> <span class="variable declaration">song</span> <span class="operator">=</span> <span class="unresolved_reference">learn_song</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="operator">.</span><span class="keyword control async">await</span><span class="semicolon">;</span>
-    <span class="unresolved_reference">sing_song</span><span class="parenthesis">(</span><span class="variable consuming">song</span><span class="parenthesis">)</span><span class="operator">.</span><span class="keyword control async">await</span><span class="semicolon">;</span>
-<span class="brace">}</span>
-
-<span class="keyword async">async</span> <span class="keyword">fn</span> <span class="function declaration async">async_main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
-    <span class="keyword">let</span> <span class="variable declaration">f1</span> <span class="operator">=</span> <span class="function async">learn_and_sing</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="keyword">let</span> <span class="variable declaration">f2</span> <span class="operator">=</span> <span class="unresolved_reference">dance</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    futures::<span class="macro">join!</span><span class="parenthesis">(</span>f1<span class="comma">,</span> f2<span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 8c8878d367..b6e952b088 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -39,11 +39,11 @@ struct Foo {
     pub y: i32,
 }
 
-trait Bar where Self: {
+trait Bar {
     fn bar(&self) -> i32;
 }
 
-impl Bar for Foo where Self: {
+impl Bar for Foo {
     fn bar(&self) -> i32 {
         self.x
     }
@@ -208,17 +208,6 @@ impl<T> Option<T> {
         }
     }
 }
-
-async fn learn_and_sing() {
-    let song = learn_song().await;
-    sing_song(song).await;
-}
-
-async fn async_main() {
-    let f1 = learn_and_sing();
-    let f2 = dance();
-    futures::join!(f1, f2);
-}
 "#
         .trim(),
         expect_file!["./test_data/highlighting.html"],
@@ -524,11 +513,6 @@ fn main() {
 fn test_highlight_doc_comment() {
     check_highlighting(
         r#"
-//! This is a module to test doc injection.
-//! ```
-//! fn test() {}
-//! ```
-
 /// ```
 /// let _ = "early doctests should not go boom";
 /// ```
@@ -536,13 +520,6 @@ struct Foo {
     bar: bool,
 }
 
-/// This is an impl with a code block.
-///
-/// ```
-/// fn foo() {
-///
-/// }
-/// ```
 impl Foo {
     /// ```
     /// let _ = "Call me
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 4406406a27..49c1a97760 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -50,6 +50,7 @@ trait Trait {
 impl Trait for () {
     type X = ();
     fn foo(&self) {}$0
+
 }
 "#####,
         r#####"
diff --git a/crates/syntax/test_data/parser/ok/0045_block_attrs.rast b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast
index 5e50b4e0bc..50ab52d325 100644
--- a/crates/syntax/test_data/parser/ok/0045_block_attrs.rast
+++ b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast
@@ -127,9 +127,9 @@ SOURCE_FILE@0..764
       WHITESPACE@537..538 "\n"
       R_CURLY@538..539 "}"
   WHITESPACE@539..541 "\n\n"
-  IMPL@541..763
-    COMMENT@541..601 "// https://github.com ..."
-    WHITESPACE@601..602 "\n"
+  COMMENT@541..601 "// https://github.com ..."
+  WHITESPACE@601..602 "\n"
+  IMPL@602..763
     IMPL_KW@602..606 "impl"
     WHITESPACE@606..607 " "
     PATH_TYPE@607..615