Repository: rust-lang/rust-analyzer
Pull request #17394 (2024-06-11 15:04:11)

fix: Fix `HirDisplay` stackoverflow for parameter Self defaults
Fixes https://github.com/rust-lang/rust-analyzer/issues/10932


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 57d43dad3f..efb313cd3c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -512,7 +512,6 @@ dependencies = [
  "hir-def",
  "hir-expand",
  "hir-ty",
- "intern",
  "itertools",
  "once_cell",
  "rustc-hash",
diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs
index c48d16d053..cbb5ca887f 100644
--- a/crates/hir-def/src/body/pretty.rs
+++ b/crates/hir-def/src/body/pretty.rs
@@ -48,30 +48,21 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
     let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false };
     if let DefWithBodyId::FunctionId(it) = owner {
         p.buf.push('(');
-        let function_data = &db.function_data(it);
-        let (mut params, ret_type) = (function_data.params.iter(), &function_data.ret_type);
+        let params = &db.function_data(it).params;
+        let mut params = params.iter();
         if let Some(self_param) = body.self_param {
             p.print_binding(self_param);
-            p.buf.push_str(": ");
+            p.buf.push(':');
             if let Some(ty) = params.next() {
                 p.print_type_ref(ty);
-                p.buf.push_str(", ");
             }
         }
         body.params.iter().zip(params).for_each(|(&param, ty)| {
             p.print_pat(param);
-            p.buf.push_str(": ");
+            p.buf.push(':');
             p.print_type_ref(ty);
-            p.buf.push_str(", ");
         });
-        // remove the last ", " in param list
-        if body.params.len() > 0 {
-            p.buf.truncate(p.buf.len() - 2);
-        }
         p.buf.push(')');
-        // return type
-        p.buf.push_str(" -> ");
-        p.print_type_ref(ret_type);
         p.buf.push(' ');
     }
     p.print_expr(body.body_expr);
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 5e3996f65a..7f01f6072c 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -4,7 +4,7 @@
 
 use std::{
     fmt::{self, Debug},
-    mem::size_of,
+    mem::{self, size_of},
 };
 
 use base_db::CrateId;
@@ -74,8 +74,6 @@ pub struct HirFormatter<'a> {
     /// When rendering something that has a concept of "children" (like fields in a struct), this limits
     /// how many should be rendered.
     pub entity_limit: Option<usize>,
-    /// When rendering functions, whether to show the constraint from the container
-    show_container_bounds: bool,
     omit_verbose_types: bool,
     closure_style: ClosureStyle,
     display_target: DisplayTarget,
@@ -103,7 +101,6 @@ pub trait HirDisplay {
         omit_verbose_types: bool,
         display_target: DisplayTarget,
         closure_style: ClosureStyle,
-        show_container_bounds: bool,
     ) -> HirDisplayWrapper<'a, Self>
     where
         Self: Sized,
@@ -120,7 +117,6 @@ pub trait HirDisplay {
             omit_verbose_types,
             display_target,
             closure_style,
-            show_container_bounds,
         }
     }
 
@@ -138,7 +134,6 @@ pub trait HirDisplay {
             omit_verbose_types: false,
             closure_style: ClosureStyle::ImplFn,
             display_target: DisplayTarget::Diagnostics,
-            show_container_bounds: false,
         }
     }
 
@@ -160,7 +155,6 @@ pub trait HirDisplay {
             omit_verbose_types: true,
             closure_style: ClosureStyle::ImplFn,
             display_target: DisplayTarget::Diagnostics,
-            show_container_bounds: false,
         }
     }
 
@@ -182,7 +176,6 @@ pub trait HirDisplay {
             omit_verbose_types: true,
             closure_style: ClosureStyle::ImplFn,
             display_target: DisplayTarget::Diagnostics,
-            show_container_bounds: false,
         }
     }
 
@@ -205,7 +198,6 @@ pub trait HirDisplay {
             omit_verbose_types: false,
             closure_style: ClosureStyle::ImplFn,
             display_target: DisplayTarget::SourceCode { module_id, allow_opaque },
-            show_container_bounds: false,
         }) {
             Ok(()) => {}
             Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
@@ -227,29 +219,6 @@ pub trait HirDisplay {
             omit_verbose_types: false,
             closure_style: ClosureStyle::ImplFn,
             display_target: DisplayTarget::Test,
-            show_container_bounds: false,
-        }
-    }
-
-    /// Returns a String representation of `self` that shows the constraint from
-    /// the container for functions
-    fn display_with_container_bounds<'a>(
-        &'a self,
-        db: &'a dyn HirDatabase,
-        show_container_bounds: bool,
-    ) -> HirDisplayWrapper<'a, Self>
-    where
-        Self: Sized,
-    {
-        HirDisplayWrapper {
-            db,
-            t: self,
-            max_size: None,
-            limited_size: None,
-            omit_verbose_types: false,
-            closure_style: ClosureStyle::ImplFn,
-            display_target: DisplayTarget::Diagnostics,
-            show_container_bounds,
         }
     }
 }
@@ -308,10 +277,6 @@ impl HirFormatter<'_> {
     pub fn omit_verbose_types(&self) -> bool {
         self.omit_verbose_types
     }
-
-    pub fn show_container_bounds(&self) -> bool {
-        self.show_container_bounds
-    }
 }
 
 #[derive(Clone, Copy)]
@@ -371,7 +336,6 @@ pub struct HirDisplayWrapper<'a, T> {
     omit_verbose_types: bool,
     closure_style: ClosureStyle,
     display_target: DisplayTarget,
-    show_container_bounds: bool,
 }
 
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
@@ -401,7 +365,6 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
             omit_verbose_types: self.omit_verbose_types,
             display_target: self.display_target,
             closure_style: self.closure_style,
-            show_container_bounds: self.show_container_bounds,
         })
     }
 
@@ -460,7 +423,7 @@ impl HirDisplay for ProjectionTy {
         let proj_params_count =
             self.substitution.len(Interner) - trait_ref.substitution.len(Interner);
         let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count];
-        hir_fmt_generics(f, proj_params, None)
+        hir_fmt_generics(f, proj_params, None, None)
     }
 }
 
@@ -505,6 +468,7 @@ impl HirDisplay for Const {
                         f,
                         parameters.as_slice(Interner),
                         c.generic_def(f.db.upcast()),
+                        None,
                     )?;
                     Ok(())
                 }
@@ -1004,11 +968,11 @@ impl HirDisplay for Ty {
                         let fn_params = generic_args_sans_defaults(f, Some(def.into()), fn_params);
 
                         write!(f, "<")?;
-                        hir_fmt_generic_arguments(f, parent_params)?;
+                        hir_fmt_generic_arguments(f, parent_params, None)?;
                         if !parent_params.is_empty() && !fn_params.is_empty() {
                             write!(f, ", ")?;
                         }
-                        hir_fmt_generic_arguments(f, fn_params)?;
+                        hir_fmt_generic_arguments(f, fn_params, None)?;
                         write!(f, ">")?;
                     }
                 }
@@ -1053,7 +1017,7 @@ impl HirDisplay for Ty {
 
                 let generic_def = self.as_generic_def(db);
 
-                hir_fmt_generics(f, parameters.as_slice(Interner), generic_def)?;
+                hir_fmt_generics(f, parameters.as_slice(Interner), generic_def, None)?;
             }
             TyKind::AssociatedType(assoc_type_id, parameters) => {
                 let type_alias = from_assoc_type_id(*assoc_type_id);
@@ -1076,7 +1040,7 @@ impl HirDisplay for Ty {
                     f.end_location_link();
                     // Note that the generic args for the associated type come before those for the
                     // trait (including the self type).
-                    hir_fmt_generics(f, parameters.as_slice(Interner), None)
+                    hir_fmt_generics(f, parameters.as_slice(Interner), None, None)
                 } else {
                     let projection_ty = ProjectionTy {
                         associated_ty_id: to_assoc_type_id(type_alias),
@@ -1178,7 +1142,7 @@ impl HirDisplay for Ty {
                     }
                     ClosureStyle::ClosureWithSubst => {
                         write!(f, "{{closure#{:?}}}", id.0.as_u32())?;
-                        return hir_fmt_generics(f, substs.as_slice(Interner), None);
+                        return hir_fmt_generics(f, substs.as_slice(Interner), None, None);
                     }
                     _ => (),
                 }
@@ -1366,6 +1330,7 @@ fn hir_fmt_generics(
     f: &mut HirFormatter<'_>,
     parameters: &[GenericArg],
     generic_def: Option<hir_def::GenericDefId>,
+    self_: Option<&Ty>,
 ) -> Result<(), HirDisplayError> {
     if parameters.is_empty() {
         return Ok(());
@@ -1385,7 +1350,7 @@ fn hir_fmt_generics(
         });
     if !parameters_to_write.is_empty() && !only_err_lifetimes {
         write!(f, "<")?;
-        hir_fmt_generic_arguments(f, parameters_to_write)?;
+        hir_fmt_generic_arguments(f, parameters_to_write, self_)?;
         write!(f, ">")?;
     }
 
@@ -1447,6 +1412,7 @@ fn generic_args_sans_defaults<'ga>(
 fn hir_fmt_generic_arguments(
     f: &mut HirFormatter<'_>,
     parameters: &[GenericArg],
+    self_: Option<&Ty>,
 ) -> Result<(), HirDisplayError> {
     let mut first = true;
     let lifetime_offset = parameters.iter().position(|arg| arg.lifetime(Interner).is_some());
@@ -1468,11 +1434,13 @@ fn hir_fmt_generic_arguments(
             continue;
         }
 
-        if !first {
+        if !mem::take(&mut first) {
             write!(f, ", ")?;
         }
-        first = false;
-        generic_arg.hir_fmt(f)?;
+        match self_ {
+            self_ @ Some(_) if generic_arg.ty(Interner) == self_ => write!(f, "Self")?,
+            _ => generic_arg.hir_fmt(f)?,
+        }
     }
     Ok(())
 }
@@ -1595,12 +1563,16 @@ fn write_bounds_like_dyn_trait(
                 write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
                 f.end_location_link();
                 if is_fn_trait {
-                    if let [_self, params @ ..] = trait_ref.substitution.as_slice(Interner) {
+                    if let [self_, params @ ..] = trait_ref.substitution.as_slice(Interner) {
                         if let Some(args) =
                             params.first().and_then(|it| it.assert_ty_ref(Interner).as_tuple())
                         {
                             write!(f, "(")?;
-                            hir_fmt_generic_arguments(f, args.as_slice(Interner))?;
+                            hir_fmt_generic_arguments(
+                                f,
+                                args.as_slice(Interner),
+                                self_.ty(Interner),
+                            )?;
                             write!(f, ")")?;
                         }
                     }
@@ -1610,10 +1582,10 @@ fn write_bounds_like_dyn_trait(
                         Some(trait_.into()),
                         trait_ref.substitution.as_slice(Interner),
                     );
-                    if let [_self, params @ ..] = params {
+                    if let [self_, params @ ..] = params {
                         if !params.is_empty() {
                             write!(f, "<")?;
-                            hir_fmt_generic_arguments(f, params)?;
+                            hir_fmt_generic_arguments(f, params, self_.ty(Interner))?;
                             // there might be assoc type bindings, so we leave the angle brackets open
                             angle_open = true;
                         }
@@ -1671,6 +1643,7 @@ fn write_bounds_like_dyn_trait(
                         hir_fmt_generic_arguments(
                             f,
                             &proj.substitution.as_slice(Interner)[..proj_arg_count],
+                            None,
                         )?;
                         write!(f, ">")?;
                     }
@@ -1727,7 +1700,8 @@ fn fmt_trait_ref(
     f.start_location_link(trait_.into());
     write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
     f.end_location_link();
-    hir_fmt_generics(f, &tr.substitution.as_slice(Interner)[1..], None)
+    let substs = tr.substitution.as_slice(Interner);
+    hir_fmt_generics(f, &substs[1..], None, substs[0].ty(Interner))
 }
 
 impl HirDisplay for TraitRef {
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml
index edf26a07a7..6d7ecd1e50 100644
--- a/crates/hir/Cargo.toml
+++ b/crates/hir/Cargo.toml
@@ -27,7 +27,6 @@ cfg.workspace = true
 hir-def.workspace = true
 hir-expand.workspace = true
 hir-ty.workspace = true
-intern.workspace = true
 stdx.workspace = true
 syntax.workspace = true
 tt.workspace = true
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 79069ed66b..c276e87786 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -3,8 +3,7 @@ use either::Either;
 use hir_def::{
     data::adt::{StructKind, VariantData},
     generics::{
-        GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
-        WherePredicateTypeTarget,
+        TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
     },
     lang_item::LangItem,
     type_ref::{TypeBound, TypeRef},
@@ -17,12 +16,10 @@ use hir_ty::{
     },
     AliasEq, AliasTy, Interner, ProjectionTyExt, TraitRefExt, TyKind, WhereClause,
 };
-use intern::Interned;
-use itertools::Itertools;
 
 use crate::{
     Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl,
-    Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, Macro, Module,
+    Field, Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module,
     SelfParam, Static, Struct, Trait, TraitAlias, TupleField, TyBuilder, Type, TypeAlias,
     TypeOrConstParam, TypeParam, Union, Variant,
 };
@@ -33,42 +30,12 @@ impl HirDisplay for Function {
         let data = db.function_data(self.id);
         let container = self.as_assoc_item(db).map(|it| it.container(db));
         let mut module = self.module(db);
-
-        // Write container (trait or impl)
-        let container_params = match container {
-            Some(AssocItemContainer::Trait(trait_)) => {
-                let params = f.db.generic_params(trait_.id.into());
-                if f.show_container_bounds() && !params.is_empty() {
-                    write_trait_header(&trait_, f)?;
-                    f.write_char('\n')?;
-                    has_disaplayable_predicates(&params).then_some(params)
-                } else {
-                    None
-                }
-            }
-            Some(AssocItemContainer::Impl(impl_)) => {
-                let params = f.db.generic_params(impl_.id.into());
-                if f.show_container_bounds() && !params.is_empty() {
-                    write_impl_header(&impl_, f)?;
-                    f.write_char('\n')?;
-                    has_disaplayable_predicates(&params).then_some(params)
-                } else {
-                    None
-                }
-            }
-            None => None,
-        };
-
-        // Write signature of the function
-
-        // Block-local impls are "hoisted" to the nearest (non-block) module.
         if let Some(AssocItemContainer::Impl(_)) = container {
+            // Block-local impls are "hoisted" to the nearest (non-block) module.
             module = module.nearest_non_block_module(db);
         }
         let module_id = module.id;
-
         write_visibility(module_id, self.visibility(db), f)?;
-
         if data.has_default_kw() {
             f.write_str("default ")?;
         }
@@ -149,39 +116,10 @@ impl HirDisplay for Function {
             }
         }
 
-        // Write where clauses
-        let has_written_where = write_where_clause(GenericDefId::FunctionId(self.id), f)?;
-        if let Some(container_params) = container_params {
-            if !has_written_where {
-                f.write_str("\nwhere")?;
-            }
-            let container_name = match container.unwrap() {
-                AssocItemContainer::Trait(_) => "trait",
-                AssocItemContainer::Impl(_) => "impl",
-            };
-            write!(f, "\n    // Bounds from {container_name}:",)?;
-            write_where_predicates(&container_params, f)?;
-        }
-        Ok(())
-    }
-}
-
-fn write_impl_header(impl_: &Impl, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
-    let db = f.db;
+        write_where_clause(GenericDefId::FunctionId(self.id), f)?;
 
-    f.write_str("impl")?;
-    let def_id = GenericDefId::ImplId(impl_.id);
-    write_generic_params(def_id, f)?;
-
-    if let Some(trait_) = impl_.trait_(db) {
-        let trait_data = db.trait_data(trait_.id);
-        write!(f, " {} for", trait_data.name.display(db.upcast()))?;
+        Ok(())
     }
-
-    f.write_char(' ')?;
-    impl_.self_ty(db).hir_fmt(f)?;
-
-    Ok(())
 }
 
 impl HirDisplay for SelfParam {
@@ -250,7 +188,7 @@ impl HirDisplay for Struct {
             StructKind::Record => {
                 let has_where_clause = write_where_clause(def_id, f)?;
                 if let Some(limit) = f.entity_limit {
-                    write_fields(&self.fields(f.db), has_where_clause, limit, false, f)?;
+                    display_fields(&self.fields(f.db), has_where_clause, limit, false, f)?;
                 }
             }
             StructKind::Unit => _ = write_where_clause(def_id, f)?,
@@ -270,7 +208,7 @@ impl HirDisplay for Enum {
 
         let has_where_clause = write_where_clause(def_id, f)?;
         if let Some(limit) = f.entity_limit {
-            write_variants(&self.variants(f.db), has_where_clause, limit, f)?;
+            display_variants(&self.variants(f.db), has_where_clause, limit, f)?;
         }
 
         Ok(())
@@ -287,13 +225,13 @@ impl HirDisplay for Union {
 
         let has_where_clause = write_where_clause(def_id, f)?;
         if let Some(limit) = f.entity_limit {
-            write_fields(&self.fields(f.db), has_where_clause, limit, false, f)?;
+            display_fields(&self.fields(f.db), has_where_clause, limit, false, f)?;
         }
         Ok(())
     }
 }
 
-fn write_fields(
+fn display_fields(
     fields: &[Field],
     has_where_clause: bool,
     limit: usize,
@@ -304,7 +242,11 @@ fn write_fields(
     let (indent, separator) = if in_line { ("", ' ') } else { ("    ", '\n') };
     f.write_char(if !has_where_clause { ' ' } else { separator })?;
     if count == 0 {
-        f.write_str(if fields.is_empty() { "{}" } else { "{ /* … */ }" })?;
+        if fields.is_empty() {
+            f.write_str("{}")?;
+        } else {
+            f.write_str("{ /* … */ }")?;
+        }
     } else {
         f.write_char('{')?;
 
@@ -313,11 +255,14 @@ fn write_fields(
             for field in &fields[..count] {
                 f.write_str(indent)?;
                 field.hir_fmt(f)?;
-                write!(f, ",{separator}")?;
+                f.write_char(',')?;
+                f.write_char(separator)?;
             }
 
             if fields.len() > count {
-                write!(f, "{indent}/* … */{separator}")?;
+                f.write_str(indent)?;
+                f.write_str("/* … */")?;
+                f.write_char(separator)?;
             }
         }
 
@@ -327,7 +272,7 @@ fn write_fields(
     Ok(())
 }
 
-fn write_variants(
+fn display_variants(
     variants: &[Variant],
     has_where_clause: bool,
     limit: usize,
@@ -336,22 +281,30 @@ fn write_variants(
     let count = variants.len().min(limit);
     f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
     if count == 0 {
-        let variants = if variants.is_empty() { "{}" } else { "{ /* … */ }" };
-        f.write_str(variants)?;
+        if variants.is_empty() {
+            f.write_str("{}")?;
+        } else {
+            f.write_str("{ /* … */ }")?;
+        }
     } else {
         f.write_str("{\n")?;
         for variant in &variants[..count] {
-            write!(f, "    {}", variant.name(f.db).display(f.db.upcast()))?;
+            f.write_str("    ")?;
+            write!(f, "{}", variant.name(f.db).display(f.db.upcast()))?;
             match variant.kind(f.db) {
                 StructKind::Tuple => {
-                    let fields_str =
-                        if variant.fields(f.db).is_empty() { "()" } else { "( /* … */ )" };
-                    f.write_str(fields_str)?;
+                    if variant.fields(f.db).is_empty() {
+                        f.write_str("()")?;
+                    } else {
+                        f.write_str("( /* … */ )")?;
+                    }
                 }
                 StructKind::Record => {
-                    let fields_str =
-                        if variant.fields(f.db).is_empty() { " {}" } else { " { /* … */ }" };
-                    f.write_str(fields_str)?;
+                    if variant.fields(f.db).is_empty() {
+                        f.write_str(" {}")?;
+                    } else {
+                        f.write_str(" { /* … */ }")?;
+                    }
                 }
                 StructKind::Unit => {}
             }
@@ -404,7 +357,7 @@ impl HirDisplay for Variant {
             }
             VariantData::Record(_) => {
                 if let Some(limit) = f.entity_limit {
-                    write_fields(&self.fields(f.db), false, limit, true, f)?;
+                    display_fields(&self.fields(f.db), false, limit, true, f)?;
                 }
             }
         }
@@ -601,96 +554,102 @@ fn write_where_clause(
     f: &mut HirFormatter<'_>,
 ) -> Result<bool, HirDisplayError> {
     let params = f.db.generic_params(def);
-    if !has_disaplayable_predicates(&params) {
-        return Ok(false);
-    }
-
-    f.write_str("\nwhere")?;
-    write_where_predicates(&params, f)?;
 
-    Ok(true)
-}
-
-fn has_disaplayable_predicates(params: &Interned<GenericParams>) -> bool {
-    params.where_predicates.iter().any(|pred| {
-        !matches!(
-            pred,
-            WherePredicate::TypeBound { target: WherePredicateTypeTarget::TypeOrConstParam(id), .. }
-            if params.type_or_consts[*id].name().is_none()
-        )
-    })
-}
+    // unnamed type targets are displayed inline with the argument itself, e.g. `f: impl Y`.
+    let is_unnamed_type_target = |target: &WherePredicateTypeTarget| match target {
+        WherePredicateTypeTarget::TypeRef(_) => false,
+        WherePredicateTypeTarget::TypeOrConstParam(id) => {
+            params.type_or_consts[*id].name().is_none()
+        }
+    };
 
-fn write_where_predicates(
-    params: &Interned<GenericParams>,
-    f: &mut HirFormatter<'_>,
-) -> Result<(), HirDisplayError> {
-    use WherePredicate::*;
+    let has_displayable_predicate = params
+        .where_predicates
+        .iter()
+        .any(|pred| {
+            !matches!(pred, WherePredicate::TypeBound { target, .. } if is_unnamed_type_target(target))
+        });
 
-    // unnamed type targets are displayed inline with the argument itself, e.g. `f: impl Y`.
-    let is_unnamed_type_target =
-        |params: &Interned<GenericParams>, target: &WherePredicateTypeTarget| {
-            matches!(target,
-                WherePredicateTypeTarget::TypeOrConstParam(id) if params.type_or_consts[*id].name().is_none()
-            )
-        };
+    if !has_displayable_predicate {
+        return Ok(false);
+    }
 
     let write_target = |target: &WherePredicateTypeTarget, f: &mut HirFormatter<'_>| match target {
         WherePredicateTypeTarget::TypeRef(ty) => ty.hir_fmt(f),
-        WherePredicateTypeTarget::TypeOrConstParam(id) => match params.type_or_consts[*id].name() {
-            Some(name) => write!(f, "{}", name.display(f.db.upcast())),
-            None => f.write_str("{unnamed}"),
-        },
+        WherePredicateTypeTarget::TypeOrConstParam(id) => {
+            match &params.type_or_consts[*id].name() {
+                Some(name) => write!(f, "{}", name.display(f.db.upcast())),
+                None => f.write_str("{unnamed}"),
+            }
+        }
     };
 
-    let check_same_target = |pred1: &WherePredicate, pred2: &WherePredicate| match (pred1, pred2) {
-        (TypeBound { target: t1, .. }, TypeBound { target: t2, .. }) => t1 == t2,
-        (Lifetime { target: t1, .. }, Lifetime { target: t2, .. }) => t1 == t2,
-        (
-            ForLifetime { lifetimes: l1, target: t1, .. },
-            ForLifetime { lifetimes: l2, target: t2, .. },
-        ) => l1 == l2 && t1 == t2,
-        _ => false,
-    };
+    f.write_str("\nwhere")?;
 
-    let mut iter = params.where_predicates.iter().peekable();
-    while let Some(pred) = iter.next() {
-        if matches!(pred, TypeBound { target, .. } if is_unnamed_type_target(params, target)) {
-            continue;
-        }
+    for (pred_idx, pred) in params.where_predicates.iter().enumerate() {
+        let prev_pred =
+            if pred_idx == 0 { None } else { Some(&params.where_predicates[pred_idx - 1]) };
+
+        let new_predicate = |f: &mut HirFormatter<'_>| {
+            f.write_str(if pred_idx == 0 { "\n    " } else { ",\n    " })
+        };
 
-        f.write_str("\n    ")?;
         match pred {
-            TypeBound { target, bound } => {
-                write_target(target, f)?;
-                f.write_str(": ")?;
+            WherePredicate::TypeBound { target, .. } if is_unnamed_type_target(target) => {}
+            WherePredicate::TypeBound { target, bound } => {
+                if matches!(prev_pred, Some(WherePredicate::TypeBound { target: target_, .. }) if target_ == target)
+                {
+                    f.write_str(" + ")?;
+                } else {
+                    new_predicate(f)?;
+                    write_target(target, f)?;
+                    f.write_str(": ")?;
+                }
                 bound.hir_fmt(f)?;
             }
-            Lifetime { target, bound } => {
-                let target = target.name.display(f.db.upcast());
-                let bound = bound.name.display(f.db.upcast());
-                write!(f, "{target}: {bound}")?;
+            WherePredicate::Lifetime { target, bound } => {
+                if matches!(prev_pred, Some(WherePredicate::Lifetime { target: target_, .. }) if target_ == target)
+                {
+                    write!(f, " + {}", bound.name.display(f.db.upcast()))?;
+                } else {
+                    new_predicate(f)?;
+                    write!(
+                        f,
+                        "{}: {}",
+                        target.name.display(f.db.upcast()),
+                        bound.name.display(f.db.upcast())
+                    )?;
+                }
             }
-            ForLifetime { lifetimes, target, bound } => {
-                let lifetimes = lifetimes.iter().map(|it| it.display(f.db.upcast())).join(", ");
-                write!(f, "for<{lifetimes}> ")?;
-                write_target(target, f)?;
-                f.write_str(": ")?;
+            WherePredicate::ForLifetime { lifetimes, target, bound } => {
+                if matches!(
+                    prev_pred,
+                    Some(WherePredicate::ForLifetime { lifetimes: lifetimes_, target: target_, .. })
+                    if lifetimes_ == lifetimes && target_ == target,
+                ) {
+                    f.write_str(" + ")?;
+                } else {
+                    new_predicate(f)?;
+                    f.write_str("for<")?;
+                    for (idx, lifetime) in lifetimes.iter().enumerate() {
+                        if idx != 0 {
+                            f.write_str(", ")?;
+                        }
+                        write!(f, "{}", lifetime.display(f.db.upcast()))?;
+                    }
+                    f.write_str("> ")?;
+                    write_target(target, f)?;
+                    f.write_str(": ")?;
+                }
                 bound.hir_fmt(f)?;
             }
         }
-
-        while let Some(nxt) = iter.next_if(|nxt| check_same_target(pred, nxt)) {
-            f.write_str(" + ")?;
-            match nxt {
-                TypeBound { bound, .. } | ForLifetime { bound, .. } => bound.hir_fmt(f)?,
-                Lifetime { bound, .. } => write!(f, "{}", bound.name.display(f.db.upcast()))?,
-            }
-        }
-        f.write_str(",")?;
     }
 
-    Ok(())
+    // End of final predicate. There must be at least one predicate here.
+    f.write_char(',')?;
+
+    Ok(true)
 }
 
 impl HirDisplay for Const {
@@ -730,8 +689,17 @@ impl HirDisplay for Static {
 
 impl HirDisplay for Trait {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
-        write_trait_header(self, f)?;
+        write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
+        let data = f.db.trait_data(self.id);
+        if data.is_unsafe {
+            f.write_str("unsafe ")?;
+        }
+        if data.is_auto {
+            f.write_str("auto ")?;
+        }
+        write!(f, "trait {}", data.name.display(f.db.upcast()))?;
         let def_id = GenericDefId::TraitId(self.id);
+        write_generic_params(def_id, f)?;
         let has_where_clause = write_where_clause(def_id, f)?;
 
         if let Some(limit) = f.entity_limit {
@@ -767,20 +735,6 @@ impl HirDisplay for Trait {
     }
 }
 
-fn write_trait_header(trait_: &Trait, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
-    write_visibility(trait_.module(f.db).id, trait_.visibility(f.db), f)?;
-    let data = f.db.trait_data(trait_.id);
-    if data.is_unsafe {
-        f.write_str("unsafe ")?;
-    }
-    if data.is_auto {
-        f.write_str("auto ")?;
-    }
-    write!(f, "trait {}", data.name.display(f.db.upcast()))?;
-    write_generic_params(GenericDefId::TraitId(trait_.id), f)?;
-    Ok(())
-}
-
 impl HirDisplay for TraitAlias {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs
index 4b3b7ff4c4..308e8a31ca 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -8,14 +8,13 @@ use hir_def::{
     Lookup, MacroId, VariantId,
 };
 use hir_expand::{HirFileId, InFile};
-use hir_ty::{db::InternedClosure, CallableDefId};
 use syntax::ast;
 use tt::TextRange;
 
 use crate::{
-    db::HirDatabase, Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
-    Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static, Struct, Trait,
-    TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant,
+    db::HirDatabase, Adt, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
+    LifetimeParam, LocalSource, Macro, Module, Static, Struct, Trait, TraitAlias, TypeAlias,
+    TypeOrConstParam, Union, Variant,
 };
 
 pub trait HasSource {
@@ -223,68 +222,6 @@ impl HasSource for LocalSource {
     }
 }
 
-impl HasSource for Param {
-    type Ast = Either<ast::SelfParam, ast::Param>;
-
-    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
-        match self.func {
-            Callee::Def(CallableDefId::FunctionId(func)) => {
-                let InFile { file_id, value } = Function { id: func }.source(db)?;
-                let params = value.param_list()?;
-                if let Some(self_param) = params.self_param() {
-                    if let Some(idx) = self.idx.checked_sub(1) {
-                        params.params().nth(idx).map(Either::Right)
-                    } else {
-                        Some(Either::Left(self_param))
-                    }
-                } else {
-                    params.params().nth(self.idx).map(Either::Right)
-                }
-                .map(|value| InFile { file_id, value })
-            }
-            Callee::Closure(closure, _) => {
-                let InternedClosure(owner, expr_id) = db.lookup_intern_closure(closure.into());
-                let (_, source_map) = db.body_with_source_map(owner);
-                let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?;
-                let root = db.parse_or_expand(file_id);
-                match value.to_node(&root) {
-                    ast::Expr::ClosureExpr(it) => it
-                        .param_list()?
-                        .params()
-                        .nth(self.idx)
-                        .map(Either::Right)
-                        .map(|value| InFile { file_id: ast.file_id, value }),
-                    _ => None,
-                }
-            }
-            _ => None,
-        }
-    }
-}
-
-impl HasSource for SelfParam {
-    type Ast = ast::SelfParam;
-
-    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
-        let InFile { file_id, value } = Function::from(self.func).source(db)?;
-        value
-            .param_list()
-            .and_then(|params| params.self_param())
-            .map(|value| InFile { file_id, value })
-    }
-}
-
-impl HasSource for Label {
-    type Ast = ast::Label;
-
-    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
-        let (_body, source_map) = db.body_with_source_map(self.parent);
-        let src = source_map.label_syntax(self.label_id);
-        let root = src.file_syntax(db.upcast());
-        Some(src.map(|ast| ast.to_node(&root)))
-    }
-}
-
 impl HasSource for ExternCrateDecl {
     type Ast = ast::ExternCrate;
 
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 05fa7c4964..c69336407e 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -64,6 +64,7 @@ use hir_expand::{
 use hir_ty::{
     all_super_traits, autoderef, check_orphan_rules,
     consteval::{try_const_usize, unknown_const_as_generic, ConstExt},
+    db::InternedClosure,
     diagnostics::BodyValidationDiagnostic,
     error_lifetime, known_const_to_ast,
     layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
@@ -1098,35 +1099,6 @@ pub enum FieldSource {
     Pos(ast::TupleField),
 }
 
-impl AstNode for FieldSource {
-    fn can_cast(kind: syntax::SyntaxKind) -> bool
-    where
-        Self: Sized,
-    {
-        ast::RecordField::can_cast(kind) || ast::TupleField::can_cast(kind)
-    }
-
-    fn cast(syntax: SyntaxNode) -> Option<Self>
-    where
-        Self: Sized,
-    {
-        if ast::RecordField::can_cast(syntax.kind()) {
-            <ast::RecordField as AstNode>::cast(syntax).map(FieldSource::Named)
-        } else if ast::TupleField::can_cast(syntax.kind()) {
-            <ast::TupleField as AstNode>::cast(syntax).map(FieldSource::Pos)
-        } else {
-            None
-        }
-    }
-
-    fn syntax(&self) -> &SyntaxNode {
-        match self {
-            FieldSource::Named(it) => it.syntax(),
-            FieldSource::Pos(it) => it.syntax(),
-        }
-    }
-}
-
 impl Field {
     pub fn name(&self, db: &dyn HirDatabase) -> Name {
         self.parent.variant_data(db).fields()[self.id].name.clone()
@@ -2244,9 +2216,47 @@ impl Param {
         }
     }
 
-    pub fn pattern_source(self, db: &dyn HirDatabase) -> Option<ast::Pat> {
+    pub fn pattern_source(&self, db: &dyn HirDatabase) -> Option<ast::Pat> {
         self.source(db).and_then(|p| p.value.right()?.pat())
     }
+
+    pub fn source(
+        &self,
+        db: &dyn HirDatabase,
+    ) -> Option<InFile<Either<ast::SelfParam, ast::Param>>> {
+        match self.func {
+            Callee::Def(CallableDefId::FunctionId(func)) => {
+                let InFile { file_id, value } = Function { id: func }.source(db)?;
+                let params = value.param_list()?;
+                if let Some(self_param) = params.self_param() {
+                    if let Some(idx) = self.idx.checked_sub(1) {
+                        params.params().nth(idx).map(Either::Right)
+                    } else {
+                        Some(Either::Left(self_param))
+                    }
+                } else {
+                    params.params().nth(self.idx).map(Either::Right)
+                }
+                .map(|value| InFile { file_id, value })
+            }
+            Callee::Closure(closure, _) => {
+                let InternedClosure(owner, expr_id) = db.lookup_intern_closure(closure.into());
+                let (_, source_map) = db.body_with_source_map(owner);
+                let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?;
+                let root = db.parse_or_expand(file_id);
+                match value.to_node(&root) {
+                    ast::Expr::ClosureExpr(it) => it
+                        .param_list()?
+                        .params()
+                        .nth(self.idx)
+                        .map(Either::Right)
+                        .map(|value| InFile { file_id: ast.file_id, value }),
+                    _ => None,
+                }
+            }
+            _ => None,
+        }
+    }
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -2270,6 +2280,14 @@ impl SelfParam {
             .unwrap_or(Access::Owned)
     }
 
+    pub fn source(&self, db: &dyn HirDatabase) -> Option<InFile<ast::SelfParam>> {
+        let InFile { file_id, value } = Function::from(self.func).source(db)?;
+        value
+            .param_list()
+            .and_then(|params| params.self_param())
+            .map(|value| InFile { file_id, value })
+    }
+
     pub fn parent_fn(&self) -> Function {
         Function::from(self.func)
     }
@@ -3440,6 +3458,13 @@ impl Label {
         let body = db.body(self.parent);
         body[self.label_id].name.clone()
     }
+
+    pub fn source(self, db: &dyn HirDatabase) -> InFile<ast::Label> {
+        let (_body, source_map) = db.body_with_source_map(self.parent);
+        let src = source_map.label_syntax(self.label_id);
+        let root = src.file_syntax(db.upcast());
+        src.map(|ast| ast.to_node(&root))
+    }
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index a38cef2fd5..964401d48d 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -132,6 +132,9 @@ pub struct SemanticsImpl<'db> {
     s2d_cache: RefCell<SourceToDefCache>,
     /// Rootnode to HirFileId cache
     root_to_file_cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>,
+    /// HirFileId to Rootnode cache (this adds a layer over the database LRU cache to prevent
+    /// possibly frequent invalidation)
+    parse_cache: RefCell<FxHashMap<HirFileId, SyntaxNode>>,
     /// MacroCall to its expansion's MacroFileId cache
     macro_call_cache: RefCell<FxHashMap<InFile<ast::MacroCall>, MacroFileId>>,
 }
@@ -292,6 +295,7 @@ impl<'db> SemanticsImpl<'db> {
             db,
             s2d_cache: Default::default(),
             root_to_file_cache: Default::default(),
+            parse_cache: Default::default(),
             macro_call_cache: Default::default(),
         }
     }
@@ -303,6 +307,9 @@ impl<'db> SemanticsImpl<'db> {
     }
 
     pub fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode {
+        if let Some(root) = self.parse_cache.borrow().get(&file_id) {
+            return root.clone();
+        }
         let node = self.db.parse_or_expand(file_id);
         self.cache(node.clone(), file_id);
         node
@@ -1483,8 +1490,9 @@ impl<'db> SemanticsImpl<'db> {
     fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) {
         assert!(root_node.parent().is_none());
         let mut cache = self.root_to_file_cache.borrow_mut();
-        let prev = cache.insert(root_node, file_id);
+        let prev = cache.insert(root_node.clone(), file_id);
         assert!(prev.is_none() || prev == Some(file_id));
+        self.parse_cache.borrow_mut().insert(file_id, root_node);
     }
 
     pub fn assert_contains_node(&self, node: &SyntaxNode) {
diff --git a/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-assists/src/utils/suggest_name.rs
index f2a097afc8..23a06404f3 100644
--- a/crates/ide-assists/src/utils/suggest_name.rs
+++ b/crates/ide-assists/src/utils/suggest_name.rs
@@ -254,7 +254,7 @@ fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<St
 
     let (idx, _) = arg_list.args().find_position(|it| it == expr).unwrap();
     let param = func.params().into_iter().nth(idx)?;
-    let pat = sema.source(param)?.value.right()?.pat()?;
+    let pat = param.source(sema.db)?.value.right()?.pat()?;
     let name = var_name_from_pat(&pat)?;
     normalize(&name.to_string())
 }
diff --git a/crates/ide-db/src/active_parameter.rs b/crates/ide-db/src/active_parameter.rs
index 088d2ec5e3..98d2e81754 100644
--- a/crates/ide-db/src/active_parameter.rs
+++ b/crates/ide-db/src/active_parameter.rs
@@ -28,7 +28,7 @@ impl ActiveParameter {
             return None;
         }
         let param = params.swap_remove(idx);
-        Some(ActiveParameter { ty: param.ty().clone(), src: sema.source(param) })
+        Some(ActiveParameter { ty: param.ty().clone(), src: param.source(sema.db) })
     }
 
     pub fn ident(&self) -> Option<ast::Name> {
diff --git a/crates/ide-db/src/prime_caches.rs b/crates/ide-db/src/prime_caches.rs
index b4486d1502..1ca5206011 100644
--- a/crates/ide-db/src/prime_caches.rs
+++ b/crates/ide-db/src/prime_caches.rs
@@ -29,7 +29,7 @@ pub struct ParallelPrimeCachesProgress {
 
 pub fn parallel_prime_caches(
     db: &RootDatabase,
-    num_worker_threads: usize,
+    num_worker_threads: u8,
     cb: &(dyn Fn(ParallelPrimeCachesProgress) + Sync),
 ) {
     let _p = tracing::info_span!("parallel_prime_caches").entered();
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs
index 484c65c2b0..288d56b534 100644
--- a/crates/ide-db/src/rename.rs
+++ b/crates/ide-db/src/rename.rs
@@ -24,7 +24,7 @@ use std::fmt;
 
 use base_db::{AnchoredPathBuf, FileId, FileRange};
 use either::Either;
-use hir::{FieldSource, HirFileIdExt, InFile, ModuleSource, Semantics};
+use hir::{FieldSource, HasSource, HirFileIdExt, InFile, ModuleSource, Semantics};
 use span::SyntaxContextId;
 use stdx::{never, TupleExt};
 use syntax::{
@@ -109,7 +109,7 @@ impl Definition {
         let syn_ctx_is_root = |(range, ctx): (_, SyntaxContextId)| ctx.is_root().then_some(range);
         let res = match self {
             Definition::Macro(mac) => {
-                let src = sema.source(mac)?;
+                let src = mac.source(sema.db)?;
                 let name = match &src.value {
                     Either::Left(it) => it.name()?,
                     Either::Right(it) => it.name()?,
@@ -119,7 +119,7 @@ impl Definition {
                     .and_then(syn_ctx_is_root)
             }
             Definition::Field(field) => {
-                let src = sema.source(field)?;
+                let src = field.source(sema.db)?;
                 match &src.value {
                     FieldSource::Named(record_field) => {
                         let name = record_field.name()?;
@@ -154,18 +154,18 @@ impl Definition {
             }
             Definition::GenericParam(generic_param) => match generic_param {
                 hir::GenericParam::LifetimeParam(lifetime_param) => {
-                    let src = sema.source(lifetime_param)?;
+                    let src = lifetime_param.source(sema.db)?;
                     src.with_value(src.value.lifetime()?.syntax())
                         .original_file_range_opt(sema.db)
                         .and_then(syn_ctx_is_root)
                 }
                 _ => {
-                    let param = match generic_param {
+                    let x = match generic_param {
                         hir::GenericParam::TypeParam(it) => it.merge(),
                         hir::GenericParam::ConstParam(it) => it.merge(),
                         hir::GenericParam::LifetimeParam(_) => return None,
                     };
-                    let src = sema.source(param)?;
+                    let src = x.source(sema.db)?;
                     let name = match &src.value {
                         Either::Left(x) => x.name()?,
                         Either::Right(_) => return None,
@@ -176,14 +176,14 @@ impl Definition {
                 }
             },
             Definition::Label(label) => {
-                let src = sema.source(label)?;
+                let src = label.source(sema.db);
                 let lifetime = src.value.lifetime()?;
                 src.with_value(lifetime.syntax())
                     .original_file_range_opt(sema.db)
                     .and_then(syn_ctx_is_root)
             }
             Definition::ExternCrateDecl(it) => {
-                let src = sema.source(it)?;
+                let src = it.source(sema.db)?;
                 if let Some(rename) = src.value.rename() {
                     let name = rename.name()?;
                     src.with_value(name.syntax())
@@ -212,10 +212,10 @@ impl Definition {
             sema: &Semantics<'_, RootDatabase>,
         ) -> Option<(FileRange, SyntaxContextId)>
         where
-            D: hir::HasSource,
+            D: HasSource,
             D::Ast: ast::HasName,
         {
-            let src = sema.source(def)?;
+            let src = def.source(sema.db)?;
             let name = src.value.name()?;
             src.with_value(name.syntax()).original_file_range_opt(sema.db)
         }
diff --git a/crates/ide-ssr/src/matching.rs b/crates/ide-ssr/src/matching.rs
index b29053c0c2..cf7e7e08bc 100644
--- a/crates/ide-ssr/src/matching.rs
+++ b/crates/ide-ssr/src/matching.rs
@@ -575,7 +575,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
                             .resolve_method_call_as_callable(code)
                             .and_then(|callable| {
                                 let (self_param, _) = callable.receiver_param(self.sema.db)?;
-                                Some(self.sema.source(self_param)?.value.kind())
+                                Some(self_param.source(self.sema.db)?.value.kind())
                             })
                             .unwrap_or(ast::SelfParamKind::Owned);
                     }
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index f7e5b40dde..654a1cd316 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -19,6 +19,13 @@ pub struct CallItem {
     pub ranges: Vec<TextRange>,
 }
 
+impl CallItem {
+    #[cfg(test)]
+    pub(crate) fn debug_render(&self) -> String {
+        format!("{} : {:?}", self.target.debug_render(), self.ranges)
+    }
+}
+
 pub(crate) fn call_hierarchy(
     db: &RootDatabase,
     position: FilePosition,
@@ -152,10 +159,6 @@ mod tests {
         expected_incoming: Expect,
         expected_outgoing: Expect,
     ) {
-        fn debug_render(item: crate::CallItem) -> String {
-            format!("{} : {:?}", item.target.debug_render(), item.ranges)
-        }
-
         let (analysis, pos) = fixture::position(ra_fixture);
 
         let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info;
@@ -166,10 +169,12 @@ mod tests {
         let item_pos =
             FilePosition { file_id: nav.file_id, offset: nav.focus_or_full_range().start() };
         let incoming_calls = analysis.incoming_calls(item_pos).unwrap().unwrap();
-        expected_incoming.assert_eq(&incoming_calls.into_iter().map(debug_render).join("\n"));
+        expected_incoming
+            .assert_eq(&incoming_calls.into_iter().map(|call| call.debug_render()).join("\n"));
 
         let outgoing_calls = analysis.outgoing_calls(item_pos).unwrap().unwrap();
-        expected_outgoing.assert_eq(&outgoing_calls.into_iter().map(debug_render).join("\n"));
+        expected_outgoing
+            .assert_eq(&outgoing_calls.into_iter().map(|call| call.debug_render()).join("\n"));
     }
 
     #[test]
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 99568c9922..3bc17f95e7 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -430,7 +430,6 @@ pub(super) fn definition(
             }
             label
         }
-        Definition::Function(fn_) => fn_.display_with_container_bounds(db, true).to_string(),
         _ => def.label(db),
     };
     let docs = def.docs(db, famous_defs);
diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs
index 20128a286f..7932d8efbc 100644
--- a/crates/ide/src/inlay_hints/adjustment.rs
+++ b/crates/ide/src/inlay_hints/adjustment.rs
@@ -709,4 +709,25 @@ fn main() {
             "#,
         )
     }
+
+    // regression test for a stackoverflow in hir display code
+    #[test]
+    fn adjustment_hints_method_call_on_impl_trait_self() {
+        check_with_config(
+            InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
+            r#"
+//- minicore: slice, coerce_unsized
+trait T<RHS = Self> {}
+
+fn hello(it: &&[impl T]) {
+    it.len();
+  //^^(
+  //^^&
+  //^^*
+  //^^*
+  //^^)
+}
+"#,
+        );
+    }
 }
diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs
index 9819d0e3fb..fb50c49a3a 100644
--- a/crates/ide/src/inlay_hints/param_name.rs
+++ b/crates/ide/src/inlay_hints/param_name.rs
@@ -30,7 +30,7 @@ pub(super) fn hints(
         .filter_map(|(p, arg)| {
             // Only annotate hints for expressions that exist in the original file
             let range = sema.original_range_opt(arg.syntax())?;
-            let source = sema.source(p)?;
+            let source = p.source(sema.db)?;
             let (param_name, name_syntax) = match source.value.as_ref() {
                 Either::Left(pat) => (pat.name()?, pat.name()),
                 Either::Right(param) => match param.pat()? {
@@ -38,6 +38,8 @@ pub(super) fn hints(
                     _ => return None,
                 },
             };
+            // make sure the file is cached so we can map out of macros
+            sema.parse_or_expand(source.file_id);
             Some((name_syntax, param_name, arg, range))
         })
         .filter(|(_, param_name, arg, _)| {
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index a2ac62341d..e9408bf897 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -284,7 +284,7 @@ impl Analysis {
         })
     }
 
-    pub fn parallel_prime_caches<F>(&self, num_worker_threads: usize, cb: F) -> Cancellable<()>
+    pub fn parallel_prime_caches<F>(&self, num_worker_threads: u8, cb: F) -> Cancellable<()>
     where
         F: Fn(ParallelPrimeCachesProgress) + Sync + std::panic::UnwindSafe,
     {
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index bfd62e7624..a93a8da57e 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -220,7 +220,7 @@ impl TryToNav for Definition {
     fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
         match self {
             Definition::Local(it) => Some(it.to_nav(db)),
-            Definition::Label(it) => it.try_to_nav(db),
+            Definition::Label(it) => Some(it.to_nav(db)),
             Definition::Module(it) => Some(it.to_nav(db)),
             Definition::Macro(it) => it.try_to_nav(db),
             Definition::Field(it) => it.try_to_nav(db),
@@ -562,12 +562,12 @@ impl ToNav for hir::Local {
     }
 }
 
-impl TryToNav for hir::Label {
-    fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
-        let InFile { file_id, value } = self.source(db)?;
+impl ToNav for hir::Label {
+    fn to_nav(&self, db: &RootDatabase) -> UpmappingResult<NavigationTarget> {
+        let InFile { file_id, value } = self.source(db);
         let name = self.name(db).to_smol_str();
 
-        Some(orig_range_with_focus(db, file_id, value.syntax(), value.lifetime()).map(
+        orig_range_with_focus(db, file_id, value.syntax(), value.lifetime()).map(
             |(FileRange { file_id, range: full_range }, focus_range)| NavigationTarget {
                 file_id,
                 name: name.clone(),
@@ -579,7 +579,7 @@ impl TryToNav for hir::Label {
                 description: None,
                 docs: None,
             },
-        ))
+        )
     }
 }
 
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index 3d08e2f371..a13758a9f4 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -361,9 +361,8 @@ fn rename_to_self(
         bail!("Parameter type differs from impl block type");
     }
 
-    let InFile { file_id, value: param_source } = sema
-        .source(first_param.clone())
-        .ok_or_else(|| format_err!("No source for parameter found"))?;
+    let InFile { file_id, value: param_source } =
+        first_param.source(sema.db).ok_or_else(|| format_err!("No source for parameter found"))?;
 
     let def = Definition::Local(local);
     let usages = def.usages(sema).all();
@@ -393,7 +392,7 @@ fn rename_self_to_param(
     let identifier_kind = IdentifierKind::classify(new_name)?;
 
     let InFile { file_id, value: self_param } =
-        sema.source(self_param).ok_or_else(|| format_err!("cannot find function source"))?;
+        self_param.source(sema.db).ok_or_else(|| format_err!("cannot find function source"))?;
 
     let def = Definition::Local(local);
     let usages = def.usages(sema).all();
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index 89c725a6c4..378a38892c 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -226,7 +226,7 @@ fn signature_help_for_call(
         let mut buf = String::new();
         for (idx, p) in callable.params().into_iter().enumerate() {
             buf.clear();
-            if let Some(param) = sema.source(p.clone()) {
+            if let Some(param) = p.source(sema.db) {
                 match param.value {
                     Either::Right(param) => match param.pat() {
                         Some(pat) => format_to!(buf, "{}: ", pat),
diff --git a/crates/ide/src/syntax_highlighting/escape.rs b/crates/ide/src/syntax_highlighting/escape.rs
index 552ce9cd8c..2f387968c9 100644
--- a/crates/ide/src/syntax_highlighting/escape.rs
+++ b/crates/ide/src/syntax_highlighting/escape.rs
@@ -28,7 +28,7 @@ pub(super) fn highlight_escape_string<T: IsString>(
 pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: TextSize) {
     if char.value().is_err() {
         // We do not emit invalid escapes highlighting here. The lexer would likely be in a bad
-        // state and this token contains junk, since `'` is not a reliable delimiter (consider
+        // state and this token contains junks, since `'` is not a reliable delimiter (consider
         // lifetimes). Nonetheless, parser errors should already be emitted.
         return;
     }
diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs
index cb785195a0..10c41fda75 100644
--- a/crates/load-cargo/src/lib.rs
+++ b/crates/load-cargo/src/lib.rs
@@ -285,6 +285,7 @@ impl SourceRootConfig {
     /// If a `SourceRoot` doesn't have a parent and is local then it is not contained in this mapping but it can be asserted that it is a root `SourceRoot`.
     pub fn source_root_parent_map(&self) -> FxHashMap<SourceRootId, SourceRootId> {
         let roots = self.fsc.roots();
+        let mut i = 0;
         roots
             .iter()
             .enumerate()
@@ -292,14 +293,9 @@ impl SourceRootConfig {
             .filter_map(|(idx, (root, root_id))| {
                 // We are interested in parents if they are also local source roots.
                 // So instead of a non-local parent we may take a local ancestor as a parent to a node.
-                //
-                // Here paths in roots are sorted lexicographically, so if a root
-                // is a parent of another root, it will be before it in the list.
                 roots[..idx].iter().find_map(|(root2, root2_id)| {
-                    if self.local_filesets.contains(root2_id)
-                        && root.starts_with(root2)
-                        && root_id != root2_id
-                    {
+                    i += 1;
+                    if self.local_filesets.contains(root2_id) && root.starts_with(root2) {
                         return Some((root_id, root2_id));
                     }
                     None
@@ -576,20 +572,4 @@ mod tests {
 
         assert_eq!(vc, vec![(SourceRootId(3), SourceRootId(1)),])
     }
-
-    #[test]
-    fn parents_with_identical_root_id() {
-        let mut builder = FileSetConfigBuilder::default();
-        builder.add_file_set(vec![
-            VfsPath::new_virtual_path("/ROOT/def".to_owned()),
-            VfsPath::new_virtual_path("/ROOT/def/abc/def".to_owned()),
-        ]);
-        builder.add_file_set(vec![VfsPath::new_virtual_path("/ROOT/def/abc/def/ghi".to_owned())]);
-        let fsc = builder.build();
-        let src = SourceRootConfig { fsc, local_filesets: vec![0, 1] };
-        let mut vc = src.source_root_parent_map().into_iter().collect::<Vec<_>>();
-        vc.sort_by(|x, y| x.0 .0.cmp(&y.0 .0));
-
-        assert_eq!(vc, vec![(SourceRootId(1), SourceRootId(0)),])
-    }
 }
diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs
index 19ba5c7a15..f73e188c79 100644
--- a/crates/mbe/src/benchmark.rs
+++ b/crates/mbe/src/benchmark.rs
@@ -215,7 +215,7 @@ fn invocation_fixtures(
 
                 token_trees.push(subtree.into());
             }
-            Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Len { .. } => {}
+            Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Length { .. } => {}
         };
 
         // Simple linear congruential generator for deterministic result
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index 0cec4e70da..78d4bfee2a 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -566,7 +566,7 @@ fn match_loop_inner<'t>(
                 error_items.push(item);
             }
             OpDelimited::Op(
-                Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Len { .. },
+                Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Length { .. },
             ) => {
                 stdx::never!("metavariable expression in lhs found");
             }
@@ -832,7 +832,7 @@ fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate)
             Op::Subtree { tokens, .. } => collect_vars(collector_fun, tokens),
             Op::Repeat { tokens, .. } => collect_vars(collector_fun, tokens),
             Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => {}
-            Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Len { .. } => {
+            Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Length { .. } => {
                 stdx::never!("metavariable expression in lhs found");
             }
         }
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs
index 0f689a2692..5e6e45f152 100644
--- a/crates/mbe/src/expander/transcriber.rs
+++ b/crates/mbe/src/expander/transcriber.rs
@@ -242,7 +242,7 @@ fn expand_subtree(
                     .into(),
                 );
             }
-            Op::Len { depth } => {
+            Op::Length { depth } => {
                 let length = ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |_nest| {
                     // FIXME: to be implemented
                     0
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index bbe00f0afc..eaf2fd8c27 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -75,7 +75,7 @@ pub(crate) enum Op {
     Index {
         depth: usize,
     },
-    Len {
+    Length {
         depth: usize,
     },
     Count {
@@ -345,7 +345,7 @@ fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, Span>) -> Result
             Op::Ignore { name: ident.text.clone(), id: ident.span }
         }
         "index" => Op::Index { depth: parse_depth(&mut args)? },
-        "len" => Op::Len { depth: parse_depth(&mut args)? },
+        "length" => Op::Length { depth: parse_depth(&mut args)? },
         "count" => {
             if new_meta_vars {
                 args.expect_dollar()?;
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index e8504979be..497fd67e92 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -73,7 +73,7 @@ config_data! {
         /// Warm up caches on project load.
         cachePriming_enable: bool = true,
         /// How many worker threads to handle priming caches. The default `0` means to pick automatically.
-        cachePriming_numThreads: NumThreads = NumThreads::Physical,
+        cachePriming_numThreads: ParallelCachePrimingNumThreads = 0u8,
 
         /// Pass `--all-targets` to cargo invocation.
         cargo_allTargets: bool           = true,
@@ -583,7 +583,7 @@ config_data! {
         notifications_unindexedProject: bool      = false,
 
         /// How many worker threads in the main loop. The default `null` means to pick automatically.
-        numThreads: Option<NumThreads> = None,
+        numThreads: Option<usize> = None,
 
         /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
         procMacro_attributes_enable: bool = true,
@@ -968,6 +968,8 @@ macro_rules! try_or_def {
     };
 }
 
+type ParallelCachePrimingNumThreads = u8;
+
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum LinkedProject {
     ProjectManifest(ProjectManifest),
@@ -2093,22 +2095,15 @@ impl Config {
         }
     }
 
-    pub fn prime_caches_num_threads(&self) -> usize {
-        match self.cachePriming_numThreads() {
-            NumThreads::Concrete(0) | NumThreads::Physical => num_cpus::get_physical(),
-            &NumThreads::Concrete(n) => n,
-            NumThreads::Logical => num_cpus::get(),
+    pub fn prime_caches_num_threads(&self) -> u8 {
+        match *self.cachePriming_numThreads() {
+            0 => num_cpus::get_physical().try_into().unwrap_or(u8::MAX),
+            n => n,
         }
     }
 
     pub fn main_loop_num_threads(&self) -> usize {
-        match self.numThreads() {
-            Some(NumThreads::Concrete(0)) | None | Some(NumThreads::Physical) => {
-                num_cpus::get_physical()
-            }
-            &Some(NumThreads::Concrete(n)) => n,
-            Some(NumThreads::Logical) => num_cpus::get(),
-        }
+        self.numThreads().unwrap_or(num_cpus::get_physical())
     }
 
     pub fn typing_autoclose_angle(&self) -> bool {
@@ -2203,6 +2198,51 @@ macro_rules! create_bool_or_string_serde {
 create_bool_or_string_serde!(true_or_always<true, "always">);
 create_bool_or_string_serde!(false_or_never<false, "never">);
 
+macro_rules! named_unit_variant {
+    ($variant:ident) => {
+        pub(super) mod $variant {
+            pub(in super::super) fn deserialize<'de, D>(deserializer: D) -> Result<(), D::Error>
+            where
+                D: serde::Deserializer<'de>,
+            {
+                struct V;
+                impl<'de> serde::de::Visitor<'de> for V {
+                    type Value = ();
+                    fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+                        f.write_str(concat!("\"", stringify!($variant), "\""))
+                    }
+                    fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
+                        if value == stringify!($variant) {
+                            Ok(())
+                        } else {
+                            Err(E::invalid_value(serde::de::Unexpected::Str(value), &self))
+                        }
+                    }
+                }
+                deserializer.deserialize_str(V)
+            }
+            pub(in super::super) fn serialize<S>(serializer: S) -> Result<S::Ok, S::Error>
+            where
+                S: serde::Serializer,
+            {
+                serializer.serialize_str(stringify!($variant))
+            }
+        }
+    };
+}
+
+mod unit_v {
+    named_unit_variant!(all);
+    named_unit_variant!(skip_trivial);
+    named_unit_variant!(mutable);
+    named_unit_variant!(reborrow);
+    named_unit_variant!(fieldless);
+    named_unit_variant!(with_block);
+    named_unit_variant!(decimal);
+    named_unit_variant!(hexadecimal);
+    named_unit_variant!(both);
+}
+
 #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
 #[serde(rename_all = "snake_case")]
 #[derive(Default)]
@@ -2317,10 +2357,10 @@ pub(crate) enum CallableCompletionDef {
 }
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "snake_case")]
+#[serde(untagged)]
 enum CargoFeaturesDef {
+    #[serde(with = "unit_v::all")]
     All,
-    #[serde(untagged)]
     Selected(Vec<String>),
 }
 
@@ -2342,27 +2382,25 @@ enum InvocationLocation {
 }
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "snake_case")]
+#[serde(untagged)]
 enum LifetimeElisionDef {
-    SkipTrivial,
     #[serde(with = "true_or_always")]
-    #[serde(untagged)]
     Always,
     #[serde(with = "false_or_never")]
-    #[serde(untagged)]
     Never,
+    #[serde(with = "unit_v::skip_trivial")]
+    SkipTrivial,
 }
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "snake_case")]
+#[serde(untagged)]
 enum ClosureReturnTypeHintsDef {
-    WithBlock,
     #[serde(with = "true_or_always")]
-    #[serde(untagged)]
     Always,
     #[serde(with = "false_or_never")]
-    #[serde(untagged)]
     Never,
+    #[serde(with = "unit_v::with_block")]
+    WithBlock,
 }
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
@@ -2375,39 +2413,36 @@ enum ClosureStyle {
 }
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "snake_case")]
+#[serde(untagged)]
 enum ReborrowHintsDef {
-    Mutable,
     #[serde(with = "true_or_always")]
-    #[serde(untagged)]
     Always,
     #[serde(with = "false_or_never")]
-    #[serde(untagged)]
     Never,
+    #[serde(with = "unit_v::mutable")]
+    Mutable,
 }
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "snake_case")]
+#[serde(untagged)]
 enum AdjustmentHintsDef {
-    Reborrow,
     #[serde(with = "true_or_always")]
-    #[serde(untagged)]
     Always,
     #[serde(with = "false_or_never")]
-    #[serde(untagged)]
     Never,
+    #[serde(with = "unit_v::reborrow")]
+    Reborrow,
 }
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "snake_case")]
+#[serde(untagged)]
 enum DiscriminantHintsDef {
-    Fieldless,
     #[serde(with = "true_or_always")]
-    #[serde(untagged)]
     Always,
     #[serde(with = "false_or_never")]
-    #[serde(untagged)]
     Never,
+    #[serde(with = "unit_v::fieldless")]
+    Fieldless,
 }
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
@@ -2431,11 +2466,9 @@ enum FilesWatcherDef {
 #[serde(rename_all = "snake_case")]
 enum ImportPrefixDef {
     Plain,
-    #[serde(rename = "self")]
-    #[serde(alias = "by_self")]
+    #[serde(alias = "self")]
     BySelf,
-    #[serde(rename = "crate")]
-    #[serde(alias = "by_crate")]
+    #[serde(alias = "crate")]
     ByCrate,
 }
 
@@ -2462,9 +2495,13 @@ enum WorkspaceSymbolSearchKindDef {
 
 #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
 #[serde(rename_all = "snake_case")]
+#[serde(untagged)]
 enum MemoryLayoutHoverRenderKindDef {
+    #[serde(with = "unit_v::decimal")]
     Decimal,
+    #[serde(with = "unit_v::hexadecimal")]
     Hexadecimal,
+    #[serde(with = "unit_v::both")]
     Both,
 }
 
@@ -2487,15 +2524,6 @@ pub enum TargetDirectory {
     Directory(Utf8PathBuf),
 }
 
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
-#[serde(rename_all = "snake_case")]
-pub enum NumThreads {
-    Physical,
-    Logical,
-    #[serde(untagged)]
-    Concrete(usize),
-}
-
 macro_rules! _default_val {
     (@verbatim: $s:literal, $ty:ty) => {{
         let default_: $ty = serde_json::from_str(&$s).unwrap();
@@ -2748,10 +2776,6 @@ impl FullConfigInput {
         ClientConfigInput::schema_fields(&mut fields);
         fields.sort_by_key(|&(x, ..)| x);
         fields
-            .iter()
-            .tuple_windows()
-            .for_each(|(a, b)| assert!(a.0 != b.0, "{a:?} duplicate field"));
-        fields
     }
 
     fn json_schema() -> serde_json::Value {
@@ -3010,6 +3034,11 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
                 "Search for all symbols kinds."
             ],
         },
+        "ParallelCachePrimingNumThreads" => set! {
+            "type": "number",
+            "minimum": 0,
+            "maximum": 255
+        },
         "LifetimeElisionDef" => set! {
             "type": "string",
             "enum": [
@@ -3231,44 +3260,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
                 },
             ],
         },
-        "NumThreads" => set! {
-            "anyOf": [
-                {
-                    "type": "number",
-                    "minimum": 0,
-                    "maximum": 255
-                },
-                {
-                    "type": "string",
-                    "enum": ["physical", "logical", ],
-                    "enumDescriptions": [
-                        "Use the number of physical cores",
-                        "Use the number of logical cores",
-                    ],
-                },
-            ],
-        },
-        "Option<NumThreads>" => set! {
-            "anyOf": [
-                {
-                    "type": "null"
-                },
-                {
-                    "type": "number",
-                    "minimum": 0,
-                    "maximum": 255
-                },
-                {
-                    "type": "string",
-                    "enum": ["physical", "logical", ],
-                    "enumDescriptions": [
-                        "Use the number of physical cores",
-                        "Use the number of logical cores",
-                    ],
-                },
-            ],
-        },
-        _ => panic!("missing entry for {ty}: {default} (field {field})"),
+        _ => panic!("missing entry for {ty}: {default}"),
     }
 
     map.into()
@@ -3349,7 +3341,7 @@ mod tests {
             .trim_start_matches('[')
             .trim_end_matches(']')
             .replace("  ", "    ")
-            .replace('\n', "\n        ")
+            .replace('\n', "\n            ")
             .trim_start_matches('\n')
             .trim_end()
             .to_owned();
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 14aae91741..8993a46d2b 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -19,7 +19,7 @@ Term search fuel in "units of work" for assists (Defaults to 400).
 --
 Warm up caches on project load.
 --
-[[rust-analyzer.cachePriming.numThreads]]rust-analyzer.cachePriming.numThreads (default: `"physical"`)::
+[[rust-analyzer.cachePriming.numThreads]]rust-analyzer.cachePriming.numThreads (default: `0`)::
 +
 --
 How many worker threads to handle priming caches. The default `0` means to pick automatically.
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 7de7576317..bd8b0e9c4e 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -1087,12 +1087,12 @@
             }
         },
         "node_modules/braces": {
-            "version": "3.0.3",
-            "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
-            "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+            "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
             "dev": true,
             "dependencies": {
-                "fill-range": "^7.1.1"
+                "fill-range": "^7.0.1"
             },
             "engines": {
                 "node": ">=8"
@@ -2243,9 +2243,9 @@
             }
         },
         "node_modules/fill-range": {
-            "version": "7.1.1",
-            "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
-            "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+            "version": "7.0.1",
+            "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+            "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
             "dev": true,
             "dependencies": {
                 "to-regex-range": "^5.0.1"
diff --git a/editors/code/package.json b/editors/code/package.json
index b447106d99..cd11b56d15 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -609,25 +609,10 @@
                 "properties": {
                     "rust-analyzer.cachePriming.numThreads": {
                         "markdownDescription": "How many worker threads to handle priming caches. The default `0` means to pick automatically.",
-                        "default": "physical",
-                        "anyOf": [
-                            {
-                                "type": "number",
-                                "minimum": 0,
-                                "maximum": 255
-                            },
-                            {
-                                "type": "string",
-                                "enum": [
-                                    "physical",
-                                    "logical"
-                                ],
-                                "enumDescriptions": [
-                                    "Use the number of physical cores",
-                                    "Use the number of logical cores"
-                                ]
-                            }
-                        ]
+                        "default": 0,
+                        "type": "number",
+                        "minimum": 0,
+                        "maximum": 255
                     }
                 }
             },
@@ -2240,27 +2225,11 @@
                     "rust-analyzer.numThreads": {
                         "markdownDescription": "How many worker threads in the main loop. The default `null` means to pick automatically.",
                         "default": null,
-                        "anyOf": [
-                            {
-                                "type": "null"
-                            },
-                            {
-                                "type": "number",
-                                "minimum": 0,
-                                "maximum": 255
-                            },
-                            {
-                                "type": "string",
-                                "enum": [
-                                    "physical",
-                                    "logical"
-                                ],
-                                "enumDescriptions": [
-                                    "Use the number of physical cores",
-                                    "Use the number of logical cores"
-                                ]
-                            }
-                        ]
+                        "type": [
+                            "null",
+                            "integer"
+                        ],
+                        "minimum": 0
                     }
                 }
             },


Test patch:
diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs
index 0011d3a20c..e8b26d5373 100644
--- a/crates/hir-def/src/body/tests.rs
+++ b/crates/hir-def/src/body/tests.rs
@@ -156,7 +156,7 @@ fn main() {
     );
 
     expect![[r#"
-        fn main() -> () {
+        fn main() {
             let are = "are";
             let count = 10;
             builtin#lang(Arguments::new_v1_formatted)(
@@ -258,7 +258,7 @@ impl SsrError {
 
     assert_eq!(db.body_with_source_map(def).1.diagnostics(), &[]);
     expect![[r#"
-        fn main() -> () {
+        fn main() {
             _ = $crate::error::SsrError::new(
                 builtin#lang(Arguments::new_v1_formatted)(
                     &[
@@ -303,7 +303,7 @@ macro_rules! m {
     };
 }
 
-fn f(a: i32, b: u32) -> String {
+fn f() {
     m!();
 }
 "#,
@@ -317,7 +317,7 @@ fn f(a: i32, b: u32) -> String {
     }
 
     expect![[r#"
-        fn f(a: i32, b: u32) -> String {
+        fn f() {
             {
                 $crate::panicking::panic_fmt(
                     builtin#lang(Arguments::new_v1_formatted)(