about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs36
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs237
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs101
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs4
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs4
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/lib.rs7
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/sysroot.rs64
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/tests.rs6
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs61
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs8
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs24
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs4
-rw-r--r--src/tools/rust-analyzer/docs/dev/architecture.md2
-rw-r--r--src/tools/rust-analyzer/docs/user/generated_config.adoc6
-rw-r--r--src/tools/rust-analyzer/editors/code/package.json18
-rw-r--r--src/tools/rust-analyzer/rust-version2
22 files changed, 459 insertions, 194 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
index e4881d75201..54aa18ce207 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
@@ -916,6 +916,32 @@ impl<'a> InferenceTable<'a> {
 
     /// Check if given type is `Sized` or not
     pub(crate) fn is_sized(&mut self, ty: &Ty) -> bool {
+        let mut ty = ty.clone();
+        {
+            let mut structs = SmallVec::<[_; 8]>::new();
+            // Must use a loop here and not recursion because otherwise users will conduct completely
+            // artificial examples of structs that have themselves as the tail field and complain r-a crashes.
+            while let Some((AdtId::StructId(id), subst)) = ty.as_adt() {
+                let struct_data = self.db.struct_data(id);
+                if let Some((last_field, _)) = struct_data.variant_data.fields().iter().next_back()
+                {
+                    let last_field_ty = self.db.field_types(id.into())[last_field]
+                        .clone()
+                        .substitute(Interner, subst);
+                    if structs.contains(&ty) {
+                        // A struct recursively contains itself as a tail field somewhere.
+                        return true; // Don't overload the users with too many errors.
+                    }
+                    structs.push(ty);
+                    // Structs can have DST as its last field and such cases are not handled
+                    // as unsized by the chalk, so we do this manually.
+                    ty = last_field_ty;
+                } else {
+                    break;
+                };
+            }
+        }
+
         // Early return for some obvious types
         if matches!(
             ty.kind(Interner),
@@ -930,16 +956,6 @@ impl<'a> InferenceTable<'a> {
             return true;
         }
 
-        if let Some((AdtId::StructId(id), subst)) = ty.as_adt() {
-            let struct_data = self.db.struct_data(id);
-            if let Some((last_field, _)) = struct_data.variant_data.fields().iter().last() {
-                let last_field_ty =
-                    self.db.field_types(id.into())[last_field].clone().substitute(Interner, subst);
-                // Structs can have DST as its last field and such cases are not handled
-                // as unsized by the chalk, so we do this manually
-                return self.is_sized(&last_field_ty);
-            }
-        }
         let Some(sized) = self
             .db
             .lang_item(self.trait_env.krate, LangItem::Sized)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index 9b982a124e7..624148cab20 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -4790,3 +4790,24 @@ fn allowed3(baz: impl Baz<Assoc = Qux<impl Foo>>) {}
         "#]],
     )
 }
+
+#[test]
+fn recursive_tail_sized() {
+    check_infer(
+        r#"
+struct WeirdFoo(WeirdBar);
+struct WeirdBar(WeirdFoo);
+
+fn bar(v: *const ()) {
+    let _ = v as *const WeirdFoo;
+}
+    "#,
+        expect![[r#"
+            62..63 'v': *const ()
+            76..113 '{     ...Foo; }': ()
+            86..87 '_': *const WeirdFoo
+            90..91 'v': *const ()
+            90..110 'v as *...irdFoo': *const WeirdFoo
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs
index 1d2d3350f7c..0146369f298 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs
@@ -116,6 +116,9 @@ impl<'a> AssistContext<'a> {
     pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> {
         find_node_at_offset(self.source_file.syntax(), self.offset())
     }
+    pub(crate) fn find_node_at_trimmed_offset<N: AstNode>(&self) -> Option<N> {
+        find_node_at_offset(self.source_file.syntax(), self.trimmed_range.start())
+    }
     pub(crate) fn find_node_at_range<N: AstNode>(&self) -> Option<N> {
         find_node_at_range(self.source_file.syntax(), self.trimmed_range)
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs
index 2f4a263ee07..42f615e71da 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs
@@ -1,5 +1,5 @@
 use syntax::{
-    ast::{self, edit::AstNodeEdit, make},
+    ast::{self, edit_in_place::Indent, syntax_factory::SyntaxFactory},
     AstNode,
 };
 
@@ -39,12 +39,16 @@ pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
         },
         expr.syntax().text_range(),
         |builder| {
-            let block_expr = AstNodeEdit::indent(
-                &make::block_expr(None, Some(expr.clone())),
-                AstNodeEdit::indent_level(&expr),
-            );
+            let make = SyntaxFactory::new();
+            let mut editor = builder.make_editor(expr.syntax());
 
-            builder.replace(expr.syntax().text_range(), block_expr.syntax().text());
+            let block_expr = make.block_expr(None, Some(expr.clone()));
+            block_expr.indent(expr.indent_level());
+
+            editor.replace(expr.syntax(), block_expr.syntax());
+
+            editor.add_mappings(make.finish_with_mappings());
+            builder.add_file_edits(ctx.file_id(), editor);
         },
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
index a211ca8f2d6..236d33878ed 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
@@ -30,8 +30,8 @@ use crate::{utils, AssistContext, AssistId, AssistKind, Assists};
 //
 // fn handle(action: Action) {
 //     match action {
-//         $0Action::Move { distance } => todo!(),
-//         Action::Stop => todo!(),
+//         Action::Move { distance } => ${1:todo!()},
+//         Action::Stop => ${2:todo!()},$0
 //     }
 // }
 // ```
@@ -234,10 +234,11 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
                 }
             }
 
-            let mut first_new_arm = None;
+            let mut added_arms = Vec::new();
+            let mut todo_placeholders = Vec::new();
             for arm in missing_arms {
-                first_new_arm.get_or_insert_with(|| arm.clone());
-                new_match_arm_list.add_arm(arm);
+                todo_placeholders.push(arm.expr().unwrap());
+                added_arms.push(arm);
             }
 
             if needs_catch_all_arm && !has_catch_all_arm {
@@ -248,14 +249,28 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
                     make::ext::expr_todo(),
                 )
                 .clone_for_update();
-                first_new_arm.get_or_insert_with(|| arm.clone());
+                todo_placeholders.push(arm.expr().unwrap());
+                added_arms.push(arm);
+            }
+
+            let first_new_arm = added_arms.first().cloned();
+            let last_new_arm = added_arms.last().cloned();
+
+            for arm in added_arms {
                 new_match_arm_list.add_arm(arm);
             }
 
-            if let (Some(first_new_arm), Some(cap)) = (first_new_arm, ctx.config.snippet_cap) {
-                match first_new_arm.syntax().descendants().find_map(ast::WildcardPat::cast) {
-                    Some(it) => edit.add_placeholder_snippet(cap, it),
-                    None => edit.add_tabstop_before(cap, first_new_arm),
+            if let Some(cap) = ctx.config.snippet_cap {
+                if let Some(it) = first_new_arm.and_then(|arm| arm.syntax().descendants().find_map(ast::WildcardPat::cast)) {
+                    edit.add_placeholder_snippet(cap, it);
+                }
+
+                for placeholder in todo_placeholders {
+                    edit.add_placeholder_snippet(cap, placeholder);
+                }
+
+                if let Some(arm) = last_new_arm {
+                    edit.add_tabstop_after(cap, arm);
                 }
             }
 
@@ -581,8 +596,8 @@ fn foo(a: bool) {
             r#"
 fn foo(a: bool) {
     match a {
-        $0true => todo!(),
-        false => todo!(),
+        true => ${1:todo!()},
+        false => ${2:todo!()},$0
     }
 }
 "#,
@@ -604,7 +619,7 @@ fn foo(a: bool) {
 fn foo(a: bool) {
     match a {
         true => {}
-        $0false => todo!(),
+        false => ${1:todo!()},$0
     }
 }
 "#,
@@ -654,10 +669,10 @@ fn foo(a: bool) {
             r#"
 fn foo(a: bool) {
     match (a, a) {
-        $0(true, true) => todo!(),
-        (true, false) => todo!(),
-        (false, true) => todo!(),
-        (false, false) => todo!(),
+        (true, true) => ${1:todo!()},
+        (true, false) => ${2:todo!()},
+        (false, true) => ${3:todo!()},
+        (false, false) => ${4:todo!()},$0
     }
 }
 "#,
@@ -677,8 +692,8 @@ fn foo(a: bool) {
             r#"
 fn foo(a: bool) {
     match [a] {
-        $0[true] => todo!(),
-        [false] => todo!(),
+        [true] => ${1:todo!()},
+        [false] => ${2:todo!()},$0
     }
 }
 "#,
@@ -695,8 +710,8 @@ fn foo(a: bool) {
             r#"
 fn foo(a: bool) {
     match [a,] {
-        $0[true] => todo!(),
-        [false] => todo!(),
+        [true] => ${1:todo!()},
+        [false] => ${2:todo!()},$0
     }
 }
 "#,
@@ -715,9 +730,9 @@ fn foo(a: bool) {
 fn foo(a: bool) {
     match [a, a] {
         [true, true] => todo!(),
-        $0[true, false] => todo!(),
-        [false, true] => todo!(),
-        [false, false] => todo!(),
+        [true, false] => ${1:todo!()},
+        [false, true] => ${2:todo!()},
+        [false, false] => ${3:todo!()},$0
     }
 }
 "#,
@@ -734,10 +749,10 @@ fn foo(a: bool) {
             r#"
 fn foo(a: bool) {
     match [a, a] {
-        $0[true, true] => todo!(),
-        [true, false] => todo!(),
-        [false, true] => todo!(),
-        [false, false] => todo!(),
+        [true, true] => ${1:todo!()},
+        [true, false] => ${2:todo!()},
+        [false, true] => ${3:todo!()},
+        [false, false] => ${4:todo!()},$0
     }
 }
 "#,
@@ -759,8 +774,8 @@ fn foo(a: bool) {
 fn foo(a: bool) {
     match (a, a) {
         (true | false, true) => {}
-        $0(true, false) => todo!(),
-        (false, false) => todo!(),
+        (true, false) => ${1:todo!()},
+        (false, false) => ${2:todo!()},$0
     }
 }
 "#,
@@ -779,9 +794,9 @@ fn foo(a: bool) {
 fn foo(a: bool) {
     match (a, a) {
         (false, true) => {}
-        $0(true, true) => todo!(),
-        (true, false) => todo!(),
-        (false, false) => todo!(),
+        (true, true) => ${1:todo!()},
+        (true, false) => ${2:todo!()},
+        (false, false) => ${3:todo!()},$0
     }
 }
 "#,
@@ -815,7 +830,7 @@ fn main() {
     match A::As {
         A::Bs { x, y: Some(_) } => {}
         A::Cs(_, Some(_)) => {}
-        $0A::As => todo!(),
+        A::As => ${1:todo!()},$0
     }
 }
 "#,
@@ -838,7 +853,7 @@ fn main() {
 fn main() {
     match None {
         None => {}
-        Some(${0:_}) => todo!(),
+        Some(${1:_}) => ${2:todo!()},$0
     }
 }
 "#,
@@ -862,7 +877,7 @@ enum A { As, Bs, Cs(Option<i32>) }
 fn main() {
     match A::As {
         A::Cs(_) | A::Bs => {}
-        $0A::As => todo!(),
+        A::As => ${1:todo!()},$0
     }
 }
 "#,
@@ -892,8 +907,8 @@ fn main() {
         A::Bs if 0 < 1 => {}
         A::Ds(_value) => { let x = 1; }
         A::Es(B::Xs) => (),
-        $0A::As => todo!(),
-        A::Cs => todo!(),
+        A::As => ${1:todo!()},
+        A::Cs => ${2:todo!()},$0
     }
 }
 "#,
@@ -919,7 +934,7 @@ fn main() {
     match A::As {
         A::As(_) => {}
         a @ A::Bs(_) => {}
-        A::Cs(${0:_}) => todo!(),
+        A::Cs(${1:_}) => ${2:todo!()},$0
     }
 }
 "#,
@@ -945,11 +960,11 @@ enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
 fn main() {
     let a = A::As;
     match a {
-        $0A::As => todo!(),
-        A::Bs => todo!(),
-        A::Cs(_) => todo!(),
-        A::Ds(_, _) => todo!(),
-        A::Es { x, y } => todo!(),
+        A::As => ${1:todo!()},
+        A::Bs => ${2:todo!()},
+        A::Cs(_) => ${3:todo!()},
+        A::Ds(_, _) => ${4:todo!()},
+        A::Es { x, y } => ${5:todo!()},$0
     }
 }
 "#,
@@ -982,9 +997,9 @@ fn main() {
     let b = B::One;
     match (a, b) {
         (A::Two, B::One) => {},
-        $0(A::One, B::One) => todo!(),
-        (A::One, B::Two) => todo!(),
-        (A::Two, B::Two) => todo!(),
+        (A::One, B::One) => ${1:todo!()},
+        (A::One, B::Two) => ${2:todo!()},
+        (A::Two, B::Two) => ${3:todo!()},$0
     }
 }
 "#,
@@ -1013,10 +1028,10 @@ fn main() {
     let a = A::One;
     let b = B::One;
     match (a, b) {
-        $0(A::One, B::One) => todo!(),
-        (A::One, B::Two) => todo!(),
-        (A::Two, B::One) => todo!(),
-        (A::Two, B::Two) => todo!(),
+        (A::One, B::One) => ${1:todo!()},
+        (A::One, B::Two) => ${2:todo!()},
+        (A::Two, B::One) => ${3:todo!()},
+        (A::Two, B::Two) => ${4:todo!()},$0
     }
 }
 "#,
@@ -1045,10 +1060,10 @@ fn main() {
     let a = A::One;
     let b = B::One;
     match (&a, &b) {
-        $0(A::One, B::One) => todo!(),
-        (A::One, B::Two) => todo!(),
-        (A::Two, B::One) => todo!(),
-        (A::Two, B::Two) => todo!(),
+        (A::One, B::One) => ${1:todo!()},
+        (A::One, B::Two) => ${2:todo!()},
+        (A::Two, B::One) => ${3:todo!()},
+        (A::Two, B::Two) => ${4:todo!()},$0
     }
 }
 "#,
@@ -1080,9 +1095,9 @@ fn main() {
     let b = B::One;
     match (a, b) {
         (A::Two, B::One) => {}
-        $0(A::One, B::One) => todo!(),
-        (A::One, B::Two) => todo!(),
-        (A::Two, B::Two) => todo!(),
+        (A::One, B::One) => ${1:todo!()},
+        (A::One, B::Two) => ${2:todo!()},
+        (A::Two, B::Two) => ${3:todo!()},$0
     }
 }
 "#,
@@ -1107,9 +1122,9 @@ fn main() {
     match (A, B, C) {
         (A | B , A, A | B | C) => (),
         (A | B | C , B | C, A | B | C) => (),
-        $0(C, A, A) => todo!(),
-        (C, A, B) => todo!(),
-        (C, A, C) => todo!(),
+        (C, A, A) => ${1:todo!()},
+        (C, A, B) => ${2:todo!()},
+        (C, A, C) => ${3:todo!()},$0
     }
 }
 "#,
@@ -1138,7 +1153,7 @@ fn main() {
     match (a, b) {
         (Some(_), _) => {}
         (None, Some(_)) => {}
-        $0(None, None) => todo!(),
+        (None, None) => ${1:todo!()},$0
     }
 }
 "#,
@@ -1203,8 +1218,8 @@ enum A { One, Two }
 fn main() {
     let a = A::One;
     match (a, ) {
-        $0(A::One,) => todo!(),
-        (A::Two,) => todo!(),
+        (A::One,) => ${1:todo!()},
+        (A::Two,) => ${2:todo!()},$0
     }
 }
 "#,
@@ -1228,7 +1243,7 @@ enum A { As }
 
 fn foo(a: &A) {
     match a {
-        $0A::As => todo!(),
+        A::As => ${1:todo!()},$0
     }
 }
 "#,
@@ -1253,7 +1268,7 @@ enum A {
 
 fn foo(a: &mut A) {
     match a {
-        $0A::Es { x, y } => todo!(),
+        A::Es { x, y } => ${1:todo!()},$0
     }
 }
 "#,
@@ -1313,8 +1328,8 @@ enum E { X, Y }
 
 fn main() {
     match E::X {
-        $0E::X => todo!(),
-        E::Y => todo!(),
+        E::X => ${1:todo!()},
+        E::Y => ${2:todo!()},$0
     }
 }
 "#,
@@ -1360,8 +1375,8 @@ use foo::E::X;
 
 fn main() {
     match X {
-        $0X => todo!(),
-        foo::E::Y => todo!(),
+        X => ${1:todo!()},
+        foo::E::Y => ${2:todo!()},$0
     }
 }
 "#,
@@ -1388,7 +1403,7 @@ fn foo(a: A) {
     match a  {
         // foo bar baz
         A::One => {}
-        $0A::Two => todo!(),
+        A::Two => ${1:todo!()},$0
         // This is where the rest should be
     }
 }
@@ -1412,8 +1427,8 @@ fn foo(a: A) {
 enum A { One, Two }
 fn foo(a: A) {
     match a {
-        $0A::One => todo!(),
-        A::Two => todo!(),
+        A::One => ${1:todo!()},
+        A::Two => ${2:todo!()},$0
         // foo bar baz
     }
 }
@@ -1437,8 +1452,8 @@ fn foo(a: A) {
 enum A { One, Two, }
 fn foo(a: A) {
     match a {
-        $0A::One => todo!(),
-        A::Two => todo!(),
+        A::One => ${1:todo!()},
+        A::Two => ${2:todo!()},$0
     }
 }
 "#,
@@ -1460,8 +1475,8 @@ fn foo(opt: Option<i32>) {
             r#"
 fn foo(opt: Option<i32>) {
     match opt {
-        Some(${0:_}) => todo!(),
-        None => todo!(),
+        Some(${1:_}) => ${2:todo!()},
+        None => ${3:todo!()},$0
     }
 }
 "#,
@@ -1493,9 +1508,9 @@ enum Test {
 
 fn foo(t: Test) {
     m!(match t {
-    $0Test::A => todo!(),
-    Test::B => todo!(),
-    Test::C => todo!(),
+    Test::A => ${1:todo!()},
+    Test::B => ${2:todo!()},
+    Test::C => ${3:todo!()},$0
 });
 }"#,
         );
@@ -1530,7 +1545,7 @@ fn foo(t: bool) {
 fn foo(t: bool) {
     match t {
         true => 1 + 2,
-        $0false => todo!(),
+        false => ${1:todo!()},$0
     }
 }"#,
         );
@@ -1550,7 +1565,7 @@ fn foo(t: bool) {
 fn foo(t: bool) {
     match t {
         true => 1 + 2,
-        $0false => todo!(),
+        false => ${1:todo!()},$0
     }
 }"#,
         );
@@ -1571,8 +1586,8 @@ fn foo(t: bool) {
 fn foo(t: bool) {
     match t {
         _ => 1 + 2,
-        $0true => todo!(),
-        false => todo!(),
+        true => ${1:todo!()},
+        false => ${2:todo!()},$0
     }
 }"#,
         );
@@ -1595,8 +1610,8 @@ pub enum E { A, #[doc(hidden)] B, }
             r#"
 fn foo(t: ::e::E) {
     match t {
-        $0e::E::A => todo!(),
-        _ => todo!(),
+        e::E::A => ${1:todo!()},
+        _ => ${2:todo!()},$0
     }
 }
 "#,
@@ -1620,9 +1635,9 @@ pub enum E { A, #[doc(hidden)] B, }
             r#"
 fn foo(t: (bool, ::e::E)) {
     match t {
-        $0(true, e::E::A) => todo!(),
-        (false, e::E::A) => todo!(),
-        _ => todo!(),
+        (true, e::E::A) => ${1:todo!()},
+        (false, e::E::A) => ${2:todo!()},
+        _ => ${3:todo!()},$0
     }
 }
 "#,
@@ -1646,7 +1661,7 @@ pub enum E { #[doc(hidden)] A, }
             r#"
 fn foo(t: ::e::E) {
     match t {
-        ${0:_} => todo!(),
+        ${1:_} => ${2:todo!()},$0
     }
 }
 "#,
@@ -1707,7 +1722,7 @@ pub enum E { A, }
 fn foo(t: ::e::E) {
     match t {
         e::E::A => todo!(),
-        ${0:_} => todo!(),
+        ${1:_} => ${2:todo!()},$0
     }
 }
 "#,
@@ -1732,8 +1747,8 @@ pub enum E { A, }
             r#"
 fn foo(t: ::e::E) {
     match t {
-        $0e::E::A => todo!(),
-        _ => todo!(),
+        e::E::A => ${1:todo!()},
+        _ => ${2:todo!()},$0
     }
 }
 "#,
@@ -1757,8 +1772,8 @@ pub enum E { A, #[doc(hidden)] B }"#,
             r#"
 fn foo(t: ::e::E) {
     match t {
-        $0e::E::A => todo!(),
-        _ => todo!(),
+        e::E::A => ${1:todo!()},
+        _ => ${2:todo!()},$0
     }
 }
 "#,
@@ -1784,7 +1799,7 @@ pub enum E { A, #[doc(hidden)] B }"#,
 fn foo(t: ::e::E) {
     match t {
         e::E::A => todo!(),
-        ${0:_} => todo!(),
+        ${1:_} => ${2:todo!()},$0
     }
 }
 "#,
@@ -1809,7 +1824,7 @@ pub enum E { #[doc(hidden)] A, }"#,
 fn foo(t: ::e::E, b: bool) {
     match t {
         _ if b => todo!(),
-        ${0:_} => todo!(),
+        ${1:_} => ${2:todo!()},$0
     }
 }
 "#,
@@ -1850,8 +1865,8 @@ pub enum E { A, #[doc(hidden)] B, }"#,
             r#"
 fn foo(t: ::e::E) {
     match t {
-        $0e::E::A => todo!(),
-        _ => todo!(),
+        e::E::A => ${1:todo!()},
+        _ => ${2:todo!()},$0
     }
 }
 "#,
@@ -1874,8 +1889,8 @@ enum E { A, #[doc(hidden)] B, }
 
 fn foo(t: E) {
     match t {
-        $0E::A => todo!(),
-        E::B => todo!(),
+        E::A => ${1:todo!()},
+        E::B => ${2:todo!()},$0
     }
 }"#,
         );
@@ -1899,8 +1914,8 @@ enum E { A, B, }
 
 fn foo(t: E) {
     match t {
-        $0E::A => todo!(),
-        E::B => todo!(),
+        E::A => ${1:todo!()},
+        E::B => ${2:todo!()},$0
     }
 }"#,
         );
@@ -1924,8 +1939,8 @@ enum E { A, #[doc(hidden)] B, }
 
 fn foo(t: E) {
     match t {
-        $0E::A => todo!(),
-        E::B => todo!(),
+        E::A => ${1:todo!()},
+        E::B => ${2:todo!()},$0
     }
 }"#,
         );
@@ -1975,8 +1990,8 @@ enum A {
 fn a() {
     let b = A::A;
     match b {
-        $0A::A => todo!(),
-        A::Missing { a, u32, c } => todo!(),
+        A::A => ${1:todo!()},
+        A::Missing { a, u32, c } => ${2:todo!()},$0
     }
 }"#,
         )
@@ -2013,8 +2028,8 @@ pub enum E {
 fn f() {
     let value = E::A;
     match value {
-        $0E::A => todo!(),
-        E::B(s) => todo!(),
+        E::A => ${1:todo!()},
+        E::B(s) => ${2:todo!()},$0
     }
 }
 "#,
@@ -2051,8 +2066,8 @@ pub enum E {
 fn f() {
     let value = E::A;
     match value {
-        $0E::A => todo!(),
-        E::B(s1, s2) => todo!(),
+        E::A => ${1:todo!()},
+        E::B(s1, s2) => ${2:todo!()},$0
     }
 }
 "#,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
index 4608e9494bc..f83de931eab 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
@@ -34,7 +34,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, TextRange};
 // }
 // ```
 pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
-    let current_arm = ctx.find_node_at_offset::<ast::MatchArm>()?;
+    let current_arm = ctx.find_node_at_trimmed_offset::<ast::MatchArm>()?;
     // Don't try to handle arms with guards for now - can add support for this later
     if current_arm.guard().is_some() {
         return None;
@@ -42,12 +42,21 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
     let current_expr = current_arm.expr()?;
     let current_text_range = current_arm.syntax().text_range();
     let current_arm_types = get_arm_types(ctx, &current_arm);
+    let multi_arm_selection = !ctx.has_empty_selection()
+        && ctx.selection_trimmed().end() > current_arm.syntax().text_range().end();
 
     // We check if the following match arms match this one. We could, but don't,
     // compare to the previous match arm as well.
     let arms_to_merge = successors(Some(current_arm), |it| neighbor(it, Direction::Next))
         .take_while(|arm| match arm.expr() {
             Some(expr) if arm.guard().is_none() => {
+                // don't include match arms that start after our selection
+                if multi_arm_selection
+                    && arm.syntax().text_range().start() >= ctx.selection_trimmed().end()
+                {
+                    return false;
+                }
+
                 let same_text = expr.syntax().text() == current_expr.syntax().text();
                 if !same_text {
                     return false;
@@ -299,6 +308,96 @@ fn main() {
     }
 
     #[test]
+    fn merge_match_arms_selection_has_leading_whitespace() {
+        check_assist(
+            merge_match_arms,
+            r#"
+#[derive(Debug)]
+enum X { A, B, C }
+
+fn main() {
+    match X::A {
+    $0    X::A => 0,
+        X::B => 0,$0
+        X::C => 1,
+    }
+}
+"#,
+            r#"
+#[derive(Debug)]
+enum X { A, B, C }
+
+fn main() {
+    match X::A {
+        X::A | X::B => 0,
+        X::C => 1,
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn merge_match_arms_stops_at_end_of_selection() {
+        check_assist(
+            merge_match_arms,
+            r#"
+#[derive(Debug)]
+enum X { A, B, C }
+
+fn main() {
+    match X::A {
+    $0    X::A => 0,
+        X::B => 0,
+        $0X::C => 0,
+    }
+}
+"#,
+            r#"
+#[derive(Debug)]
+enum X { A, B, C }
+
+fn main() {
+    match X::A {
+        X::A | X::B => 0,
+        X::C => 0,
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn merge_match_arms_works_despite_accidental_selection() {
+        check_assist(
+            merge_match_arms,
+            r#"
+#[derive(Debug)]
+enum X { A, B, C }
+
+fn main() {
+    match X::A {
+        X::$0A$0 => 0,
+        X::B => 0,
+        X::C => 1,
+    }
+}
+"#,
+            r#"
+#[derive(Debug)]
+enum X { A, B, C }
+
+fn main() {
+    match X::A {
+        X::A | X::B => 0,
+        X::C => 1,
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
     fn merge_match_arms_rejects_guards() {
         check_assist_not_applicable(
             merge_match_arms,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs
index ada89ce7c40..d7fa8826125 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs
@@ -3,7 +3,7 @@ use ide_db::{FxHashMap, RootDatabase};
 use itertools::Itertools;
 use syntax::{
     ast::{self, HasName},
-    ted, AstNode,
+    AstNode, SyntaxElement,
 };
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -46,6 +46,11 @@ pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) ->
     let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?;
     let items = impl_ast.assoc_item_list()?;
 
+    let parent_node = match ctx.covering_element() {
+        SyntaxElement::Node(n) => n,
+        SyntaxElement::Token(t) => t.parent()?,
+    };
+
     // restrict the range
     // if cursor is in assoc_items, abort
     let assoc_range = items.syntax().text_range();
@@ -94,12 +99,14 @@ pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) ->
         "Sort items by trait definition",
         target,
         |builder| {
-            let assoc_items =
-                assoc_items.into_iter().map(|item| builder.make_mut(item)).collect::<Vec<_>>();
-            assoc_items
-                .into_iter()
-                .zip(sorted)
-                .for_each(|(old, new)| ted::replace(old.syntax(), new.clone_for_update().syntax()));
+            let mut editor = builder.make_editor(&parent_node);
+
+            assoc_items.into_iter().zip(sorted).for_each(|(old, new)| {
+                // FIXME: remove `clone_for_update` when `SyntaxEditor` handles it for us
+                editor.replace(old.syntax(), new.clone_for_update().syntax())
+            });
+
+            builder.add_file_edits(ctx.file_id(), editor);
         },
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index 64b7ab1a123..eef4da55e94 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -187,8 +187,8 @@ enum Action { Move { distance: u32 }, Stop }
 
 fn handle(action: Action) {
     match action {
-        $0Action::Move { distance } => todo!(),
-        Action::Stop => todo!(),
+        Action::Move { distance } => ${1:todo!()},
+        Action::Stop => ${2:todo!()},$0
     }
 }
 "#####,
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index 2dc6f0357e3..cb5738a3b40 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
@@ -13,7 +13,7 @@ use serde_json::from_value;
 use span::Edition;
 use toolchain::Tool;
 
-use crate::{utf8_stdout, ManifestPath, Sysroot};
+use crate::{utf8_stdout, ManifestPath, Sysroot, SysrootQueryMetadata};
 use crate::{CfgOverrides, InvocationStrategy};
 
 /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
@@ -85,6 +85,8 @@ pub struct CargoConfig {
     pub target: Option<String>,
     /// Sysroot loading behavior
     pub sysroot: Option<RustLibSource>,
+    /// How to query metadata for the sysroot crate.
+    pub sysroot_query_metadata: SysrootQueryMetadata,
     pub sysroot_src: Option<AbsPathBuf>,
     /// rustc private crate source
     pub rustc_source: Option<RustLibSource>,
diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
index 91bdef4889c..da8afc5d3a1 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
@@ -240,3 +240,10 @@ fn parse_cfg(s: &str) -> Result<cfg::CfgAtom, String> {
     };
     Ok(res)
 }
+
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+pub enum SysrootQueryMetadata {
+    #[default]
+    CargoMetadata,
+    None,
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index 19f4c35b5ad..8426e689a64 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -14,7 +14,7 @@ use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
 use rustc_hash::FxHashMap;
 use toolchain::{probe_for_binary, Tool};
 
-use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath};
+use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, SysrootQueryMetadata};
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Sysroot {
@@ -123,27 +123,43 @@ impl Sysroot {
 // FIXME: Expose a builder api as loading the sysroot got way too modular and complicated.
 impl Sysroot {
     /// Attempts to discover the toolchain's sysroot from the given `dir`.
-    pub fn discover(dir: &AbsPath, extra_env: &FxHashMap<String, String>) -> Sysroot {
+    pub fn discover(
+        dir: &AbsPath,
+        extra_env: &FxHashMap<String, String>,
+        sysroot_query_metadata: SysrootQueryMetadata,
+    ) -> Sysroot {
         let sysroot_dir = discover_sysroot_dir(dir, extra_env);
         let sysroot_src_dir = sysroot_dir.as_ref().ok().map(|sysroot_dir| {
             discover_sysroot_src_dir_or_add_component(sysroot_dir, dir, extra_env)
         });
-        Sysroot::load_core_check(Some(sysroot_dir), sysroot_src_dir)
+        Sysroot::load_core_check(Some(sysroot_dir), sysroot_src_dir, sysroot_query_metadata)
     }
 
     pub fn discover_with_src_override(
         current_dir: &AbsPath,
         extra_env: &FxHashMap<String, String>,
         sysroot_src_dir: AbsPathBuf,
+        sysroot_query_metadata: SysrootQueryMetadata,
     ) -> Sysroot {
         let sysroot_dir = discover_sysroot_dir(current_dir, extra_env);
-        Sysroot::load_core_check(Some(sysroot_dir), Some(Ok(sysroot_src_dir)))
+        Sysroot::load_core_check(
+            Some(sysroot_dir),
+            Some(Ok(sysroot_src_dir)),
+            sysroot_query_metadata,
+        )
     }
 
-    pub fn discover_sysroot_src_dir(sysroot_dir: AbsPathBuf) -> Sysroot {
+    pub fn discover_sysroot_src_dir(
+        sysroot_dir: AbsPathBuf,
+        sysroot_query_metadata: SysrootQueryMetadata,
+    ) -> Sysroot {
         let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir)
             .ok_or_else(|| format_err!("can't find standard library sources in {sysroot_dir}"));
-        Sysroot::load_core_check(Some(Ok(sysroot_dir)), Some(sysroot_src_dir))
+        Sysroot::load_core_check(
+            Some(Ok(sysroot_dir)),
+            Some(sysroot_src_dir),
+            sysroot_query_metadata,
+        )
     }
 
     pub fn discover_rustc_src(&self) -> Option<ManifestPath> {
@@ -186,15 +202,20 @@ impl Sysroot {
             })
     }
 
-    pub fn load(sysroot_dir: Option<AbsPathBuf>, sysroot_src_dir: Option<AbsPathBuf>) -> Sysroot {
-        Self::load_core_check(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok))
+    pub fn load(
+        sysroot_dir: Option<AbsPathBuf>,
+        sysroot_src_dir: Option<AbsPathBuf>,
+        sysroot_query_metadata: SysrootQueryMetadata,
+    ) -> Sysroot {
+        Self::load_core_check(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok), sysroot_query_metadata)
     }
 
     fn load_core_check(
         sysroot_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
         sysroot_src_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
+        sysroot_query_metadata: SysrootQueryMetadata,
     ) -> Sysroot {
-        let mut sysroot = Self::load_(sysroot_dir, sysroot_src_dir);
+        let mut sysroot = Self::load_(sysroot_dir, sysroot_src_dir, sysroot_query_metadata);
         if sysroot.error.is_none() {
             if let Some(src_root) = &sysroot.src_root {
                 let has_core = match &sysroot.mode {
@@ -220,6 +241,7 @@ impl Sysroot {
     fn load_(
         sysroot_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
         sysroot_src_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
+        sysroot_query_metadata: SysrootQueryMetadata,
     ) -> Sysroot {
         let sysroot_dir = match sysroot_dir {
             Some(Ok(sysroot_dir)) => Some(sysroot_dir),
@@ -252,12 +274,15 @@ impl Sysroot {
                 }
             }
         };
-        let library_manifest = ManifestPath::try_from(sysroot_src_dir.join("Cargo.toml")).unwrap();
-        if fs::metadata(&library_manifest).is_ok() {
-            if let Some(sysroot) =
-                Self::load_library_via_cargo(library_manifest, &sysroot_dir, &sysroot_src_dir)
-            {
-                return sysroot;
+        if sysroot_query_metadata == SysrootQueryMetadata::CargoMetadata {
+            let library_manifest =
+                ManifestPath::try_from(sysroot_src_dir.join("Cargo.toml")).unwrap();
+            if fs::metadata(&library_manifest).is_ok() {
+                if let Some(sysroot) =
+                    Self::load_library_via_cargo(library_manifest, &sysroot_dir, &sysroot_src_dir)
+                {
+                    return sysroot;
+                }
             }
         }
         tracing::debug!("Stitching sysroot library: {sysroot_src_dir}");
@@ -444,13 +469,14 @@ fn discover_sysroot_src_dir_or_add_component(
             get_rust_src(sysroot_path)
         })
         .ok_or_else(|| {
-            let error = "\
+            tracing::error!(%sysroot_path, "can't load standard library, try installing `rust-src`");
+            format_err!(
+                "\
 can't load standard library from sysroot
 {sysroot_path}
 (discovered via `rustc --print sysroot`)
-try installing the Rust source the same way you installed rustc";
-            tracing::error!(error);
-            format_err!(error)
+try installing `rust-src` the same way you installed `rustc`"
+            )
         })
 }
 
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index ef115494a88..f3cf2d83eac 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -13,7 +13,8 @@ use triomphe::Arc;
 
 use crate::{
     sysroot::SysrootMode, workspace::ProjectWorkspaceKind, CargoWorkspace, CfgOverrides,
-    ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot, WorkspaceBuildScripts,
+    ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot, SysrootQueryMetadata,
+    WorkspaceBuildScripts,
 };
 
 fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) {
@@ -116,7 +117,7 @@ fn get_fake_sysroot() -> Sysroot {
     // fake sysroot, so we give them both the same path:
     let sysroot_dir = AbsPathBuf::assert(sysroot_path);
     let sysroot_src_dir = sysroot_dir.clone();
-    Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir))
+    Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir), SysrootQueryMetadata::CargoMetadata)
 }
 
 fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
@@ -231,6 +232,7 @@ fn smoke_test_real_sysroot_cargo() {
     let sysroot = Sysroot::discover(
         AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))),
         &Default::default(),
+        SysrootQueryMetadata::CargoMetadata,
     );
     assert!(matches!(sysroot.mode(), SysrootMode::Workspace(_)));
     let project_workspace = ProjectWorkspace {
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index d53639e2423..f5ba71fcd05 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -211,34 +211,35 @@ impl ProjectWorkspace {
                 let project_location = project_json.parent().to_path_buf();
                 let project_json: ProjectJson =
                     ProjectJson::new(Some(project_json.clone()), &project_location, data);
-                ProjectWorkspace::load_inline(
-                    project_json,
-                    config.target.as_deref(),
-                    &config.extra_env,
-                    &config.cfg_overrides,
-                )
+                ProjectWorkspace::load_inline(project_json, config)
             }
             ProjectManifest::CargoScript(rust_file) => {
                 ProjectWorkspace::load_detached_file(rust_file, config)?
             }
             ProjectManifest::CargoToml(cargo_toml) => {
                 let sysroot = match (&config.sysroot, &config.sysroot_src) {
-                    (Some(RustLibSource::Discover), None) => {
-                        Sysroot::discover(cargo_toml.parent(), &config.extra_env)
-                    }
+                    (Some(RustLibSource::Discover), None) => Sysroot::discover(
+                        cargo_toml.parent(),
+                        &config.extra_env,
+                        config.sysroot_query_metadata,
+                    ),
                     (Some(RustLibSource::Discover), Some(sysroot_src)) => {
                         Sysroot::discover_with_src_override(
                             cargo_toml.parent(),
                             &config.extra_env,
                             sysroot_src.clone(),
+                            config.sysroot_query_metadata,
                         )
                     }
-                    (Some(RustLibSource::Path(path)), None) => {
-                        Sysroot::discover_sysroot_src_dir(path.clone())
-                    }
-                    (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => {
-                        Sysroot::load(Some(sysroot.clone()), Some(sysroot_src.clone()))
-                    }
+                    (Some(RustLibSource::Path(path)), None) => Sysroot::discover_sysroot_src_dir(
+                        path.clone(),
+                        config.sysroot_query_metadata,
+                    ),
+                    (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => Sysroot::load(
+                        Some(sysroot.clone()),
+                        Some(sysroot_src.clone()),
+                        config.sysroot_query_metadata,
+                    ),
                     (None, _) => Sysroot::empty(),
                 };
                 tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = ?sysroot.root(), "Using sysroot");
@@ -352,20 +353,19 @@ impl ProjectWorkspace {
         Ok(res)
     }
 
-    pub fn load_inline(
-        project_json: ProjectJson,
-        target: Option<&str>,
-        extra_env: &FxHashMap<String, String>,
-        cfg_overrides: &CfgOverrides,
-    ) -> ProjectWorkspace {
-        let sysroot = Sysroot::load(project_json.sysroot.clone(), project_json.sysroot_src.clone());
+    pub fn load_inline(project_json: ProjectJson, config: &CargoConfig) -> ProjectWorkspace {
+        let sysroot = Sysroot::load(
+            project_json.sysroot.clone(),
+            project_json.sysroot_src.clone(),
+            config.sysroot_query_metadata,
+        );
         let cfg_config = RustcCfgConfig::Rustc(&sysroot);
         let data_layout_config = RustcDataLayoutConfig::Rustc(&sysroot);
         let toolchain = match get_toolchain_version(
             project_json.path(),
             &sysroot,
             Tool::Rustc,
-            extra_env,
+            &config.extra_env,
             "rustc ",
         ) {
             Ok(it) => it,
@@ -375,15 +375,16 @@ impl ProjectWorkspace {
             }
         };
 
-        let rustc_cfg = rustc_cfg::get(target, extra_env, cfg_config);
-        let data_layout = target_data_layout::get(data_layout_config, target, extra_env);
+        let target = config.target.as_deref();
+        let rustc_cfg = rustc_cfg::get(target, &config.extra_env, cfg_config);
+        let data_layout = target_data_layout::get(data_layout_config, target, &config.extra_env);
         ProjectWorkspace {
             kind: ProjectWorkspaceKind::Json(project_json),
             sysroot,
             rustc_cfg,
             toolchain,
             target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
-            cfg_overrides: cfg_overrides.clone(),
+            cfg_overrides: config.cfg_overrides.clone(),
         }
     }
 
@@ -393,8 +394,12 @@ impl ProjectWorkspace {
     ) -> anyhow::Result<ProjectWorkspace> {
         let dir = detached_file.parent();
         let sysroot = match &config.sysroot {
-            Some(RustLibSource::Path(path)) => Sysroot::discover_sysroot_src_dir(path.clone()),
-            Some(RustLibSource::Discover) => Sysroot::discover(dir, &config.extra_env),
+            Some(RustLibSource::Path(path)) => {
+                Sysroot::discover_sysroot_src_dir(path.clone(), config.sysroot_query_metadata)
+            }
+            Some(RustLibSource::Discover) => {
+                Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata)
+            }
             None => Sysroot::empty(),
         };
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index 155fb6f7c88..802d0c69a47 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -65,6 +65,10 @@ impl flags::AnalysisStats {
                 true => None,
                 false => Some(RustLibSource::Discover),
             },
+            sysroot_query_metadata: match self.no_query_sysroot_metadata {
+                true => project_model::SysrootQueryMetadata::None,
+                false => project_model::SysrootQueryMetadata::CargoMetadata,
+            },
             all_targets: true,
             set_test: !self.no_test,
             cfg_overrides: CfgOverrides {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
index ff24602144a..920a2a37efb 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
@@ -71,6 +71,9 @@ xflags::xflags! {
             optional --with-deps
             /// Don't load sysroot crates (`std`, `core` & friends).
             optional --no-sysroot
+            /// Don't run cargo metadata on the sysroot to analyze its third-party dependencies.
+            /// Requires --no-sysroot to not be set.
+            optional --no-query-sysroot-metadata
             /// Don't set #[cfg(test)].
             optional --no-test
 
@@ -235,6 +238,7 @@ pub struct AnalysisStats {
     pub only: Option<String>,
     pub with_deps: bool,
     pub no_sysroot: bool,
+    pub no_query_sysroot_metadata: bool,
     pub no_test: bool,
     pub disable_build_scripts: bool,
     pub disable_proc_macros: bool,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
index 30378db0b38..6483afc85b2 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -13,7 +13,7 @@ use profile::StopWatch;
 use project_model::target_data_layout::RustcDataLayoutConfig;
 use project_model::{
     target_data_layout, CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind,
-    RustLibSource, Sysroot,
+    RustLibSource, Sysroot, SysrootQueryMetadata,
 };
 
 use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice};
@@ -74,7 +74,11 @@ impl Tester {
             ..Default::default()
         };
 
-        let sysroot = Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env);
+        let sysroot = Sysroot::discover(
+            tmp_file.parent().unwrap(),
+            &cargo_config.extra_env,
+            SysrootQueryMetadata::CargoMetadata,
+        );
         let data_layout = target_data_layout::get(
             RustcDataLayoutConfig::Rustc(&sysroot),
             None,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index f5b0fcecf39..37d45255e29 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -558,6 +558,9 @@ config_data! {
         ///
         /// This option does not take effect until rust-analyzer is restarted.
         cargo_sysroot: Option<String>    = Some("discover".to_owned()),
+        /// How to query metadata for the sysroot crate. Using cargo metadata allows rust-analyzer
+        /// to analyze third-party dependencies of the standard libraries.
+        cargo_sysrootQueryMetadata: SysrootQueryMetadata = SysrootQueryMetadata::CargoMetadata,
         /// Relative path to the sysroot library sources. If left unset, this will default to
         /// `{cargo.sysroot}/lib/rustlib/src/rust/library`.
         ///
@@ -1868,6 +1871,12 @@ impl Config {
             },
             target: self.cargo_target(source_root).clone(),
             sysroot,
+            sysroot_query_metadata: match self.cargo_sysrootQueryMetadata(None) {
+                SysrootQueryMetadata::CargoMetadata => {
+                    project_model::SysrootQueryMetadata::CargoMetadata
+                }
+                SysrootQueryMetadata::None => project_model::SysrootQueryMetadata::None,
+            },
             sysroot_src,
             rustc_source,
             cfg_overrides: project_model::CfgOverrides {
@@ -2559,6 +2568,13 @@ pub enum NumThreads {
     Concrete(usize),
 }
 
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
+#[serde(rename_all = "snake_case")]
+pub enum SysrootQueryMetadata {
+    CargoMetadata,
+    None,
+}
+
 macro_rules! _default_val {
     (@verbatim: $s:literal, $ty:ty) => {{
         let default_: $ty = serde_json::from_str(&$s).unwrap();
@@ -3410,6 +3426,14 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
                 }
             ]
         },
+        "SysrootQueryMetadata" => set! {
+            "type": "string",
+            "enum": ["none", "cargo_metadata"],
+            "enumDescriptions": [
+                "Do not query sysroot metadata, always use stitched sysroot.",
+                "Use `cargo metadata` to query sysroot metadata."
+            ],
+        },
         _ => panic!("missing entry for {ty}: {default} (field {field})"),
     }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index 0b24833358d..bc85afa0e49 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -316,9 +316,7 @@ impl GlobalState {
                         LinkedProject::InlineJsonProject(it) => {
                             let workspace = project_model::ProjectWorkspace::load_inline(
                                 it.clone(),
-                                cargo_config.target.as_deref(),
-                                &cargo_config.extra_env,
-                                &cargo_config.cfg_overrides,
+                                &cargo_config,
                             );
                             Ok(workspace)
                         }
diff --git a/src/tools/rust-analyzer/docs/dev/architecture.md b/src/tools/rust-analyzer/docs/dev/architecture.md
index 6aa57b2f9be..9c9e05a429b 100644
--- a/src/tools/rust-analyzer/docs/dev/architecture.md
+++ b/src/tools/rust-analyzer/docs/dev/architecture.md
@@ -42,7 +42,7 @@ The underlying engine makes sure that model is computed lazily (on-demand) and c
 `crates/rust-analyzer/src/bin/main.rs` contains the main function which spawns LSP.
 This is *the* entry point, but it front-loads a lot of complexity, so it's fine to just skim through it.
 
-`crates/rust-analyzer/src/handlers/requests.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP.
+`crates/rust-analyzer/src/handlers/request.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP.
 
 `Analysis` and `AnalysisHost` types define the main API for consumers of IDE services.
 
diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc
index 463718835b9..052d0a2a41d 100644
--- a/src/tools/rust-analyzer/docs/user/generated_config.adoc
+++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc
@@ -135,6 +135,12 @@ Unsetting this disables sysroot loading.
 
 This option does not take effect until rust-analyzer is restarted.
 --
+[[rust-analyzer.cargo.sysrootQueryMetadata]]rust-analyzer.cargo.sysrootQueryMetadata (default: `"cargo_metadata"`)::
++
+--
+How to query metadata for the sysroot crate. Using cargo metadata allows rust-analyzer
+to analyze third-party dependencies of the standard libraries.
+--
 [[rust-analyzer.cargo.sysrootSrc]]rust-analyzer.cargo.sysrootSrc (default: `null`)::
 +
 --
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index b868622b3a5..82c43b76fdd 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -819,6 +819,24 @@
             {
                 "title": "cargo",
                 "properties": {
+                    "rust-analyzer.cargo.sysrootQueryMetadata": {
+                        "markdownDescription": "How to query metadata for the sysroot crate. Using cargo metadata allows rust-analyzer\nto analyze third-party dependencies of the standard libraries.",
+                        "default": "cargo_metadata",
+                        "type": "string",
+                        "enum": [
+                            "none",
+                            "cargo_metadata"
+                        ],
+                        "enumDescriptions": [
+                            "Do not query sysroot metadata, always use stitched sysroot.",
+                            "Use `cargo metadata` to query sysroot metadata."
+                        ]
+                    }
+                }
+            },
+            {
+                "title": "cargo",
+                "properties": {
                     "rust-analyzer.cargo.sysrootSrc": {
                         "markdownDescription": "Relative path to the sysroot library sources. If left unset, this will default to\n`{cargo.sysroot}/lib/rustlib/src/rust/library`.\n\nThis option does not take effect until rust-analyzer is restarted.",
                         "default": null,
diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index 00f4f743a7c..8f41ed9e14f 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-145f9cf95de1fbde3fa11e98461310e0373253e6
+f005c7437def424a1c43cbc380352a58d8ac920b