Repository: rust-lang/rust-analyzer
Pull request #17374 (2024-06-09 11:01:00)

Allow choosing logical cores for num threads config


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 583c7bbe33..ccc27e2133 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -162,11 +162,7 @@ xshell = "0.2.5"
 dashmap = { version = "=5.5.3", features = ["raw-api"] }
 
 [workspace.lints.rust]
-bare_trait_objects = "warn"
-elided_lifetimes_in_paths = "warn"
-ellipsis_inclusive_range_patterns = "warn"
-explicit_outlives_requirements = "warn"
-unused_extern_crates = "warn"
+rust_2018_idioms = "warn"
 unused_lifetimes = "warn"
 unreachable_pub = "warn"
 semicolon_in_expressions_from_macros = "warn"
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-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index db0feb055e..85ec02ae07 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -54,7 +54,7 @@ impl RawAttrs {
                     let span = span_map.span_for_range(comment.syntax().text_range());
                     Attr {
                         id,
-                        input: Some(Box::new(AttrInput::Literal(tt::Literal {
+                        input: Some(Interned::new(AttrInput::Literal(tt::Literal {
                             text: SmolStr::new(format_smolstr!("\"{}\"", Self::escape_chars(doc))),
                             span,
                         }))),
@@ -199,7 +199,7 @@ impl AttrId {
 pub struct Attr {
     pub id: AttrId,
     pub path: Interned<ModPath>,
-    pub input: Option<Box<AttrInput>>,
+    pub input: Option<Interned<AttrInput>>,
     pub ctxt: SyntaxContextId,
 }
 
@@ -234,7 +234,7 @@ impl Attr {
         })?);
         let span = span_map.span_for_range(range);
         let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
-            Some(Box::new(AttrInput::Literal(tt::Literal {
+            Some(Interned::new(AttrInput::Literal(tt::Literal {
                 text: lit.token().text().into(),
                 span,
             })))
@@ -245,7 +245,7 @@ impl Attr {
                 span,
                 DocCommentDesugarMode::ProcMacro,
             );
-            Some(Box::new(AttrInput::TokenTree(Box::new(tree))))
+            Some(Interned::new(AttrInput::TokenTree(Box::new(tree))))
         } else {
             None
         };
@@ -281,12 +281,12 @@ impl Attr {
 
         let input = match input.first() {
             Some(tt::TokenTree::Subtree(tree)) => {
-                Some(Box::new(AttrInput::TokenTree(Box::new(tree.clone()))))
+                Some(Interned::new(AttrInput::TokenTree(Box::new(tree.clone()))))
             }
             Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))) => {
                 let input = match input.get(1) {
                     Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => {
-                        Some(Box::new(AttrInput::Literal(lit.clone())))
+                        Some(Interned::new(AttrInput::Literal(lit.clone())))
                     }
                     _ => None,
                 };
diff --git a/crates/hir-ty/src/builder.rs b/crates/hir-ty/src/builder.rs
index d6374658f1..41acd3555e 100644
--- a/crates/hir-ty/src/builder.rs
+++ b/crates/hir-ty/src/builder.rs
@@ -246,7 +246,6 @@ impl TyBuilder<()> {
     /// - yield type of coroutine ([`Coroutine::Yield`](std::ops::Coroutine::Yield))
     /// - return type of coroutine ([`Coroutine::Return`](std::ops::Coroutine::Return))
     /// - generic parameters in scope on `parent`
-    ///
     /// in this order.
     ///
     /// This method prepopulates the builder with placeholder substitution of `parent`, so you
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index 2e106877cb..d513355037 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -898,19 +898,20 @@ pub enum Rvalue {
     Cast(CastKind, Operand, Ty),
 
     // FIXME link to `pointer::offset` when it hits stable.
-    // /// * `Offset` has the same semantics as `pointer::offset`, except that the second
-    // ///   parameter may be a `usize` as well.
-    // /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats,
-    // ///   raw pointers, or function pointers and return a `bool`. The types of the operands must be
-    // ///   matching, up to the usual caveat of the lifetimes in function pointers.
-    // /// * Left and right shift operations accept signed or unsigned integers not necessarily of the
-    // ///   same type and return a value of the same type as their LHS. Like in Rust, the RHS is
-    // ///   truncated as needed.
-    // /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching
-    // ///   types and return a value of that type.
-    // /// * The remaining operations accept signed integers, unsigned integers, or floats with
-    // ///   matching types and return a value of that type.
+    /// * `Offset` has the same semantics as `pointer::offset`, except that the second
+    ///   parameter may be a `usize` as well.
+    /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats,
+    ///   raw pointers, or function pointers and return a `bool`. The types of the operands must be
+    ///   matching, up to the usual caveat of the lifetimes in function pointers.
+    /// * Left and right shift operations accept signed or unsigned integers not necessarily of the
+    ///   same type and return a value of the same type as their LHS. Like in Rust, the RHS is
+    ///   truncated as needed.
+    /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching
+    ///   types and return a value of that type.
+    /// * The remaining operations accept signed integers, unsigned integers, or floats with
+    ///   matching types and return a value of that type.
     //BinaryOp(BinOp, Box<(Operand, Operand)>),
+
     /// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition.
     ///
     /// When overflow checking is disabled and we are generating run-time code, the error condition
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/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs
index 7ac63562bb..f95ff1dc0f 100644
--- a/crates/hir/src/term_search/tactics.rs
+++ b/crates/hir/src/term_search/tactics.rs
@@ -5,7 +5,6 @@
 //! * `defs` - Set of items in scope at term search target location
 //! * `lookup` - Lookup table for types
 //! * `should_continue` - Function that indicates when to stop iterating
-//!
 //! And they return iterator that yields type trees that unify with the `goal` type.
 
 use std::iter;
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs
index 41693855be..0fc122d623 100644
--- a/crates/ide-assists/src/handlers/generate_function.rs
+++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -393,9 +393,9 @@ impl FunctionBuilder {
 /// The rule for whether we focus a return type or not (and thus focus the function body),
 /// is rather simple:
 /// * If we could *not* infer what the return type should be, focus it (so the user can fill-in
-///   the correct return type).
+/// the correct return type).
 /// * If we could infer the return type, don't focus it (and thus focus the function body) so the
-///   user can change the `todo!` function body.
+/// user can change the `todo!` function body.
 fn make_return_type(
     ctx: &AssistContext<'_>,
     expr: &ast::Expr,
@@ -918,9 +918,9 @@ fn filter_generic_params(ctx: &AssistContext<'_>, node: SyntaxNode) -> Option<hi
 /// Say we have a trait bound `Struct<T>: Trait<U>`. Given `necessary_params`, when is it relevant
 /// and when not? Some observations:
 /// - When `necessary_params` contains `T`, it's likely that we want this bound, but now we have
-///   an extra param to consider: `U`.
+/// an extra param to consider: `U`.
 /// - On the other hand, when `necessary_params` contains `U` (but not `T`), then it's unlikely
-///   that we want this bound because it doesn't really constrain `U`.
+/// that we want this bound because it doesn't really constrain `U`.
 ///
 /// (FIXME?: The latter clause might be overstating. We may want to include the bound if the self
 /// type does *not* include generic params at all - like `Option<i32>: From<U>`)
@@ -928,7 +928,7 @@ fn filter_generic_params(ctx: &AssistContext<'_>, node: SyntaxNode) -> Option<hi
 /// Can we make this a bit more formal? Let's define "dependency" between generic parameters and
 /// trait bounds:
 /// - A generic parameter `T` depends on a trait bound if `T` appears in the self type (i.e. left
-///   part) of the bound.
+/// part) of the bound.
 /// - A trait bound depends on a generic parameter `T` if `T` appears in the bound.
 ///
 /// Using the notion, what we want is all the bounds that params in `necessary_params`
diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs
index 88fa6dc745..44307ffd75 100644
--- a/crates/ide-assists/src/handlers/inline_call.rs
+++ b/crates/ide-assists/src/handlers/inline_call.rs
@@ -368,7 +368,7 @@ fn inline(
                     _ => None,
                 })
                 .for_each(|usage| {
-                    ted::replace(usage, this());
+                    ted::replace(usage, &this());
                 });
         }
     }
@@ -483,7 +483,7 @@ fn inline(
                 cov_mark::hit!(inline_call_inline_direct_field);
                 field.replace_expr(replacement.clone_for_update());
             } else {
-                ted::replace(usage.syntax(), replacement.syntax().clone_for_update());
+                ted::replace(usage.syntax(), &replacement.syntax().clone_for_update());
             }
         };
 
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-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 992ca18bb0..8283497d9a 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -452,7 +452,6 @@ pub(crate) struct CompletionContext<'a> {
     /// - crate-root
     ///  - mod foo
     ///   - mod bar
-    ///
     /// Here depth will be 2
     pub(crate) depth_from_crate_root: usize,
 }
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 1ca5206011..b4486d1502 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: u8,
+    num_worker_threads: usize,
     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 654a1cd316..f7e5b40dde 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -19,13 +19,6 @@ 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,
@@ -159,6 +152,10 @@ 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;
@@ -169,12 +166,10 @@ 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(|call| call.debug_render()).join("\n"));
+        expected_incoming.assert_eq(&incoming_calls.into_iter().map(debug_render).join("\n"));
 
         let outgoing_calls = analysis.outgoing_calls(item_pos).unwrap().unwrap();
-        expected_outgoing
-            .assert_eq(&outgoing_calls.into_iter().map(|call| call.debug_render()).join("\n"));
+        expected_outgoing.assert_eq(&outgoing_calls.into_iter().map(debug_render).join("\n"));
     }
 
     #[test]
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 e9408bf897..a2ac62341d 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: u8, cb: F) -> Cancellable<()>
+    pub fn parallel_prime_caches<F>(&self, num_worker_threads: usize, 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 2f387968c9..552ce9cd8c 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 junks, since `'` is not a reliable delimiter (consider
+        // state and this token contains junk, 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..128e86f1d6 100644
--- a/crates/load-cargo/src/lib.rs
+++ b/crates/load-cargo/src/lib.rs
@@ -15,7 +15,7 @@ use ide_db::{
 };
 use itertools::Itertools;
 use proc_macro_api::{MacroDylib, ProcMacroServer};
-use project_model::{CargoConfig, ManifestPath, PackageRoot, ProjectManifest, ProjectWorkspace};
+use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace};
 use span::Span;
 use tracing::instrument;
 use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf, VfsPath};
@@ -238,19 +238,6 @@ impl ProjectFolders {
             fsc.add_file_set(file_set_roots)
         }
 
-        // register the workspace manifest as well, note that this currently causes duplicates for
-        // non-virtual cargo workspaces! We ought to fix that
-        for manifest in workspaces.iter().filter_map(|ws| ws.manifest().map(ManifestPath::as_ref)) {
-            let file_set_roots: Vec<VfsPath> = vec![VfsPath::from(manifest.to_owned())];
-
-            let entry = vfs::loader::Entry::Files(vec![manifest.to_owned()]);
-
-            res.watch.push(res.load.len());
-            res.load.push(entry);
-            local_filesets.push(fsc.len() as u64);
-            fsc.add_file_set(file_set_roots)
-        }
-
         let fsc = fsc.build();
         res.source_root_config = SourceRootConfig { fsc, local_filesets };
 
@@ -285,6 +272,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 +280,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 +559,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/proc-macro-api/src/version.rs b/crates/proc-macro-api/src/version.rs
index 09b8125071..f768de3e31 100644
--- a/crates/proc-macro-api/src/version.rs
+++ b/crates/proc-macro-api/src/version.rs
@@ -93,7 +93,6 @@ fn read_section<'a>(dylib_binary: &'a [u8], section_name: &str) -> io::Result<&'
 ///   means bytes from here(including this sequence) are compressed in
 ///   snappy compression format. Version info is inside here, so decompress
 ///   this.
-///
 /// The bytes you get after decompressing the snappy format portion has
 /// following layout:
 /// * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes(again)
@@ -103,7 +102,6 @@ fn read_section<'a>(dylib_binary: &'a [u8], section_name: &str) -> io::Result<&'
 ///   for the version string's utf8 bytes
 /// * [version string bytes encoded in utf8] <- GET THIS BOI
 /// * [some more bytes that we don't really care but about still there] :-)
-///
 /// Check this issue for more about the bytes layout:
 /// <https://github.com/rust-lang/rust-analyzer/issues/6174>
 pub fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
diff --git a/crates/project-model/src/project_json.rs b/crates/project-model/src/project_json.rs
index 408593ea8a..5bee446f61 100644
--- a/crates/project-model/src/project_json.rs
+++ b/crates/project-model/src/project_json.rs
@@ -167,11 +167,6 @@ impl ProjectJson {
         &self.project_root
     }
 
-    /// Returns the path to the project's manifest file, if it exists.
-    pub fn manifest(&self) -> Option<&ManifestPath> {
-        self.manifest.as_ref()
-    }
-
     /// Returns the path to the project's manifest or root folder, if no manifest exists.
     pub fn manifest_or_root(&self) -> &AbsPath {
         self.manifest.as_ref().map_or(&self.project_root, |manifest| manifest.as_ref())
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 4dba11eac3..6b18b768cd 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -527,16 +527,6 @@ impl ProjectWorkspace {
         }
     }
 
-    pub fn manifest(&self) -> Option<&ManifestPath> {
-        match &self.kind {
-            ProjectWorkspaceKind::Cargo { cargo, .. } => Some(cargo.manifest_path()),
-            ProjectWorkspaceKind::Json(project) => project.manifest(),
-            ProjectWorkspaceKind::DetachedFile { cargo, .. } => {
-                Some(cargo.as_ref()?.0.manifest_path())
-            }
-        }
-    }
-
     pub fn find_sysroot_proc_macro_srv(&self) -> anyhow::Result<AbsPathBuf> {
         self.sysroot.discover_proc_macro_srv()
     }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 497fd67e92..e8504979be 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: ParallelCachePrimingNumThreads = 0u8,
+        cachePriming_numThreads: NumThreads = NumThreads::Physical,
 
         /// 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<usize> = None,
+        numThreads: Option<NumThreads> = None,
 
         /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
         procMacro_attributes_enable: bool = true,
@@ -968,8 +968,6 @@ macro_rules! try_or_def {
     };
 }
 
-type ParallelCachePrimingNumThreads = u8;
-
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum LinkedProject {
     ProjectManifest(ProjectManifest),
@@ -2095,15 +2093,22 @@ impl Config {
         }
     }
 
-    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 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 main_loop_num_threads(&self) -> usize {
-        self.numThreads().unwrap_or(num_cpus::get_physical())
+        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(),
+        }
     }
 
     pub fn typing_autoclose_angle(&self) -> bool {
@@ -2198,51 +2203,6 @@ 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)]
@@ -2357,10 +2317,10 @@ pub(crate) enum CallableCompletionDef {
 }
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(untagged)]
+#[serde(rename_all = "snake_case")]
 enum CargoFeaturesDef {
-    #[serde(with = "unit_v::all")]
     All,
+    #[serde(untagged)]
     Selected(Vec<String>),
 }
 
@@ -2382,25 +2342,27 @@ enum InvocationLocation {
 }
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(untagged)]
+#[serde(rename_all = "snake_case")]
 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(untagged)]
+#[serde(rename_all = "snake_case")]
 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)]
@@ -2413,36 +2375,39 @@ enum ClosureStyle {
 }
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(untagged)]
+#[serde(rename_all = "snake_case")]
 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(untagged)]
+#[serde(rename_all = "snake_case")]
 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(untagged)]
+#[serde(rename_all = "snake_case")]
 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)]
@@ -2466,9 +2431,11 @@ enum FilesWatcherDef {
 #[serde(rename_all = "snake_case")]
 enum ImportPrefixDef {
     Plain,
-    #[serde(alias = "self")]
+    #[serde(rename = "self")]
+    #[serde(alias = "by_self")]
     BySelf,
-    #[serde(alias = "crate")]
+    #[serde(rename = "crate")]
+    #[serde(alias = "by_crate")]
     ByCrate,
 }
 
@@ -2495,13 +2462,9 @@ 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,
 }
 
@@ -2524,6 +2487,15 @@ 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();
@@ -2776,6 +2748,10 @@ 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 {
@@ -3034,11 +3010,6 @@ 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": [
@@ -3260,7 +3231,44 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
                 },
             ],
         },
-        _ => panic!("missing entry for {ty}: {default}"),
+        "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})"),
     }
 
     map.into()
@@ -3341,7 +3349,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/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index b23e7b7e98..6798e058db 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -8,7 +8,6 @@ use ide_db::FxHashMap;
 use itertools::Itertools;
 use nohash_hasher::{IntMap, IntSet};
 use rustc_hash::FxHashSet;
-use stdx::iter_eq_by;
 use triomphe::Arc;
 
 use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext};
@@ -23,21 +22,14 @@ pub struct DiagnosticsMapConfig {
     pub check_ignore: FxHashSet<String>,
 }
 
-pub(crate) type DiagnosticsGeneration = usize;
-
 #[derive(Debug, Default, Clone)]
 pub(crate) struct DiagnosticCollection {
     // FIXME: should be IntMap<FileId, Vec<ra_id::Diagnostic>>
-    pub(crate) native: IntMap<FileId, (DiagnosticsGeneration, Vec<lsp_types::Diagnostic>)>,
+    pub(crate) native: IntMap<FileId, Vec<lsp_types::Diagnostic>>,
     // FIXME: should be Vec<flycheck::Diagnostic>
     pub(crate) check: IntMap<usize, IntMap<FileId, Vec<lsp_types::Diagnostic>>>,
     pub(crate) check_fixes: CheckFixes,
     changes: IntSet<FileId>,
-    /// Counter for supplying a new generation number for diagnostics.
-    /// This is used to keep track of when to clear the diagnostics for a given file as we compute
-    /// diagnostics on multiple worker threads simultaneously which may result in multiple diagnostics
-    /// updates for the same file in a single generation update (due to macros affecting multiple files).
-    generation: DiagnosticsGeneration,
 }
 
 #[derive(Debug, Clone)]
@@ -90,31 +82,21 @@ impl DiagnosticCollection {
 
     pub(crate) fn set_native_diagnostics(
         &mut self,
-        generation: DiagnosticsGeneration,
         file_id: FileId,
-        mut diagnostics: Vec<lsp_types::Diagnostic>,
+        diagnostics: Vec<lsp_types::Diagnostic>,
     ) {
-        diagnostics.sort_by_key(|it| (it.range.start, it.range.end));
-        if let Some((old_gen, existing_diagnostics)) = self.native.get_mut(&file_id) {
+        if let Some(existing_diagnostics) = self.native.get(&file_id) {
             if existing_diagnostics.len() == diagnostics.len()
-                && iter_eq_by(&diagnostics, &*existing_diagnostics, |new, existing| {
-                    are_diagnostics_equal(new, existing)
-                })
+                && diagnostics
+                    .iter()
+                    .zip(existing_diagnostics)
+                    .all(|(new, existing)| are_diagnostics_equal(new, existing))
             {
-                // don't signal an update if the diagnostics are the same
                 return;
             }
-            if *old_gen < generation || generation == 0 {
-                self.native.insert(file_id, (generation, diagnostics));
-            } else {
-                existing_diagnostics.extend(diagnostics);
-                // FIXME: Doing the merge step of a merge sort here would be a bit more performant
-                // but eh
-                existing_diagnostics.sort_by_key(|it| (it.range.start, it.range.end))
-            }
-        } else {
-            self.native.insert(file_id, (generation, diagnostics));
         }
+
+        self.native.insert(file_id, diagnostics);
         self.changes.insert(file_id);
     }
 
@@ -122,7 +104,7 @@ impl DiagnosticCollection {
         &self,
         file_id: FileId,
     ) -> impl Iterator<Item = &lsp_types::Diagnostic> {
-        let native = self.native.get(&file_id).into_iter().flat_map(|(_, d)| d);
+        let native = self.native.get(&file_id).into_iter().flatten();
         let check = self.check.values().filter_map(move |it| it.get(&file_id)).flatten();
         native.chain(check)
     }
@@ -133,11 +115,6 @@ impl DiagnosticCollection {
         }
         Some(mem::take(&mut self.changes))
     }
-
-    pub(crate) fn next_generation(&mut self) -> usize {
-        self.generation += 1;
-        self.generation
-    }
 }
 
 fn are_diagnostics_equal(left: &lsp_types::Diagnostic, right: &lsp_types::Diagnostic) -> bool {
@@ -149,8 +126,7 @@ fn are_diagnostics_equal(left: &lsp_types::Diagnostic, right: &lsp_types::Diagno
 
 pub(crate) fn fetch_native_diagnostics(
     snapshot: GlobalStateSnapshot,
-    subscriptions: std::sync::Arc<[FileId]>,
-    slice: std::ops::Range<usize>,
+    subscriptions: Vec<FileId>,
 ) -> Vec<(FileId, Vec<lsp_types::Diagnostic>)> {
     let _p = tracing::info_span!("fetch_native_diagnostics").entered();
     let _ctx = stdx::panic_context::enter("fetch_native_diagnostics".to_owned());
@@ -173,7 +149,7 @@ pub(crate) fn fetch_native_diagnostics(
     // the diagnostics produced may point to different files not requested by the concrete request,
     // put those into here and filter later
     let mut odd_ones = Vec::new();
-    let mut diagnostics = subscriptions[slice]
+    let mut diagnostics = subscriptions
         .iter()
         .copied()
         .filter_map(|file_id| {
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 3d5f525aaf..59431d7d42 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -163,9 +163,7 @@ pub(crate) struct GlobalStateSnapshot {
     pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
     vfs: Arc<RwLock<(vfs::Vfs, IntMap<FileId, LineEndings>)>>,
     pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
-    // used to signal semantic highlighting to fall back to syntax based highlighting until
-    // proc-macros have been loaded
-    // FIXME: Can we derive this from somewhere else?
+    // used to signal semantic highlighting to fall back to syntax based highlighting until proc-macros have been loaded
     pub(crate) proc_macros_loaded: bool,
     pub(crate) flycheck: Arc<[FlycheckHandle]>,
 }
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 07414a6e49..9b19e58eaa 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -3,7 +3,6 @@
 
 use std::{
     fmt,
-    ops::Div as _,
     time::{Duration, Instant},
 };
 
@@ -18,7 +17,7 @@ use vfs::FileId;
 
 use crate::{
     config::Config,
-    diagnostics::{fetch_native_diagnostics, DiagnosticsGeneration},
+    diagnostics::fetch_native_diagnostics,
     dispatch::{NotificationDispatcher, RequestDispatcher},
     global_state::{file_id_to_url, url_to_file_id, GlobalState},
     hack_recover_crate_name,
@@ -88,7 +87,7 @@ pub(crate) enum Task {
     Response(lsp_server::Response),
     ClientNotification(lsp_ext::UnindexedProjectParams),
     Retry(lsp_server::Request),
-    Diagnostics(DiagnosticsGeneration, Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
+    Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
     DiscoverTest(lsp_ext::DiscoverTestResults),
     PrimeCaches(PrimeCachesProgress),
     FetchWorkspace(ProjectWorkspaceProgress),
@@ -480,7 +479,7 @@ impl GlobalState {
 
     fn update_diagnostics(&mut self) {
         let db = self.analysis_host.raw_database();
-        let generation = self.diagnostics.next_generation();
+        // spawn a task per subscription?
         let subscriptions = {
             let vfs = &self.vfs.read().0;
             self.mem_docs
@@ -495,37 +494,16 @@ impl GlobalState {
                     // forever if we emitted them here.
                     !db.source_root(source_root).is_library
                 })
-                .collect::<std::sync::Arc<_>>()
+                .collect::<Vec<_>>()
         };
         tracing::trace!("updating notifications for {:?}", subscriptions);
-        // Split up the work on multiple threads, but we don't wanna fill the entire task pool with
-        // diagnostic tasks, so we limit the number of tasks to a quarter of the total thread pool.
-        let max_tasks = self.config.main_loop_num_threads().div(4).max(1);
-        let chunk_length = subscriptions.len() / max_tasks;
-        let remainder = subscriptions.len() % max_tasks;
-
-        let mut start = 0;
-        for task_idx in 0..max_tasks {
-            let extra = if task_idx < remainder { 1 } else { 0 };
-            let end = start + chunk_length + extra;
-            let slice = start..end;
-            if slice.is_empty() {
-                break;
-            }
-            // Diagnostics are triggered by the user typing
-            // so we run them on a latency sensitive thread.
-            self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, {
-                let snapshot = self.snapshot();
-                let subscriptions = subscriptions.clone();
-                move || {
-                    Task::Diagnostics(
-                        generation,
-                        fetch_native_diagnostics(snapshot, subscriptions, slice),
-                    )
-                }
-            });
-            start = end;
-        }
+
+        // Diagnostics are triggered by the user typing
+        // so we run them on a latency sensitive thread.
+        self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, {
+            let snapshot = self.snapshot();
+            move || Task::Diagnostics(fetch_native_diagnostics(snapshot, subscriptions))
+        });
     }
 
     fn update_tests(&mut self) {
@@ -612,9 +590,9 @@ impl GlobalState {
             // Only retry requests that haven't been cancelled. Otherwise we do unnecessary work.
             Task::Retry(req) if !self.is_completed(&req) => self.on_request(req),
             Task::Retry(_) => (),
-            Task::Diagnostics(generation, diagnostics_per_file) => {
+            Task::Diagnostics(diagnostics_per_file) => {
                 for (file_id, diagnostics) in diagnostics_per_file {
-                    self.diagnostics.set_native_diagnostics(generation, file_id, diagnostics)
+                    self.diagnostics.set_native_diagnostics(file_id, diagnostics)
                 }
             }
             Task::PrimeCaches(progress) => match progress {
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index bd0f733ef3..9d8f2b5fcc 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -440,19 +440,15 @@ impl GlobalState {
         }
 
         if let FilesWatcher::Client = self.config.files().watcher {
-            let filter = self
-                .workspaces
-                .iter()
-                .flat_map(|ws| ws.to_roots())
-                .filter(|it| it.is_local)
-                .map(|it| it.include);
+            let filter =
+                self.workspaces.iter().flat_map(|ws| ws.to_roots()).filter(|it| it.is_local);
 
             let mut watchers: Vec<FileSystemWatcher> =
                 if self.config.did_change_watched_files_relative_pattern_support() {
                     // When relative patterns are supported by the client, prefer using them
                     filter
-                        .flat_map(|include| {
-                            include.into_iter().flat_map(|base| {
+                        .flat_map(|root| {
+                            root.include.into_iter().flat_map(|base| {
                                 [
                                     (base.clone(), "**/*.rs"),
                                     (base.clone(), "**/Cargo.{lock,toml}"),
@@ -475,8 +471,8 @@ impl GlobalState {
                 } else {
                     // When they're not, integrate the base to make them into absolute patterns
                     filter
-                        .flat_map(|include| {
-                            include.into_iter().flat_map(|base| {
+                        .flat_map(|root| {
+                            root.include.into_iter().flat_map(|base| {
                                 [
                                     format!("{base}/**/*.rs"),
                                     format!("{base}/**/Cargo.{{toml,lock}}"),
@@ -492,14 +488,13 @@ impl GlobalState {
                 };
 
             watchers.extend(
-                iter::once(self.config.user_config_path().as_path())
-                    .chain(iter::once(self.config.root_ratoml_path().as_path()))
-                    .chain(self.workspaces.iter().map(|ws| ws.manifest().map(ManifestPath::as_ref)))
-                    .flatten()
+                iter::once(self.config.user_config_path().to_string())
+                    .chain(iter::once(self.config.root_ratoml_path().to_string()))
                     .map(|glob_pattern| lsp_types::FileSystemWatcher {
-                        glob_pattern: lsp_types::GlobPattern::String(glob_pattern.to_string()),
+                        glob_pattern: lsp_types::GlobPattern::String(glob_pattern),
                         kind: None,
-                    }),
+                    })
+                    .collect::<Vec<FileSystemWatcher>>(),
             );
 
             let registration_options =
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs
index eab66f10a9..b07e97cd6c 100644
--- a/crates/vfs/src/lib.rs
+++ b/crates/vfs/src/lib.rs
@@ -282,7 +282,7 @@ impl Vfs {
     /// Returns the id associated with `path`
     ///
     /// - If `path` does not exists in the `Vfs`, allocate a new id for it, associated with a
-    ///   deleted file;
+    /// deleted file;
     /// - Else, returns `path`'s id.
     ///
     /// Does not record a change.
diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs
index 92a49e0793..2d3fb9d88c 100644
--- a/crates/vfs/src/vfs_path.rs
+++ b/crates/vfs/src/vfs_path.rs
@@ -384,7 +384,8 @@ impl VirtualPath {
     ///
     /// # Returns
     /// - `None` if `self` ends with `"//"`.
-    /// - `Some((name, None))` if `self`'s base contains no `.`, or only one `.` at the start.
+    /// - `Some((name, None))` if `self`'s base contains no `.`, or only one `.` at
+    /// the start.
     /// - `Some((name, Some(extension))` else.
     ///
     /// # Note
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 8993a46d2b..14aae91741 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: `0`)::
+[[rust-analyzer.cachePriming.numThreads]]rust-analyzer.cachePriming.numThreads (default: `"physical"`)::
 +
 --
 How many worker threads to handle priming caches. The default `0` means to pick automatically.
diff --git a/editors/code/package.json b/editors/code/package.json
index cd11b56d15..b447106d99 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -609,10 +609,25 @@
                 "properties": {
                     "rust-analyzer.cachePriming.numThreads": {
                         "markdownDescription": "How many worker threads to handle priming caches. The default `0` means to pick automatically.",
-                        "default": 0,
-                        "type": "number",
-                        "minimum": 0,
-                        "maximum": 255
+                        "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"
+                                ]
+                            }
+                        ]
                     }
                 }
             },
@@ -2225,11 +2240,27 @@
                     "rust-analyzer.numThreads": {
                         "markdownDescription": "How many worker threads in the main loop. The default `null` means to pick automatically.",
                         "default": null,
-                        "type": [
-                            "null",
-                            "integer"
-                        ],
-                        "minimum": 0
+                        "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"
+                                ]
+                            }
+                        ]
                     }
                 }
             },


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)(