Merge pull request #20389 from Veykril/push-ssyssvnrywvy

Slim down compile time artifact progress reports
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index ad17f17..b8eadb6 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -206,6 +206,7 @@
 
 #[salsa_macros::input(debug)]
 pub struct FileText {
+    #[returns(ref)]
     pub text: Arc<str>,
     pub file_id: vfs::FileId,
 }
@@ -357,7 +358,7 @@
     let _p = tracing::info_span!("parse", ?file_id).entered();
     let (file_id, edition) = file_id.unpack(db.as_dyn_database());
     let text = db.file_text(file_id).text(db);
-    ast::SourceFile::parse(&text, edition)
+    ast::SourceFile::parse(text, edition)
 }
 
 fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<&[SyntaxError]> {
diff --git a/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs
index 58ab7f4..ec34461 100644
--- a/crates/hir-expand/src/builtin/fn_macro.rs
+++ b/crates/hir-expand/src/builtin/fn_macro.rs
@@ -890,7 +890,7 @@
     };
 
     let text = db.file_text(file_id.file_id(db));
-    let text = &*text.text(db);
+    let text = &**text.text(db);
 
     ExpandResult::ok(quote!(call_site =>#text))
 }
diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs
index 6730b33..a7f3e27 100644
--- a/crates/hir-expand/src/files.rs
+++ b/crates/hir-expand/src/files.rs
@@ -99,6 +99,16 @@
     pub fn into_file_id(self, db: &dyn ExpandDatabase) -> FileRangeWrapper<FileId> {
         FileRangeWrapper { file_id: self.file_id.file_id(db), range: self.range }
     }
+
+    #[inline]
+    pub fn file_text(self, db: &dyn ExpandDatabase) -> &triomphe::Arc<str> {
+        db.file_text(self.file_id.file_id(db)).text(db)
+    }
+
+    #[inline]
+    pub fn text(self, db: &dyn ExpandDatabase) -> &str {
+        &self.file_text(db)[self.range]
+    }
 }
 
 /// `AstId` points to an AST node in any file.
diff --git a/crates/hir-ty/src/test_db.rs b/crates/hir-ty/src/test_db.rs
index b5de0e5..775136d 100644
--- a/crates/hir-ty/src/test_db.rs
+++ b/crates/hir-ty/src/test_db.rs
@@ -149,7 +149,7 @@
             .into_iter()
             .filter_map(|file_id| {
                 let text = self.file_text(file_id.file_id(self));
-                let annotations = extract_annotations(&text.text(self));
+                let annotations = extract_annotations(text.text(self));
                 if annotations.is_empty() {
                     return None;
                 }
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 7f1e7cc..11201af 100644
--- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -183,13 +183,11 @@
             .clone()
             .into_iter()
             .chain(other_items.iter().cloned())
-            .map(either::Either::Right)
             .collect::<Vec<_>>();
 
         let mut editor = edit.make_editor(impl_def.syntax());
         if let Some(assoc_item_list) = impl_def.assoc_item_list() {
-            let items = new_assoc_items.into_iter().filter_map(either::Either::right).collect();
-            assoc_item_list.add_items(&mut editor, items);
+            assoc_item_list.add_items(&mut editor, new_assoc_items);
         } else {
             let assoc_item_list = make::assoc_item_list(Some(new_assoc_items)).clone_for_update();
             editor.insert_all(
diff --git a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
index a4742bc..f1cc3d9 100644
--- a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
+++ b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
@@ -1,9 +1,7 @@
 use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait};
-use itertools::Itertools;
-use syntax::{
-    ast::{self, AstNode, HasGenericArgs, HasName, make},
-    ted,
-};
+use syntax::ast::edit::IndentLevel;
+use syntax::ast::{self, AstNode, HasGenericArgs, HasName, make};
+use syntax::syntax_editor::{Element, Position};
 
 use crate::{AssistContext, AssistId, Assists};
 
@@ -49,6 +47,7 @@
     };
 
     let associated_items = impl_.assoc_item_list()?;
+    let associated_l_curly = associated_items.l_curly_token()?;
     let from_fn = associated_items.assoc_items().find_map(|item| {
         if let ast::AssocItem::Fn(f) = item
             && f.name()?.text() == "from"
@@ -75,30 +74,25 @@
         "Convert From to TryFrom",
         impl_.syntax().text_range(),
         |builder| {
-            let trait_ty = builder.make_mut(trait_ty);
-            let from_fn_return_type = builder.make_mut(from_fn_return_type);
-            let from_fn_name = builder.make_mut(from_fn_name);
-            let tail_expr = builder.make_mut(tail_expr);
-            let return_exprs = return_exprs.map(|r| builder.make_mut(r)).collect_vec();
-            let associated_items = builder.make_mut(associated_items);
-
-            ted::replace(
+            let mut editor = builder.make_editor(impl_.syntax());
+            editor.replace(
                 trait_ty.syntax(),
                 make::ty(&format!("TryFrom<{from_type}>")).syntax().clone_for_update(),
             );
-            ted::replace(
+            editor.replace(
                 from_fn_return_type.syntax(),
                 make::ty("Result<Self, Self::Error>").syntax().clone_for_update(),
             );
-            ted::replace(from_fn_name.syntax(), make::name("try_from").syntax().clone_for_update());
-            ted::replace(
+            editor
+                .replace(from_fn_name.syntax(), make::name("try_from").syntax().clone_for_update());
+            editor.replace(
                 tail_expr.syntax(),
                 wrap_ok(tail_expr.clone()).syntax().clone_for_update(),
             );
 
             for r in return_exprs {
                 let t = r.expr().unwrap_or_else(make::ext::expr_unit);
-                ted::replace(t.syntax(), wrap_ok(t.clone()).syntax().clone_for_update());
+                editor.replace(t.syntax(), wrap_ok(t.clone()).syntax().clone_for_update());
             }
 
             let error_type = ast::AssocItem::TypeAlias(make::ty_alias(
@@ -114,10 +108,20 @@
                 && let ast::AssocItem::TypeAlias(type_alias) = &error_type
                 && let Some(ty) = type_alias.ty()
             {
-                builder.add_placeholder_snippet(cap, ty);
+                let placeholder = builder.make_placeholder_snippet(cap);
+                editor.add_annotation(ty.syntax(), placeholder);
             }
 
-            associated_items.add_item_at_start(error_type);
+            let indent = IndentLevel::from_token(&associated_l_curly) + 1;
+            editor.insert_all(
+                Position::after(associated_l_curly),
+                vec![
+                    make::tokens::whitespace(&format!("\n{indent}")).syntax_element(),
+                    error_type.syntax().syntax_element(),
+                    make::tokens::whitespace("\n").syntax_element(),
+                ],
+            );
+            builder.add_file_edits(ctx.vfs_file_id(), editor);
         },
     )
 }
diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index 6063898..2c81e28 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -2,9 +2,11 @@
 use ide_db::{FxHashSet, path_transform::PathTransform};
 use syntax::{
     ast::{
-        self, AstNode, HasGenericParams, HasName, HasVisibility as _, edit_in_place::Indent, make,
+        self, AstNode, HasGenericParams, HasName, HasVisibility as _,
+        edit::{AstNodeEdit, IndentLevel},
+        make,
     },
-    ted,
+    syntax_editor::Position,
 };
 
 use crate::{
@@ -165,54 +167,66 @@
                     is_unsafe,
                     is_gen,
                 )
-                .clone_for_update();
+                .indent(IndentLevel(1));
+                let item = ast::AssocItem::Fn(f.clone());
 
-                // Get the impl to update, or create one if we need to.
-                let impl_def = match impl_def {
-                    Some(impl_def) => edit.make_mut(impl_def),
+                let mut editor = edit.make_editor(strukt.syntax());
+                let fn_: Option<ast::AssocItem> = match impl_def {
+                    Some(impl_def) => match impl_def.assoc_item_list() {
+                        Some(assoc_item_list) => {
+                            let item = item.indent(IndentLevel::from_node(impl_def.syntax()));
+                            assoc_item_list.add_items(&mut editor, vec![item.clone()]);
+                            Some(item)
+                        }
+                        None => {
+                            let assoc_item_list = make::assoc_item_list(Some(vec![item]));
+                            editor.insert(
+                                Position::last_child_of(impl_def.syntax()),
+                                assoc_item_list.syntax(),
+                            );
+                            assoc_item_list.assoc_items().next()
+                        }
+                    },
                     None => {
                         let name = &strukt_name.to_string();
                         let ty_params = strukt.generic_param_list();
                         let ty_args = ty_params.as_ref().map(|it| it.to_generic_args());
                         let where_clause = strukt.where_clause();
+                        let assoc_item_list = make::assoc_item_list(Some(vec![item]));
 
                         let impl_def = make::impl_(
                             ty_params,
                             ty_args,
                             make::ty_path(make::ext::ident_path(name)),
                             where_clause,
-                            None,
+                            Some(assoc_item_list),
                         )
                         .clone_for_update();
 
                         // Fixup impl_def indentation
                         let indent = strukt.indent_level();
-                        impl_def.reindent_to(indent);
+                        let impl_def = impl_def.indent(indent);
 
                         // Insert the impl block.
                         let strukt = edit.make_mut(strukt.clone());
-                        ted::insert_all(
-                            ted::Position::after(strukt.syntax()),
+                        editor.insert_all(
+                            Position::after(strukt.syntax()),
                             vec![
                                 make::tokens::whitespace(&format!("\n\n{indent}")).into(),
                                 impl_def.syntax().clone().into(),
                             ],
                         );
-
-                        impl_def
+                        impl_def.assoc_item_list().and_then(|list| list.assoc_items().next())
                     }
                 };
 
-                // Fixup function indentation.
-                // FIXME: Should really be handled by `AssocItemList::add_item`
-                f.reindent_to(impl_def.indent_level() + 1);
-
-                let assoc_items = impl_def.get_or_create_assoc_item_list();
-                assoc_items.add_item(f.clone().into());
-
-                if let Some(cap) = ctx.config.snippet_cap {
-                    edit.add_tabstop_before(cap, f)
+                if let Some(cap) = ctx.config.snippet_cap
+                    && let Some(fn_) = fn_
+                {
+                    let tabstop = edit.make_tabstop_before(cap);
+                    editor.add_annotation(fn_.syntax(), tabstop);
                 }
+                edit.add_file_edits(ctx.vfs_file_id(), editor);
             },
         )?;
     }
diff --git a/crates/ide-assists/src/handlers/generate_impl.rs b/crates/ide-assists/src/handlers/generate_impl.rs
index fcb81d2..b38ee6f 100644
--- a/crates/ide-assists/src/handlers/generate_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_impl.rs
@@ -201,7 +201,6 @@
                     &impl_,
                     &target_scope,
                 );
-                let assoc_items = assoc_items.into_iter().map(either::Either::Right).collect();
                 let assoc_item_list = make::assoc_item_list(Some(assoc_items));
                 make_impl_(Some(assoc_item_list))
             };
diff --git a/crates/ide-assists/src/handlers/generate_new.rs b/crates/ide-assists/src/handlers/generate_new.rs
index 5bda122..351f134 100644
--- a/crates/ide-assists/src/handlers/generate_new.rs
+++ b/crates/ide-assists/src/handlers/generate_new.rs
@@ -168,7 +168,7 @@
                 );
                 fn_.syntax().clone()
             } else {
-                let items = vec![either::Either::Right(ast::AssocItem::Fn(fn_))];
+                let items = vec![ast::AssocItem::Fn(fn_)];
                 let list = make::assoc_item_list(Some(items));
                 editor.insert(Position::after(impl_def.syntax()), list.syntax());
                 list.syntax().clone()
@@ -176,7 +176,7 @@
         } else {
             // Generate a new impl to add the method to
             let indent_level = strukt.indent_level();
-            let body = vec![either::Either::Right(ast::AssocItem::Fn(fn_))];
+            let body = vec![ast::AssocItem::Fn(fn_)];
             let list = make::assoc_item_list(Some(body));
             let impl_def = generate_impl_with_item(&ast::Adt::Struct(strukt.clone()), Some(list));
 
diff --git a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
index dc3dc73..56500cf 100644
--- a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
@@ -2,12 +2,8 @@
 use ide_db::assists::AssistId;
 use syntax::{
     AstNode, SyntaxKind, T,
-    ast::{
-        self, HasGenericParams, HasName, HasVisibility,
-        edit_in_place::{HasVisibilityEdit, Indent},
-        make,
-    },
-    ted::{self, Position},
+    ast::{self, HasGenericParams, HasName, HasVisibility, edit_in_place::Indent, make},
+    syntax_editor::{Position, SyntaxEditor},
 };
 
 // NOTES :
@@ -88,8 +84,8 @@
         return None;
     }
 
-    let assoc_items = impl_ast.assoc_item_list()?;
-    let first_element = assoc_items.assoc_items().next();
+    let impl_assoc_items = impl_ast.assoc_item_list()?;
+    let first_element = impl_assoc_items.assoc_items().next();
     first_element.as_ref()?;
 
     let impl_name = impl_ast.self_ty()?;
@@ -99,20 +95,16 @@
         "Generate trait from impl",
         impl_ast.syntax().text_range(),
         |builder| {
-            let impl_ast = builder.make_mut(impl_ast);
-            let trait_items = assoc_items.clone_for_update();
-            let impl_items = builder.make_mut(assoc_items);
-            let impl_name = builder.make_mut(impl_name);
+            let trait_items: ast::AssocItemList = {
+                let trait_items = impl_assoc_items.clone_subtree();
+                let mut trait_items_editor = SyntaxEditor::new(trait_items.syntax().clone());
 
-            trait_items.assoc_items().for_each(|item| {
-                strip_body(&item);
-                remove_items_visibility(&item);
-            });
-
-            impl_items.assoc_items().for_each(|item| {
-                remove_items_visibility(&item);
-            });
-
+                trait_items.assoc_items().for_each(|item| {
+                    strip_body(&mut trait_items_editor, &item);
+                    remove_items_visibility(&mut trait_items_editor, &item);
+                });
+                ast::AssocItemList::cast(trait_items_editor.finish().new_root().clone()).unwrap()
+            };
             let trait_ast = make::trait_(
                 false,
                 "NewTrait",
@@ -130,6 +122,7 @@
                 trait_name_ref.syntax().clone().into(),
                 make::tokens::single_space().into(),
                 make::token(T![for]).into(),
+                make::tokens::single_space().into(),
             ];
 
             if let Some(params) = impl_ast.generic_param_list() {
@@ -137,10 +130,15 @@
                 elements.insert(1, gen_args.syntax().clone().into());
             }
 
-            ted::insert_all(Position::before(impl_name.syntax()), elements);
+            let mut editor = builder.make_editor(impl_ast.syntax());
+            impl_assoc_items.assoc_items().for_each(|item| {
+                remove_items_visibility(&mut editor, &item);
+            });
+
+            editor.insert_all(Position::before(impl_name.syntax()), elements);
 
             // Insert trait before TraitImpl
-            ted::insert_all_raw(
+            editor.insert_all(
                 Position::before(impl_ast.syntax()),
                 vec![
                     trait_ast.syntax().clone().into(),
@@ -150,11 +148,12 @@
 
             // Link the trait name & trait ref names together as a placeholder snippet group
             if let Some(cap) = ctx.config.snippet_cap {
-                builder.add_placeholder_snippet_group(
-                    cap,
-                    vec![trait_name.syntax().clone(), trait_name_ref.syntax().clone()],
-                );
+                let placeholder = builder.make_placeholder_snippet(cap);
+                editor.add_annotation(trait_name.syntax(), placeholder);
+                editor.add_annotation(trait_name_ref.syntax(), placeholder);
             }
+
+            builder.add_file_edits(ctx.vfs_file_id(), editor);
         },
     );
 
@@ -162,19 +161,21 @@
 }
 
 /// `E0449` Trait items always share the visibility of their trait
-fn remove_items_visibility(item: &ast::AssocItem) {
+fn remove_items_visibility(editor: &mut SyntaxEditor, item: &ast::AssocItem) {
     if let Some(has_vis) = ast::AnyHasVisibility::cast(item.syntax().clone()) {
         if let Some(vis) = has_vis.visibility()
             && let Some(token) = vis.syntax().next_sibling_or_token()
             && token.kind() == SyntaxKind::WHITESPACE
         {
-            ted::remove(token);
+            editor.delete(token);
         }
-        has_vis.set_visibility(None);
+        if let Some(vis) = has_vis.visibility() {
+            editor.delete(vis.syntax());
+        }
     }
 }
 
-fn strip_body(item: &ast::AssocItem) {
+fn strip_body(editor: &mut SyntaxEditor, item: &ast::AssocItem) {
     if let ast::AssocItem::Fn(f) = item
         && let Some(body) = f.body()
     {
@@ -183,10 +184,10 @@
         if let Some(prev) = body.syntax().prev_sibling_or_token()
             && prev.kind() == SyntaxKind::WHITESPACE
         {
-            ted::remove(prev);
+            editor.delete(prev);
         }
 
-        ted::replace(body.syntax(), make::tokens::semicolon());
+        editor.replace(body.syntax(), make::tokens::semicolon());
     };
 }
 
diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
index 45bb6ce..175f261 100644
--- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -221,11 +221,7 @@
         } else {
             Some(first.clone())
         };
-        let items = first_item
-            .into_iter()
-            .chain(other.iter().cloned())
-            .map(either::Either::Right)
-            .collect();
+        let items = first_item.into_iter().chain(other.iter().cloned()).collect();
         make::assoc_item_list(Some(items))
     } else {
         make::assoc_item_list(None)
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index c94be7e..49f7f63 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -244,7 +244,7 @@
 
 fn line_index(db: &dyn LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> {
     let text = db.file_text(file_id).text(db);
-    Arc::new(LineIndex::new(&text))
+    Arc::new(LineIndex::new(text))
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index 4dd6422..abd4dc8 100644
--- a/crates/ide-db/src/search.rs
+++ b/crates/ide-db/src/search.rs
@@ -487,9 +487,9 @@
         scope.entries.iter().map(|(&file_id, &search_range)| {
             let text = db.file_text(file_id.file_id(db)).text(db);
             let search_range =
-                search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text)));
+                search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&**text)));
 
-            (text, file_id, search_range)
+            (text.clone(), file_id, search_range)
         })
     }
 
@@ -854,14 +854,7 @@
                 &finder,
                 name,
                 is_possibly_self.into_iter().map(|position| {
-                    (
-                        self.sema
-                            .db
-                            .file_text(position.file_id.file_id(self.sema.db))
-                            .text(self.sema.db),
-                        position.file_id,
-                        position.range,
-                    )
+                    (position.file_text(self.sema.db).clone(), position.file_id, position.range)
                 }),
                 |path, name_position| {
                     let has_self = path
@@ -1067,12 +1060,12 @@
                 let file_text = sema.db.file_text(file_id.file_id(self.sema.db));
                 let text = file_text.text(sema.db);
                 let search_range =
-                    search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text)));
+                    search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&**text)));
 
                 let tree = LazyCell::new(|| sema.parse(file_id).syntax().clone());
                 let finder = &Finder::new("self");
 
-                for offset in Self::match_indices(&text, finder, search_range) {
+                for offset in Self::match_indices(text, finder, search_range) {
                     for name_ref in Self::find_nodes(sema, "self", file_id, &tree, offset)
                         .filter_map(ast::NameRef::cast)
                     {
diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs
index 4e4bd47..1819931 100644
--- a/crates/ide-diagnostics/src/tests.rs
+++ b/crates/ide-diagnostics/src/tests.rs
@@ -229,7 +229,7 @@
         let line_index = db.line_index(file_id);
 
         let mut actual = annotations.remove(&file_id).unwrap_or_default();
-        let mut expected = extract_annotations(&db.file_text(file_id).text(&db));
+        let mut expected = extract_annotations(db.file_text(file_id).text(&db));
         expected.sort_by_key(|(range, s)| (range.start(), s.clone()));
         actual.sort_by_key(|(range, s)| (range.start(), s.clone()));
         // FIXME: We should panic on duplicates instead, but includes currently cause us to report
diff --git a/crates/ide-ssr/src/lib.rs b/crates/ide-ssr/src/lib.rs
index 138af22..43ad12c1 100644
--- a/crates/ide-ssr/src/lib.rs
+++ b/crates/ide-ssr/src/lib.rs
@@ -186,7 +186,7 @@
                     replacing::matches_to_edit(
                         self.sema.db,
                         &matches,
-                        &self.sema.db.file_text(file_id).text(self.sema.db),
+                        self.sema.db.file_text(file_id).text(self.sema.db),
                         &self.rules,
                     ),
                 )
@@ -228,7 +228,7 @@
         let file = self.sema.parse(file_id);
         let mut res = Vec::new();
         let file_text = self.sema.db.file_text(file_id.file_id(self.sema.db)).text(self.sema.db);
-        let mut remaining_text = &*file_text;
+        let mut remaining_text = &**file_text;
         let mut base = 0;
         let len = snippet.len() as u32;
         while let Some(offset) = remaining_text.find(snippet) {
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
old mode 100755
new mode 100644
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 84e4127..f768d4b 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -83,14 +83,14 @@
         return Some(RangeInfo::new(original_token.text_range(), navs));
     }
 
-    if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &original_token) {
-        return Some(RangeInfo::new(original_token.text_range(), navs));
-    }
-
     let navs = sema
         .descend_into_macros_no_opaque(original_token.clone(), false)
         .into_iter()
         .filter_map(|token| {
+            if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &token.value) {
+                return Some(navs);
+            }
+
             let parent = token.value.parent()?;
 
             let token_file_id = token.file_id;
@@ -3284,6 +3284,32 @@
     }
 
     #[test]
+    fn into_call_to_from_definition_within_macro() {
+        check(
+            r#"
+//- proc_macros: identity
+//- minicore: from
+struct A;
+
+struct B;
+
+impl From<A> for B {
+    fn from(value: A) -> Self {
+     //^^^^
+        B
+    }
+}
+
+#[proc_macros::identity]
+fn f() {
+    let a = A;
+    let b: B = a.into$0();
+}
+        "#,
+        );
+    }
+
+    #[test]
     fn into_call_to_from_definition_with_trait_bounds() {
         check(
             r#"
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index b3b8deb..9887748 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -299,7 +299,7 @@
 
     /// Gets the text of the source file.
     pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<str>> {
-        self.with_db(|db| SourceDatabase::file_text(db, file_id).text(db))
+        self.with_db(|db| SourceDatabase::file_text(db, file_id).text(db).clone())
     }
 
     /// Gets the syntax tree of the file.
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index 634edaa..aea4ae0 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -13,8 +13,11 @@
 };
 use itertools::Itertools;
 use std::fmt::Write;
-use stdx::{always, never};
-use syntax::{AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize, ast};
+use stdx::{always, format_to, never};
+use syntax::{
+    AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize,
+    ast::{self, HasArgList, prec::ExprPrecedence},
+};
 
 use ide_db::text_edit::TextEdit;
 
@@ -331,6 +334,85 @@
     }
 }
 
+fn transform_assoc_fn_into_method_call(
+    sema: &Semantics<'_, RootDatabase>,
+    source_change: &mut SourceChange,
+    f: hir::Function,
+) {
+    let calls = Definition::Function(f).usages(sema).all();
+    for (file_id, calls) in calls {
+        for call in calls {
+            let Some(fn_name) = call.name.as_name_ref() else { continue };
+            let Some(path) = fn_name.syntax().parent().and_then(ast::PathSegment::cast) else {
+                continue;
+            };
+            let path = path.parent_path();
+            // The `PathExpr` is the direct parent, above it is the `CallExpr`.
+            let Some(call) =
+                path.syntax().parent().and_then(|it| ast::CallExpr::cast(it.parent()?))
+            else {
+                continue;
+            };
+
+            let Some(arg_list) = call.arg_list() else { continue };
+            let mut args = arg_list.args();
+            let Some(mut self_arg) = args.next() else { continue };
+            let second_arg = args.next();
+
+            // Strip (de)references, as they will be taken automatically by auto(de)ref.
+            loop {
+                let self_ = match &self_arg {
+                    ast::Expr::RefExpr(self_) => self_.expr(),
+                    ast::Expr::ParenExpr(self_) => self_.expr(),
+                    ast::Expr::PrefixExpr(self_)
+                        if self_.op_kind() == Some(ast::UnaryOp::Deref) =>
+                    {
+                        self_.expr()
+                    }
+                    _ => break,
+                };
+                self_arg = match self_ {
+                    Some(it) => it,
+                    None => break,
+                };
+            }
+
+            let self_needs_parens =
+                self_arg.precedence().needs_parentheses_in(ExprPrecedence::Postfix);
+
+            let replace_start = path.syntax().text_range().start();
+            let replace_end = match second_arg {
+                Some(second_arg) => second_arg.syntax().text_range().start(),
+                None => arg_list
+                    .r_paren_token()
+                    .map(|it| it.text_range().start())
+                    .unwrap_or_else(|| arg_list.syntax().text_range().end()),
+            };
+            let replace_range = TextRange::new(replace_start, replace_end);
+
+            let Some(macro_mapped_self) = sema.original_range_opt(self_arg.syntax()) else {
+                continue;
+            };
+            let mut replacement = String::new();
+            if self_needs_parens {
+                replacement.push('(');
+            }
+            replacement.push_str(macro_mapped_self.text(sema.db));
+            if self_needs_parens {
+                replacement.push(')');
+            }
+            replacement.push('.');
+            format_to!(replacement, "{fn_name}");
+            replacement.push('(');
+
+            source_change.insert_source_edit(
+                file_id.file_id(sema.db),
+                TextEdit::replace(replace_range, replacement),
+            );
+        }
+    }
+}
+
 fn rename_to_self(
     sema: &Semantics<'_, RootDatabase>,
     local: hir::Local,
@@ -408,6 +490,7 @@
         file_id.original_file(sema.db).file_id(sema.db),
         TextEdit::replace(param_source.syntax().text_range(), String::from(self_param)),
     );
+    transform_assoc_fn_into_method_call(sema, &mut source_change, fn_def);
     Ok(source_change)
 }
 
@@ -3412,4 +3495,78 @@
 "#,
         );
     }
+
+    #[test]
+    fn rename_to_self_callers() {
+        check(
+            "self",
+            r#"
+//- minicore: add
+struct Foo;
+impl core::ops::Add for Foo {
+    type Target = Foo;
+    fn add(self, _: Self) -> Foo { Foo }
+}
+
+impl Foo {
+    fn foo(th$0is: &Self) {}
+}
+
+fn bar(v: &Foo) {
+    Foo::foo(v);
+}
+
+fn baz() {
+    Foo::foo(&Foo);
+    Foo::foo(Foo + Foo);
+}
+        "#,
+            r#"
+struct Foo;
+impl core::ops::Add for Foo {
+    type Target = Foo;
+    fn add(self, _: Self) -> Foo { Foo }
+}
+
+impl Foo {
+    fn foo(&self) {}
+}
+
+fn bar(v: &Foo) {
+    v.foo();
+}
+
+fn baz() {
+    Foo.foo();
+    (Foo + Foo).foo();
+}
+        "#,
+        );
+        // Multiple arguments:
+        check(
+            "self",
+            r#"
+struct Foo;
+
+impl Foo {
+    fn foo(th$0is: &Self, v: i32) {}
+}
+
+fn bar(v: Foo) {
+    Foo::foo(&v, 123);
+}
+        "#,
+            r#"
+struct Foo;
+
+impl Foo {
+    fn foo(&self, v: i32) {}
+}
+
+fn bar(v: Foo) {
+    v.foo(123);
+}
+        "#,
+        );
+    }
 }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 70d0448..1a00295 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -726,7 +726,9 @@
         /// ```bash
         /// cargo check --quiet --workspace --message-format=json --all-targets --keep-going
         /// ```
-        /// .
+        ///
+        /// Note: The option must be specified as an array of command line arguments, with
+        /// the first argument being the name of the command to run.
         cargo_buildScripts_overrideCommand: Option<Vec<String>> = None,
         /// Rerun proc-macros building/build-scripts running when proc-macro
         /// or build-script sources change and are saved.
@@ -840,7 +842,9 @@
         /// ```bash
         /// cargo check --workspace --message-format=json --all-targets
         /// ```
-        /// .
+        ///
+        /// Note: The option must be specified as an array of command line arguments, with
+        /// the first argument being the name of the command to run.
         check_overrideCommand | checkOnSave_overrideCommand: Option<Vec<String>>             = None,
         /// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.
         ///
@@ -890,6 +894,9 @@
         /// not that of `cargo fmt`. The file contents will be passed on the
         /// standard input and the formatted result will be read from the
         /// standard output.
+        ///
+        /// Note: The option must be specified as an array of command line arguments, with
+        /// the first argument being the name of the command to run.
         rustfmt_overrideCommand: Option<Vec<String>> = None,
         /// Enables the use of rustfmt's unstable range formatting command for the
         /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 3171bdd..2f1afba 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -448,7 +448,7 @@
                     tracing::info!(%vfs_path, ?change_kind, "Processing rust-analyzer.toml changes");
                     if vfs_path.as_path() == user_config_abs_path {
                         tracing::info!(%vfs_path, ?change_kind, "Use config rust-analyzer.toml changes");
-                        change.change_user_config(Some(db.file_text(file_id).text(db)));
+                        change.change_user_config(Some(db.file_text(file_id).text(db).clone()));
                     }
 
                     // If change has been made to a ratoml file that
@@ -462,14 +462,14 @@
                             change.change_workspace_ratoml(
                                 source_root_id,
                                 vfs_path.clone(),
-                                Some(db.file_text(file_id).text(db)),
+                                Some(db.file_text(file_id).text(db).clone()),
                             )
                         } else {
                             tracing::info!(%vfs_path, ?source_root_id, "crate rust-analyzer.toml changes");
                             change.change_ratoml(
                                 source_root_id,
                                 vfs_path.clone(),
-                                Some(db.file_text(file_id).text(db)),
+                                Some(db.file_text(file_id).text(db).clone()),
                             )
                         };
 
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index f01ac08..b50ce64 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -9,11 +9,11 @@
     SyntaxKind::{ATTR, COMMENT, WHITESPACE},
     SyntaxNode, SyntaxToken,
     algo::{self, neighbor},
-    ast::{self, HasGenericArgs, HasGenericParams, edit::IndentLevel, make},
+    ast::{self, HasGenericParams, edit::IndentLevel, make},
     ted::{self, Position},
 };
 
-use super::{GenericParam, HasArgList, HasName};
+use super::{GenericParam, HasName};
 
 pub trait GenericParamsOwnerEdit: ast::HasGenericParams {
     fn get_or_create_generic_param_list(&self) -> ast::GenericParamList;
@@ -419,34 +419,6 @@
     }
 }
 
-impl ast::PathSegment {
-    pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList {
-        if self.generic_arg_list().is_none() {
-            let arg_list = make::generic_arg_list(empty()).clone_for_update();
-            ted::append_child(self.syntax(), arg_list.syntax());
-        }
-        self.generic_arg_list().unwrap()
-    }
-}
-
-impl ast::MethodCallExpr {
-    pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList {
-        if self.generic_arg_list().is_none() {
-            let generic_arg_list = make::turbofish_generic_arg_list(empty()).clone_for_update();
-
-            if let Some(arg_list) = self.arg_list() {
-                ted::insert_raw(
-                    ted::Position::before(arg_list.syntax()),
-                    generic_arg_list.syntax(),
-                );
-            } else {
-                ted::append_child(self.syntax(), generic_arg_list.syntax());
-            }
-        }
-        self.generic_arg_list().unwrap()
-    }
-}
-
 impl Removable for ast::UseTree {
     fn remove(&self) {
         for dir in [Direction::Next, Direction::Prev] {
@@ -677,106 +649,6 @@
         ];
         ted::insert_all(position, elements);
     }
-
-    /// Adds a new associated item at the start of the associated item list.
-    ///
-    /// Attention! This function does align the first line of `item` with respect to `self`,
-    /// but it does _not_ change indentation of other lines (if any).
-    pub fn add_item_at_start(&self, item: ast::AssocItem) {
-        match self.assoc_items().next() {
-            Some(first_item) => {
-                let indent = IndentLevel::from_node(first_item.syntax());
-                let before = Position::before(first_item.syntax());
-
-                ted::insert_all(
-                    before,
-                    vec![
-                        item.syntax().clone().into(),
-                        make::tokens::whitespace(&format!("\n\n{indent}")).into(),
-                    ],
-                )
-            }
-            None => {
-                let (indent, position, whitespace) = 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::first_child_of(self.syntax()), ""),
-                };
-
-                let mut elements = vec![];
-
-                // Avoid pushing an empty whitespace token
-                if !indent.is_zero() || !whitespace.is_empty() {
-                    elements.push(make::tokens::whitespace(&format!("{whitespace}{indent}")).into())
-                }
-                elements.push(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::LetStmt {
-    pub fn set_ty(&self, ty: Option<ast::Type>) {
-        match ty {
-            None => {
-                if let Some(colon_token) = self.colon_token() {
-                    ted::remove(colon_token);
-                }
-
-                if let Some(existing_ty) = self.ty() {
-                    if let Some(sibling) = existing_ty.syntax().prev_sibling_or_token()
-                        && sibling.kind() == SyntaxKind::WHITESPACE
-                    {
-                        ted::remove(sibling);
-                    }
-
-                    ted::remove(existing_ty.syntax());
-                }
-
-                // Remove any trailing ws
-                if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE)
-                {
-                    last.detach();
-                }
-            }
-            Some(new_ty) => {
-                if self.colon_token().is_none() {
-                    ted::insert_raw(
-                        Position::after(
-                            self.pat().expect("let stmt should have a pattern").syntax(),
-                        ),
-                        make::token(T![:]),
-                    );
-                }
-
-                if let Some(old_ty) = self.ty() {
-                    ted::replace(old_ty.syntax(), new_ty.syntax());
-                } else {
-                    ted::insert(Position::after(self.colon_token().unwrap()), new_ty.syntax());
-                }
-            }
-        }
-    }
 }
 
 impl ast::RecordExprFieldList {
@@ -1091,35 +963,4 @@
         check("let a @ ()", "let a", None);
         check("let a @ ", "let a", None);
     }
-
-    #[test]
-    fn test_let_stmt_set_ty() {
-        #[track_caller]
-        fn check(before: &str, expected: &str, ty: Option<ast::Type>) {
-            let ty = ty.map(|it| it.clone_for_update());
-
-            let let_stmt = ast_mut_from_text::<ast::LetStmt>(&format!("fn f() {{ {before} }}"));
-            let_stmt.set_ty(ty);
-
-            let after = ast_mut_from_text::<ast::LetStmt>(&format!("fn f() {{ {expected} }}"));
-            assert_eq!(let_stmt.to_string(), after.to_string(), "{let_stmt:#?}\n!=\n{after:#?}");
-        }
-
-        // adding
-        check("let a;", "let a: ();", Some(make::ty_tuple([])));
-        // no semicolon due to it being eaten during error recovery
-        check("let a:", "let a: ()", Some(make::ty_tuple([])));
-
-        // replacing
-        check("let a: u8;", "let a: ();", Some(make::ty_tuple([])));
-        check("let a: u8 = 3;", "let a: () = 3;", Some(make::ty_tuple([])));
-        check("let a: = 3;", "let a: () = 3;", Some(make::ty_tuple([])));
-
-        // removing
-        check("let a: u8;", "let a;", None);
-        check("let a:;", "let a;", None);
-
-        check("let a: u8 = 3;", "let a = 3;", None);
-        check("let a: = 3;", "let a = 3;", None);
-    }
 }
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 2a7b51c..daeb79c 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -229,9 +229,7 @@
     }
 }
 
-pub fn assoc_item_list(
-    body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>,
-) -> ast::AssocItemList {
+pub fn assoc_item_list(body: Option<Vec<ast::AssocItem>>) -> ast::AssocItemList {
     let is_break_braces = body.is_some();
     let body_newline = if is_break_braces { "\n".to_owned() } else { String::new() };
     let body_indent = if is_break_braces { "    ".to_owned() } else { String::new() };
diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md
index 05299f1..99a30d8 100644
--- a/docs/book/src/configuration_generated.md
+++ b/docs/book/src/configuration_generated.md
@@ -104,7 +104,9 @@
 ```bash
 cargo check --quiet --workspace --message-format=json --all-targets --keep-going
 ```
-.
+
+Note: The option must be specified as an array of command line arguments, with
+the first argument being the name of the command to run.
 
 
 ## rust-analyzer.cargo.buildScripts.rebuildOnSave {#cargo.buildScripts.rebuildOnSave}
@@ -331,7 +333,9 @@
 ```bash
 cargo check --workspace --message-format=json --all-targets
 ```
-.
+
+Note: The option must be specified as an array of command line arguments, with
+the first argument being the name of the command to run.
 
 
 ## rust-analyzer.check.targets {#check.targets}
@@ -1343,6 +1347,9 @@
 standard input and the formatted result will be read from the
 standard output.
 
+Note: The option must be specified as an array of command line arguments, with
+the first argument being the name of the command to run.
+
 
 ## rust-analyzer.rustfmt.rangeFormatting.enable {#rustfmt.rangeFormatting.enable}
 
diff --git a/docs/book/src/contributing/style.md b/docs/book/src/contributing/style.md
index 5654e37..746f3eb 100644
--- a/docs/book/src/contributing/style.md
+++ b/docs/book/src/contributing/style.md
@@ -49,8 +49,8 @@
 Changes of the third group should be pretty rare, so we don't specify any specific process for them.
 That said, adding an innocent-looking `pub use` is a very simple way to break encapsulation, keep an eye on it!
 
-Note: if you enjoyed this abstract hand-waving about boundaries, you might appreciate
-https://www.tedinski.com/2018/02/06/system-boundaries.html
+Note: if you enjoyed this abstract hand-waving about boundaries, you might appreciate [this post](https://www.tedinski.com/2018/02/06/system-boundaries.html).
+
 
 ## Crates.io Dependencies
 
@@ -231,7 +231,7 @@
 }
 ```
 
-In the "Not as good" version, the precondition that `1` is a valid char boundary is checked in `is_string_literal` and used in `foo`.
+In the "Bad" version, the precondition that `1` and `s.len() - 1` are valid string literal boundaries is checked in `is_string_literal` but used in `main`.
 In the "Good" version, the precondition check and usage are checked in the same block, and then encoded in the types.
 
 **Rationale:** non-local code properties degrade under change.
@@ -271,6 +271,8 @@
 }
 ```
 
+See also [this post](https://matklad.github.io/2023/11/15/push-ifs-up-and-fors-down.html)
+
 ## Assertions
 
 Assert liberally.
@@ -608,7 +610,7 @@
 
 ```rust
 // GOOD
-fn frobnicate(f: impl FnMut()) {
+fn frobnicate(mut f: impl FnMut()) {
     frobnicate_impl(&mut f)
 }
 fn frobnicate_impl(f: &mut dyn FnMut()) {
@@ -616,7 +618,7 @@
 }
 
 // BAD
-fn frobnicate(f: impl FnMut()) {
+fn frobnicate(mut f: impl FnMut()) {
     // lots of code
 }
 ```
@@ -975,7 +977,7 @@
 **Rationale:** consistency & simplicity.
 `ref` was required before [match ergonomics](https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md).
 Today, it is redundant.
-Between `ref` and mach ergonomics, the latter is more ergonomic in most cases, and is simpler (does not require a keyword).
+Between `ref` and match ergonomics, the latter is more ergonomic in most cases, and is simpler (does not require a keyword).
 
 ## Empty Match Arms
 
diff --git a/editors/code/package.json b/editors/code/package.json
index 8953a30..470db24 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -887,7 +887,7 @@
                 "title": "Cargo",
                 "properties": {
                     "rust-analyzer.cargo.buildScripts.overrideCommand": {
-                        "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets --keep-going\n```\n.",
+                        "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets --keep-going\n```\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.",
                         "default": null,
                         "type": [
                             "null",
@@ -1207,7 +1207,7 @@
                 "title": "Check",
                 "properties": {
                     "rust-analyzer.check.overrideCommand": {
-                        "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#`.\n\nIf `$saved_file` is part of the command, rust-analyzer will pass\nthe absolute path of the saved file to the provided command. This is\nintended to be used with non-Cargo build systems.\nNote that `$saved_file` is experimental and may be removed in the future.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.",
+                        "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#`.\n\nIf `$saved_file` is part of the command, rust-analyzer will pass\nthe absolute path of the saved file to the provided command. This is\nintended to be used with non-Cargo build systems.\nNote that `$saved_file` is experimental and may be removed in the future.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.",
                         "default": null,
                         "type": [
                             "null",
@@ -2808,7 +2808,7 @@
                 "title": "Rustfmt",
                 "properties": {
                     "rust-analyzer.rustfmt.overrideCommand": {
-                        "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting. This should be the equivalent of `rustfmt` here, and\nnot that of `cargo fmt`. The file contents will be passed on the\nstandard input and the formatted result will be read from the\nstandard output.",
+                        "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting. This should be the equivalent of `rustfmt` here, and\nnot that of `cargo fmt`. The file contents will be passed on the\nstandard input and the formatted result will be read from the\nstandard output.\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.",
                         "default": null,
                         "type": [
                             "null",