about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/Cargo.lock1
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/change.rs21
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/input.rs510
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/lib.rs86
-rw-r--r--src/tools/rust-analyzer/crates/cfg/src/lib.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/db.rs41
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expander.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs19
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/find_path.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/generics.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/import_map.rs29
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs47
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres.rs110
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs162
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs60
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs75
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/pretty.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs102
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/src.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/test_db.rs28
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/change.rs23
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/db.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/eager.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/name.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs159
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs22
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/db.rs43
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs29
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/traits.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/utils.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/from_id.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs70
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/symbols.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/lib.rs27
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs36
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt20
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/expand_macro.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs44
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/parent_module.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/runnables.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/static_index.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/status.rs23
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/test_explorer.rs37
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs60
-rw-r--r--src/tools/rust-analyzer/crates/intern/src/symbol.rs14
-rw-r--r--src/tools/rust-analyzer/crates/load-cargo/src/lib.rs31
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs10
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs130
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/project_json.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/tests.rs12
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs184
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt448
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt448
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt448
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt962
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt832
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs6
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs20
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs95
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs4
-rw-r--r--src/tools/rust-analyzer/crates/test-fixture/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/test-fixture/src/lib.rs98
108 files changed, 3629 insertions, 2511 deletions
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index efd7362594c..00da1c973dc 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -2418,6 +2418,7 @@ dependencies = [
  "span",
  "stdx",
  "test-utils",
+ "triomphe",
  "tt",
 ]
 
diff --git a/src/tools/rust-analyzer/crates/base-db/src/change.rs b/src/tools/rust-analyzer/crates/base-db/src/change.rs
index 90413a573ae..1f19556a766 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/change.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/change.rs
@@ -3,20 +3,18 @@
 
 use std::fmt;
 
-use rustc_hash::FxHashMap;
 use salsa::Durability;
 use triomphe::Arc;
 use vfs::FileId;
 
-use crate::{CrateGraph, CrateId, CrateWorkspaceData, RootQueryDb, SourceRoot, SourceRootId};
+use crate::{CrateGraphBuilder, CratesIdMap, RootQueryDb, SourceRoot, SourceRootId};
 
 /// Encapsulate a bunch of raw `.set` calls on the database.
 #[derive(Default)]
 pub struct FileChange {
     pub roots: Option<Vec<SourceRoot>>,
     pub files_changed: Vec<(FileId, Option<String>)>,
-    pub crate_graph: Option<CrateGraph>,
-    pub ws_data: Option<FxHashMap<CrateId, Arc<CrateWorkspaceData>>>,
+    pub crate_graph: Option<CrateGraphBuilder>,
 }
 
 impl fmt::Debug for FileChange {
@@ -48,15 +46,11 @@ impl FileChange {
         self.files_changed.push((file_id, new_text))
     }
 
-    pub fn set_crate_graph(&mut self, graph: CrateGraph) {
+    pub fn set_crate_graph(&mut self, graph: CrateGraphBuilder) {
         self.crate_graph = Some(graph);
     }
 
-    pub fn set_ws_data(&mut self, data: FxHashMap<CrateId, Arc<CrateWorkspaceData>>) {
-        self.ws_data = Some(data);
-    }
-
-    pub fn apply(self, db: &mut dyn RootQueryDb) {
+    pub fn apply(self, db: &mut dyn RootQueryDb) -> Option<CratesIdMap> {
         let _p = tracing::info_span!("FileChange::apply").entered();
         if let Some(roots) = self.roots {
             for (idx, root) in roots.into_iter().enumerate() {
@@ -79,12 +73,11 @@ impl FileChange {
             let text = text.unwrap_or_default();
             db.set_file_text_with_durability(file_id, &text, durability)
         }
+
         if let Some(crate_graph) = self.crate_graph {
-            db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH);
-        }
-        if let Some(data) = self.ws_data {
-            db.set_crate_workspace_data_with_durability(Arc::new(data), Durability::HIGH);
+            return Some(crate_graph.set_in_db(db));
         }
+        None
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs
index bd08387b582..c4e64b372f3 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/input.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs
@@ -6,17 +6,23 @@
 //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how
 //! actual IO is done and lowered to input.
 
+use std::hash::BuildHasherDefault;
 use std::{fmt, mem, ops};
 
-use cfg::CfgOptions;
+use cfg::{CfgOptions, HashableCfgOptions};
+use dashmap::mapref::entry::Entry;
+use dashmap::DashMap;
 use intern::Symbol;
 use la_arena::{Arena, Idx, RawIdx};
-use rustc_hash::{FxHashMap, FxHashSet};
+use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
+use salsa::{Durability, Setter};
 use span::{Edition, EditionedFileId};
 use triomphe::Arc;
 use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath};
 
-pub type ProcMacroPaths = FxHashMap<CrateId, Result<(String, AbsPathBuf), String>>;
+use crate::{CrateWorkspaceData, RootQueryDb};
+
+pub type ProcMacroPaths = FxHashMap<CrateBuilderId, Result<(String, AbsPathBuf), String>>;
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct SourceRootId(pub u32);
@@ -64,30 +70,31 @@ impl SourceRoot {
     }
 }
 
-/// `CrateGraph` is a bit of information which turns a set of text files into a
-/// number of Rust crates.
-///
-/// Each crate is defined by the `FileId` of its root module, the set of enabled
-/// `cfg` flags and the set of dependencies.
-///
-/// Note that, due to cfg's, there might be several crates for a single `FileId`!
-///
-/// For the purposes of analysis, a crate does not have a name. Instead, names
-/// are specified on dependency edges. That is, a crate might be known under
-/// different names in different dependent crates.
-///
-/// Note that `CrateGraph` is build-system agnostic: it's a concept of the Rust
-/// language proper, not a concept of the build system. In practice, we get
-/// `CrateGraph` by lowering `cargo metadata` output.
-///
-/// `CrateGraph` is `!Serialize` by design, see
-/// <https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/architecture.md#serialization>
-#[derive(Clone, Default)]
-pub struct CrateGraph {
-    arena: Arena<CrateData>,
+#[derive(Default, Clone)]
+pub struct CrateGraphBuilder {
+    arena: Arena<CrateBuilder>,
 }
 
-impl fmt::Debug for CrateGraph {
+pub type CrateBuilderId = Idx<CrateBuilder>;
+
+impl ops::Index<CrateBuilderId> for CrateGraphBuilder {
+    type Output = CrateBuilder;
+
+    fn index(&self, index: CrateBuilderId) -> &Self::Output {
+        &self.arena[index]
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct CrateBuilder {
+    pub basic: CrateDataBuilder,
+    pub extra: ExtraCrateData,
+    pub cfg_options: Arc<CfgOptions>,
+    pub env: Env,
+    ws_data: Arc<CrateWorkspaceData>,
+}
+
+impl fmt::Debug for CrateGraphBuilder {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_map()
             .entries(self.arena.iter().map(|(id, data)| (u32::from(id.into_raw()), data)))
@@ -95,8 +102,6 @@ impl fmt::Debug for CrateGraph {
     }
 }
 
-pub type CrateId = Idx<CrateData>;
-
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct CrateName(Symbol);
 
@@ -272,28 +277,34 @@ impl ReleaseChannel {
     }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct CrateData {
+/// The crate data from which we derive the `Crate`.
+///
+/// We want this to contain as little data as possible, because if it contains dependencies and
+/// something changes, this crate and all of its dependencies ids are invalidated, which causes
+/// pretty much everything to be recomputed. If the crate id is not invalidated, only this crate's
+/// information needs to be recomputed.
+///
+/// *Most* different crates have different root files (actually, pretty much all of them).
+/// Still, it is possible to have crates distinguished by other factors (e.g. dependencies).
+/// So we store only the root file - unless we find that this crate has the same root file as
+/// another crate, in which case we store all data for one of them (if one is a dependency of
+/// the other, we store for it, because it has more dependencies to be invalidated).
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct UniqueCrateData {
+    root_file_id: FileId,
+    disambiguator: Option<Box<(BuiltCrateData, HashableCfgOptions)>>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct CrateData<Id> {
     pub root_file_id: FileId,
     pub edition: Edition,
-    pub version: Option<String>,
-    /// A name used in the package's project declaration: for Cargo projects,
-    /// its `[package].name` can be different for other project types or even
-    /// absent (a dummy crate for the code snippet, for example).
-    ///
-    /// For purposes of analysis, crates are anonymous (only names in
-    /// `Dependency` matters), this name should only be used for UI.
-    pub display_name: Option<CrateDisplayName>,
-    pub cfg_options: Arc<CfgOptions>,
-    /// The cfg options that could be used by the crate
-    pub potential_cfg_options: Option<Arc<CfgOptions>>,
-    pub env: Env,
     /// The dependencies of this crate.
     ///
     /// Note that this may contain more dependencies than the crate actually uses.
     /// A common example is the test crate which is included but only actually is active when
     /// declared in source via `extern crate test`.
-    pub dependencies: Vec<Dependency>,
+    pub dependencies: Vec<Dependency<Id>>,
     pub origin: CrateOrigin,
     pub is_proc_macro: bool,
     /// The working directory to run proc-macros in. This is the workspace root of the cargo workspace
@@ -301,6 +312,23 @@ pub struct CrateData {
     pub proc_macro_cwd: Option<AbsPathBuf>,
 }
 
+pub type CrateDataBuilder = CrateData<CrateBuilderId>;
+pub type BuiltCrateData = CrateData<Crate>;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ExtraCrateData {
+    pub version: Option<String>,
+    /// A name used in the package's project declaration: for Cargo projects,
+    /// its `[package].name` can be different for other project types or even
+    /// absent (a dummy crate for the code snippet, for example).
+    ///
+    /// For purposes of analysis, crates are anonymous (only names in
+    /// `Dependency` matters), this name should only be used for UI.
+    pub display_name: Option<CrateDisplayName>,
+    /// The cfg options that could be used by the crate
+    pub potential_cfg_options: Option<CfgOptions>,
+}
+
 #[derive(Default, Clone, PartialEq, Eq)]
 pub struct Env {
     entries: FxHashMap<String, String>,
@@ -326,22 +354,32 @@ impl fmt::Debug for Env {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Dependency {
-    pub crate_id: CrateId,
+pub struct Dependency<Id> {
+    pub crate_id: Id,
     pub name: CrateName,
     prelude: bool,
     sysroot: bool,
 }
 
-impl Dependency {
-    pub fn new(name: CrateName, crate_id: CrateId) -> Self {
+pub type DependencyBuilder = Dependency<CrateBuilderId>;
+pub type BuiltDependency = Dependency<Crate>;
+
+impl DependencyBuilder {
+    pub fn new(name: CrateName, crate_id: CrateBuilderId) -> Self {
         Self { name, crate_id, prelude: true, sysroot: false }
     }
 
-    pub fn with_prelude(name: CrateName, crate_id: CrateId, prelude: bool, sysroot: bool) -> Self {
+    pub fn with_prelude(
+        name: CrateName,
+        crate_id: CrateBuilderId,
+        prelude: bool,
+        sysroot: bool,
+    ) -> Self {
         Self { name, crate_id, prelude, sysroot }
     }
+}
 
+impl BuiltDependency {
     /// Whether this dependency is to be added to the depending crate's extern prelude.
     pub fn is_prelude(&self) -> bool {
         self.prelude
@@ -353,7 +391,32 @@ impl Dependency {
     }
 }
 
-impl CrateGraph {
+pub type CratesIdMap = FxHashMap<CrateBuilderId, Crate>;
+
+#[salsa::input]
+pub struct Crate {
+    #[return_ref]
+    pub data: BuiltCrateData,
+    /// Crate data that is not needed for analysis.
+    ///
+    /// This is split into a separate field to increase incrementality.
+    #[return_ref]
+    pub extra_data: ExtraCrateData,
+    // This is in `Arc` because it is shared for all crates in a workspace.
+    #[return_ref]
+    pub workspace_data: Arc<CrateWorkspaceData>,
+    // FIXME: Remove this `Arc`.
+    #[return_ref]
+    pub cfg_options: Arc<CfgOptions>,
+    #[return_ref]
+    pub env: Env,
+}
+
+/// The mapping from [`UniqueCrateData`] to their [`Crate`] input.
+#[derive(Debug, Default)]
+pub struct CratesMap(DashMap<UniqueCrateData, Crate, BuildHasherDefault<FxHasher>>);
+
+impl CrateGraphBuilder {
     pub fn add_crate_root(
         &mut self,
         root_file_id: FileId,
@@ -361,33 +424,34 @@ impl CrateGraph {
         display_name: Option<CrateDisplayName>,
         version: Option<String>,
         cfg_options: Arc<CfgOptions>,
-        potential_cfg_options: Option<Arc<CfgOptions>>,
+        potential_cfg_options: Option<CfgOptions>,
         mut env: Env,
         origin: CrateOrigin,
         is_proc_macro: bool,
         proc_macro_cwd: Option<AbsPathBuf>,
-    ) -> CrateId {
+        ws_data: Arc<CrateWorkspaceData>,
+    ) -> CrateBuilderId {
         env.entries.shrink_to_fit();
-        let data = CrateData {
-            root_file_id,
-            edition,
-            version,
-            display_name,
+        self.arena.alloc(CrateBuilder {
+            basic: CrateData {
+                root_file_id,
+                edition,
+                dependencies: Vec::new(),
+                origin,
+                is_proc_macro,
+                proc_macro_cwd,
+            },
+            extra: ExtraCrateData { version, display_name, potential_cfg_options },
             cfg_options,
-            potential_cfg_options,
             env,
-            dependencies: Vec::new(),
-            origin,
-            is_proc_macro,
-            proc_macro_cwd,
-        };
-        self.arena.alloc(data)
+            ws_data,
+        })
     }
 
     pub fn add_dep(
         &mut self,
-        from: CrateId,
-        dep: Dependency,
+        from: CrateBuilderId,
+        dep: DependencyBuilder,
     ) -> Result<(), CyclicDependenciesError> {
         let _p = tracing::info_span!("add_dep").entered();
 
@@ -395,37 +459,160 @@ impl CrateGraph {
         // that out, look for a  path in the *opposite* direction, from `to` to
         // `from`.
         if let Some(path) = self.find_path(&mut FxHashSet::default(), dep.crate_id, from) {
-            let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect();
+            let path =
+                path.into_iter().map(|it| (it, self[it].extra.display_name.clone())).collect();
             let err = CyclicDependenciesError { path };
             assert!(err.from().0 == from && err.to().0 == dep.crate_id);
             return Err(err);
         }
 
-        self.arena[from].add_dep(dep);
+        self.arena[from].basic.dependencies.push(dep);
         Ok(())
     }
 
-    pub fn is_empty(&self) -> bool {
-        self.arena.is_empty()
-    }
+    pub fn set_in_db(self, db: &mut dyn RootQueryDb) -> CratesIdMap {
+        let mut all_crates = Vec::with_capacity(self.arena.len());
+        let mut visited = FxHashMap::default();
+        let mut visited_root_files = FxHashSet::default();
 
-    pub fn len(&self) -> usize {
-        self.arena.len()
+        let old_all_crates = db.all_crates();
+
+        let crates_map = db.crates_map();
+        // salsa doesn't compare new input to old input to see if they are the same, so here we are doing all the work ourselves.
+        for krate in self.iter() {
+            go(
+                &self,
+                db,
+                &crates_map,
+                &mut visited,
+                &mut visited_root_files,
+                &mut all_crates,
+                krate,
+            );
+        }
+
+        if **old_all_crates != *all_crates {
+            db.set_all_crates_with_durability(
+                Arc::new(all_crates.into_boxed_slice()),
+                Durability::HIGH,
+            );
+        }
+
+        return visited;
+
+        fn go(
+            graph: &CrateGraphBuilder,
+            db: &mut dyn RootQueryDb,
+            crates_map: &CratesMap,
+            visited: &mut FxHashMap<CrateBuilderId, Crate>,
+            visited_root_files: &mut FxHashSet<FileId>,
+            all_crates: &mut Vec<Crate>,
+            source: CrateBuilderId,
+        ) -> Crate {
+            if let Some(&crate_id) = visited.get(&source) {
+                return crate_id;
+            }
+            let krate = &graph[source];
+            let dependencies = krate
+                .basic
+                .dependencies
+                .iter()
+                .map(|dep| BuiltDependency {
+                    crate_id: go(
+                        graph,
+                        db,
+                        crates_map,
+                        visited,
+                        visited_root_files,
+                        all_crates,
+                        dep.crate_id,
+                    ),
+                    name: dep.name.clone(),
+                    prelude: dep.prelude,
+                    sysroot: dep.sysroot,
+                })
+                .collect::<Vec<_>>();
+            let crate_data = BuiltCrateData {
+                dependencies,
+                edition: krate.basic.edition,
+                is_proc_macro: krate.basic.is_proc_macro,
+                origin: krate.basic.origin.clone(),
+                root_file_id: krate.basic.root_file_id,
+                proc_macro_cwd: krate.basic.proc_macro_cwd.clone(),
+            };
+            let disambiguator = if visited_root_files.insert(krate.basic.root_file_id) {
+                None
+            } else {
+                Some(Box::new((crate_data.clone(), krate.cfg_options.to_hashable())))
+            };
+
+            let unique_crate_data =
+                UniqueCrateData { root_file_id: krate.basic.root_file_id, disambiguator };
+            let crate_input = match crates_map.0.entry(unique_crate_data) {
+                Entry::Occupied(entry) => {
+                    let old_crate = *entry.get();
+                    if crate_data != *old_crate.data(db) {
+                        old_crate.set_data(db).with_durability(Durability::HIGH).to(crate_data);
+                    }
+                    if krate.extra != *old_crate.extra_data(db) {
+                        old_crate
+                            .set_extra_data(db)
+                            .with_durability(Durability::HIGH)
+                            .to(krate.extra.clone());
+                    }
+                    if krate.cfg_options != *old_crate.cfg_options(db) {
+                        old_crate
+                            .set_cfg_options(db)
+                            .with_durability(Durability::HIGH)
+                            .to(krate.cfg_options.clone());
+                    }
+                    if krate.env != *old_crate.env(db) {
+                        old_crate
+                            .set_env(db)
+                            .with_durability(Durability::HIGH)
+                            .to(krate.env.clone());
+                    }
+                    if krate.ws_data != *old_crate.workspace_data(db) {
+                        old_crate
+                            .set_workspace_data(db)
+                            .with_durability(Durability::HIGH)
+                            .to(krate.ws_data.clone());
+                    }
+                    old_crate
+                }
+                Entry::Vacant(entry) => {
+                    let input = Crate::builder(
+                        crate_data,
+                        krate.extra.clone(),
+                        krate.ws_data.clone(),
+                        krate.cfg_options.clone(),
+                        krate.env.clone(),
+                    )
+                    .durability(Durability::HIGH)
+                    .new(db);
+                    entry.insert(input);
+                    input
+                }
+            };
+            all_crates.push(crate_input);
+            visited.insert(source, crate_input);
+            crate_input
+        }
     }
 
-    pub fn iter(&self) -> impl Iterator<Item = CrateId> + '_ {
+    pub fn iter(&self) -> impl Iterator<Item = CrateBuilderId> + '_ {
         self.arena.iter().map(|(idx, _)| idx)
     }
 
     // FIXME: used for fixing up the toolchain sysroot, should be removed and done differently
     #[doc(hidden)]
-    pub fn iter_mut(&mut self) -> impl Iterator<Item = (CrateId, &mut CrateData)> + '_ {
+    pub fn iter_mut(&mut self) -> impl Iterator<Item = (CrateBuilderId, &mut CrateBuilder)> + '_ {
         self.arena.iter_mut()
     }
 
     /// Returns an iterator over all transitive dependencies of the given crate,
     /// including the crate itself.
-    pub fn transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> {
+    pub fn transitive_deps(&self, of: CrateBuilderId) -> impl Iterator<Item = CrateBuilderId> {
         let mut worklist = vec![of];
         let mut deps = FxHashSet::default();
 
@@ -434,42 +621,15 @@ impl CrateGraph {
                 continue;
             }
 
-            worklist.extend(self[krate].dependencies.iter().map(|dep| dep.crate_id));
+            worklist.extend(self[krate].basic.dependencies.iter().map(|dep| dep.crate_id));
         }
 
         deps.into_iter()
     }
 
-    /// Returns all transitive reverse dependencies of the given crate,
-    /// including the crate itself.
-    pub fn transitive_rev_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> {
-        let mut worklist = vec![of];
-        let mut rev_deps = FxHashSet::default();
-        rev_deps.insert(of);
-
-        let mut inverted_graph = FxHashMap::<_, Vec<_>>::default();
-        self.arena.iter().for_each(|(krate, data)| {
-            data.dependencies
-                .iter()
-                .for_each(|dep| inverted_graph.entry(dep.crate_id).or_default().push(krate))
-        });
-
-        while let Some(krate) = worklist.pop() {
-            if let Some(krate_rev_deps) = inverted_graph.get(&krate) {
-                krate_rev_deps
-                    .iter()
-                    .copied()
-                    .filter(|&rev_dep| rev_deps.insert(rev_dep))
-                    .for_each(|rev_dep| worklist.push(rev_dep));
-            }
-        }
-
-        rev_deps.into_iter()
-    }
-
     /// Returns all crates in the graph, sorted in topological order (ie. dependencies of a crate
     /// come before the crate itself).
-    pub fn crates_in_topological_order(&self) -> Vec<CrateId> {
+    fn crates_in_topological_order(&self) -> Vec<CrateBuilderId> {
         let mut res = Vec::new();
         let mut visited = FxHashSet::default();
 
@@ -480,15 +640,15 @@ impl CrateGraph {
         return res;
 
         fn go(
-            graph: &CrateGraph,
-            visited: &mut FxHashSet<CrateId>,
-            res: &mut Vec<CrateId>,
-            source: CrateId,
+            graph: &CrateGraphBuilder,
+            visited: &mut FxHashSet<CrateBuilderId>,
+            res: &mut Vec<CrateBuilderId>,
+            source: CrateBuilderId,
         ) {
             if !visited.insert(source) {
                 return;
             }
-            for dep in graph[source].dependencies.iter() {
+            for dep in graph[source].basic.dependencies.iter() {
                 go(graph, visited, res, dep.crate_id)
             }
             res.push(source)
@@ -504,23 +664,27 @@ impl CrateGraph {
     /// Returns a map mapping `other`'s IDs to the new IDs in `self`.
     pub fn extend(
         &mut self,
-        mut other: CrateGraph,
+        mut other: CrateGraphBuilder,
         proc_macros: &mut ProcMacroPaths,
-    ) -> FxHashMap<CrateId, CrateId> {
+    ) -> FxHashMap<CrateBuilderId, CrateBuilderId> {
         // Sorting here is a bit pointless because the input is likely already sorted.
         // However, the overhead is small and it makes the `extend` method harder to misuse.
         self.arena
             .iter_mut()
-            .for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id));
+            .for_each(|(_, data)| data.basic.dependencies.sort_by_key(|dep| dep.crate_id));
 
-        let m = self.len();
+        let m = self.arena.len();
         let topo = other.crates_in_topological_order();
-        let mut id_map: FxHashMap<CrateId, CrateId> = FxHashMap::default();
+        let mut id_map: FxHashMap<CrateBuilderId, CrateBuilderId> = FxHashMap::default();
         for topo in topo {
             let crate_data = &mut other.arena[topo];
 
-            crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]);
-            crate_data.dependencies.sort_by_key(|dep| dep.crate_id);
+            crate_data
+                .basic
+                .dependencies
+                .iter_mut()
+                .for_each(|dep| dep.crate_id = id_map[&dep.crate_id]);
+            crate_data.basic.dependencies.sort_by_key(|dep| dep.crate_id);
 
             let find = self.arena.iter().take(m).find_map(|(k, v)| (v == crate_data).then_some(k));
             let new_id = find.unwrap_or_else(|| self.arena.alloc(crate_data.clone()));
@@ -534,10 +698,10 @@ impl CrateGraph {
 
     fn find_path(
         &self,
-        visited: &mut FxHashSet<CrateId>,
-        from: CrateId,
-        to: CrateId,
-    ) -> Option<Vec<CrateId>> {
+        visited: &mut FxHashSet<CrateBuilderId>,
+        from: CrateBuilderId,
+        to: CrateBuilderId,
+    ) -> Option<Vec<CrateBuilderId>> {
         if !visited.insert(from) {
             return None;
         }
@@ -546,7 +710,7 @@ impl CrateGraph {
             return Some(vec![to]);
         }
 
-        for dep in &self[from].dependencies {
+        for dep in &self[from].basic.dependencies {
             let crate_id = dep.crate_id;
             if let Some(mut path) = self.find_path(visited, crate_id, to) {
                 path.push(from);
@@ -559,7 +723,10 @@ impl CrateGraph {
 
     /// Removes all crates from this crate graph except for the ones in `to_keep` and fixes up the dependencies.
     /// Returns a mapping from old crate ids to new crate ids.
-    pub fn remove_crates_except(&mut self, to_keep: &[CrateId]) -> Vec<Option<CrateId>> {
+    pub fn remove_crates_except(
+        &mut self,
+        to_keep: &[CrateBuilderId],
+    ) -> Vec<Option<CrateBuilderId>> {
         let mut id_map = vec![None; self.arena.len()];
         self.arena = std::mem::take(&mut self.arena)
             .into_iter()
@@ -567,12 +734,12 @@ impl CrateGraph {
             .enumerate()
             .map(|(new_id, (id, data))| {
                 id_map[id.into_raw().into_u32() as usize] =
-                    Some(CrateId::from_raw(RawIdx::from_u32(new_id as u32)));
+                    Some(CrateBuilderId::from_raw(RawIdx::from_u32(new_id as u32)));
                 data
             })
             .collect();
         for (_, data) in self.arena.iter_mut() {
-            data.dependencies.iter_mut().for_each(|dep| {
+            data.basic.dependencies.iter_mut().for_each(|dep| {
                 dep.crate_id =
                     id_map[dep.crate_id.into_raw().into_u32() as usize].expect("crate was filtered")
             });
@@ -585,20 +752,34 @@ impl CrateGraph {
     }
 }
 
-impl ops::Index<CrateId> for CrateGraph {
-    type Output = CrateData;
-    fn index(&self, crate_id: CrateId) -> &CrateData {
-        &self.arena[crate_id]
+pub(crate) fn transitive_rev_deps(db: &dyn RootQueryDb, of: Crate) -> FxHashSet<Crate> {
+    let mut worklist = vec![of];
+    let mut rev_deps = FxHashSet::default();
+    rev_deps.insert(of);
+
+    let mut inverted_graph = FxHashMap::<_, Vec<_>>::default();
+    db.all_crates().iter().for_each(|&krate| {
+        krate
+            .data(db)
+            .dependencies
+            .iter()
+            .for_each(|dep| inverted_graph.entry(dep.crate_id).or_default().push(krate))
+    });
+
+    while let Some(krate) = worklist.pop() {
+        if let Some(crate_rev_deps) = inverted_graph.get(&krate) {
+            crate_rev_deps
+                .iter()
+                .copied()
+                .filter(|&rev_dep| rev_deps.insert(rev_dep))
+                .for_each(|rev_dep| worklist.push(rev_dep));
+        }
     }
-}
 
-impl CrateData {
-    /// Add a dependency to `self` without checking if the dependency
-    // is existent among `self.dependencies`.
-    fn add_dep(&mut self, dep: Dependency) {
-        self.dependencies.push(dep)
-    }
+    rev_deps
+}
 
+impl BuiltCrateData {
     pub fn root_file_id(&self) -> EditionedFileId {
         EditionedFileId::new(self.root_file_id, self.edition)
     }
@@ -657,21 +838,21 @@ impl<'a> IntoIterator for &'a Env {
 
 #[derive(Debug)]
 pub struct CyclicDependenciesError {
-    path: Vec<(CrateId, Option<CrateDisplayName>)>,
+    path: Vec<(CrateBuilderId, Option<CrateDisplayName>)>,
 }
 
 impl CyclicDependenciesError {
-    fn from(&self) -> &(CrateId, Option<CrateDisplayName>) {
+    fn from(&self) -> &(CrateBuilderId, Option<CrateDisplayName>) {
         self.path.first().unwrap()
     }
-    fn to(&self) -> &(CrateId, Option<CrateDisplayName>) {
+    fn to(&self) -> &(CrateBuilderId, Option<CrateDisplayName>) {
         self.path.last().unwrap()
     }
 }
 
 impl fmt::Display for CyclicDependenciesError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let render = |(id, name): &(CrateId, Option<CrateDisplayName>)| match name {
+        let render = |(id, name): &(CrateBuilderId, Option<CrateDisplayName>)| match name {
             Some(it) => format!("{it}({id:?})"),
             None => format!("{id:?}"),
         };
@@ -688,13 +869,19 @@ impl fmt::Display for CyclicDependenciesError {
 
 #[cfg(test)]
 mod tests {
-    use crate::CrateOrigin;
+    use triomphe::Arc;
+
+    use crate::{CrateWorkspaceData, DependencyBuilder};
 
-    use super::{CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
+    use super::{CrateGraphBuilder, CrateName, CrateOrigin, Edition::Edition2018, Env, FileId};
+
+    fn empty_ws_data() -> Arc<CrateWorkspaceData> {
+        Arc::new(CrateWorkspaceData { data_layout: Err("".into()), toolchain: None })
+    }
 
     #[test]
     fn detect_cyclic_dependency_indirect() {
-        let mut graph = CrateGraph::default();
+        let mut graph = CrateGraphBuilder::default();
         let crate1 = graph.add_crate_root(
             FileId::from_raw(1u32),
             Edition2018,
@@ -706,6 +893,7 @@ mod tests {
             CrateOrigin::Local { repo: None, name: None },
             false,
             None,
+            empty_ws_data(),
         );
         let crate2 = graph.add_crate_root(
             FileId::from_raw(2u32),
@@ -718,6 +906,7 @@ mod tests {
             CrateOrigin::Local { repo: None, name: None },
             false,
             None,
+            empty_ws_data(),
         );
         let crate3 = graph.add_crate_root(
             FileId::from_raw(3u32),
@@ -730,21 +919,22 @@ mod tests {
             CrateOrigin::Local { repo: None, name: None },
             false,
             None,
+            empty_ws_data(),
         );
         assert!(graph
-            .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2,))
+            .add_dep(crate1, DependencyBuilder::new(CrateName::new("crate2").unwrap(), crate2,))
             .is_ok());
         assert!(graph
-            .add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3,))
+            .add_dep(crate2, DependencyBuilder::new(CrateName::new("crate3").unwrap(), crate3,))
             .is_ok());
         assert!(graph
-            .add_dep(crate3, Dependency::new(CrateName::new("crate1").unwrap(), crate1,))
+            .add_dep(crate3, DependencyBuilder::new(CrateName::new("crate1").unwrap(), crate1,))
             .is_err());
     }
 
     #[test]
     fn detect_cyclic_dependency_direct() {
-        let mut graph = CrateGraph::default();
+        let mut graph = CrateGraphBuilder::default();
         let crate1 = graph.add_crate_root(
             FileId::from_raw(1u32),
             Edition2018,
@@ -756,6 +946,7 @@ mod tests {
             CrateOrigin::Local { repo: None, name: None },
             false,
             None,
+            empty_ws_data(),
         );
         let crate2 = graph.add_crate_root(
             FileId::from_raw(2u32),
@@ -768,18 +959,19 @@ mod tests {
             CrateOrigin::Local { repo: None, name: None },
             false,
             None,
+            empty_ws_data(),
         );
         assert!(graph
-            .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2,))
+            .add_dep(crate1, DependencyBuilder::new(CrateName::new("crate2").unwrap(), crate2,))
             .is_ok());
         assert!(graph
-            .add_dep(crate2, Dependency::new(CrateName::new("crate2").unwrap(), crate2,))
+            .add_dep(crate2, DependencyBuilder::new(CrateName::new("crate2").unwrap(), crate2,))
             .is_err());
     }
 
     #[test]
     fn it_works() {
-        let mut graph = CrateGraph::default();
+        let mut graph = CrateGraphBuilder::default();
         let crate1 = graph.add_crate_root(
             FileId::from_raw(1u32),
             Edition2018,
@@ -791,6 +983,7 @@ mod tests {
             CrateOrigin::Local { repo: None, name: None },
             false,
             None,
+            empty_ws_data(),
         );
         let crate2 = graph.add_crate_root(
             FileId::from_raw(2u32),
@@ -803,6 +996,7 @@ mod tests {
             CrateOrigin::Local { repo: None, name: None },
             false,
             None,
+            empty_ws_data(),
         );
         let crate3 = graph.add_crate_root(
             FileId::from_raw(3u32),
@@ -815,18 +1009,19 @@ mod tests {
             CrateOrigin::Local { repo: None, name: None },
             false,
             None,
+            empty_ws_data(),
         );
         assert!(graph
-            .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2,))
+            .add_dep(crate1, DependencyBuilder::new(CrateName::new("crate2").unwrap(), crate2,))
             .is_ok());
         assert!(graph
-            .add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3,))
+            .add_dep(crate2, DependencyBuilder::new(CrateName::new("crate3").unwrap(), crate3,))
             .is_ok());
     }
 
     #[test]
     fn dashes_are_normalized() {
-        let mut graph = CrateGraph::default();
+        let mut graph = CrateGraphBuilder::default();
         let crate1 = graph.add_crate_root(
             FileId::from_raw(1u32),
             Edition2018,
@@ -838,6 +1033,7 @@ mod tests {
             CrateOrigin::Local { repo: None, name: None },
             false,
             None,
+            empty_ws_data(),
         );
         let crate2 = graph.add_crate_root(
             FileId::from_raw(2u32),
@@ -850,16 +1046,22 @@ mod tests {
             CrateOrigin::Local { repo: None, name: None },
             false,
             None,
+            empty_ws_data(),
         );
         assert!(graph
             .add_dep(
                 crate1,
-                Dependency::new(CrateName::normalize_dashes("crate-name-with-dashes"), crate2,)
+                DependencyBuilder::new(
+                    CrateName::normalize_dashes("crate-name-with-dashes"),
+                    crate2,
+                )
             )
             .is_ok());
         assert_eq!(
-            graph[crate1].dependencies,
-            vec![Dependency::new(CrateName::new("crate_name_with_dashes").unwrap(), crate2,)]
+            graph.arena[crate1].basic.dependencies,
+            vec![
+                DependencyBuilder::new(CrateName::new("crate_name_with_dashes").unwrap(), crate2,)
+            ]
         );
     }
 }
diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
index 9ec9100968d..324979b2e46 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
@@ -8,14 +8,15 @@ use std::hash::BuildHasherDefault;
 pub use crate::{
     change::FileChange,
     input::{
-        CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env,
-        LangCrateOrigin, ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId,
-        TargetLayoutLoadResult,
+        BuiltCrateData, BuiltDependency, Crate, CrateBuilder, CrateBuilderId, CrateDataBuilder,
+        CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CratesIdMap, CratesMap,
+        DependencyBuilder, Env, ExtraCrateData, LangCrateOrigin, ProcMacroPaths, ReleaseChannel,
+        SourceRoot, SourceRootId, TargetLayoutLoadResult, UniqueCrateData,
     },
 };
 use dashmap::{mapref::entry::Entry, DashMap};
 pub use query_group::{self};
-use rustc_hash::{FxHashMap, FxHasher};
+use rustc_hash::{FxHashSet, FxHasher};
 pub use salsa::{self};
 use salsa::{Durability, Setter};
 pub use semver::{BuildMetadata, Prerelease, Version, VersionReq};
@@ -200,21 +201,53 @@ pub trait RootQueryDb: SourceDatabase + salsa::Database {
     /// Returns the set of errors obtained from parsing the file including validation errors.
     fn parse_errors(&self, file_id: EditionedFileId) -> Option<Arc<[SyntaxError]>>;
 
-    /// The crate graph.
-    #[salsa::input]
-    fn crate_graph(&self) -> Arc<CrateGraph>;
+    #[salsa::transparent]
+    fn toolchain_channel(&self, krate: Crate) -> Option<ReleaseChannel>;
 
-    #[salsa::input]
-    fn crate_workspace_data(&self) -> Arc<FxHashMap<CrateId, Arc<CrateWorkspaceData>>>;
+    /// Crates whose root file is in `id`.
+    fn source_root_crates(&self, id: SourceRootId) -> Arc<[Crate]>;
 
     #[salsa::transparent]
-    fn toolchain_channel(&self, krate: CrateId) -> Option<ReleaseChannel>;
+    fn relevant_crates(&self, file_id: FileId) -> Arc<[Crate]>;
 
-    /// Crates whose root file is in `id`.
-    fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>;
+    /// Returns the crates in topological order.
+    ///
+    /// **Warning**: do not use this query in analysis! It kills incrementality across crate metadata modifications.
+    #[salsa::input]
+    fn all_crates(&self) -> Arc<Box<[Crate]>>;
+
+    /// Returns an iterator over all transitive dependencies of the given crate,
+    /// including the crate itself.
+    ///
+    /// **Warning**: do not use this query in analysis! It kills incrementality across crate metadata modifications.
+    ///
+    #[salsa::transparent]
+    fn transitive_deps(&self, crate_id: Crate) -> FxHashSet<Crate>;
 
+    /// Returns all transitive reverse dependencies of the given crate,
+    /// including the crate itself.
+    ///
+    /// **Warning**: Do not use this query in analysis! It kills incrementality across crate metadata modifications.
+    #[salsa::invoke(input::transitive_rev_deps)]
     #[salsa::transparent]
-    fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]>;
+    fn transitive_rev_deps(&self, of: Crate) -> FxHashSet<Crate>;
+}
+
+pub fn transitive_deps(db: &dyn SourceDatabase, crate_id: Crate) -> FxHashSet<Crate> {
+    // There is a bit of duplication here and in `CrateGraphBuilder` in the same method, but it's not terrible
+    // and removing that is a bit difficult.
+    let mut worklist = vec![crate_id];
+    let mut deps = FxHashSet::default();
+
+    while let Some(krate) = worklist.pop() {
+        if !deps.insert(krate) {
+            continue;
+        }
+
+        worklist.extend(krate.data(db).dependencies.iter().map(|dep| dep.crate_id));
+    }
+
+    deps
 }
 
 #[salsa::db]
@@ -257,6 +290,9 @@ pub trait SourceDatabase: salsa::Database {
         let source_root = self.source_root(source_root.source_root_id(self));
         source_root.source_root(self).resolve_path(path)
     }
+
+    #[doc(hidden)]
+    fn crates_map(&self) -> Arc<CratesMap>;
 }
 
 /// Crate related data shared by the whole workspace.
@@ -268,12 +304,8 @@ pub struct CrateWorkspaceData {
     pub toolchain: Option<Version>,
 }
 
-fn toolchain_channel(db: &dyn RootQueryDb, krate: CrateId) -> Option<ReleaseChannel> {
-    db.crate_workspace_data()
-        .get(&krate)?
-        .toolchain
-        .as_ref()
-        .and_then(|v| ReleaseChannel::from_str(&v.pre))
+fn toolchain_channel(db: &dyn RootQueryDb, krate: Crate) -> Option<ReleaseChannel> {
+    krate.workspace_data(db).toolchain.as_ref().and_then(|v| ReleaseChannel::from_str(&v.pre))
 }
 
 fn parse(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Parse<ast::SourceFile> {
@@ -291,21 +323,19 @@ fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<Arc<[S
     }
 }
 
-fn source_root_crates(db: &dyn RootQueryDb, id: SourceRootId) -> Arc<[CrateId]> {
-    let graph = db.crate_graph();
-    let mut crates = graph
+fn source_root_crates(db: &dyn RootQueryDb, id: SourceRootId) -> Arc<[Crate]> {
+    let crates = db.all_crates();
+    crates
         .iter()
+        .copied()
         .filter(|&krate| {
-            let root_file = graph[krate].root_file_id;
+            let root_file = krate.data(db).root_file_id;
             db.file_source_root(root_file).source_root_id(db) == id
         })
-        .collect::<Vec<_>>();
-    crates.sort();
-    crates.dedup();
-    crates.into_iter().collect()
+        .collect()
 }
 
-fn relevant_crates(db: &dyn RootQueryDb, file_id: FileId) -> Arc<[CrateId]> {
+fn relevant_crates(db: &dyn RootQueryDb, file_id: FileId) -> Arc<[Crate]> {
     let _p = tracing::info_span!("relevant_crates").entered();
 
     let source_root = db.file_source_root(file_id);
diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
index 08545b68511..26860fb9320 100644
--- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
@@ -104,6 +104,12 @@ impl CfgOptions {
             _ => None,
         })
     }
+
+    pub fn to_hashable(&self) -> HashableCfgOptions {
+        let mut enabled = self.enabled.iter().cloned().collect::<Box<[_]>>();
+        enabled.sort_unstable();
+        HashableCfgOptions { _enabled: enabled }
+    }
 }
 
 impl Extend<CfgAtom> for CfgOptions {
@@ -256,3 +262,9 @@ impl fmt::Display for InactiveReason {
         Ok(())
     }
 }
+
+/// A `CfgOptions` that implements `Hash`, for the sake of hashing only.
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct HashableCfgOptions {
+    _enabled: Box<[CfgAtom]>,
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
index 52a72bce91d..579ea12e6ae 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
@@ -2,7 +2,7 @@
 
 use std::{borrow::Cow, hash::Hash, ops};
 
-use base_db::CrateId;
+use base_db::Crate;
 use cfg::{CfgExpr, CfgOptions};
 use either::Either;
 use hir_expand::{
@@ -44,7 +44,7 @@ impl Attrs {
         (**self).iter().find(|attr| attr.id == id)
     }
 
-    pub(crate) fn filter(db: &dyn DefDatabase, krate: CrateId, raw_attrs: RawAttrs) -> Attrs {
+    pub(crate) fn filter(db: &dyn DefDatabase, krate: Crate, raw_attrs: RawAttrs) -> Attrs {
         Attrs(raw_attrs.filter(db.upcast(), krate))
     }
 }
@@ -76,7 +76,6 @@ impl Attrs {
         // FIXME: There should be some proper form of mapping between item tree field ids and hir field ids
         let mut res = ArenaMap::default();
 
-        let crate_graph = db.crate_graph();
         let item_tree;
         let (parent, fields, krate) = match v {
             VariantId::EnumVariantId(it) => {
@@ -102,7 +101,7 @@ impl Attrs {
             }
         };
 
-        let cfg_options = &crate_graph[krate].cfg_options;
+        let cfg_options = krate.cfg_options(db);
 
         let mut idx = 0;
         for (id, _field) in fields.iter().enumerate() {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
index bec66278772..9c87dc31dba 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -2,7 +2,7 @@
 
 pub mod adt;
 
-use base_db::CrateId;
+use base_db::Crate;
 use hir_expand::{
     name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefKind,
 };
@@ -22,7 +22,7 @@ use crate::{
         attr_resolution::ResolvedAttr,
         diagnostics::{DefDiagnostic, DefDiagnostics},
         proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroKind},
-        DefMap, MacroSubNs,
+        DefMap, LocalDefMap, MacroSubNs,
     },
     path::ImportAlias,
     type_ref::{TraitRef, TypeBound, TypeRefId, TypesMap},
@@ -57,8 +57,7 @@ impl FunctionData {
             item_tree[func.visibility].clone()
         };
 
-        let crate_graph = db.crate_graph();
-        let cfg_options = &crate_graph[krate].cfg_options;
+        let cfg_options = krate.cfg_options(db);
         let attr_owner = |idx| {
             item_tree::AttrOwner::Param(loc.id.value, Idx::from_raw(RawIdx::from(idx as u32)))
         };
@@ -525,7 +524,7 @@ pub struct ExternCrateDeclData {
     pub name: Name,
     pub alias: Option<ImportAlias>,
     pub visibility: RawVisibility,
-    pub crate_id: Option<CrateId>,
+    pub crate_id: Option<Crate>,
 }
 
 impl ExternCrateDeclData {
@@ -542,7 +541,7 @@ impl ExternCrateDeclData {
         let crate_id = if name == sym::self_.clone() {
             Some(krate)
         } else {
-            db.crate_graph()[krate].dependencies.iter().find_map(|dep| {
+            krate.data(db).dependencies.iter().find_map(|dep| {
                 if dep.name.symbol() == name.symbol() {
                     Some(dep.crate_id)
                 } else {
@@ -633,6 +632,7 @@ struct AssocItemCollector<'a> {
     db: &'a dyn DefDatabase,
     module_id: ModuleId,
     def_map: Arc<DefMap>,
+    local_def_map: Arc<LocalDefMap>,
     diagnostics: Vec<DefDiagnostic>,
     container: ItemContainerId,
     expander: Expander,
@@ -648,10 +648,12 @@ impl<'a> AssocItemCollector<'a> {
         file_id: HirFileId,
         container: ItemContainerId,
     ) -> Self {
+        let (def_map, local_def_map) = module_id.local_def_map(db);
         Self {
             db,
             module_id,
-            def_map: module_id.def_map(db),
+            def_map,
+            local_def_map,
             container,
             expander: Expander::new(db, file_id, module_id),
             items: Vec::new(),
@@ -697,6 +699,7 @@ impl<'a> AssocItemCollector<'a> {
                 let ast_id_with_path = AstIdWithPath { path: attr.path.clone(), ast_id };
 
                 match self.def_map.resolve_attr_macro(
+                    &self.local_def_map,
                     self.db,
                     self.module_id.local_id,
                     ast_id_with_path,
@@ -780,6 +783,7 @@ impl<'a> AssocItemCollector<'a> {
                 let resolver = |path: &_| {
                     self.def_map
                         .resolve_path(
+                            &self.local_def_map,
                             self.db,
                             module,
                             path,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
index 28992ec600a..bf8f5024c2d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
@@ -1,6 +1,6 @@
 //! Defines hir-level representation of structs, enums and unions
 
-use base_db::CrateId;
+use base_db::Crate;
 use bitflags::bitflags;
 use cfg::CfgOptions;
 use either::Either;
@@ -90,7 +90,7 @@ pub struct FieldData {
 
 fn repr_from_value(
     db: &dyn DefDatabase,
-    krate: CrateId,
+    krate: Crate,
     item_tree: &ItemTree,
     of: AttrOwner,
 ) -> Option<ReprOptions> {
@@ -222,7 +222,7 @@ impl StructData {
             loc.container.local_id,
             loc.id.tree_id(),
             &item_tree,
-            &db.crate_graph()[krate].cfg_options,
+            krate.cfg_options(db),
             FieldParent::Struct(loc.id.value),
             &strukt.fields,
             None,
@@ -274,7 +274,7 @@ impl StructData {
             loc.container.local_id,
             loc.id.tree_id(),
             &item_tree,
-            &db.crate_graph()[krate].cfg_options,
+            krate.cfg_options(db),
             FieldParent::Union(loc.id.value),
             &union.fields,
             None,
@@ -377,7 +377,7 @@ impl EnumVariantData {
             container.local_id,
             loc.id.tree_id(),
             &item_tree,
-            &db.crate_graph()[krate].cfg_options,
+            krate.cfg_options(db),
             FieldParent::Variant(loc.id.value),
             &variant.fields,
             Some(item_tree[loc.parent.lookup(db).id.value].visibility),
@@ -448,7 +448,7 @@ pub enum StructKind {
 
 fn lower_fields(
     db: &dyn DefDatabase,
-    krate: CrateId,
+    krate: Crate,
     container: LocalModuleId,
     tree_id: TreeId,
     item_tree: &ItemTree,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
index 0772d00f03c..b6707af6181 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
@@ -1,5 +1,5 @@
 //! Defines database & queries for name resolution.
-use base_db::{CrateId, RootQueryDb, SourceDatabase, Upcast};
+use base_db::{Crate, RootQueryDb, SourceDatabase, Upcast};
 use either::Either;
 use hir_expand::{db::ExpandDatabase, HirFileId, MacroDefId};
 use intern::sym;
@@ -20,7 +20,7 @@ use crate::{
     import_map::ImportMap,
     item_tree::{AttrOwner, ItemTree, ItemTreeSourceMaps},
     lang_item::{self, LangItem, LangItemTarget, LangItems},
-    nameres::{diagnostics::DefDiagnostics, DefMap},
+    nameres::{diagnostics::DefDiagnostics, DefMap, LocalDefMap},
     tt,
     type_ref::TypesSourceMap,
     visibility::{self, Visibility},
@@ -130,8 +130,11 @@ pub trait DefDatabase:
         block_id: BlockId,
     ) -> (Arc<ItemTree>, Arc<ItemTreeSourceMaps>);
 
-    #[salsa::invoke(DefMap::crate_def_map_query)]
-    fn crate_def_map(&self, krate: CrateId) -> Arc<DefMap>;
+    #[salsa::invoke_actual(DefMap::crate_local_def_map_query)]
+    fn crate_local_def_map(&self, krate: Crate) -> (Arc<DefMap>, Arc<LocalDefMap>);
+
+    #[salsa::invoke_actual(DefMap::crate_def_map_query)]
+    fn crate_def_map(&self, krate: Crate) -> Arc<DefMap>;
 
     /// Computes the block-level `DefMap`.
     #[salsa::invoke_actual(DefMap::block_def_map_query)]
@@ -258,10 +261,10 @@ pub trait DefDatabase:
     // endregion:attrs
 
     #[salsa::invoke(LangItems::lang_item_query)]
-    fn lang_item(&self, start_crate: CrateId, item: LangItem) -> Option<LangItemTarget>;
+    fn lang_item(&self, start_crate: Crate, item: LangItem) -> Option<LangItemTarget>;
 
-    #[salsa::invoke(ImportMap::import_map_query)]
-    fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
+    #[salsa::invoke_actual(ImportMap::import_map_query)]
+    fn import_map(&self, krate: Crate) -> Arc<ImportMap>;
 
     // region:visibilities
 
@@ -277,23 +280,25 @@ pub trait DefDatabase:
 
     // endregion:visibilities
 
-    #[salsa::invoke(LangItems::crate_lang_items_query)]
-    fn crate_lang_items(&self, krate: CrateId) -> Option<Arc<LangItems>>;
+    #[salsa::invoke_actual(LangItems::crate_lang_items_query)]
+    fn crate_lang_items(&self, krate: Crate) -> Option<Arc<LangItems>>;
 
-    #[salsa::invoke(crate::lang_item::notable_traits_in_deps)]
-    fn notable_traits_in_deps(&self, krate: CrateId) -> Arc<[Arc<[TraitId]>]>;
-    #[salsa::invoke(crate::lang_item::crate_notable_traits)]
-    fn crate_notable_traits(&self, krate: CrateId) -> Option<Arc<[TraitId]>>;
+    #[salsa::invoke_actual(crate::lang_item::notable_traits_in_deps)]
+    fn notable_traits_in_deps(&self, krate: Crate) -> Arc<[Arc<[TraitId]>]>;
+    #[salsa::invoke_actual(crate::lang_item::crate_notable_traits)]
+    fn crate_notable_traits(&self, krate: Crate) -> Option<Arc<[TraitId]>>;
 
-    fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
+    #[salsa::invoke_actual(crate_supports_no_std)]
+    fn crate_supports_no_std(&self, crate_id: Crate) -> bool;
 
-    fn include_macro_invoc(&self, crate_id: CrateId) -> Arc<[(MacroCallId, EditionedFileId)]>;
+    #[salsa::invoke_actual(include_macro_invoc)]
+    fn include_macro_invoc(&self, crate_id: Crate) -> Arc<[(MacroCallId, EditionedFileId)]>;
 }
 
 // return: macro call id and include file id
 fn include_macro_invoc(
     db: &dyn DefDatabase,
-    krate: CrateId,
+    krate: Crate,
 ) -> Arc<[(MacroCallId, EditionedFileId)]> {
     db.crate_def_map(krate)
         .modules
@@ -307,8 +312,8 @@ fn include_macro_invoc(
         .collect()
 }
 
-fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool {
-    let file = db.crate_graph()[crate_id].root_file_id();
+fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: Crate) -> bool {
+    let file = crate_id.data(db).root_file_id();
     let item_tree = db.file_item_tree(file.into());
     let attrs = item_tree.raw_attrs(AttrOwner::TopLevel);
     for attr in &**attrs {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
index a1b3123c991..c5ce8c454ce 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
@@ -2,7 +2,7 @@
 
 use std::cell::OnceCell;
 
-use base_db::CrateId;
+use base_db::Crate;
 use cfg::CfgOptions;
 use drop_bomb::DropBomb;
 use hir_expand::{
@@ -44,7 +44,7 @@ impl Expander {
             module,
             recursion_depth: 0,
             recursion_limit,
-            cfg_options: db.crate_graph()[module.krate].cfg_options.clone(),
+            cfg_options: Arc::clone(module.krate.cfg_options(db)),
             span_map: OnceCell::new(),
         }
     }
@@ -53,7 +53,7 @@ impl Expander {
         self.span_map.get_or_init(|| db.span_map(self.current_file_id))
     }
 
-    pub fn krate(&self) -> CrateId {
+    pub fn krate(&self) -> Crate {
         self.module.krate
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
index a55fec4f8b1..8aca4eb9bc1 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
@@ -86,7 +86,6 @@ impl Body {
                         let item_tree = f.id.item_tree(db);
                         let func = &item_tree[f.id.value];
                         let krate = f.container.module(db).krate;
-                        let crate_graph = db.crate_graph();
                         (
                             param_list,
                             (0..func.params.len()).map(move |idx| {
@@ -99,7 +98,7 @@ impl Body {
                                             Idx::from_raw(RawIdx::from(idx as u32)),
                                         ),
                                     )
-                                    .is_cfg_enabled(&crate_graph[krate].cfg_options)
+                                    .is_cfg_enabled(krate.cfg_options(db))
                             }),
                         )
                     });
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
index c3ca610faec..c346cec242e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
@@ -5,7 +5,7 @@ mod asm;
 
 use std::mem;
 
-use base_db::CrateId;
+use base_db::Crate;
 use either::Either;
 use hir_expand::{
     mod_path::tool_path,
@@ -50,7 +50,7 @@ use crate::{
     item_scope::BuiltinShadowMode,
     lang_item::LangItem,
     lower::LowerCtx,
-    nameres::{DefMap, MacroSubNs},
+    nameres::{DefMap, LocalDefMap, MacroSubNs},
     path::{GenericArgs, Path},
     type_ref::{Mutability, Rawness, TypeRef},
     AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, MacroId, ModuleDefId, UnresolvedMacro,
@@ -64,7 +64,7 @@ pub(super) fn lower_body(
     expander: Expander,
     parameters: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
     body: Option<ast::Expr>,
-    krate: CrateId,
+    krate: Crate,
     is_async_fn: bool,
 ) -> (Body, BodySourceMap) {
     // We cannot leave the root span map empty and let any identifier from it be treated as root,
@@ -189,7 +189,7 @@ pub(super) fn lower(
     owner: ExprStoreOwnerId,
     expander: Expander,
     body: Option<ast::Expr>,
-    krate: CrateId,
+    krate: Crate,
 ) -> (ExpressionStore, ExpressionStoreSourceMap) {
     // We cannot leave the root span map empty and let any identifier from it be treated as root,
     // because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved
@@ -214,8 +214,9 @@ struct ExprCollector<'a> {
     expander: Expander,
     owner: ExprStoreOwnerId,
     def_map: Arc<DefMap>,
+    local_def_map: Arc<LocalDefMap>,
     ast_id_map: Arc<AstIdMap>,
-    krate: CrateId,
+    krate: Crate,
     store: ExpressionStoreBuilder,
     source_map: ExpressionStoreSourceMap,
 
@@ -327,14 +328,16 @@ impl ExprCollector<'_> {
         db: &dyn DefDatabase,
         owner: ExprStoreOwnerId,
         expander: Expander,
-        krate: CrateId,
+        krate: Crate,
         span_map: Option<Arc<ExpansionSpanMap>>,
     ) -> ExprCollector<'_> {
+        let (def_map, local_def_map) = expander.module.local_def_map(db);
         ExprCollector {
             db,
             owner,
             krate,
-            def_map: expander.module.def_map(db),
+            def_map,
+            local_def_map,
             source_map: ExpressionStoreSourceMap::default(),
             ast_id_map: db.ast_id_map(expander.current_file_id()),
             store: ExpressionStoreBuilder::default(),
@@ -1306,6 +1309,7 @@ impl ExprCollector<'_> {
             None => self.expander.enter_expand(self.db, mcall, |path| {
                 self.def_map
                     .resolve_path(
+                        &self.local_def_map,
                         self.db,
                         module,
                         path,
@@ -1608,6 +1612,7 @@ impl ExprCollector<'_> {
                     // This could also be a single-segment path pattern. To
                     // decide that, we need to try resolving the name.
                     let (resolved, _) = self.def_map.resolve_path(
+                        &self.local_def_map,
                         self.db,
                         self.expander.module.local_id,
                         &name.clone().into(),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests.rs
index 16bf46d3e3f..7bf27747c46 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests.rs
@@ -293,7 +293,7 @@ impl SsrError {
     assert_eq!(db.body_with_source_map(def).1.diagnostics(), &[]);
     expect![[r#"
         fn main() -> () {
-            _ = $crate::error::SsrError::new(
+            _ = ra_test_fixture::error::SsrError::new(
                 builtin#lang(Arguments::new_v1_formatted)(
                     &[
                         "Failed to resolve path `", "`",
@@ -353,7 +353,7 @@ fn f(a: i32, b: u32) -> String {
     expect![[r#"
         fn f(a: i32, b: u32) -> String {
             {
-                $crate::panicking::panic_fmt(
+                core::panicking::panic_fmt(
                     builtin#lang(Arguments::new_v1_formatted)(
                         &[
                             "cc",
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
index c30ad0163b9..48f31698ddf 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
@@ -2,7 +2,7 @@
 
 use std::{cell::Cell, cmp::Ordering, iter};
 
-use base_db::{CrateId, CrateOrigin, LangCrateOrigin};
+use base_db::{Crate, CrateOrigin, LangCrateOrigin};
 use hir_expand::{
     name::{AsName, Name},
     Lookup,
@@ -50,7 +50,7 @@ pub fn find_path(
             prefix: prefix_kind,
             cfg,
             ignore_local_imports,
-            is_std_item: db.crate_graph()[item_module.krate()].origin.is_lang(),
+            is_std_item: item_module.krate().data(db).origin.is_lang(),
             from,
             from_def_map: &from.def_map(db),
             fuel: Cell::new(FIND_PATH_FUEL),
@@ -174,9 +174,9 @@ fn find_path_for_module(
         }
         // - otherwise if the item is the crate root of a dependency crate, return the name from the extern prelude
 
-        let root_def_map = ctx.from.derive_crate_root().def_map(ctx.db);
+        let root_local_def_map = ctx.from.derive_crate_root().local_def_map(ctx.db).1;
         // rev here so we prefer looking at renamed extern decls first
-        for (name, (def_id, _extern_crate)) in root_def_map.extern_prelude().rev() {
+        for (name, (def_id, _extern_crate)) in root_local_def_map.extern_prelude().rev() {
             if crate_root != def_id {
                 continue;
             }
@@ -360,7 +360,7 @@ fn calculate_best_path(
         // too (unless we can't name it at all). It could *also* be (re)exported by the same crate
         // that wants to import it here, but we always prefer to use the external path here.
 
-        ctx.db.crate_graph()[ctx.from.krate].dependencies.iter().for_each(|dep| {
+        ctx.from.krate.data(ctx.db).dependencies.iter().for_each(|dep| {
             find_in_dep(ctx, visited_modules, item, max_len, best_choice, dep.crate_id)
         });
     }
@@ -373,11 +373,10 @@ fn find_in_sysroot(
     max_len: usize,
     best_choice: &mut Option<Choice>,
 ) {
-    let crate_graph = ctx.db.crate_graph();
-    let dependencies = &crate_graph[ctx.from.krate].dependencies;
+    let dependencies = &ctx.from.krate.data(ctx.db).dependencies;
     let mut search = |lang, best_choice: &mut _| {
         if let Some(dep) = dependencies.iter().filter(|it| it.is_sysroot()).find(|dep| {
-            match crate_graph[dep.crate_id].origin {
+            match dep.crate_id.data(ctx.db).origin {
                 CrateOrigin::Lang(l) => l == lang,
                 _ => false,
             }
@@ -419,7 +418,7 @@ fn find_in_dep(
     item: ItemInNs,
     max_len: usize,
     best_choice: &mut Option<Choice>,
-    dep: CrateId,
+    dep: Crate,
 ) {
     let import_map = ctx.db.import_map(dep);
     let Some(import_info_for) = import_map.import_info_for(item) else {
@@ -688,9 +687,10 @@ mod tests {
         })
         .unwrap();
 
-        let def_map = module.def_map(&db);
+        let (def_map, local_def_map) = module.local_def_map(&db);
         let resolved = def_map
             .resolve_path(
+                &local_def_map,
                 &db,
                 module.local_id,
                 &mod_path,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
index ed5038c5aeb..6f1650adeb6 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
@@ -24,7 +24,7 @@ use crate::{
     expander::Expander,
     item_tree::{AttrOwner, FileItemTreeId, GenericModItem, GenericsItemTreeNode, ItemTree},
     lower::LowerCtx,
-    nameres::{DefMap, MacroSubNs},
+    nameres::{DefMap, LocalDefMap, MacroSubNs},
     path::{AssociatedTypeBinding, GenericArg, GenericArgs, NormalPath, Path},
     type_ref::{
         ArrayType, ConstRef, FnType, LifetimeRef, PathId, RefType, TypeBound, TypeRef, TypeRefId,
@@ -314,8 +314,7 @@ impl GenericParams {
         let _p = tracing::info_span!("generic_params_query").entered();
 
         let krate = def.krate(db);
-        let cfg_options = db.crate_graph();
-        let cfg_options = &cfg_options[krate].cfg_options;
+        let cfg_options = &krate.cfg_options(db);
 
         // Returns the generic parameters that are enabled under the current `#[cfg]` options
         let enabled_params =
@@ -414,7 +413,12 @@ impl GenericParams {
                             &mut types_source_maps,
                             &mut expander,
                             &mut || {
-                                (module.def_map(db), Expander::new(db, loc.id.file_id(), module))
+                                let (def_map, local_def_map) = module.local_def_map(db);
+                                (
+                                    def_map,
+                                    local_def_map,
+                                    Expander::new(db, loc.id.file_id(), module),
+                                )
                             },
                             param,
                             &item.types_map,
@@ -638,8 +642,8 @@ impl GenericParamsCollector {
         generics_types_map: &mut TypesMap,
         generics_types_source_map: &mut TypesSourceMap,
         // FIXME: Change this back to `LazyCell` if https://github.com/rust-lang/libs-team/issues/429 is accepted.
-        exp: &mut Option<(Arc<DefMap>, Expander)>,
-        exp_fill: &mut dyn FnMut() -> (Arc<DefMap>, Expander),
+        exp: &mut Option<(Arc<DefMap>, Arc<LocalDefMap>, Expander)>,
+        exp_fill: &mut dyn FnMut() -> (Arc<DefMap>, Arc<LocalDefMap>, Expander),
         type_ref: TypeRefId,
         types_map: &TypesMap,
         types_source_map: &TypesSourceMap,
@@ -669,12 +673,13 @@ impl GenericParamsCollector {
 
             if let TypeRef::Macro(mc) = type_ref {
                 let macro_call = mc.to_node(db.upcast());
-                let (def_map, expander) = exp.get_or_insert_with(&mut *exp_fill);
+                let (def_map, local_def_map, expander) = exp.get_or_insert_with(&mut *exp_fill);
 
                 let module = expander.module.local_id;
                 let resolver = |path: &_| {
                     def_map
                         .resolve_path(
+                            local_def_map,
                             db,
                             module,
                             path,
@@ -702,7 +707,7 @@ impl GenericParamsCollector {
                         &macro_types_map,
                         &macro_types_source_map,
                     );
-                    exp.get_or_insert_with(&mut *exp_fill).1.exit(mark);
+                    exp.get_or_insert_with(&mut *exp_fill).2.exit(mark);
                 }
             }
         });
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
index 64c1d0d274f..1cdc4c02597 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
@@ -2,7 +2,7 @@
 
 use std::fmt;
 
-use base_db::CrateId;
+use base_db::Crate;
 use fst::{raw::IndexedValue, Automaton, Streamer};
 use hir_expand::name::Name;
 use itertools::Itertools;
@@ -78,7 +78,7 @@ impl ImportMap {
         out
     }
 
-    pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
+    pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: Crate) -> Arc<Self> {
         let _p = tracing::info_span!("import_map_query").entered();
 
         let map = Self::collect_import_map(db, krate);
@@ -129,7 +129,7 @@ impl ImportMap {
         self.item_to_info_map.get(&item).map(|(info, _)| &**info)
     }
 
-    fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMapIndex {
+    fn collect_import_map(db: &dyn DefDatabase, krate: Crate) -> ImportMapIndex {
         let _p = tracing::info_span!("collect_import_map").entered();
 
         let def_map = db.crate_def_map(krate);
@@ -400,15 +400,13 @@ impl Query {
 /// This returns a list of items that could be imported from dependencies of `krate`.
 pub fn search_dependencies(
     db: &dyn DefDatabase,
-    krate: CrateId,
+    krate: Crate,
     query: &Query,
 ) -> FxHashSet<ItemInNs> {
     let _p = tracing::info_span!("search_dependencies", ?query).entered();
 
-    let graph = db.crate_graph();
-
     let import_maps: Vec<_> =
-        graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect();
+        krate.data(db).dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect();
 
     let mut op = fst::map::OpBuilder::new();
 
@@ -512,11 +510,13 @@ mod tests {
         expect: Expect,
     ) {
         let db = TestDB::with_files(ra_fixture);
-        let crate_graph = db.crate_graph();
-        let krate = crate_graph
+        let all_crates = db.all_crates();
+        let krate = all_crates
             .iter()
+            .copied()
             .find(|&krate| {
-                crate_graph[krate]
+                krate
+                    .extra_data(&db)
                     .display_name
                     .as_ref()
                     .is_some_and(|it| it.crate_name().as_str() == crate_name)
@@ -545,7 +545,7 @@ mod tests {
 
                 Some(format!(
                     "{}::{} ({})\n",
-                    crate_graph[dependency_krate].display_name.as_ref()?,
+                    dependency_krate.extra_data(&db).display_name.as_ref()?,
                     path,
                     mark
                 ))
@@ -590,12 +590,13 @@ mod tests {
 
     fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
         let db = TestDB::with_files(ra_fixture);
-        let crate_graph = db.crate_graph();
+        let all_crates = db.all_crates();
 
-        let actual = crate_graph
+        let actual = all_crates
             .iter()
+            .copied()
             .filter_map(|krate| {
-                let cdata = &crate_graph[krate];
+                let cdata = &krate.extra_data(&db);
                 let name = cdata.display_name.as_ref()?;
 
                 let map = db.import_map(krate);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
index 0ca1eb9bcfe..0c683f3531c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
@@ -3,7 +3,7 @@
 
 use std::sync::LazyLock;
 
-use base_db::CrateId;
+use base_db::Crate;
 use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId};
 use indexmap::map::Entry;
 use itertools::Itertools;
@@ -916,7 +916,7 @@ impl ItemInNs {
     }
 
     /// Returns the crate defining this item (or `None` if `self` is built-in).
-    pub fn krate(&self, db: &dyn DefDatabase) -> Option<CrateId> {
+    pub fn krate(&self, db: &dyn DefDatabase) -> Option<Crate> {
         match self {
             ItemInNs::Types(id) | ItemInNs::Values(id) => id.module(db).map(|m| m.krate),
             ItemInNs::Macros(id) => Some(id.module(db).krate),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
index 382afbcb1dd..9acf98e62cd 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
@@ -44,7 +44,7 @@ use std::{
 };
 
 use ast::{AstNode, StructKind};
-use base_db::CrateId;
+use base_db::Crate;
 use either::Either;
 use hir_expand::{attrs::RawAttrs, name::Name, ExpandTo, HirFileId, InFile};
 use intern::{Interned, Symbol};
@@ -202,7 +202,7 @@ impl ItemTree {
     }
 
     /// Returns the inner attributes of the source file.
-    pub fn top_level_attrs(&self, db: &dyn DefDatabase, krate: CrateId) -> Attrs {
+    pub fn top_level_attrs(&self, db: &dyn DefDatabase, krate: Crate) -> Attrs {
         Attrs::filter(
             db,
             krate,
@@ -214,7 +214,7 @@ impl ItemTree {
         self.attrs.get(&of).unwrap_or(&RawAttrs::EMPTY)
     }
 
-    pub(crate) fn attrs(&self, db: &dyn DefDatabase, krate: CrateId, of: AttrOwner) -> Attrs {
+    pub(crate) fn attrs(&self, db: &dyn DefDatabase, krate: Crate, of: AttrOwner) -> Attrs {
         Attrs::filter(db, krate, self.raw_attrs(of).clone())
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index 59f51db9f74..70c28009f4b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -8,7 +8,7 @@ use rustc_hash::FxHashMap;
 use triomphe::Arc;
 
 use crate::{
-    db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId,
+    db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId,
     FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
 };
 
@@ -96,7 +96,7 @@ impl LangItems {
     /// Salsa query. This will look for lang items in a specific crate.
     pub(crate) fn crate_lang_items_query(
         db: &dyn DefDatabase,
-        krate: CrateId,
+        krate: Crate,
     ) -> Option<Arc<LangItems>> {
         let _p = tracing::info_span!("crate_lang_items_query").entered();
 
@@ -175,7 +175,7 @@ impl LangItems {
     /// traversing its dependencies.
     pub(crate) fn lang_item_query(
         db: &dyn DefDatabase,
-        start_crate: CrateId,
+        start_crate: Crate,
         item: LangItem,
     ) -> Option<LangItemTarget> {
         let _p = tracing::info_span!("lang_item_query").entered();
@@ -184,10 +184,7 @@ impl LangItems {
         {
             return Some(target);
         }
-        db.crate_graph()[start_crate]
-            .dependencies
-            .iter()
-            .find_map(|dep| db.lang_item(dep.crate_id, item))
+        start_crate.data(db).dependencies.iter().find_map(|dep| db.lang_item(dep.crate_id, item))
     }
 
     fn collect_lang_item<T>(
@@ -209,19 +206,14 @@ pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option<LangIte
     db.attrs(item).lang_item()
 }
 
-pub(crate) fn notable_traits_in_deps(
-    db: &dyn DefDatabase,
-    krate: CrateId,
-) -> Arc<[Arc<[TraitId]>]> {
+pub(crate) fn notable_traits_in_deps(db: &dyn DefDatabase, krate: Crate) -> Arc<[Arc<[TraitId]>]> {
     let _p = tracing::info_span!("notable_traits_in_deps", ?krate).entered();
-    let crate_graph = db.crate_graph();
-
     Arc::from_iter(
-        crate_graph.transitive_deps(krate).filter_map(|krate| db.crate_notable_traits(krate)),
+        db.transitive_deps(krate).into_iter().filter_map(|krate| db.crate_notable_traits(krate)),
     )
 }
 
-pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: CrateId) -> Option<Arc<[TraitId]>> {
+pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: Crate) -> Option<Arc<[TraitId]>> {
     let _p = tracing::info_span!("crate_notable_traits", ?krate).entered();
 
     let mut traits = Vec::new();
@@ -290,17 +282,12 @@ impl LangItem {
         Self::from_symbol(name.symbol())
     }
 
-    pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
+    pub fn path(&self, db: &dyn DefDatabase, start_crate: Crate) -> Option<Path> {
         let t = db.lang_item(start_crate, *self)?;
         Some(Path::LangItem(t, None))
     }
 
-    pub fn ty_rel_path(
-        &self,
-        db: &dyn DefDatabase,
-        start_crate: CrateId,
-        seg: Name,
-    ) -> Option<Path> {
+    pub fn ty_rel_path(&self, db: &dyn DefDatabase, start_crate: Crate, seg: Name) -> Option<Path> {
         let t = db.lang_item(start_crate, *self)?;
         Some(Path::LangItem(t, Some(seg)))
     }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index 15ef8364ed2..fbbeb4beb99 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -71,7 +71,7 @@ mod test_db;
 
 use std::hash::{Hash, Hasher};
 
-use base_db::{impl_intern_key, CrateId};
+use base_db::{impl_intern_key, Crate};
 use hir_expand::{
     builtin::{BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander},
     db::ExpandDatabase,
@@ -99,10 +99,10 @@ use crate::{
         Const, Enum, ExternCrate, Function, Impl, ItemTreeId, ItemTreeNode, Macro2, MacroRules,
         Static, Struct, Trait, TraitAlias, TypeAlias, Union, Use, Variant,
     },
+    nameres::LocalDefMap,
 };
 
-type FxIndexMap<K, V> =
-    indexmap::IndexMap<K, V, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
+type FxIndexMap<K, V> = indexmap::IndexMap<K, V, rustc_hash::FxBuildHasher>;
 /// A wrapper around three booleans
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
 pub struct ImportPathConfig {
@@ -338,7 +338,7 @@ pub struct ConstBlockLoc {
 /// A `ModuleId` that is always a crate's root module.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct CrateRootModuleId {
-    krate: CrateId,
+    krate: Crate,
 }
 
 impl CrateRootModuleId {
@@ -346,7 +346,11 @@ impl CrateRootModuleId {
         db.crate_def_map(self.krate)
     }
 
-    pub fn krate(self) -> CrateId {
+    pub(crate) fn local_def_map(&self, db: &dyn DefDatabase) -> (Arc<DefMap>, Arc<LocalDefMap>) {
+        db.crate_local_def_map(self.krate)
+    }
+
+    pub fn krate(self) -> Crate {
         self.krate
     }
 }
@@ -374,8 +378,8 @@ impl From<CrateRootModuleId> for ModuleDefId {
     }
 }
 
-impl From<CrateId> for CrateRootModuleId {
-    fn from(krate: CrateId) -> Self {
+impl From<Crate> for CrateRootModuleId {
+    fn from(krate: Crate) -> Self {
         CrateRootModuleId { krate }
     }
 }
@@ -394,7 +398,7 @@ impl TryFrom<ModuleId> for CrateRootModuleId {
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct ModuleId {
-    krate: CrateId,
+    krate: Crate,
     /// If this `ModuleId` was derived from a `DefMap` for a block expression, this stores the
     /// `BlockId` of that block expression. If `None`, this module is part of the crate-level
     /// `DefMap` of `krate`.
@@ -411,11 +415,22 @@ impl ModuleId {
         }
     }
 
+    pub(crate) fn local_def_map(self, db: &dyn DefDatabase) -> (Arc<DefMap>, Arc<LocalDefMap>) {
+        match self.block {
+            Some(block) => (db.block_def_map(block), self.only_local_def_map(db)),
+            None => db.crate_local_def_map(self.krate),
+        }
+    }
+
+    pub(crate) fn only_local_def_map(self, db: &dyn DefDatabase) -> Arc<LocalDefMap> {
+        db.crate_local_def_map(self.krate).1
+    }
+
     pub fn crate_def_map(self, db: &dyn DefDatabase) -> Arc<DefMap> {
         db.crate_def_map(self.krate)
     }
 
-    pub fn krate(self) -> CrateId {
+    pub fn krate(self) -> Crate {
         self.krate
     }
 
@@ -982,7 +997,7 @@ impl From<CallableDefId> for ModuleDefId {
 }
 
 impl CallableDefId {
-    pub fn krate(self, db: &dyn DefDatabase) -> CrateId {
+    pub fn krate(self, db: &dyn DefDatabase) -> Crate {
         match self {
             CallableDefId::FunctionId(f) => f.krate(db),
             CallableDefId::StructId(s) => s.krate(db),
@@ -1119,7 +1134,7 @@ pub trait HasModule {
     /// Returns the crate this thing is defined within.
     #[inline]
     #[doc(alias = "crate")]
-    fn krate(&self, db: &dyn DefDatabase) -> CrateId {
+    fn krate(&self, db: &dyn DefDatabase) -> Crate {
         self.module(db).krate
     }
 }
@@ -1367,7 +1382,7 @@ pub trait AsMacroCall {
     fn as_call_id(
         &self,
         db: &dyn ExpandDatabase,
-        krate: CrateId,
+        krate: Crate,
         resolver: impl Fn(&path::ModPath) -> Option<MacroDefId> + Copy,
     ) -> Option<MacroCallId> {
         self.as_call_id_with_errors(db, krate, resolver).ok()?.value
@@ -1376,7 +1391,7 @@ pub trait AsMacroCall {
     fn as_call_id_with_errors(
         &self,
         db: &dyn ExpandDatabase,
-        krate: CrateId,
+        krate: Crate,
         resolver: impl Fn(&path::ModPath) -> Option<MacroDefId> + Copy,
     ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro>;
 }
@@ -1385,7 +1400,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
     fn as_call_id_with_errors(
         &self,
         db: &dyn ExpandDatabase,
-        krate: CrateId,
+        krate: Crate,
         resolver: impl Fn(&path::ModPath) -> Option<MacroDefId> + Copy,
     ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
         let expands_to = hir_expand::ExpandTo::from_call_site(self.value);
@@ -1442,7 +1457,7 @@ fn macro_call_as_call_id(
     call: &AstIdWithPath<ast::MacroCall>,
     call_site: SyntaxContextId,
     expand_to: ExpandTo,
-    krate: CrateId,
+    krate: Crate,
     resolver: impl Fn(&path::ModPath) -> Option<MacroDefId> + Copy,
 ) -> Result<Option<MacroCallId>, UnresolvedMacro> {
     macro_call_as_call_id_with_eager(
@@ -1464,7 +1479,7 @@ fn macro_call_as_call_id_with_eager(
     path: &path::ModPath,
     call_site: SyntaxContextId,
     expand_to: ExpandTo,
-    krate: CrateId,
+    krate: Crate,
     resolver: impl FnOnce(&path::ModPath) -> Option<MacroDefId>,
     eager_resolver: impl Fn(&path::ModPath) -> Option<MacroDefId>,
 ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
index d0678a40652..70b512c014b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -376,4 +376,8 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander {
             panic!("got invalid macro input: {:?}", parse.errors());
         }
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.as_any().type_id() == std::any::TypeId::of::<Self>()
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index 5b3d75c4ee6..054c285fa25 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -59,7 +59,7 @@ mod tests;
 
 use std::ops::Deref;
 
-use base_db::CrateId;
+use base_db::Crate;
 use hir_expand::{
     name::Name, proc_macro::ProcMacroKind, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId,
 };
@@ -69,7 +69,7 @@ use la_arena::Arena;
 use rustc_hash::{FxHashMap, FxHashSet};
 use span::{Edition, EditionedFileId, FileAstId, FileId, ROOT_ERASED_FILE_AST_ID};
 use stdx::format_to;
-use syntax::{ast, AstNode, SmolStr, SyntaxNode};
+use syntax::{ast, AstNode, SmolStr, SyntaxNode, ToSmolStr};
 use triomphe::Arc;
 use tt::TextRange;
 
@@ -95,6 +95,39 @@ const PREDEFINED_TOOLS: &[SmolStr] = &[
     SmolStr::new_static("rust_analyzer"),
 ];
 
+/// Parts of the def map that are only needed when analyzing code in the same crate.
+///
+/// There are some data in the def map (e.g. extern prelude) that is only needed when analyzing
+/// things in the same crate (and maybe in the IDE layer), e.g. the extern prelude. If we put
+/// it in the DefMap dependant DefMaps will be invalidated when they change (e.g. when we add
+/// a dependency to the crate). Instead we split them out of the DefMap into a LocalDefMap struct.
+/// `crate_local_def_map()` returns both, and `crate_def_map()` returns only the external-relevant
+/// DefMap.
+#[derive(Debug, PartialEq, Eq, Default)]
+pub struct LocalDefMap {
+    // FIXME: There are probably some other things that could be here, but this is less severe and you
+    // need to be careful with things that block def maps also have.
+    /// The extern prelude which contains all root modules of external crates that are in scope.
+    extern_prelude: FxIndexMap<Name, (CrateRootModuleId, Option<ExternCrateId>)>,
+}
+
+impl LocalDefMap {
+    pub(crate) const EMPTY: &Self =
+        &Self { extern_prelude: FxIndexMap::with_hasher(rustc_hash::FxBuildHasher) };
+
+    fn shrink_to_fit(&mut self) {
+        let Self { extern_prelude } = self;
+        extern_prelude.shrink_to_fit();
+    }
+
+    pub(crate) fn extern_prelude(
+        &self,
+    ) -> impl DoubleEndedIterator<Item = (&Name, (CrateRootModuleId, Option<ExternCrateId>))> + '_
+    {
+        self.extern_prelude.iter().map(|(name, &def)| (name, def))
+    }
+}
+
 /// Contains the results of (early) name resolution.
 ///
 /// A `DefMap` stores the module tree and the definitions that are in scope in every module after
@@ -107,7 +140,7 @@ const PREDEFINED_TOOLS: &[SmolStr] = &[
 #[derive(Debug, PartialEq, Eq)]
 pub struct DefMap {
     /// The crate this `DefMap` belongs to.
-    krate: CrateId,
+    krate: Crate,
     /// When this is a block def map, this will hold the block id of the block and module that
     /// contains this block.
     block: Option<BlockInfo>,
@@ -141,9 +174,6 @@ pub struct DefMap {
 /// Data that belongs to a crate which is shared between a crate's def map and all its block def maps.
 #[derive(Clone, Debug, PartialEq, Eq)]
 struct DefMapCrateData {
-    /// The extern prelude which contains all root modules of external crates that are in scope.
-    extern_prelude: FxIndexMap<Name, (CrateRootModuleId, Option<ExternCrateId>)>,
-
     /// Side table for resolving derive helpers.
     exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
     fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>,
@@ -166,7 +196,6 @@ struct DefMapCrateData {
 impl DefMapCrateData {
     fn new(edition: Edition) -> Self {
         Self {
-            extern_prelude: FxIndexMap::default(),
             exported_derives: FxHashMap::default(),
             fn_proc_macro_mapping: FxHashMap::default(),
             registered_attrs: Vec::new(),
@@ -182,7 +211,6 @@ impl DefMapCrateData {
 
     fn shrink_to_fit(&mut self) {
         let Self {
-            extern_prelude,
             exported_derives,
             fn_proc_macro_mapping,
             registered_attrs,
@@ -194,7 +222,6 @@ impl DefMapCrateData {
             edition: _,
             recursion_limit: _,
         } = self;
-        extern_prelude.shrink_to_fit();
         exported_derives.shrink_to_fit();
         fn_proc_macro_mapping.shrink_to_fit();
         registered_attrs.shrink_to_fit();
@@ -219,11 +246,11 @@ struct BlockRelativeModuleId {
 }
 
 impl BlockRelativeModuleId {
-    fn def_map(self, db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
+    fn def_map(self, db: &dyn DefDatabase, krate: Crate) -> Arc<DefMap> {
         self.into_module(krate).def_map(db)
     }
 
-    fn into_module(self, krate: CrateId) -> ModuleId {
+    fn into_module(self, krate: Crate) -> ModuleId {
         ModuleId { krate, block: self.block, local_id: self.local_id }
     }
 
@@ -337,11 +364,25 @@ impl DefMap {
         self.data.edition
     }
 
-    pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, crate_id: CrateId) -> Arc<DefMap> {
-        let crate_graph = db.crate_graph();
-        let krate = &crate_graph[crate_id];
-        let name = krate.display_name.as_deref().map(Symbol::as_str).unwrap_or_default();
-        let _p = tracing::info_span!("crate_def_map_query", ?name).entered();
+    pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, crate_id: Crate) -> Arc<DefMap> {
+        db.crate_local_def_map(crate_id).0
+    }
+
+    pub(crate) fn crate_local_def_map_query(
+        db: &dyn DefDatabase,
+        crate_id: Crate,
+    ) -> (Arc<DefMap>, Arc<LocalDefMap>) {
+        let krate = crate_id.data(db);
+        let _p = tracing::info_span!(
+            "crate_def_map_query",
+            name=?crate_id
+                .extra_data(db)
+                .display_name
+                .as_ref()
+                .map(|it| it.crate_name().to_smolstr())
+                .unwrap_or_default()
+        )
+        .entered();
 
         let module_data = ModuleData::new(
             ModuleOrigin::CrateRoot { definition: krate.root_file_id() },
@@ -354,10 +395,14 @@ impl DefMap {
             module_data,
             None,
         );
-        let def_map =
-            collector::collect_defs(db, def_map, TreeId::new(krate.root_file_id().into(), None));
+        let (def_map, local_def_map) = collector::collect_defs(
+            db,
+            def_map,
+            TreeId::new(krate.root_file_id().into(), None),
+            None,
+        );
 
-        Arc::new(def_map)
+        (Arc::new(def_map), Arc::new(local_def_map))
     }
 
     pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc<DefMap> {
@@ -370,10 +415,10 @@ impl DefMap {
         let module_data =
             ModuleData::new(ModuleOrigin::BlockExpr { block: ast_id, id: block_id }, visibility);
 
-        let parent_map = module.def_map(db);
+        let (crate_map, crate_local_map) = db.crate_local_def_map(module.krate);
         let def_map = DefMap::empty(
             module.krate,
-            parent_map.data.clone(),
+            crate_map.data.clone(),
             module_data,
             Some(BlockInfo {
                 block: block_id,
@@ -381,13 +426,17 @@ impl DefMap {
             }),
         );
 
-        let def_map =
-            collector::collect_defs(db, def_map, TreeId::new(ast_id.file_id, Some(block_id)));
+        let (def_map, _) = collector::collect_defs(
+            db,
+            def_map,
+            TreeId::new(ast_id.file_id, Some(block_id)),
+            Some(crate_local_map),
+        );
         Arc::new(def_map)
     }
 
     fn empty(
-        krate: CrateId,
+        krate: Crate,
         crate_data: Arc<DefMapCrateData>,
         module_data: ModuleData,
         block: Option<BlockInfo>,
@@ -479,7 +528,7 @@ impl DefMap {
         self.data.fn_proc_macro_mapping.get(&id).copied()
     }
 
-    pub fn krate(&self) -> CrateId {
+    pub fn krate(&self) -> Crate {
         self.krate
     }
 
@@ -590,19 +639,13 @@ impl DefMap {
         self.prelude
     }
 
-    pub(crate) fn extern_prelude(
-        &self,
-    ) -> impl DoubleEndedIterator<Item = (&Name, (CrateRootModuleId, Option<ExternCrateId>))> + '_
-    {
-        self.data.extern_prelude.iter().map(|(name, &def)| (name, def))
-    }
-
     pub(crate) fn macro_use_prelude(&self) -> &FxHashMap<Name, (MacroId, Option<ExternCrateId>)> {
         &self.macro_use_prelude
     }
 
     pub(crate) fn resolve_path(
         &self,
+        local_def_map: &LocalDefMap,
         db: &dyn DefDatabase,
         original_module: LocalModuleId,
         path: &ModPath,
@@ -610,6 +653,7 @@ impl DefMap {
         expected_macro_subns: Option<MacroSubNs>,
     ) -> (PerNs, Option<usize>) {
         let res = self.resolve_path_fp_with_macro(
+            local_def_map,
             db,
             ResolveMode::Other,
             original_module,
@@ -624,12 +668,14 @@ impl DefMap {
     /// points at the unresolved segments.
     pub(crate) fn resolve_path_locally(
         &self,
+        local_def_map: &LocalDefMap,
         db: &dyn DefDatabase,
         original_module: LocalModuleId,
         path: &ModPath,
         shadow: BuiltinShadowMode,
     ) -> (PerNs, Option<usize>, ResolvePathResultPrefixInfo) {
         let res = self.resolve_path_fp_with_macro_single(
+            local_def_map,
             db,
             ResolveMode::Other,
             original_module,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
index d1f6ed023c2..289b9e2fb7e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
@@ -1,6 +1,6 @@
 //! Post-nameres attribute resolution.
 
-use base_db::CrateId;
+use base_db::Crate;
 use hir_expand::{
     attrs::{Attr, AttrId, AttrInput},
     inert_attr_macro::find_builtin_attr_idx,
@@ -13,7 +13,7 @@ use triomphe::Arc;
 use crate::{
     db::DefDatabase,
     item_scope::BuiltinShadowMode,
-    nameres::path_resolution::ResolveMode,
+    nameres::{path_resolution::ResolveMode, LocalDefMap},
     path::{self, ModPath, PathKind},
     AstIdWithPath, LocalModuleId, MacroId, UnresolvedMacro,
 };
@@ -30,6 +30,7 @@ pub enum ResolvedAttr {
 impl DefMap {
     pub(crate) fn resolve_attr_macro(
         &self,
+        local_def_map: &LocalDefMap,
         db: &dyn DefDatabase,
         original_module: LocalModuleId,
         ast_id: AstIdWithPath<ast::Item>,
@@ -42,6 +43,7 @@ impl DefMap {
         }
 
         let resolved_res = self.resolve_path_fp_with_macro(
+            local_def_map,
             db,
             ResolveMode::Other,
             original_module,
@@ -105,7 +107,7 @@ pub(super) fn attr_macro_as_call_id(
     db: &dyn DefDatabase,
     item_attr: &AstIdWithPath<ast::Item>,
     macro_attr: &Attr,
-    krate: CrateId,
+    krate: Crate,
     def: MacroDefId,
 ) -> MacroCallId {
     let arg = match macro_attr.input.as_deref() {
@@ -136,7 +138,7 @@ pub(super) fn derive_macro_as_call_id(
     derive_attr_index: AttrId,
     derive_pos: u32,
     call_site: SyntaxContextId,
-    krate: CrateId,
+    krate: Crate,
     resolver: impl Fn(&path::ModPath) -> Option<(MacroId, MacroDefId)>,
     derive_macro_id: MacroCallId,
 ) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index bf013c25ef5..371e9943346 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -5,7 +5,7 @@
 
 use std::{cmp::Ordering, iter, mem, ops::Not};
 
-use base_db::{CrateId, CrateOrigin, Dependency, LangCrateOrigin};
+use base_db::{BuiltDependency, Crate, CrateOrigin, LangCrateOrigin};
 use cfg::{CfgAtom, CfgExpr, CfgOptions};
 use either::Either;
 use hir_expand::{
@@ -39,8 +39,8 @@ use crate::{
         mod_resolution::ModDir,
         path_resolution::ReachedFixedPoint,
         proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind},
-        sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin,
-        ResolveMode,
+        sub_namespace_match, BuiltinShadowMode, DefMap, LocalDefMap, MacroSubNs, ModuleData,
+        ModuleOrigin, ResolveMode,
     },
     path::{ImportAlias, ModPath, PathKind},
     per_ns::{Item, PerNs},
@@ -57,10 +57,14 @@ use crate::{
 const GLOB_RECURSION_LIMIT: usize = 100;
 const FIXED_POINT_LIMIT: usize = 8192;
 
-pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId) -> DefMap {
-    let crate_graph = db.crate_graph();
-
-    let krate = &crate_graph[def_map.krate];
+pub(super) fn collect_defs(
+    db: &dyn DefDatabase,
+    def_map: DefMap,
+    tree_id: TreeId,
+    crate_local_def_map: Option<Arc<LocalDefMap>>,
+) -> (DefMap, LocalDefMap) {
+    let krate = &def_map.krate.data(db);
+    let cfg_options = def_map.krate.cfg_options(db);
 
     // populate external prelude and dependency list
     let mut deps =
@@ -72,8 +76,10 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
     }
 
     let proc_macros = if krate.is_proc_macro {
-        db.proc_macros()
-            .for_crate(def_map.krate, db.syntax_context(tree_id.file_id(), krate.edition))
+        db.proc_macros_for_crate(def_map.krate)
+            .and_then(|proc_macros| {
+                proc_macros.list(db.syntax_context(tree_id.file_id(), krate.edition))
+            })
             .unwrap_or_default()
     } else {
         Default::default()
@@ -82,13 +88,15 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
     let mut collector = DefCollector {
         db,
         def_map,
+        local_def_map: LocalDefMap::default(),
+        crate_local_def_map,
         deps,
         glob_imports: FxHashMap::default(),
         unresolved_imports: Vec::new(),
         indeterminate_imports: Vec::new(),
         unresolved_macros: Vec::new(),
         mod_dirs: FxHashMap::default(),
-        cfg_options: &krate.cfg_options,
+        cfg_options,
         proc_macros,
         from_glob_import: Default::default(),
         skip_attrs: Default::default(),
@@ -101,9 +109,10 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
         collector.seed_with_top_level();
     }
     collector.collect();
-    let mut def_map = collector.finish();
+    let (mut def_map, mut local_def_map) = collector.finish();
     def_map.shrink_to_fit();
-    def_map
+    local_def_map.shrink_to_fit();
+    (def_map, local_def_map)
 }
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -205,8 +214,11 @@ enum MacroDirectiveKind {
 struct DefCollector<'a> {
     db: &'a dyn DefDatabase,
     def_map: DefMap,
+    local_def_map: LocalDefMap,
+    /// Set only in case of blocks.
+    crate_local_def_map: Option<Arc<LocalDefMap>>,
     // The dependencies of the current crate, including optional deps like `test`.
-    deps: FxHashMap<Name, Dependency>,
+    deps: FxHashMap<Name, BuiltDependency>,
     glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, GlobId)>>,
     unresolved_imports: Vec<ImportDirective>,
     indeterminate_imports: Vec<(ImportDirective, PerNs)>,
@@ -238,8 +250,7 @@ impl DefCollector<'_> {
     fn seed_with_top_level(&mut self) {
         let _p = tracing::info_span!("seed_with_top_level").entered();
 
-        let crate_graph = self.db.crate_graph();
-        let file_id = crate_graph[self.def_map.krate].root_file_id();
+        let file_id = self.def_map.krate.data(self.db).root_file_id();
         let item_tree = self.db.file_item_tree(file_id.into());
         let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
         let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
@@ -310,20 +321,24 @@ impl DefCollector<'_> {
                 // don't do pre-configured attribute resolution yet.
                 // So here check if we are no_core / no_std and we are trying to add the
                 // corresponding dep from the sysroot
-                let skip = match crate_graph[dep.crate_id].origin {
-                    CrateOrigin::Lang(LangCrateOrigin::Core) => {
-                        crate_data.no_core && dep.is_sysroot()
-                    }
-                    CrateOrigin::Lang(LangCrateOrigin::Std) => {
-                        crate_data.no_std && dep.is_sysroot()
-                    }
-                    _ => false,
-                };
+
+                // Depending on the crate data of a dependency seems bad for incrementality, but
+                // we only do that for sysroot crates (this is why the order of the `&&` is important)
+                // - which are normally standard library crate, which realistically aren't going
+                // to have their crate ID invalidated, because they stay on the same root file and
+                // they're dependencies of everything else, so if some collision miraculously occurs
+                // we will resolve it by disambiguating the other crate.
+                let skip = dep.is_sysroot()
+                    && match dep.crate_id.data(self.db).origin {
+                        CrateOrigin::Lang(LangCrateOrigin::Core) => crate_data.no_core,
+                        CrateOrigin::Lang(LangCrateOrigin::Std) => crate_data.no_std,
+                        _ => false,
+                    };
                 if skip {
                     continue;
                 }
 
-                crate_data
+                self.local_def_map
                     .extern_prelude
                     .insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None));
             }
@@ -494,7 +509,7 @@ impl DefCollector<'_> {
 
         let krate = if self.def_map.data.no_std {
             Name::new_symbol_root(sym::core.clone())
-        } else if self.def_map.extern_prelude().any(|(name, _)| *name == sym::std.clone()) {
+        } else if self.local_def_map().extern_prelude().any(|(name, _)| *name == sym::std.clone()) {
             Name::new_symbol_root(sym::std.clone())
         } else {
             // If `std` does not exist for some reason, fall back to core. This mostly helps
@@ -518,8 +533,14 @@ impl DefCollector<'_> {
             [krate, Name::new_symbol_root(sym::prelude.clone()), edition],
         );
 
-        let (per_ns, _) =
-            self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None);
+        let (per_ns, _) = self.def_map.resolve_path(
+            self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map),
+            self.db,
+            DefMap::ROOT,
+            &path,
+            BuiltinShadowMode::Other,
+            None,
+        );
 
         match per_ns.types {
             Some(Item { def: ModuleDefId::ModuleId(m), import, .. }) => {
@@ -535,6 +556,10 @@ impl DefCollector<'_> {
         }
     }
 
+    fn local_def_map(&mut self) -> &LocalDefMap {
+        self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map)
+    }
+
     /// Adds a definition of procedural macro `name` to the root module.
     ///
     /// # Notes on procedural macro resolution
@@ -660,7 +685,13 @@ impl DefCollector<'_> {
     ) {
         let vis = self
             .def_map
-            .resolve_visibility(self.db, module_id, vis, false)
+            .resolve_visibility(
+                self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map),
+                self.db,
+                module_id,
+                vis,
+                false,
+            )
             .unwrap_or(Visibility::Public);
         self.def_map.modules[module_id].scope.declare(macro_.into());
         self.update(
@@ -694,7 +725,7 @@ impl DefCollector<'_> {
     /// created by `use` in the root module, ignoring the visibility of `use`.
     fn import_macros_from_extern_crate(
         &mut self,
-        krate: CrateId,
+        krate: Crate,
         names: Option<Vec<Name>>,
         extern_crate: Option<ExternCrateId>,
     ) {
@@ -779,6 +810,7 @@ impl DefCollector<'_> {
             .entered();
         tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
         let res = self.def_map.resolve_path_fp_with_macro(
+            self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map),
             self.db,
             ResolveMode::Import,
             module_id,
@@ -814,7 +846,13 @@ impl DefCollector<'_> {
         let mut def = directive.status.namespaces();
         let vis = self
             .def_map
-            .resolve_visibility(self.db, module_id, &directive.import.visibility, false)
+            .resolve_visibility(
+                self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map),
+                self.db,
+                module_id,
+                &directive.import.visibility,
+                false,
+            )
             .unwrap_or(Visibility::Public);
 
         match import.source {
@@ -1210,6 +1248,7 @@ impl DefCollector<'_> {
             };
             let resolver = |path: &_| {
                 let resolved_res = self.def_map.resolve_path_fp_with_macro(
+                    self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map),
                     self.db,
                     ResolveMode::Other,
                     directive.module_id,
@@ -1495,7 +1534,7 @@ impl DefCollector<'_> {
         .collect(item_tree.top_level_items(), container);
     }
 
-    fn finish(mut self) -> DefMap {
+    fn finish(mut self) -> (DefMap, LocalDefMap) {
         // Emit diagnostics for all remaining unexpanded macros.
         let _p = tracing::info_span!("DefCollector::finish").entered();
 
@@ -1511,6 +1550,7 @@ impl DefCollector<'_> {
                         self.def_map.krate,
                         |path| {
                             let resolved_res = self.def_map.resolve_path_fp_with_macro(
+                                self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map),
                                 self.db,
                                 ResolveMode::Other,
                                 directive.module_id,
@@ -1582,7 +1622,7 @@ impl DefCollector<'_> {
             ));
         }
 
-        self.def_map
+        (self.def_map, self.local_def_map)
     }
 }
 
@@ -1635,9 +1675,9 @@ impl ModCollector<'_, '_> {
                     None,
                 )
             };
-        let resolve_vis = |def_map: &DefMap, visibility| {
+        let resolve_vis = |def_map: &DefMap, local_def_map: &LocalDefMap, visibility| {
             def_map
-                .resolve_visibility(db, module_id, visibility, false)
+                .resolve_visibility(local_def_map, db, module_id, visibility, false)
                 .unwrap_or(Visibility::Public)
         };
 
@@ -1658,6 +1698,11 @@ impl ModCollector<'_, '_> {
 
             let module = self.def_collector.def_map.module_id(module_id);
             let def_map = &mut self.def_collector.def_map;
+            let local_def_map = self
+                .def_collector
+                .crate_local_def_map
+                .as_deref()
+                .unwrap_or(&self.def_collector.local_def_map);
 
             match item {
                 ModItem::Mod(m) => self.collect_module(m, &attrs),
@@ -1711,13 +1756,13 @@ impl ModCollector<'_, '_> {
                     };
 
                     if let Some(resolved) = resolved {
-                        let vis = resolve_vis(def_map, &self.item_tree[*visibility]);
+                        let vis = resolve_vis(def_map, local_def_map, &self.item_tree[*visibility]);
 
                         if is_crate_root {
                             // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
                             if let Some(name) = name {
-                                Arc::get_mut(&mut def_map.data)
-                                    .unwrap()
+                                self.def_collector
+                                    .local_def_map
                                     .extern_prelude
                                     .insert(name.clone(), (resolved, Some(id)));
                             }
@@ -1784,7 +1829,7 @@ impl ModCollector<'_, '_> {
                     let fn_id =
                         FunctionLoc { container, id: ItemTreeId::new(self.tree_id, id) }.intern(db);
 
-                    let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
+                    let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
 
                     if self.def_collector.def_map.block.is_none()
                         && self.def_collector.is_proc_macro
@@ -1804,7 +1849,7 @@ impl ModCollector<'_, '_> {
                 ModItem::Struct(id) => {
                     let it = &self.item_tree[id];
 
-                    let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
+                    let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(
                         self.def_collector,
                         StructLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
@@ -1818,7 +1863,7 @@ impl ModCollector<'_, '_> {
                 ModItem::Union(id) => {
                     let it = &self.item_tree[id];
 
-                    let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
+                    let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(
                         self.def_collector,
                         UnionLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
@@ -1835,7 +1880,7 @@ impl ModCollector<'_, '_> {
                         EnumLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
                             .intern(db);
 
-                    let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
+                    let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(self.def_collector, enum_.into(), &it.name, vis, false);
 
                     let mut index = 0;
@@ -1878,7 +1923,8 @@ impl ModCollector<'_, '_> {
 
                     match &it.name {
                         Some(name) => {
-                            let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
+                            let vis =
+                                resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                             update_def(self.def_collector, const_id.into(), name, vis, false);
                         }
                         None => {
@@ -1892,7 +1938,7 @@ impl ModCollector<'_, '_> {
                 ModItem::Static(id) => {
                     let it = &self.item_tree[id];
 
-                    let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
+                    let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(
                         self.def_collector,
                         StaticLoc { container, id: ItemTreeId::new(self.tree_id, id) }
@@ -1906,7 +1952,7 @@ impl ModCollector<'_, '_> {
                 ModItem::Trait(id) => {
                     let it = &self.item_tree[id];
 
-                    let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
+                    let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(
                         self.def_collector,
                         TraitLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
@@ -1920,7 +1966,7 @@ impl ModCollector<'_, '_> {
                 ModItem::TraitAlias(id) => {
                     let it = &self.item_tree[id];
 
-                    let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
+                    let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(
                         self.def_collector,
                         TraitAliasLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
@@ -1934,7 +1980,7 @@ impl ModCollector<'_, '_> {
                 ModItem::TypeAlias(id) => {
                     let it = &self.item_tree[id];
 
-                    let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
+                    let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(
                         self.def_collector,
                         TypeAliasLoc { container, id: ItemTreeId::new(self.tree_id, id) }
@@ -1971,7 +2017,7 @@ impl ModCollector<'_, '_> {
         &mut self,
         extern_crate_id: ExternCrateId,
         macro_use_attrs: impl Iterator<Item = &'a Attr>,
-        target_crate: CrateId,
+        target_crate: Crate,
     ) {
         cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
         let mut single_imports = Vec::new();
@@ -2115,7 +2161,16 @@ impl ModCollector<'_, '_> {
     ) -> LocalModuleId {
         let def_map = &mut self.def_collector.def_map;
         let vis = def_map
-            .resolve_visibility(self.def_collector.db, self.module_id, visibility, false)
+            .resolve_visibility(
+                self.def_collector
+                    .crate_local_def_map
+                    .as_deref()
+                    .unwrap_or(&self.def_collector.local_def_map),
+                self.def_collector.db,
+                self.module_id,
+                visibility,
+                false,
+            )
             .unwrap_or(Visibility::Public);
         let origin = match definition {
             None => ModuleOrigin::Inline {
@@ -2417,6 +2472,10 @@ impl ModCollector<'_, '_> {
             },
             |path| {
                 let resolved_res = self.def_collector.def_map.resolve_path_fp_with_macro(
+                    self.def_collector
+                        .crate_local_def_map
+                        .as_deref()
+                        .unwrap_or(&self.def_collector.local_def_map),
                     db,
                     ResolveMode::Other,
                     self.module_id,
@@ -2517,7 +2576,6 @@ impl ModCollector<'_, '_> {
 
 #[cfg(test)]
 mod tests {
-    use base_db::RootQueryDb;
     use test_fixture::WithFixture;
 
     use crate::{nameres::DefMapCrateData, test_db::TestDB};
@@ -2528,6 +2586,8 @@ mod tests {
         let mut collector = DefCollector {
             db,
             def_map,
+            local_def_map: LocalDefMap::default(),
+            crate_local_def_map: None,
             deps: FxHashMap::default(),
             glob_imports: FxHashMap::default(),
             unresolved_imports: Vec::new(),
@@ -2550,7 +2610,7 @@ mod tests {
         let (db, file_id) = TestDB::with_single_file(not_ra_fixture);
         let krate = db.test_crate();
 
-        let edition = db.crate_graph()[krate].edition;
+        let edition = krate.data(&db).edition;
         let module_origin = ModuleOrigin::CrateRoot { definition: file_id };
         let def_map = DefMap::empty(
             krate,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
index 47c08d3d1dc..977bc16adf7 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
@@ -19,7 +19,7 @@ use crate::{
     db::DefDatabase,
     item_scope::{ImportOrExternCrate, BUILTIN_SCOPE},
     item_tree::FieldsShape,
-    nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, MacroSubNs},
+    nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, LocalDefMap, MacroSubNs},
     path::{ModPath, PathKind},
     per_ns::PerNs,
     visibility::{RawVisibility, Visibility},
@@ -91,6 +91,7 @@ impl PerNs {
 impl DefMap {
     pub(crate) fn resolve_visibility(
         &self,
+        local_def_map: &LocalDefMap,
         db: &dyn DefDatabase,
         // module to import to
         original_module: LocalModuleId,
@@ -101,8 +102,14 @@ impl DefMap {
     ) -> Option<Visibility> {
         let mut vis = match visibility {
             RawVisibility::Module(path, explicitness) => {
-                let (result, remaining) =
-                    self.resolve_path(db, original_module, path, BuiltinShadowMode::Module, None);
+                let (result, remaining) = self.resolve_path(
+                    local_def_map,
+                    db,
+                    original_module,
+                    path,
+                    BuiltinShadowMode::Module,
+                    None,
+                );
                 if remaining.is_some() {
                     return None;
                 }
@@ -137,6 +144,7 @@ impl DefMap {
     // the result.
     pub(super) fn resolve_path_fp_with_macro(
         &self,
+        local_def_map: &LocalDefMap,
         db: &dyn DefDatabase,
         mode: ResolveMode,
         // module to import to
@@ -148,6 +156,7 @@ impl DefMap {
         expected_macro_subns: Option<MacroSubNs>,
     ) -> ResolvePathResult {
         let mut result = self.resolve_path_fp_with_macro_single(
+            local_def_map,
             db,
             mode,
             original_module,
@@ -196,6 +205,7 @@ impl DefMap {
                         current_map = &arc;
 
                         let new = current_map.resolve_path_fp_in_all_preludes(
+                            local_def_map,
                             db,
                             mode,
                             original_module,
@@ -210,6 +220,7 @@ impl DefMap {
             }
 
             let new = current_map.resolve_path_fp_with_macro_single(
+                local_def_map,
                 db,
                 mode,
                 original_module,
@@ -224,6 +235,7 @@ impl DefMap {
 
     pub(super) fn resolve_path_fp_with_macro_single(
         &self,
+        local_def_map: &LocalDefMap,
         db: &dyn DefDatabase,
         mode: ResolveMode,
         original_module: LocalModuleId,
@@ -258,7 +270,12 @@ impl DefMap {
                     None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                 };
                 tracing::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
-                self.resolve_name_in_crate_root_or_extern_prelude(db, original_module, segment)
+                self.resolve_name_in_crate_root_or_extern_prelude(
+                    local_def_map,
+                    db,
+                    original_module,
+                    segment,
+                )
             }
             PathKind::Plain => {
                 let (_, segment) = match segments.next() {
@@ -276,6 +293,7 @@ impl DefMap {
 
                 tracing::debug!("resolving {:?} in module", segment);
                 self.resolve_name_in_module(
+                    local_def_map,
                     db,
                     original_module,
                     segment,
@@ -321,7 +339,9 @@ impl DefMap {
                     // with), resolve the remaining path segments in that `DefMap`.
                     let path =
                         ModPath::from_segments(PathKind::SELF, path.segments().iter().cloned());
+                    // This is the same crate, so the local def map is the same.
                     return def_map.resolve_path_fp_with_macro(
+                        local_def_map,
                         db,
                         mode,
                         local_id,
@@ -333,7 +353,7 @@ impl DefMap {
 
                 PerNs::types(module.into(), Visibility::Public, None)
             }
-            PathKind::Abs => match self.resolve_path_abs(&mut segments, path) {
+            PathKind::Abs => match self.resolve_path_abs(local_def_map, &mut segments, path) {
                 Either::Left(it) => it,
                 Either::Right(reached_fixed_point) => {
                     return ResolvePathResult::empty(reached_fixed_point)
@@ -347,6 +367,7 @@ impl DefMap {
     /// Resolves a path only in the preludes, without accounting for item scopes.
     pub(super) fn resolve_path_fp_in_all_preludes(
         &self,
+        local_def_map: &LocalDefMap,
         db: &dyn DefDatabase,
         mode: ResolveMode,
         original_module: LocalModuleId,
@@ -368,7 +389,7 @@ impl DefMap {
                     None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                 };
                 tracing::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
-                self.resolve_name_in_extern_prelude(segment)
+                self.resolve_name_in_extern_prelude(local_def_map, segment)
             }
             PathKind::Plain => {
                 let (_, segment) = match segments.next() {
@@ -376,9 +397,9 @@ impl DefMap {
                     None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                 };
                 tracing::debug!("resolving {:?} in module", segment);
-                self.resolve_name_in_all_preludes(db, segment)
+                self.resolve_name_in_all_preludes(local_def_map, db, segment)
             }
-            PathKind::Abs => match self.resolve_path_abs(&mut segments, path) {
+            PathKind::Abs => match self.resolve_path_abs(local_def_map, &mut segments, path) {
                 Either::Left(it) => it,
                 Either::Right(reached_fixed_point) => {
                     return ResolvePathResult::empty(reached_fixed_point)
@@ -395,6 +416,7 @@ impl DefMap {
     /// 2018-style absolute path -- only extern prelude
     fn resolve_path_abs<'a>(
         &self,
+        local_def_map: &LocalDefMap,
         segments: &mut impl Iterator<Item = (usize, &'a Name)>,
         path: &ModPath,
     ) -> Either<PerNs, ReachedFixedPoint> {
@@ -402,7 +424,7 @@ impl DefMap {
             Some((_, segment)) => segment,
             None => return Either::Right(ReachedFixedPoint::Yes),
         };
-        if let Some(&(def, extern_crate)) = self.data.extern_prelude.get(segment) {
+        if let Some(&(def, extern_crate)) = local_def_map.extern_prelude.get(segment) {
             tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
             Either::Left(PerNs::types(
                 def.into(),
@@ -451,6 +473,7 @@ impl DefMap {
                         // this point, we know we're resolving a multi-segment path so macro kind
                         // expectation is discarded.
                         let resolution = defp_map.resolve_path_fp_with_macro(
+                            LocalDefMap::EMPTY,
                             db,
                             ResolveMode::Other,
                             module.local_id,
@@ -568,6 +591,7 @@ impl DefMap {
 
     fn resolve_name_in_module(
         &self,
+        local_def_map: &LocalDefMap,
         db: &dyn DefDatabase,
         module: LocalModuleId,
         name: &Name,
@@ -611,7 +635,7 @@ impl DefMap {
                 // they might been shadowed by local names.
                 return PerNs::none();
             }
-            self.resolve_name_in_extern_prelude(name)
+            self.resolve_name_in_extern_prelude(local_def_map, name)
         };
         let macro_use_prelude = || self.resolve_in_macro_use_prelude(name);
         let prelude = || {
@@ -628,19 +652,24 @@ impl DefMap {
             .or_else(prelude)
     }
 
-    fn resolve_name_in_all_preludes(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
+    fn resolve_name_in_all_preludes(
+        &self,
+        local_def_map: &LocalDefMap,
+        db: &dyn DefDatabase,
+        name: &Name,
+    ) -> PerNs {
         // Resolve in:
         //  - extern prelude / macro_use prelude
         //  - std prelude
-        let extern_prelude = self.resolve_name_in_extern_prelude(name);
+        let extern_prelude = self.resolve_name_in_extern_prelude(local_def_map, name);
         let macro_use_prelude = || self.resolve_in_macro_use_prelude(name);
         let prelude = || self.resolve_in_prelude(db, name);
 
         extern_prelude.or_else(macro_use_prelude).or_else(prelude)
     }
 
-    fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
-        self.data.extern_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| {
+    fn resolve_name_in_extern_prelude(&self, local_def_map: &LocalDefMap, name: &Name) -> PerNs {
+        local_def_map.extern_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| {
             PerNs::types(
                 it.into(),
                 Visibility::Public,
@@ -662,6 +691,7 @@ impl DefMap {
 
     fn resolve_name_in_crate_root_or_extern_prelude(
         &self,
+        local_def_map: &LocalDefMap,
         db: &dyn DefDatabase,
         module: LocalModuleId,
         name: &Name,
@@ -678,7 +708,7 @@ impl DefMap {
                 // Don't resolve extern prelude in pseudo-module of a block.
                 return PerNs::none();
             }
-            self.resolve_name_in_extern_prelude(name)
+            self.resolve_name_in_extern_prelude(local_def_map, name)
         };
 
         from_crate_root.or_else(from_extern_prelude)
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
index cd590205766..9a601e0f013 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
@@ -1,5 +1,11 @@
-use base_db::SourceDatabase;
+use base_db::{
+    CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData,
+    DependencyBuilder, Env, RootQueryDb, SourceDatabase,
+};
+use intern::Symbol;
+use span::Edition;
 use test_fixture::WithFixture;
+use triomphe::Arc;
 
 use crate::{db::DefDatabase, nameres::tests::TestDB, AdtId, ModuleDefId};
 
@@ -23,6 +29,73 @@ fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change:
 }
 
 #[test]
+fn crate_metadata_changes_should_not_invalidate_unrelated_def_maps() {
+    let (mut db, files) = TestDB::with_many_files(
+        r#"
+//- /a.rs crate:a
+pub fn foo() {}
+
+//- /b.rs crate:b
+pub struct Bar;
+
+//- /c.rs crate:c deps:b
+pub const BAZ: u32 = 0;
+    "#,
+    );
+
+    for &krate in db.all_crates().iter() {
+        db.crate_def_map(krate);
+    }
+
+    let all_crates_before = db.all_crates();
+
+    {
+        // Add a dependency a -> b.
+        let mut new_crate_graph = CrateGraphBuilder::default();
+        let mut add_crate = |crate_name, root_file_idx: usize| {
+            new_crate_graph.add_crate_root(
+                files[root_file_idx].file_id(),
+                Edition::CURRENT,
+                Some(CrateDisplayName::from_canonical_name(crate_name)),
+                None,
+                Arc::default(),
+                None,
+                Env::default(),
+                CrateOrigin::Local { repo: None, name: Some(Symbol::intern(crate_name)) },
+                false,
+                None,
+                Arc::new(CrateWorkspaceData { data_layout: Err("".into()), toolchain: None }),
+            )
+        };
+        let a = add_crate("a", 0);
+        let b = add_crate("b", 1);
+        let c = add_crate("c", 2);
+        new_crate_graph
+            .add_dep(c, DependencyBuilder::new(CrateName::new("b").unwrap(), b))
+            .unwrap();
+        new_crate_graph
+            .add_dep(b, DependencyBuilder::new(CrateName::new("a").unwrap(), a))
+            .unwrap();
+        new_crate_graph.set_in_db(&mut db);
+    }
+
+    let all_crates_after = db.all_crates();
+    assert!(
+        Arc::ptr_eq(&all_crates_before, &all_crates_after),
+        "the all_crates list should not have been invalidated"
+    );
+
+    let events = db.log_executed(|| {
+        for &krate in db.all_crates().iter() {
+            db.crate_def_map(krate);
+        }
+    });
+    let invalidated_def_maps =
+        events.iter().filter(|event| event.contains("crate_def_map")).count();
+    assert_eq!(invalidated_def_maps, 1, "{events:#?}")
+}
+
+#[test]
 fn typing_inside_a_function_should_not_invalidate_def_map() {
     check_def_map_is_not_recomputed(
         r"
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
index 610886d55f4..0348a7076a8 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
@@ -1095,7 +1095,7 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
 }
 "#,
     );
-    let krate = db.crate_graph().iter().next().unwrap();
+    let krate = *db.all_crates().last().unwrap();
     let def_map = db.crate_def_map(krate);
 
     assert_eq!(def_map.data.exported_derives.len(), 1);
@@ -1445,7 +1445,7 @@ struct TokenStream;
 fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a }
     "#,
     );
-    let krate = db.crate_graph().iter().next().unwrap();
+    let krate = *db.all_crates().last().unwrap();
     let def_map = db.crate_def_map(krate);
 
     let root_module = &def_map[DefMap::ROOT].scope;
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
index eb9488feaa9..c431b45dc79 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
@@ -80,7 +80,16 @@ pub(crate) fn print_path(
             }
             PathKind::Crate => write!(buf, "crate")?,
             PathKind::Abs => {}
-            PathKind::DollarCrate(_) => write!(buf, "$crate")?,
+            PathKind::DollarCrate(krate) => write!(
+                buf,
+                "{}",
+                krate
+                    .extra_data(db)
+                    .display_name
+                    .as_ref()
+                    .map(|it| it.crate_name().symbol().as_str())
+                    .unwrap_or("$crate")
+            )?,
         },
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index f4773de0855..72723499fc9 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -1,7 +1,7 @@
 //! Name resolution façade.
 use std::{fmt, iter, mem};
 
-use base_db::CrateId;
+use base_db::Crate;
 use hir_expand::{name::Name, MacroDefId};
 use intern::{sym, Symbol};
 use itertools::Itertools as _;
@@ -22,7 +22,7 @@ use crate::{
     hir::{BindingId, ExprId, LabelId},
     item_scope::{BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, BUILTIN_SCOPE},
     lang_item::LangItemTarget,
-    nameres::{DefMap, MacroSubNs, ResolvePathResultPrefixInfo},
+    nameres::{DefMap, LocalDefMap, MacroSubNs, ResolvePathResultPrefixInfo},
     path::{ModPath, Path, PathKind},
     per_ns::PerNs,
     type_ref::{LifetimeRef, TypesMap},
@@ -47,6 +47,7 @@ pub struct Resolver {
 #[derive(Clone)]
 struct ModuleItemMap {
     def_map: Arc<DefMap>,
+    local_def_map: Arc<LocalDefMap>,
     module_id: LocalModuleId,
 }
 
@@ -278,8 +279,8 @@ impl Resolver {
         let within_impl = self.scopes().any(|scope| matches!(scope, Scope::ImplDefScope(_)));
         match visibility {
             RawVisibility::Module(_, _) => {
-                let (item_map, module) = self.item_scope();
-                item_map.resolve_visibility(db, module, visibility, within_impl)
+                let (item_map, item_local_map, module) = self.item_scope();
+                item_map.resolve_visibility(item_local_map, db, module, visibility, within_impl)
             }
             RawVisibility::Public => Some(Visibility::Public),
         }
@@ -474,9 +475,16 @@ impl Resolver {
         path: &ModPath,
         expected_macro_kind: Option<MacroSubNs>,
     ) -> Option<(MacroId, Option<ImportOrGlob>)> {
-        let (item_map, module) = self.item_scope();
+        let (item_map, item_local_map, module) = self.item_scope();
         item_map
-            .resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind)
+            .resolve_path(
+                item_local_map,
+                db,
+                module,
+                path,
+                BuiltinShadowMode::Other,
+                expected_macro_kind,
+            )
             .0
             .take_macros_import()
     }
@@ -550,7 +558,7 @@ impl Resolver {
         for scope in self.scopes() {
             scope.process_names(&mut res, db);
         }
-        let ModuleItemMap { ref def_map, module_id } = self.module_scope;
+        let ModuleItemMap { ref def_map, module_id, ref local_def_map } = self.module_scope;
         // FIXME: should we provide `self` here?
         // f(
         //     Name::self_param(),
@@ -572,7 +580,7 @@ impl Resolver {
                 res.add(name, ScopeDef::ModuleDef(def.into()));
             },
         );
-        def_map.extern_prelude().for_each(|(name, (def, _extern_crate))| {
+        local_def_map.extern_prelude().for_each(|(name, (def, _extern_crate))| {
             res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def.into())));
         });
         BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
@@ -599,7 +607,7 @@ impl Resolver {
 
     pub fn extern_crates_in_scope(&self) -> impl Iterator<Item = (Name, ModuleId)> + '_ {
         self.module_scope
-            .def_map
+            .local_def_map
             .extern_prelude()
             .map(|(name, module_id)| (name.clone(), module_id.0.into()))
     }
@@ -647,11 +655,11 @@ impl Resolver {
     }
 
     pub fn module(&self) -> ModuleId {
-        let (def_map, local_id) = self.item_scope();
+        let (def_map, _, local_id) = self.item_scope();
         def_map.module_id(local_id)
     }
 
-    pub fn krate(&self) -> CrateId {
+    pub fn krate(&self) -> Crate {
         self.module_scope.def_map.krate()
     }
 
@@ -844,9 +852,12 @@ impl Resolver {
             }));
             if let Some(block) = expr_scopes.block(scope_id) {
                 let def_map = db.block_def_map(block);
-                resolver
-                    .scopes
-                    .push(Scope::BlockScope(ModuleItemMap { def_map, module_id: DefMap::ROOT }));
+                let local_def_map = block.lookup(db).module.only_local_def_map(db);
+                resolver.scopes.push(Scope::BlockScope(ModuleItemMap {
+                    def_map,
+                    local_def_map,
+                    module_id: DefMap::ROOT,
+                }));
                 // FIXME: This adds as many module scopes as there are blocks, but resolving in each
                 // already traverses all parents, so this is O(n²). I think we could only store the
                 // innermost module scope instead?
@@ -933,9 +944,10 @@ impl Resolver {
         path: &ModPath,
         shadow: BuiltinShadowMode,
     ) -> PerNs {
-        let (item_map, module) = self.item_scope();
+        let (item_map, item_local_map, module) = self.item_scope();
         // This method resolves `path` just like import paths, so no expected macro subns is given.
-        let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow, None);
+        let (module_res, segment_index) =
+            item_map.resolve_path(item_local_map, db, module, path, shadow, None);
         if segment_index.is_some() {
             return PerNs::none();
         }
@@ -943,13 +955,17 @@ impl Resolver {
     }
 
     /// The innermost block scope that contains items or the module scope that contains this resolver.
-    fn item_scope(&self) -> (&DefMap, LocalModuleId) {
+    fn item_scope(&self) -> (&DefMap, &LocalDefMap, LocalModuleId) {
         self.scopes()
             .find_map(|scope| match scope {
-                Scope::BlockScope(m) => Some((&*m.def_map, m.module_id)),
+                Scope::BlockScope(m) => Some((&*m.def_map, &*m.local_def_map, m.module_id)),
                 _ => None,
             })
-            .unwrap_or((&self.module_scope.def_map, self.module_scope.module_id))
+            .unwrap_or((
+                &self.module_scope.def_map,
+                &self.module_scope.local_def_map,
+                self.module_scope.module_id,
+            ))
     }
 }
 
@@ -1050,7 +1066,8 @@ fn resolver_for_scope_(
     for scope in scope_chain.into_iter().rev() {
         if let Some(block) = scopes.block(scope) {
             let def_map = db.block_def_map(block);
-            r = r.push_block_scope(def_map);
+            let local_def_map = block.lookup(db).module.only_local_def_map(db);
+            r = r.push_block_scope(def_map, local_def_map);
             // FIXME: This adds as many module scopes as there are blocks, but resolving in each
             // already traverses all parents, so this is O(n²). I think we could only store the
             // innermost module scope instead?
@@ -1079,9 +1096,12 @@ impl Resolver {
         self.push_scope(Scope::ImplDefScope(impl_def))
     }
 
-    fn push_block_scope(self, def_map: Arc<DefMap>) -> Resolver {
-        debug_assert!(def_map.block_id().is_some());
-        self.push_scope(Scope::BlockScope(ModuleItemMap { def_map, module_id: DefMap::ROOT }))
+    fn push_block_scope(self, def_map: Arc<DefMap>, local_def_map: Arc<LocalDefMap>) -> Resolver {
+        self.push_scope(Scope::BlockScope(ModuleItemMap {
+            def_map,
+            local_def_map,
+            module_id: DefMap::ROOT,
+        }))
     }
 
     fn push_expr_scope(
@@ -1100,8 +1120,13 @@ impl ModuleItemMap {
         db: &dyn DefDatabase,
         path: &ModPath,
     ) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> {
-        let (module_def, unresolved_idx, prefix_info) =
-            self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
+        let (module_def, unresolved_idx, prefix_info) = self.def_map.resolve_path_locally(
+            &self.local_def_map,
+            db,
+            self.module_id,
+            path,
+            BuiltinShadowMode::Other,
+        );
         match unresolved_idx {
             None => {
                 let (value, import) = to_value_ns(module_def)?;
@@ -1134,8 +1159,13 @@ impl ModuleItemMap {
         path: &ModPath,
     ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>, ResolvePathResultPrefixInfo)>
     {
-        let (module_def, idx, prefix_info) =
-            self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
+        let (module_def, idx, prefix_info) = self.def_map.resolve_path_locally(
+            &self.local_def_map,
+            db,
+            self.module_id,
+            path,
+            BuiltinShadowMode::Other,
+        );
         let (res, import) = to_type_ns(module_def)?;
         Some((res, idx, import, prefix_info))
     }
@@ -1230,11 +1260,14 @@ pub trait HasResolver: Copy {
 
 impl HasResolver for ModuleId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
-        let mut def_map = self.def_map(db);
+        let (mut def_map, local_def_map) = self.local_def_map(db);
         let mut module_id = self.local_id;
 
         if !self.is_block_module() {
-            return Resolver { scopes: vec![], module_scope: ModuleItemMap { def_map, module_id } };
+            return Resolver {
+                scopes: vec![],
+                module_scope: ModuleItemMap { def_map, local_def_map, module_id },
+            };
         }
 
         let mut modules: SmallVec<[_; 1]> = smallvec![];
@@ -1248,10 +1281,14 @@ impl HasResolver for ModuleId {
         }
         let mut resolver = Resolver {
             scopes: Vec::with_capacity(modules.len()),
-            module_scope: ModuleItemMap { def_map, module_id },
+            module_scope: ModuleItemMap {
+                def_map,
+                local_def_map: local_def_map.clone(),
+                module_id,
+            },
         };
         for def_map in modules.into_iter().rev() {
-            resolver = resolver.push_block_scope(def_map);
+            resolver = resolver.push_block_scope(def_map, local_def_map.clone());
         }
         resolver
     }
@@ -1259,9 +1296,10 @@ impl HasResolver for ModuleId {
 
 impl HasResolver for CrateRootModuleId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
+        let (def_map, local_def_map) = self.local_def_map(db);
         Resolver {
             scopes: vec![],
-            module_scope: ModuleItemMap { def_map: self.def_map(db), module_id: DefMap::ROOT },
+            module_scope: ModuleItemMap { def_map, local_def_map, module_id: DefMap::ROOT },
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
index c7ebfeecf51..43d31aa503c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
@@ -158,7 +158,7 @@ impl HasChildSource<LocalFieldId> for VariantId {
         let mut map = ArenaMap::new();
         match &src.value {
             ast::StructKind::Tuple(fl) => {
-                let cfg_options = &db.crate_graph()[container.krate].cfg_options;
+                let cfg_options = container.krate.cfg_options(db);
                 let mut idx = 0;
                 for (i, fd) in fl.fields().enumerate() {
                     let attrs = item_tree.attrs(
@@ -177,7 +177,7 @@ impl HasChildSource<LocalFieldId> for VariantId {
                 }
             }
             ast::StructKind::Record(fl) => {
-                let cfg_options = &db.crate_graph()[container.krate].cfg_options;
+                let cfg_options = container.krate.cfg_options(db);
                 let mut idx = 0;
                 for (i, fd) in fl.fields().enumerate() {
                     let attrs = item_tree.attrs(
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
index b6f08c0cafd..0b97e6c9ce5 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
@@ -3,8 +3,8 @@
 use std::{fmt, panic, sync::Mutex};
 
 use base_db::{
-    CrateId, FileSourceRootInput, FileText, RootQueryDb, SourceDatabase, SourceRoot, SourceRootId,
-    SourceRootInput, Upcast,
+    Crate, CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, RootQueryDb,
+    SourceDatabase, SourceRoot, SourceRootId, SourceRootInput, Upcast,
 };
 use hir_expand::{db::ExpandDatabase, files::FilePosition, InFile};
 use salsa::{AsDynDatabase, Durability};
@@ -24,6 +24,7 @@ use crate::{
 pub(crate) struct TestDB {
     storage: salsa::Storage<Self>,
     files: Arc<base_db::Files>,
+    crates_map: Arc<CratesMap>,
     events: Arc<Mutex<Option<Vec<salsa::Event>>>>,
 }
 
@@ -33,8 +34,12 @@ impl Default for TestDB {
             storage: Default::default(),
             events: Default::default(),
             files: Default::default(),
+            crates_map: Default::default(),
         };
         this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
+        // This needs to be here otherwise `CrateGraphBuilder` panics.
+        this.set_all_crates(Arc::new(Box::new([])));
+        CrateGraphBuilder::default().set_in_db(&mut this);
         this
     }
 }
@@ -133,20 +138,23 @@ impl SourceDatabase for TestDB {
         let files = Arc::clone(&self.files);
         files.set_file_source_root_with_durability(self, id, source_root_id, durability);
     }
+
+    fn crates_map(&self) -> Arc<CratesMap> {
+        self.crates_map.clone()
+    }
 }
 
 impl TestDB {
-    pub(crate) fn fetch_test_crate(&self) -> CrateId {
-        let crate_graph = self.crate_graph();
-        let it = crate_graph
+    pub(crate) fn fetch_test_crate(&self) -> Crate {
+        let all_crates = self.all_crates();
+        all_crates
             .iter()
-            .find(|&idx| {
-                crate_graph[idx].display_name.as_ref().map(|it| it.canonical_name().as_str())
+            .copied()
+            .find(|&krate| {
+                krate.extra_data(self).display_name.as_ref().map(|it| it.canonical_name().as_str())
                     == Some("ra_test_fixture")
             })
-            .or_else(|| crate_graph.iter().next())
-            .unwrap();
-        it
+            .unwrap_or(*all_crates.last().unwrap())
     }
 
     pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
index c9c793d54f2..64b9b49b3e4 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
@@ -1,7 +1,7 @@
 //! A higher level attributes based on TokenTree, with also some shortcuts.
 use std::{borrow::Cow, fmt, ops};
 
-use base_db::CrateId;
+use base_db::Crate;
 use cfg::CfgExpr;
 use either::Either;
 use intern::{sym, Interned, Symbol};
@@ -119,7 +119,7 @@ impl RawAttrs {
 
     /// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
     // FIXME: This should return a different type, signaling it was filtered?
-    pub fn filter(self, db: &dyn ExpandDatabase, krate: CrateId) -> RawAttrs {
+    pub fn filter(self, db: &dyn ExpandDatabase, krate: Crate) -> RawAttrs {
         let has_cfg_attrs = self
             .iter()
             .any(|attr| attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr.clone()));
@@ -127,7 +127,7 @@ impl RawAttrs {
             return self;
         }
 
-        let crate_graph = db.crate_graph();
+        let cfg_options = krate.cfg_options(db);
         let new_attrs =
             self.iter()
                 .flat_map(|attr| -> SmallVec<[_; 1]> {
@@ -151,7 +151,6 @@ impl RawAttrs {
                         |(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)),
                     );
 
-                    let cfg_options = &crate_graph[krate].cfg_options;
                     let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg);
                     let cfg = CfgExpr::parse(&cfg);
                     if cfg_options.check(&cfg) == Some(false) {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
index d8b3f40e8f4..b56ec3d04c0 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
@@ -231,7 +231,7 @@ fn assert_expand(
     let cond = expect_fragment(
         &mut iter,
         parser::PrefixEntryPoint::Expr,
-        db.crate_graph()[id.lookup(db).krate].edition,
+        id.lookup(db).krate.data(db).edition,
         tt.top_subtree().delimiter.delim_span(),
     );
     _ = iter.expect_char(',');
@@ -333,7 +333,7 @@ fn cfg_expand(
 ) -> ExpandResult<tt::TopSubtree> {
     let loc = db.lookup_intern_macro_call(id);
     let expr = CfgExpr::parse(tt);
-    let enabled = db.crate_graph()[loc.krate].cfg_options.check(&expr) != Some(false);
+    let enabled = loc.krate.cfg_options(db).check(&expr) != Some(false);
     let expanded = if enabled { quote!(span=>true) } else { quote!(span=>false) };
     ExpandResult::ok(expanded)
 }
@@ -669,7 +669,7 @@ fn relative_file(
     if res == call_site && !allow_recursion {
         Err(ExpandError::other(err_span, format!("recursive inclusion of `{path_str}`")))
     } else {
-        Ok(EditionedFileId::new(res, db.crate_graph()[lookup.krate].edition))
+        Ok(EditionedFileId::new(res, lookup.krate.data(db).edition))
     }
 }
 
@@ -812,7 +812,7 @@ fn include_str_expand(
 
 fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &Symbol) -> Option<String> {
     let krate = db.lookup_intern_macro_call(arg_id).krate;
-    db.crate_graph()[krate].env.get(key.as_str())
+    krate.env(db).get(key.as_str())
 }
 
 fn env_expand(
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs
index 626a82ae08e..0ceab3c8902 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs
@@ -1,7 +1,7 @@
 //! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro
 use std::iter::Peekable;
 
-use base_db::CrateId;
+use base_db::Crate;
 use cfg::{CfgAtom, CfgExpr};
 use intern::{sym, Symbol};
 use rustc_hash::FxHashSet;
@@ -13,16 +13,16 @@ use tracing::{debug, warn};
 
 use crate::{db::ExpandDatabase, proc_macro::ProcMacroKind, MacroCallLoc, MacroDefKind};
 
-fn check_cfg(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Option<bool> {
+fn check_cfg(db: &dyn ExpandDatabase, attr: &Attr, krate: Crate) -> Option<bool> {
     if !attr.simple_name().as_deref().map(|v| v == "cfg")? {
         return None;
     }
     let cfg = parse_from_attr_token_tree(&attr.meta()?.token_tree()?)?;
-    let enabled = db.crate_graph()[krate].cfg_options.check(&cfg) != Some(false);
+    let enabled = krate.cfg_options(db).check(&cfg) != Some(false);
     Some(enabled)
 }
 
-fn check_cfg_attr(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Option<bool> {
+fn check_cfg_attr(db: &dyn ExpandDatabase, attr: &Attr, krate: Crate) -> Option<bool> {
     if !attr.simple_name().as_deref().map(|v| v == "cfg_attr")? {
         return None;
     }
@@ -32,17 +32,17 @@ fn check_cfg_attr(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Optio
 pub fn check_cfg_attr_value(
     db: &dyn ExpandDatabase,
     attr: &TokenTree,
-    krate: CrateId,
+    krate: Crate,
 ) -> Option<bool> {
     let cfg_expr = parse_from_attr_token_tree(attr)?;
-    let enabled = db.crate_graph()[krate].cfg_options.check(&cfg_expr) != Some(false);
+    let enabled = krate.cfg_options(db).check(&cfg_expr) != Some(false);
     Some(enabled)
 }
 
 fn process_has_attrs_with_possible_comma<I: HasAttrs>(
     db: &dyn ExpandDatabase,
     items: impl Iterator<Item = I>,
-    krate: CrateId,
+    krate: Crate,
     remove: &mut FxHashSet<SyntaxElement>,
 ) -> Option<()> {
     for item in items {
@@ -144,7 +144,7 @@ fn remove_possible_comma(item: &impl AstNode, res: &mut FxHashSet<SyntaxElement>
 fn process_enum(
     db: &dyn ExpandDatabase,
     variants: VariantList,
-    krate: CrateId,
+    krate: Crate,
     remove: &mut FxHashSet<SyntaxElement>,
 ) -> Option<()> {
     'variant: for variant in variants.variants() {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs
index 7a42d662601..3f5b8fdc9bd 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs
@@ -1,17 +1,16 @@
 //! Defines a unit of change that can applied to the database to get the next
 //! state. Changes are transactional.
-use base_db::{CrateGraph, CrateId, CrateWorkspaceData, FileChange, SourceRoot};
-use rustc_hash::FxHashMap;
+use base_db::{CrateGraphBuilder, FileChange, SourceRoot};
 use salsa::Durability;
 use span::FileId;
 use triomphe::Arc;
 
-use crate::{db::ExpandDatabase, proc_macro::ProcMacros};
+use crate::{db::ExpandDatabase, proc_macro::ProcMacrosBuilder};
 
 #[derive(Debug, Default)]
 pub struct ChangeWithProcMacros {
     pub source_change: FileChange,
-    pub proc_macros: Option<ProcMacros>,
+    pub proc_macros: Option<ProcMacrosBuilder>,
 }
 
 impl ChangeWithProcMacros {
@@ -20,8 +19,13 @@ impl ChangeWithProcMacros {
     }
 
     pub fn apply(self, db: &mut impl ExpandDatabase) {
-        self.source_change.apply(db);
+        let crates_id_map = self.source_change.apply(db);
         if let Some(proc_macros) = self.proc_macros {
+            let proc_macros = proc_macros.build(
+                crates_id_map
+                    .as_ref()
+                    .expect("cannot set proc macros without setting the crate graph too"),
+            );
             db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH);
         }
     }
@@ -30,16 +34,11 @@ impl ChangeWithProcMacros {
         self.source_change.change_file(file_id, new_text)
     }
 
-    pub fn set_crate_graph(
-        &mut self,
-        graph: CrateGraph,
-        ws_data: FxHashMap<CrateId, Arc<CrateWorkspaceData>>,
-    ) {
+    pub fn set_crate_graph(&mut self, graph: CrateGraphBuilder) {
         self.source_change.set_crate_graph(graph);
-        self.source_change.set_ws_data(ws_data);
     }
 
-    pub fn set_proc_macros(&mut self, proc_macros: ProcMacros) {
+    pub fn set_proc_macros(&mut self, proc_macros: ProcMacrosBuilder) {
         self.proc_macros = Some(proc_macros);
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
index 2f97cceab55..112327f11e1 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
@@ -1,6 +1,6 @@
 //! Defines database & queries for macro expansion.
 
-use base_db::{CrateId, RootQueryDb};
+use base_db::{Crate, RootQueryDb};
 use either::Either;
 use mbe::MatchedArmIndex;
 use rustc_hash::FxHashSet;
@@ -23,7 +23,7 @@ use crate::{
         span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt,
         SyntaxContextExt as _,
     },
-    proc_macro::{CustomProcMacroExpander, ProcMacros},
+    proc_macro::{CrateProcMacros, CustomProcMacroExpander, ProcMacros},
     span_map::{ExpansionSpanMap, RealSpanMap, SpanMap, SpanMapRef},
     tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo,
     EagerExpander, ExpandError, ExpandResult, ExpandTo, MacroCallKind, MacroCallLoc, MacroDefId,
@@ -57,10 +57,13 @@ pub enum TokenExpander {
 
 #[query_group::query_group]
 pub trait ExpandDatabase: RootQueryDb {
-    /// The proc macros.
+    /// The proc macros. Do not use this! Use `proc_macros_for_crate()` instead.
     #[salsa::input]
     fn proc_macros(&self) -> Arc<ProcMacros>;
 
+    #[salsa::invoke_actual(crate::proc_macro::proc_macros_for_crate)]
+    fn proc_macros_for_crate(&self, krate: Crate) -> Option<Arc<CrateProcMacros>>;
+
     fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
 
     #[salsa::transparent]
@@ -120,7 +123,7 @@ pub trait ExpandDatabase: RootQueryDb {
     #[salsa::invoke(DeclarativeMacroExpander::expander)]
     fn decl_macro_expander(
         &self,
-        def_crate: CrateId,
+        def_crate: Crate,
         id: AstId<ast::Macro>,
     ) -> Arc<DeclarativeMacroExpander>;
 
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs
index 91cbbc37364..fbce3207566 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs
@@ -1,6 +1,6 @@
 //! Compiled declarative macro expanders (`macro_rules!` and `macro`)
 
-use base_db::CrateId;
+use base_db::Crate;
 use intern::sym;
 use span::{Edition, HirFileIdRepr, MacroCallId, Span, SyntaxContextId};
 use stdx::TupleExt;
@@ -70,7 +70,7 @@ impl DeclarativeMacroExpander {
 
     pub(crate) fn expander(
         db: &dyn ExpandDatabase,
-        def_crate: CrateId,
+        def_crate: Crate,
         id: AstId<ast::Macro>,
     ) -> Arc<DeclarativeMacroExpander> {
         let (root, map) = crate::db::parse_with_map(db, id.file_id);
@@ -101,14 +101,12 @@ impl DeclarativeMacroExpander {
             }
         };
         let ctx_edition = |ctx: SyntaxContextId| {
-            let crate_graph = db.crate_graph();
-
             if ctx.is_root() {
-                crate_graph[def_crate].edition
+                def_crate.data(db).edition
             } else {
                 // UNWRAP-SAFETY: Only the root context has no outer expansion
                 let krate = db.lookup_intern_macro_call(ctx.outer_expn(db).unwrap()).def.krate;
-                crate_graph[krate].edition
+                krate.data(db).edition
             }
         };
         let (mac, transparency) = match id.to_ptr(db).to_node(&root) {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
index 4a98b455cab..02dc75a4a6d 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
@@ -18,7 +18,7 @@
 //!
 //!
 //! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
-use base_db::CrateId;
+use base_db::Crate;
 use span::SyntaxContextId;
 use syntax::{ted, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent};
 use syntax_bridge::DocCommentDesugarMode;
@@ -34,7 +34,7 @@ use crate::{
 
 pub fn expand_eager_macro_input(
     db: &dyn ExpandDatabase,
-    krate: CrateId,
+    krate: Crate,
     macro_call: &ast::MacroCall,
     ast_id: AstId<ast::MacroCall>,
     def: MacroDefId,
@@ -115,7 +115,7 @@ fn lazy_expand(
     def: &MacroDefId,
     macro_call: &ast::MacroCall,
     ast_id: AstId<ast::MacroCall>,
-    krate: CrateId,
+    krate: Crate,
     call_site: SyntaxContextId,
 ) -> ExpandResult<(InFile<Parse<SyntaxNode>>, Arc<ExpansionSpanMap>)> {
     let expand_to = ExpandTo::from_call_site(macro_call);
@@ -137,7 +137,7 @@ fn eager_macro_recur(
     expanded_map: &mut ExpansionSpanMap,
     mut offset: TextSize,
     curr: InFile<SyntaxNode>,
-    krate: CrateId,
+    krate: Crate,
     call_site: SyntaxContextId,
     macro_resolver: &dyn Fn(&ModPath) -> Option<MacroDefId>,
 ) -> ExpandResult<Option<(SyntaxNode, TextSize)>> {
@@ -176,7 +176,7 @@ fn eager_macro_recur(
             Some(path) => match macro_resolver(&path) {
                 Some(def) => def,
                 None => {
-                    let edition = db.crate_graph()[krate].edition;
+                    let edition = krate.data(db).edition;
                     error = Some(ExpandError::other(
                         span_map.span_at(call.syntax().text_range().start()),
                         format!("unresolved macro {}", path.display(db, edition)),
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index f8c83dce55a..a1e484c08bc 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -33,7 +33,7 @@ use triomphe::Arc;
 use core::fmt;
 use std::hash::Hash;
 
-use base_db::CrateId;
+use base_db::Crate;
 use either::Either;
 use span::{
     Edition, EditionedFileId, ErasedFileAstId, FileAstId, HirFileIdRepr, Span, SpanAnchor,
@@ -157,7 +157,7 @@ impl ExpandError {
 pub enum ExpandErrorKind {
     /// Attribute macro expansion is disabled.
     ProcMacroAttrExpansionDisabled,
-    MissingProcMacroExpander(CrateId),
+    MissingProcMacroExpander(Crate),
     /// The macro for this call is disabled.
     MacroDisabled,
     /// The macro definition has errors.
@@ -200,7 +200,7 @@ impl ExpandErrorKind {
                 kind: RenderedExpandError::DISABLED,
             },
             &ExpandErrorKind::MissingProcMacroExpander(def_crate) => {
-                match db.proc_macros().get_error_for_crate(def_crate) {
+                match db.proc_macros_for_crate(def_crate).as_ref().and_then(|it| it.get_error()) {
                     Some((e, hard_err)) => RenderedExpandError {
                         message: e.to_owned(),
                         error: hard_err,
@@ -250,14 +250,14 @@ impl From<mbe::ExpandError> for ExpandError {
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct MacroCallLoc {
     pub def: MacroDefId,
-    pub krate: CrateId,
+    pub krate: Crate,
     pub kind: MacroCallKind,
     pub ctxt: SyntaxContextId,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct MacroDefId {
-    pub krate: CrateId,
+    pub krate: Crate,
     pub edition: Edition,
     pub kind: MacroDefKind,
     pub local_inner: bool,
@@ -525,7 +525,7 @@ impl MacroDefId {
     pub fn make_call(
         self,
         db: &dyn ExpandDatabase,
-        krate: CrateId,
+        krate: Crate,
         kind: MacroCallKind,
         ctxt: SyntaxContextId,
     ) -> MacroCallId {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
index 6f6f41f322a..40b10cb4380 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
@@ -11,7 +11,7 @@ use crate::{
     name::{AsName, Name},
     tt,
 };
-use base_db::CrateId;
+use base_db::Crate;
 use intern::sym;
 use smallvec::SmallVec;
 use span::{Edition, SyntaxContextId};
@@ -33,7 +33,7 @@ pub enum PathKind {
     Abs,
     // FIXME: Can we remove this somehow?
     /// `$crate` from macro expansion
-    DollarCrate(CrateId),
+    DollarCrate(Crate),
 }
 
 impl PathKind {
@@ -333,7 +333,7 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: tt::TokenTreesView<'_>) -> Optio
     Some(ModPath { kind, segments })
 }
 
-pub fn resolve_crate_root(db: &dyn ExpandDatabase, mut ctxt: SyntaxContextId) -> Option<CrateId> {
+pub fn resolve_crate_root(db: &dyn ExpandDatabase, mut ctxt: SyntaxContextId) -> Option<Crate> {
     // When resolving `$crate` from a `macro_rules!` invoked in a `macro`,
     // we don't want to pretend that the `macro_rules!` definition is in the `macro`
     // as described in `SyntaxContextId::apply_mark`, so we ignore prepended opaque marks.
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
index 0758bd4515e..51721effa35 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -260,7 +260,7 @@ impl AsName for ast::FieldKind {
     }
 }
 
-impl AsName for base_db::Dependency {
+impl AsName for base_db::BuiltDependency {
     fn as_name(&self) -> Name {
         Name::new_symbol_root((*self.name).clone())
     }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs
index 944341ec3f2..f398b070f79 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs
@@ -1,6 +1,6 @@
 //! Pretty printing of macros output.
 
-use base_db::CrateId;
+use base_db::Crate;
 use rustc_hash::FxHashMap;
 use syntax::NodeOrToken;
 use syntax::{ast::make, SyntaxNode};
@@ -13,13 +13,12 @@ pub fn prettify_macro_expansion(
     db: &dyn ExpandDatabase,
     syn: SyntaxNode,
     span_map: &ExpansionSpanMap,
-    target_crate_id: CrateId,
+    target_crate_id: Crate,
 ) -> SyntaxNode {
     // Because `syntax_bridge::prettify_macro_expansion::prettify_macro_expansion()` clones subtree for `syn`,
     // that means it will be offsetted to the beginning.
     let span_offset = syn.text_range().start();
-    let crate_graph = db.crate_graph();
-    let target_crate = &crate_graph[target_crate_id];
+    let target_crate = target_crate_id.data(db);
     let mut syntax_ctx_id_to_dollar_crate_replacement = FxHashMap::default();
     syntax_bridge::prettify_macro_expansion::prettify_macro_expansion(syn, &mut |dollar_crate| {
         let ctx = span_map.span_at(dollar_crate.text_range().start() + span_offset).ctx;
@@ -41,7 +40,7 @@ pub fn prettify_macro_expansion(
                     target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate)
                 {
                     make::tokens::ident(dep.name.as_str())
-                } else if let Some(crate_name) = &crate_graph[macro_def_crate].display_name {
+                } else if let Some(crate_name) = &macro_def_crate.extra_data(db).display_name {
                     make::tokens::ident(crate_name.crate_name().as_str())
                 } else {
                     return dollar_crate.clone();
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
index 3dc3dcd760c..cdf63e92a89 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
@@ -1,24 +1,36 @@
 //! Proc Macro Expander stuff
 
 use core::fmt;
+use std::any::Any;
 use std::{panic::RefUnwindSafe, sync};
 
-use base_db::{CrateId, Env};
+use base_db::{Crate, CrateBuilderId, CratesIdMap, Env};
 use intern::Symbol;
 use rustc_hash::FxHashMap;
 use span::Span;
+use triomphe::Arc;
 
 use crate::{db::ExpandDatabase, tt, ExpandError, ExpandErrorKind, ExpandResult};
 
-#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
+#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Hash)]
 pub enum ProcMacroKind {
     CustomDerive,
     Bang,
     Attr,
 }
 
+pub trait AsAny: Any {
+    fn as_any(&self) -> &dyn Any;
+}
+
+impl<T: Any> AsAny for T {
+    fn as_any(&self) -> &dyn Any {
+        self
+    }
+}
+
 /// A proc-macro expander implementation.
-pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
+pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe + AsAny {
     /// Run the expander with the given input subtree, optional attribute input subtree (for
     /// [`ProcMacroKind::Attr`]), environment variables, and span information.
     fn expand(
@@ -31,8 +43,18 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
         mixed_site: Span,
         current_dir: Option<String>,
     ) -> Result<tt::TopSubtree, ProcMacroExpansionError>;
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool;
 }
 
+impl PartialEq for dyn ProcMacroExpander {
+    fn eq(&self, other: &Self) -> bool {
+        self.eq_dyn(other)
+    }
+}
+
+impl Eq for dyn ProcMacroExpander {}
+
 #[derive(Debug)]
 pub enum ProcMacroExpansionError {
     /// The proc-macro panicked.
@@ -45,41 +67,68 @@ pub type ProcMacroLoadResult = Result<Vec<ProcMacro>, (String, bool)>;
 type StoredProcMacroLoadResult = Result<Box<[ProcMacro]>, (Box<str>, bool)>;
 
 #[derive(Default, Debug)]
-pub struct ProcMacrosBuilder(FxHashMap<CrateId, StoredProcMacroLoadResult>);
+pub struct ProcMacrosBuilder(FxHashMap<CrateBuilderId, Arc<CrateProcMacros>>);
+
 impl ProcMacrosBuilder {
-    pub fn insert(&mut self, proc_macros_crate: CrateId, proc_macro: ProcMacroLoadResult) {
+    pub fn insert(
+        &mut self,
+        proc_macros_crate: CrateBuilderId,
+        mut proc_macro: ProcMacroLoadResult,
+    ) {
+        if let Ok(proc_macros) = &mut proc_macro {
+            // Sort proc macros to improve incrementality when only their order has changed (ideally the build system
+            // will not change their order, but just to be sure).
+            proc_macros
+                .sort_unstable_by_key(|proc_macro| (proc_macro.name.clone(), proc_macro.kind));
+        }
         self.0.insert(
             proc_macros_crate,
             match proc_macro {
-                Ok(it) => Ok(it.into_boxed_slice()),
-                Err((e, hard_err)) => Err((e.into_boxed_str(), hard_err)),
+                Ok(it) => Arc::new(CrateProcMacros(Ok(it.into_boxed_slice()))),
+                Err((e, hard_err)) => {
+                    Arc::new(CrateProcMacros(Err((e.into_boxed_str(), hard_err))))
+                }
             },
         );
     }
-    pub fn build(mut self) -> ProcMacros {
-        self.0.shrink_to_fit();
-        ProcMacros(self.0)
+
+    pub(crate) fn build(self, crates_id_map: &CratesIdMap) -> ProcMacros {
+        let mut map = self
+            .0
+            .into_iter()
+            .map(|(krate, proc_macro)| (crates_id_map[&krate], proc_macro))
+            .collect::<FxHashMap<_, _>>();
+        map.shrink_to_fit();
+        ProcMacros(map)
     }
 }
 
-#[derive(Default, Debug)]
-pub struct ProcMacros(FxHashMap<CrateId, StoredProcMacroLoadResult>);
-
-impl FromIterator<(CrateId, ProcMacroLoadResult)> for ProcMacros {
-    fn from_iter<T: IntoIterator<Item = (CrateId, ProcMacroLoadResult)>>(iter: T) -> Self {
+impl FromIterator<(CrateBuilderId, ProcMacroLoadResult)> for ProcMacrosBuilder {
+    fn from_iter<T: IntoIterator<Item = (CrateBuilderId, ProcMacroLoadResult)>>(iter: T) -> Self {
         let mut builder = ProcMacrosBuilder::default();
         for (k, v) in iter {
             builder.insert(k, v);
         }
-        builder.build()
+        builder
     }
 }
 
+#[derive(Debug, PartialEq, Eq)]
+pub struct CrateProcMacros(StoredProcMacroLoadResult);
+
+#[derive(Default, Debug)]
+pub struct ProcMacros(FxHashMap<Crate, Arc<CrateProcMacros>>);
 impl ProcMacros {
-    fn get(&self, krate: CrateId, idx: u32, err_span: Span) -> Result<&ProcMacro, ExpandError> {
-        let proc_macros = match self.0.get(&krate) {
-            Some(Ok(proc_macros)) => proc_macros,
-            Some(Err(_)) | None => {
+    fn get(&self, krate: Crate) -> Option<Arc<CrateProcMacros>> {
+        self.0.get(&krate).cloned()
+    }
+}
+
+impl CrateProcMacros {
+    fn get(&self, idx: u32, err_span: Span) -> Result<&ProcMacro, ExpandError> {
+        let proc_macros = match &self.0 {
+            Ok(proc_macros) => proc_macros,
+            Err(_) => {
                 return Err(ExpandError::other(
                     err_span,
                     "internal error: no proc macros for crate",
@@ -98,18 +147,17 @@ impl ProcMacros {
         )
     }
 
-    pub fn get_error_for_crate(&self, krate: CrateId) -> Option<(&str, bool)> {
-        self.0.get(&krate).and_then(|it| it.as_ref().err()).map(|(e, hard_err)| (&**e, *hard_err))
+    pub fn get_error(&self) -> Option<(&str, bool)> {
+        self.0.as_ref().err().map(|(e, hard_err)| (&**e, *hard_err))
     }
 
     /// Fetch the [`CustomProcMacroExpander`]s and their corresponding names for the given crate.
-    pub fn for_crate(
+    pub fn list(
         &self,
-        krate: CrateId,
         def_site_ctx: span::SyntaxContextId,
     ) -> Option<Box<[(crate::name::Name, CustomProcMacroExpander, bool)]>> {
-        match self.0.get(&krate) {
-            Some(Ok(proc_macros)) => Some({
+        match &self.0 {
+            Ok(proc_macros) => Some(
                 proc_macros
                     .iter()
                     .enumerate()
@@ -117,15 +165,15 @@ impl ProcMacros {
                         let name = crate::name::Name::new_symbol(it.name.clone(), def_site_ctx);
                         (name, CustomProcMacroExpander::new(idx as u32), it.disabled)
                     })
-                    .collect()
-            }),
+                    .collect(),
+            ),
             _ => None,
         }
     }
 }
 
 /// A loaded proc-macro.
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Eq)]
 pub struct ProcMacro {
     /// The name of the proc macro.
     pub name: Symbol,
@@ -137,6 +185,23 @@ pub struct ProcMacro {
     pub disabled: bool,
 }
 
+// `#[derive(PartialEq)]` generates a strange "cannot move" error.
+impl PartialEq for ProcMacro {
+    fn eq(&self, other: &Self) -> bool {
+        let Self { name, kind, expander, disabled } = self;
+        let Self {
+            name: other_name,
+            kind: other_kind,
+            expander: other_expander,
+            disabled: other_disabled,
+        } = other;
+        name == other_name
+            && kind == other_kind
+            && expander == other_expander
+            && disabled == other_disabled
+    }
+}
+
 /// A custom proc-macro expander handle. This handle together with its crate resolves to a [`ProcMacro`]
 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
 pub struct CustomProcMacroExpander {
@@ -187,7 +252,7 @@ impl CustomProcMacroExpander {
     }
 
     /// The macro is explicitly disabled due to proc-macro attribute expansion being disabled.
-    pub fn as_expand_error(&self, def_crate: CrateId) -> Option<ExpandErrorKind> {
+    pub fn as_expand_error(&self, def_crate: Crate) -> Option<ExpandErrorKind> {
         match self.proc_macro_id {
             Self::PROC_MACRO_ATTR_DISABLED => Some(ExpandErrorKind::ProcMacroAttrExpansionDisabled),
             Self::DISABLED_ID => Some(ExpandErrorKind::MacroDisabled),
@@ -199,8 +264,8 @@ impl CustomProcMacroExpander {
     pub fn expand(
         self,
         db: &dyn ExpandDatabase,
-        def_crate: CrateId,
-        calling_crate: CrateId,
+        def_crate: Crate,
+        calling_crate: Crate,
         tt: &tt::TopSubtree,
         attr_arg: Option<&tt::TopSubtree>,
         def_site: Span,
@@ -221,8 +286,22 @@ impl CustomProcMacroExpander {
                 ExpandError::new(call_site, ExpandErrorKind::MacroDisabled),
             ),
             id => {
-                let proc_macros = db.proc_macros();
-                let proc_macro = match proc_macros.get(def_crate, id, call_site) {
+                let proc_macros = match db.proc_macros_for_crate(def_crate) {
+                    Some(it) => it,
+                    None => {
+                        return ExpandResult::new(
+                            tt::TopSubtree::empty(tt::DelimSpan {
+                                open: call_site,
+                                close: call_site,
+                            }),
+                            ExpandError::other(
+                                call_site,
+                                "internal error: no proc macros for crate",
+                            ),
+                        )
+                    }
+                };
+                let proc_macro = match proc_macros.get(id, call_site) {
                     Ok(proc_macro) => proc_macro,
                     Err(e) => {
                         return ExpandResult::new(
@@ -235,11 +314,10 @@ impl CustomProcMacroExpander {
                     }
                 };
 
-                let krate_graph = db.crate_graph();
                 // Proc macros have access to the environment variables of the invoking crate.
-                let env = &krate_graph[calling_crate].env;
+                let env = calling_crate.env(db);
                 let current_dir =
-                    krate_graph[calling_crate].proc_macro_cwd.as_deref().map(ToString::to_string);
+                    calling_crate.data(db).proc_macro_cwd.as_deref().map(ToString::to_string);
 
                 match proc_macro.expander.expand(
                     tt,
@@ -278,3 +356,10 @@ impl CustomProcMacroExpander {
         }
     }
 }
+
+pub(crate) fn proc_macros_for_crate(
+    db: &dyn ExpandDatabase,
+    krate: Crate,
+) -> Option<Arc<CrateProcMacros>> {
+    db.proc_macros().get(krate)
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
index 2be32173307..84eb964d564 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
@@ -11,7 +11,7 @@ use tracing::debug;
 use chalk_ir::{cast::Caster, fold::shift::Shift, CanonicalVarKinds};
 use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
 
-use base_db::CrateId;
+use base_db::Crate;
 use hir_def::{
     data::{adt::StructFlags, TraitFlags},
     hir::Movability,
@@ -523,7 +523,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
 
 impl ChalkContext<'_> {
     fn edition(&self) -> Edition {
-        self.db.crate_graph()[self.krate].edition
+        self.krate.data(self.db).edition
     }
 
     fn for_trait_impls(
@@ -593,7 +593,7 @@ impl chalk_ir::UnificationDatabase<Interner> for &dyn HirDatabase {
 
 pub(crate) fn program_clauses_for_chalk_env_query(
     db: &dyn HirDatabase,
-    krate: CrateId,
+    krate: Crate,
     block: Option<BlockId>,
     environment: chalk_ir::Environment<Interner>,
 ) -> chalk_ir::ProgramClauses<Interner> {
@@ -665,7 +665,7 @@ pub(crate) fn associated_ty_data_query(
 
 pub(crate) fn trait_datum_query(
     db: &dyn HirDatabase,
-    krate: CrateId,
+    krate: Crate,
     trait_id: TraitId,
 ) -> Arc<TraitDatum> {
     debug!("trait_datum {:?}", trait_id);
@@ -750,7 +750,7 @@ fn lang_item_from_well_known_trait(trait_: WellKnownTrait) -> LangItem {
 
 pub(crate) fn adt_datum_query(
     db: &dyn HirDatabase,
-    krate: CrateId,
+    krate: Crate,
     chalk_ir::AdtId(adt_id): AdtId,
 ) -> Arc<AdtDatum> {
     debug!("adt_datum {:?}", adt_id);
@@ -824,7 +824,7 @@ pub(crate) fn adt_datum_query(
 
 pub(crate) fn impl_datum_query(
     db: &dyn HirDatabase,
-    krate: CrateId,
+    krate: Crate,
     impl_id: ImplId,
 ) -> Arc<ImplDatum> {
     let _p = tracing::info_span!("impl_datum_query").entered();
@@ -833,11 +833,7 @@ pub(crate) fn impl_datum_query(
     impl_def_datum(db, krate, impl_)
 }
 
-fn impl_def_datum(
-    db: &dyn HirDatabase,
-    krate: CrateId,
-    impl_id: hir_def::ImplId,
-) -> Arc<ImplDatum> {
+fn impl_def_datum(db: &dyn HirDatabase, krate: Crate, impl_id: hir_def::ImplId) -> Arc<ImplDatum> {
     let trait_ref = db
         .impl_trait(impl_id)
         // ImplIds for impls where the trait ref can't be resolved should never reach Chalk
@@ -887,7 +883,7 @@ fn impl_def_datum(
 
 pub(crate) fn associated_ty_value_query(
     db: &dyn HirDatabase,
-    krate: CrateId,
+    krate: Crate,
     id: AssociatedTyValueId,
 ) -> Arc<AssociatedTyValue> {
     let type_alias: TypeAliasAsValue = from_chalk(db, id);
@@ -896,7 +892,7 @@ pub(crate) fn associated_ty_value_query(
 
 fn type_alias_associated_ty_value(
     db: &dyn HirDatabase,
-    _krate: CrateId,
+    _krate: Crate,
     type_alias: TypeAliasId,
 ) -> Arc<AssociatedTyValue> {
     let type_alias_data = db.type_alias_data(type_alias);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
index b3c604015a0..525672bc399 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
@@ -1,6 +1,6 @@
 //! Constant evaluation details
 
-use base_db::CrateId;
+use base_db::Crate;
 use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex};
 use hir_def::{
     expr_store::{Body, HygieneId},
@@ -162,7 +162,7 @@ pub fn intern_const_ref(
     db: &dyn HirDatabase,
     value: &LiteralConstRef,
     ty: Ty,
-    krate: CrateId,
+    krate: Crate,
 ) -> Const {
     let layout = db.layout_of_ty(ty.clone(), TraitEnvironment::empty(krate));
     let bytes = match value {
@@ -185,7 +185,7 @@ pub fn intern_const_ref(
 }
 
 /// Interns a possibly-unknown target usize
-pub fn usize_const(db: &dyn HirDatabase, value: Option<u128>, krate: CrateId) -> Const {
+pub fn usize_const(db: &dyn HirDatabase, value: Option<u128>, krate: Crate) -> Const {
     intern_const_ref(
         db,
         &value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
index f2673dc58fa..37e7df6f4b4 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
@@ -101,10 +101,7 @@ fn check_answer(
 fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String {
     let mut err = String::new();
     let span_formatter = |file, range| format!("{file:?} {range:?}");
-    let display_target = DisplayTarget::from_crate(
-        &db,
-        *db.crate_graph().crates_in_topological_order().last().unwrap(),
-    );
+    let display_target = DisplayTarget::from_crate(&db, *db.all_crates().last().unwrap());
     match e {
         ConstEvalError::MirLowerError(e) => {
             e.pretty_print(&mut err, &db, span_formatter, display_target)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
index 5817ed2ef20..8f48cfc4327 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -3,7 +3,7 @@
 
 use std::sync;
 
-use base_db::{impl_intern_key, CrateId, Upcast};
+use base_db::{impl_intern_key, Crate, Upcast};
 use hir_def::{
     db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, CallableDefId,
     ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId,
@@ -103,8 +103,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> + std::fmt::Debug {
     #[salsa::cycle(crate::layout::layout_of_ty_recover)]
     fn layout_of_ty(&self, ty: Ty, env: Arc<TraitEnvironment>) -> Result<Arc<Layout>, LayoutError>;
 
-    #[salsa::invoke(crate::layout::target_data_layout_query)]
-    fn target_data_layout(&self, krate: CrateId) -> Result<Arc<TargetDataLayout>, Arc<str>>;
+    #[salsa::invoke_actual(crate::layout::target_data_layout_query)]
+    fn target_data_layout(&self, krate: Crate) -> Result<Arc<TargetDataLayout>, Arc<str>>;
 
     #[salsa::invoke_actual(crate::dyn_compatibility::dyn_compatibility_of_trait_query)]
     fn dyn_compatibility_of_trait(&self, trait_: TraitId) -> Option<DynCompatibilityViolation>;
@@ -196,8 +196,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> + std::fmt::Debug {
     #[salsa::invoke_actual(crate::lower::generic_defaults_query)]
     fn generic_defaults(&self, def: GenericDefId) -> GenericDefaults;
 
-    #[salsa::invoke(InherentImpls::inherent_impls_in_crate_query)]
-    fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc<InherentImpls>;
+    #[salsa::invoke_actual(InherentImpls::inherent_impls_in_crate_query)]
+    fn inherent_impls_in_crate(&self, krate: Crate) -> Arc<InherentImpls>;
 
     #[salsa::invoke_actual(InherentImpls::inherent_impls_in_block_query)]
     fn inherent_impls_in_block(&self, block: BlockId) -> Option<Arc<InherentImpls>>;
@@ -209,18 +209,18 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> + std::fmt::Debug {
     #[salsa::invoke(crate::method_resolution::incoherent_inherent_impl_crates)]
     fn incoherent_inherent_impl_crates(
         &self,
-        krate: CrateId,
+        krate: Crate,
         fp: TyFingerprint,
-    ) -> SmallVec<[CrateId; 2]>;
+    ) -> SmallVec<[Crate; 2]>;
 
-    #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)]
-    fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;
+    #[salsa::invoke_actual(TraitImpls::trait_impls_in_crate_query)]
+    fn trait_impls_in_crate(&self, krate: Crate) -> Arc<TraitImpls>;
 
     #[salsa::invoke_actual(TraitImpls::trait_impls_in_block_query)]
     fn trait_impls_in_block(&self, block: BlockId) -> Option<Arc<TraitImpls>>;
 
-    #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)]
-    fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<[Arc<TraitImpls>]>;
+    #[salsa::invoke_actual(TraitImpls::trait_impls_in_deps_query)]
+    fn trait_impls_in_deps(&self, krate: Crate) -> Arc<[Arc<TraitImpls>]>;
 
     // Interned IDs for Chalk integration
     #[salsa::interned]
@@ -253,23 +253,16 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> + std::fmt::Debug {
     #[salsa::invoke(chalk_db::trait_datum_query)]
     fn trait_datum(
         &self,
-        krate: CrateId,
+        krate: Crate,
         trait_id: chalk_db::TraitId,
     ) -> sync::Arc<chalk_db::TraitDatum>;
 
     #[salsa::invoke(chalk_db::adt_datum_query)]
-    fn adt_datum(
-        &self,
-        krate: CrateId,
-        struct_id: chalk_db::AdtId,
-    ) -> sync::Arc<chalk_db::AdtDatum>;
+    fn adt_datum(&self, krate: Crate, struct_id: chalk_db::AdtId) -> sync::Arc<chalk_db::AdtDatum>;
 
     #[salsa::invoke(chalk_db::impl_datum_query)]
-    fn impl_datum(
-        &self,
-        krate: CrateId,
-        impl_id: chalk_db::ImplId,
-    ) -> sync::Arc<chalk_db::ImplDatum>;
+    fn impl_datum(&self, krate: Crate, impl_id: chalk_db::ImplId)
+        -> sync::Arc<chalk_db::ImplDatum>;
 
     #[salsa::invoke(chalk_db::fn_def_datum_query)]
     fn fn_def_datum(&self, fn_def_id: FnDefId) -> sync::Arc<chalk_db::FnDefDatum>;
@@ -287,7 +280,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> + std::fmt::Debug {
     #[salsa::invoke(chalk_db::associated_ty_value_query)]
     fn associated_ty_value(
         &self,
-        krate: CrateId,
+        krate: Crate,
         id: chalk_db::AssociatedTyValueId,
     ) -> sync::Arc<chalk_db::AssociatedTyValue>;
 
@@ -302,7 +295,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> + std::fmt::Debug {
     #[salsa::invoke(crate::traits::trait_solve_query)]
     fn trait_solve(
         &self,
-        krate: CrateId,
+        krate: Crate,
         block: Option<BlockId>,
         goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
     ) -> Option<crate::Solution>;
@@ -310,7 +303,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> + std::fmt::Debug {
     #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)]
     fn program_clauses_for_chalk_env(
         &self,
-        krate: CrateId,
+        krate: Crate,
         block: Option<BlockId>,
         env: chalk_ir::Environment<Interner>,
     ) -> chalk_ir::ProgramClauses<Interner>;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
index eed74e1eee3..a0b1fe32ce5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -288,7 +288,7 @@ impl<'a> DeclValidator<'a> {
 
     fn edition(&self, id: impl HasModule) -> span::Edition {
         let krate = id.krate(self.db.upcast());
-        self.db.crate_graph()[krate].edition
+        krate.data(self.db).edition
     }
 
     fn validate_struct(&mut self, struct_id: StructId) {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
index 975143b29f2..053d1cd41e9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
@@ -4,7 +4,7 @@
 
 use std::fmt;
 
-use base_db::CrateId;
+use base_db::Crate;
 use chalk_solve::rust_ir::AdtKind;
 use either::Either;
 use hir_def::{
@@ -630,7 +630,7 @@ fn missing_match_arms<'p>(
     scrut_ty: &Ty,
     witnesses: Vec<WitnessPat<'p>>,
     arms_is_empty: bool,
-    krate: CrateId,
+    krate: Crate,
 ) -> String {
     struct DisplayWitness<'a, 'p>(&'a WitnessPat<'p>, &'a MatchCheckCtx<'p>, DisplayTarget);
     impl fmt::Display for DisplayWitness<'_, '_> {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index d2b908839c4..84dd4be67fe 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -160,7 +160,7 @@ impl<'a> UnsafeVisitor<'a> {
             DefWithBodyId::FunctionId(func) => TargetFeatures::from_attrs(&db.attrs(func.into())),
             _ => TargetFeatures::default(),
         };
-        let edition = db.crate_graph()[resolver.module().krate()].edition;
+        let edition = resolver.module().krate().data(db).edition;
         Self {
             db,
             infer,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index 95ce36390d3..2ae7e746ba2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -7,7 +7,7 @@ use std::{
     mem,
 };
 
-use base_db::CrateId;
+use base_db::Crate;
 use chalk_ir::{BoundVar, Safety, TyKind};
 use either::Either;
 use hir_def::{
@@ -339,7 +339,7 @@ pub trait HirDisplay {
 }
 
 impl HirFormatter<'_> {
-    pub fn krate(&self) -> CrateId {
+    pub fn krate(&self) -> Crate {
         self.display_target.krate
     }
 
@@ -408,13 +408,13 @@ impl HirFormatter<'_> {
 
 #[derive(Debug, Clone, Copy)]
 pub struct DisplayTarget {
-    krate: CrateId,
+    krate: Crate,
     pub edition: Edition,
 }
 
 impl DisplayTarget {
-    pub fn from_crate(db: &dyn HirDatabase, krate: CrateId) -> Self {
-        let edition = db.crate_graph()[krate].edition;
+    pub fn from_crate(db: &dyn HirDatabase, krate: Crate) -> Self {
+        let edition = krate.data(db).edition;
         Self { krate, edition }
     }
 }
@@ -1711,7 +1711,7 @@ fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = Trai
 #[derive(Clone, Copy, PartialEq, Eq)]
 pub enum SizedByDefault {
     NotSized,
-    Sized { anchor: CrateId },
+    Sized { anchor: Crate },
 }
 
 impl SizedByDefault {
@@ -2266,8 +2266,8 @@ impl HirDisplayWithTypesMap for Path {
                 // Resolve `$crate` to the crate's display name.
                 // FIXME: should use the dependency name instead if available, but that depends on
                 // the crate invoking `HirDisplay`
-                let crate_graph = f.db.crate_graph();
-                let name = crate_graph[*id]
+                let crate_data = id.extra_data(f.db);
+                let name = crate_data
                     .display_name
                     .as_ref()
                     .map(|name| name.canonical_name())
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
index 9283c46d0f6..50cac034e49 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
@@ -307,7 +307,7 @@ impl CapturedItem {
                 }
             }
         }
-        if is_raw_identifier(&result, db.crate_graph()[owner.module(db.upcast()).krate()].edition) {
+        if is_raw_identifier(&result, owner.module(db.upcast()).krate().data(db).edition) {
             result.insert_str(0, "r#");
         }
         result
@@ -316,7 +316,7 @@ impl CapturedItem {
     pub fn display_place_source_code(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String {
         let body = db.body(owner);
         let krate = owner.krate(db.upcast());
-        let edition = db.crate_graph()[krate].edition;
+        let edition = krate.data(db).edition;
         let mut result = body[self.place.local].name.display(db.upcast(), edition).to_string();
         for proj in &self.place.projections {
             match proj {
@@ -368,7 +368,7 @@ impl CapturedItem {
     pub fn display_place(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String {
         let body = db.body(owner);
         let krate = owner.krate(db.upcast());
-        let edition = db.crate_graph()[krate].edition;
+        let edition = krate.data(db).edition;
         let mut result = body[self.place.local].name.display(db.upcast(), edition).to_string();
         let mut field_need_paren = false;
         for proj in &self.place.projections {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs
index 7d77f6d0731..e1e1c44996c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs
@@ -1,6 +1,6 @@
 //! Target dependent parameters needed for layouts
 
-use base_db::CrateId;
+use base_db::Crate;
 use hir_def::layout::TargetDataLayout;
 use rustc_abi::{AlignFromBytesError, TargetDataLayoutErrors};
 use triomphe::Arc;
@@ -9,9 +9,9 @@ use crate::db::HirDatabase;
 
 pub fn target_data_layout_query(
     db: &dyn HirDatabase,
-    krate: CrateId,
+    krate: Crate,
 ) -> Result<Arc<TargetDataLayout>, Arc<str>> {
-    match &db.crate_workspace_data()[&krate].data_layout {
+    match &krate.workspace_data(db).data_layout {
         Ok(it) => match TargetDataLayout::parse_from_llvm_datalayout_string(it) {
             Ok(it) => Ok(Arc::new(it)),
             Err(e) => {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index ff7f0349638..29ab0251f83 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -14,7 +14,7 @@ use std::{
     ops::{self, Not as _},
 };
 
-use base_db::CrateId;
+use base_db::Crate;
 use chalk_ir::{
     cast::Cast,
     fold::{Shift, TypeFoldable},
@@ -801,7 +801,7 @@ impl<'a> TyLoweringContext<'a> {
         }
     }
 
-    fn lower_impl_trait(&mut self, bounds: &[TypeBound], krate: CrateId) -> ImplTrait {
+    fn lower_impl_trait(&mut self, bounds: &[TypeBound], krate: Crate) -> ImplTrait {
         cov_mark::hit!(lower_rpit);
         let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
         let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
@@ -1863,8 +1863,11 @@ pub(crate) fn const_or_path_to_chalk<'g>(
             .unwrap_or_else(|| unknown_const(expected_ty))
         }
         &ConstRef::Complex(it) => {
-            let crate_data = &db.crate_graph()[resolver.krate()];
-            if crate_data.env.get("__ra_is_test_fixture").is_none() && crate_data.origin.is_local()
+            let krate = resolver.krate();
+            // Keep the `&&` this way, because it's better to access the crate data, as we access it for
+            // a bunch of other things nevertheless.
+            if krate.data(db).origin.is_local()
+                && krate.env(db).get("__ra_is_test_fixture").is_none()
             {
                 // FIXME: current `InTypeConstId` is very unstable, so we only use it in non local crate
                 // that are unlikely to be edited.
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
index c5ad808accd..50b0fce62f7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
@@ -5,7 +5,7 @@
 use std::ops::ControlFlow;
 
 use arrayvec::ArrayVec;
-use base_db::CrateId;
+use base_db::Crate;
 use chalk_ir::{cast::Cast, UniverseIndex, WithKind};
 use hir_def::{
     data::{adt::StructFlags, ImplData, TraitFlags},
@@ -148,7 +148,7 @@ pub struct TraitImpls {
 }
 
 impl TraitImpls {
-    pub(crate) fn trait_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
+    pub(crate) fn trait_impls_in_crate_query(db: &dyn HirDatabase, krate: Crate) -> Arc<Self> {
         let _p = tracing::info_span!("trait_impls_in_crate_query", ?krate).entered();
         let mut impls = FxHashMap::default();
 
@@ -175,13 +175,11 @@ impl TraitImpls {
 
     pub(crate) fn trait_impls_in_deps_query(
         db: &dyn HirDatabase,
-        krate: CrateId,
+        krate: Crate,
     ) -> Arc<[Arc<Self>]> {
         let _p = tracing::info_span!("trait_impls_in_deps_query", ?krate).entered();
-        let crate_graph = db.crate_graph();
-
         Arc::from_iter(
-            crate_graph.transitive_deps(krate).map(|krate| db.trait_impls_in_crate(krate)),
+            db.transitive_deps(krate).into_iter().map(|krate| db.trait_impls_in_crate(krate)),
         )
     }
 
@@ -282,7 +280,7 @@ pub struct InherentImpls {
 }
 
 impl InherentImpls {
-    pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
+    pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: Crate) -> Arc<Self> {
         let _p = tracing::info_span!("inherent_impls_in_crate_query", ?krate).entered();
         let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
 
@@ -367,16 +365,15 @@ impl InherentImpls {
 
 pub(crate) fn incoherent_inherent_impl_crates(
     db: &dyn HirDatabase,
-    krate: CrateId,
+    krate: Crate,
     fp: TyFingerprint,
-) -> SmallVec<[CrateId; 2]> {
+) -> SmallVec<[Crate; 2]> {
     let _p = tracing::info_span!("incoherent_inherent_impl_crates").entered();
     let mut res = SmallVec::new();
-    let crate_graph = db.crate_graph();
 
     // should pass crate for finger print and do reverse deps
 
-    for krate in crate_graph.transitive_deps(krate) {
+    for krate in db.transitive_deps(krate) {
         let impls = db.inherent_impls_in_crate(krate);
         if impls.map.get(&fp).is_some_and(|v| !v.is_empty()) {
             res.push(krate);
@@ -386,11 +383,7 @@ pub(crate) fn incoherent_inherent_impl_crates(
     res
 }
 
-pub fn def_crates(
-    db: &dyn HirDatabase,
-    ty: &Ty,
-    cur_crate: CrateId,
-) -> Option<SmallVec<[CrateId; 2]>> {
+pub fn def_crates(db: &dyn HirDatabase, ty: &Ty, cur_crate: Crate) -> Option<SmallVec<[Crate; 2]>> {
     match ty.kind(Interner) {
         &TyKind::Adt(AdtId(def_id), _) => {
             let rustc_has_incoherent_inherent_impls = match def_id {
@@ -1226,7 +1219,7 @@ fn iterate_trait_method_candidates(
         {
             // FIXME: this should really be using the edition of the method name's span, in case it
             // comes from a macro
-            if !db.crate_graph()[krate].edition.at_least_2021() {
+            if !krate.data(db).edition.at_least_2021() {
                 continue;
             }
         }
@@ -1239,7 +1232,7 @@ fn iterate_trait_method_candidates(
         {
             // FIXME: this should really be using the edition of the method name's span, in case it
             // comes from a macro
-            if !db.crate_graph()[krate].edition.at_least_2024() {
+            if !krate.data(db).edition.at_least_2024() {
                 continue;
             }
         }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
index ae454fbe528..7faa23f818b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
@@ -12,7 +12,7 @@ use crate::{
     CallableDefId, ClosureId, Const, ConstScalar, InferenceResult, Interner, MemoryMap,
     Substitution, TraitEnvironment, Ty, TyExt, TyKind,
 };
-use base_db::CrateId;
+use base_db::Crate;
 use chalk_ir::Mutability;
 use either::Either;
 use hir_def::{
@@ -143,7 +143,7 @@ impl<V, T> ProjectionElem<V, T> {
         mut base: Ty,
         db: &dyn HirDatabase,
         closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty,
-        krate: CrateId,
+        krate: Crate,
     ) -> Ty {
         // we only bail on mir building when there are type mismatches
         // but error types may pop up resulting in us still attempting to build the mir
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
index c9d62f566c1..597e6a3ef0b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
@@ -2,7 +2,7 @@
 
 use std::{borrow::Cow, cell::RefCell, fmt::Write, iter, mem, ops::Range};
 
-use base_db::CrateId;
+use base_db::Crate;
 use chalk_ir::{cast::Cast, Mutability};
 use either::Either;
 use hir_def::{
@@ -186,7 +186,7 @@ pub struct Evaluator<'a> {
     cached_fn_trait_func: Option<FunctionId>,
     cached_fn_mut_trait_func: Option<FunctionId>,
     cached_fn_once_trait_func: Option<FunctionId>,
-    crate_id: CrateId,
+    crate_id: Crate,
     // FIXME: This is a workaround, see the comment on `interpret_mir`
     assert_placeholder_ty_is_unused: bool,
     /// A general limit on execution, to prevent non terminating programs from breaking r-a main process
@@ -2785,7 +2785,7 @@ impl Evaluator<'_> {
                 let db = self.db.upcast();
                 let loc = variant.lookup(db);
                 let enum_loc = loc.parent.lookup(db);
-                let edition = self.db.crate_graph()[self.crate_id].edition;
+                let edition = self.crate_id.data(self.db).edition;
                 let name = format!(
                     "{}::{}",
                     enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db.upcast(), edition),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
index f61ecabb7e4..346dea82526 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
@@ -569,7 +569,7 @@ impl Evaluator<'_> {
                     }
                     String::from_utf8_lossy(&name_buf)
                 };
-                let value = self.db.crate_graph()[self.crate_id].env.get(&name);
+                let value = self.crate_id.env(self.db).get(&name);
                 match value {
                     None => {
                         // Write null as fail
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index 17f1da0c9f3..57d4baa137c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -2,7 +2,7 @@
 
 use std::{fmt::Write, iter, mem};
 
-use base_db::{salsa::Cycle, CrateId};
+use base_db::{salsa::Cycle, Crate};
 use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
 use hir_def::{
     data::adt::{StructKind, VariantData},
@@ -1920,10 +1920,10 @@ impl<'ctx> MirLowerCtx<'ctx> {
     }
 
     fn edition(&self) -> Edition {
-        self.db.crate_graph()[self.krate()].edition
+        self.krate().data(self.db).edition
     }
 
-    fn krate(&self) -> CrateId {
+    fn krate(&self) -> Crate {
         self.owner.krate(self.db.upcast())
     }
 
@@ -2121,7 +2121,7 @@ pub fn mir_body_for_closure_query(
 
 pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> {
     let krate = def.krate(db.upcast());
-    let edition = db.crate_graph()[krate].edition;
+    let edition = krate.data(db).edition;
     let detail = match def {
         DefWithBodyId::FunctionId(it) => {
             db.function_data(it).name.display(db.upcast(), edition).to_string()
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
index b18a057ba0b..68d0c8cd5a3 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
@@ -3,8 +3,8 @@
 use std::{fmt, panic, sync::Mutex};
 
 use base_db::{
-    FileSourceRootInput, FileText, RootQueryDb, SourceDatabase, SourceRoot, SourceRootId,
-    SourceRootInput, Upcast,
+    CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, RootQueryDb, SourceDatabase,
+    SourceRoot, SourceRootId, SourceRootInput, Upcast,
 };
 
 use hir_def::{db::DefDatabase, ModuleId};
@@ -21,6 +21,7 @@ use triomphe::Arc;
 pub(crate) struct TestDB {
     storage: salsa::Storage<Self>,
     files: Arc<base_db::Files>,
+    crates_map: Arc<CratesMap>,
     events: Arc<Mutex<Option<Vec<salsa::Event>>>>,
 }
 
@@ -30,8 +31,12 @@ impl Default for TestDB {
             storage: Default::default(),
             events: Default::default(),
             files: Default::default(),
+            crates_map: Default::default(),
         };
         this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
+        // This needs to be here otherwise `CrateGraphBuilder` panics.
+        this.set_all_crates(Arc::new(Box::new([])));
+        CrateGraphBuilder::default().set_in_db(&mut this);
         this
     }
 }
@@ -115,6 +120,10 @@ impl SourceDatabase for TestDB {
         let files = Arc::clone(&self.files);
         files.set_file_source_root_with_durability(self, id, source_root_id, durability);
     }
+
+    fn crates_map(&self) -> Arc<CratesMap> {
+        self.crates_map.clone()
+    }
 }
 
 #[salsa::db]
@@ -151,8 +160,7 @@ impl TestDB {
         &self,
     ) -> FxHashMap<EditionedFileId, Vec<(TextRange, String)>> {
         let mut files = Vec::new();
-        let crate_graph = self.crate_graph();
-        for krate in crate_graph.iter() {
+        for &krate in self.all_crates().iter() {
             let crate_def_map = self.crate_def_map(krate);
             for (module_id, _) in crate_def_map.modules() {
                 let file_id = crate_def_map[module_id].origin.file_id();
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
index 26229040582..cdb9e9edf88 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
@@ -15,7 +15,7 @@ mod type_alias_impl_traits;
 use std::env;
 use std::sync::LazyLock;
 
-use base_db::{CrateId, SourceDatabase};
+use base_db::{Crate, SourceDatabase};
 use expect_test::Expect;
 use hir_def::{
     db::DefDatabase,
@@ -124,7 +124,7 @@ fn check_impl(
     }
     assert!(had_annotations || allow_none, "no `//^` annotations found");
 
-    let mut defs: Vec<(DefWithBodyId, CrateId)> = Vec::new();
+    let mut defs: Vec<(DefWithBodyId, Crate)> = Vec::new();
     for file_id in files {
         let module = db.module_for_file_opt(file_id);
         let module = match module {
@@ -302,7 +302,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
     let mut infer_def = |inference_result: Arc<InferenceResult>,
                          body: Arc<Body>,
                          body_source_map: Arc<BodySourceMap>,
-                         krate: CrateId| {
+                         krate: Crate| {
         let display_target = DisplayTarget::from_crate(&db, krate);
         let mut types: Vec<(InFile<SyntaxNode>, &Ty)> = Vec::new();
         let mut mismatches: Vec<(InFile<SyntaxNode>, &TypeMismatch)> = Vec::new();
@@ -391,7 +391,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
     let module = db.module_for_file(file_id);
     let def_map = module.def_map(&db);
 
-    let mut defs: Vec<(DefWithBodyId, CrateId)> = Vec::new();
+    let mut defs: Vec<(DefWithBodyId, Crate)> = Vec::new();
     visit_module(&db, &def_map, module.local_id, &mut |it| {
         let def = match it {
             ModuleDefId::FunctionId(it) => it.into(),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
index 0135e0a409b..f8db6a8298f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
@@ -7,7 +7,7 @@ use chalk_ir::{fold::TypeFoldable, DebruijnIndex, GoalData};
 use chalk_recursive::Cache;
 use chalk_solve::{logging_db::LoggingRustIrDatabase, rust_ir, Solver};
 
-use base_db::CrateId;
+use base_db::Crate;
 use hir_def::{
     lang_item::{LangItem, LangItemTarget},
     BlockId, TraitId,
@@ -30,7 +30,7 @@ const CHALK_SOLVER_FUEL: i32 = 1000;
 #[derive(Debug, Copy, Clone)]
 pub(crate) struct ChalkContext<'a> {
     pub(crate) db: &'a dyn HirDatabase,
-    pub(crate) krate: CrateId,
+    pub(crate) krate: Crate,
     pub(crate) block: Option<BlockId>,
 }
 
@@ -48,7 +48,7 @@ fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> {
 /// we assume that `T: Default`.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct TraitEnvironment {
-    pub krate: CrateId,
+    pub krate: Crate,
     pub block: Option<BlockId>,
     // FIXME make this a BTreeMap
     traits_from_clauses: Box<[(Ty, TraitId)]>,
@@ -56,7 +56,7 @@ pub struct TraitEnvironment {
 }
 
 impl TraitEnvironment {
-    pub fn empty(krate: CrateId) -> Arc<Self> {
+    pub fn empty(krate: Crate) -> Arc<Self> {
         Arc::new(TraitEnvironment {
             krate,
             block: None,
@@ -66,7 +66,7 @@ impl TraitEnvironment {
     }
 
     pub fn new(
-        krate: CrateId,
+        krate: Crate,
         block: Option<BlockId>,
         traits_from_clauses: Box<[(Ty, TraitId)]>,
         env: chalk_ir::Environment<Interner>,
@@ -109,7 +109,7 @@ pub(crate) fn normalize_projection_query(
 /// Solve a trait goal using Chalk.
 pub(crate) fn trait_solve_query(
     db: &dyn HirDatabase,
-    krate: CrateId,
+    krate: Crate,
     block: Option<BlockId>,
     goal: Canonical<InEnvironment<Goal>>,
 ) -> Option<Solution> {
@@ -148,7 +148,7 @@ pub(crate) fn trait_solve_query(
 
 fn solve(
     db: &dyn HirDatabase,
-    krate: CrateId,
+    krate: Crate,
     block: Option<BlockId>,
     goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>,
 ) -> Option<chalk_solve::Solution<Interner>> {
@@ -294,7 +294,7 @@ impl FnTrait {
         }
     }
 
-    pub fn get_id(self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
+    pub fn get_id(self, db: &dyn HirDatabase, krate: Crate) -> Option<TraitId> {
         let target = db.lang_item(krate, self.lang_item())?;
         match target {
             LangItemTarget::Trait(t) => Some(t),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
index 89d89fe2230..b90f4e4d7fa 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
@@ -3,7 +3,7 @@
 
 use std::{hash::Hash, iter};
 
-use base_db::CrateId;
+use base_db::Crate;
 use chalk_ir::{
     fold::{FallibleTypeFolder, Shift},
     DebruijnIndex,
@@ -34,10 +34,7 @@ use crate::{
     TraitRefExt, Ty, WhereClause,
 };
 
-pub(crate) fn fn_traits(
-    db: &dyn DefDatabase,
-    krate: CrateId,
-) -> impl Iterator<Item = TraitId> + '_ {
+pub(crate) fn fn_traits(db: &dyn DefDatabase, krate: Crate) -> impl Iterator<Item = TraitId> + '_ {
     [LangItem::Fn, LangItem::FnMut, LangItem::FnOnce]
         .into_iter()
         .filter_map(move |lang| db.lang_item(krate, lang))
diff --git a/src/tools/rust-analyzer/crates/hir/src/from_id.rs b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
index 72df07ef8c0..891d2fd2ba5 100644
--- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
@@ -30,7 +30,7 @@ macro_rules! from_id {
 }
 
 from_id![
-    (base_db::CrateId, crate::Crate),
+    (base_db::Crate, crate::Crate),
     (hir_def::ModuleId, crate::Module),
     (hir_def::StructId, crate::Struct),
     (hir_def::UnionId, crate::Union),
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index dbe743e7e2f..5da2b5ed093 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -39,7 +39,7 @@ use std::{
 };
 
 use arrayvec::ArrayVec;
-use base_db::{CrateDisplayName, CrateId, CrateOrigin, LangCrateOrigin};
+use base_db::{CrateDisplayName, CrateOrigin, LangCrateOrigin};
 use either::Either;
 use hir_def::{
     data::{adt::VariantData, TraitFlags},
@@ -176,7 +176,7 @@ use {
 /// root module.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct Crate {
-    pub(crate) id: CrateId,
+    pub(crate) id: base_db::Crate,
 }
 
 #[derive(Debug)]
@@ -187,7 +187,7 @@ pub struct CrateDependency {
 
 impl Crate {
     pub fn origin(self, db: &dyn HirDatabase) -> CrateOrigin {
-        db.crate_graph()[self.id].origin.clone()
+        self.id.data(db).origin.clone()
     }
 
     pub fn is_builtin(self, db: &dyn HirDatabase) -> bool {
@@ -195,7 +195,8 @@ impl Crate {
     }
 
     pub fn dependencies(self, db: &dyn HirDatabase) -> Vec<CrateDependency> {
-        db.crate_graph()[self.id]
+        self.id
+            .data(db)
             .dependencies
             .iter()
             .map(|dep| {
@@ -207,12 +208,11 @@ impl Crate {
     }
 
     pub fn reverse_dependencies(self, db: &dyn HirDatabase) -> Vec<Crate> {
-        let crate_graph = db.crate_graph();
-        crate_graph
+        let all_crates = db.all_crates();
+        all_crates
             .iter()
-            .filter(|&krate| {
-                crate_graph[krate].dependencies.iter().any(|it| it.crate_id == self.id)
-            })
+            .copied()
+            .filter(|&krate| krate.data(db).dependencies.iter().any(|it| it.crate_id == self.id))
             .map(|id| Crate { id })
             .collect()
     }
@@ -221,7 +221,7 @@ impl Crate {
         self,
         db: &dyn HirDatabase,
     ) -> impl Iterator<Item = Crate> {
-        db.crate_graph().transitive_rev_deps(self.id).map(|id| Crate { id })
+        db.transitive_rev_deps(self.id).into_iter().map(|id| Crate { id })
     }
 
     pub fn root_module(self) -> Module {
@@ -234,19 +234,19 @@ impl Crate {
     }
 
     pub fn root_file(self, db: &dyn HirDatabase) -> FileId {
-        db.crate_graph()[self.id].root_file_id
+        self.id.data(db).root_file_id
     }
 
     pub fn edition(self, db: &dyn HirDatabase) -> Edition {
-        db.crate_graph()[self.id].edition
+        self.id.data(db).edition
     }
 
     pub fn version(self, db: &dyn HirDatabase) -> Option<String> {
-        db.crate_graph()[self.id].version.clone()
+        self.id.extra_data(db).version.clone()
     }
 
     pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateDisplayName> {
-        db.crate_graph()[self.id].display_name.clone()
+        self.id.extra_data(db).display_name.clone()
     }
 
     pub fn query_external_importables(
@@ -264,7 +264,7 @@ impl Crate {
     }
 
     pub fn all(db: &dyn HirDatabase) -> Vec<Crate> {
-        db.crate_graph().iter().map(|id| Crate { id }).collect()
+        db.all_crates().iter().map(|&id| Crate { id }).collect()
     }
 
     /// Try to get the root URL of the documentation of a crate.
@@ -276,12 +276,12 @@ impl Crate {
     }
 
     pub fn cfg(&self, db: &dyn HirDatabase) -> Arc<CfgOptions> {
-        db.crate_graph()[self.id].cfg_options.clone()
+        Arc::clone(self.id.cfg_options(db))
     }
 
-    pub fn potential_cfg(&self, db: &dyn HirDatabase) -> Arc<CfgOptions> {
-        let data = &db.crate_graph()[self.id];
-        data.potential_cfg_options.clone().unwrap_or_else(|| data.cfg_options.clone())
+    pub fn potential_cfg<'db>(&self, db: &'db dyn HirDatabase) -> &'db CfgOptions {
+        let data = self.id.extra_data(db);
+        data.potential_cfg_options.as_ref().unwrap_or_else(|| self.id.cfg_options(db))
     }
 
     pub fn to_display_target(self, db: &dyn HirDatabase) -> DisplayTarget {
@@ -289,11 +289,12 @@ impl Crate {
     }
 
     fn core(db: &dyn HirDatabase) -> Option<Crate> {
-        let crate_graph = db.crate_graph();
-        let result = crate_graph
+        let result = db
+            .all_crates()
             .iter()
+            .copied()
             .find(|&krate| {
-                matches!(crate_graph[krate].origin, CrateOrigin::Lang(LangCrateOrigin::Core))
+                matches!(krate.data(db).origin, CrateOrigin::Lang(LangCrateOrigin::Core))
             })
             .map(Crate::from);
         result
@@ -490,9 +491,7 @@ impl HasCrate for ModuleDef {
     fn krate(&self, db: &dyn HirDatabase) -> Crate {
         match self.module(db) {
             Some(module) => module.krate(),
-            None => Crate::core(db).unwrap_or_else(|| {
-                (*db.crate_graph().crates_in_topological_order().last().unwrap()).into()
-            }),
+            None => Crate::core(db).unwrap_or_else(|| db.all_crates()[0].into()),
         }
     }
 }
@@ -611,7 +610,7 @@ impl Module {
         style_lints: bool,
     ) {
         let _p = tracing::info_span!("diagnostics", name = ?self.name(db)).entered();
-        let edition = db.crate_graph()[self.id.krate()].edition;
+        let edition = self.id.krate().data(db).edition;
         let def_map = self.id.def_map(db.upcast());
         for diag in def_map.diagnostics() {
             if diag.in_module != self.id.local_id {
@@ -970,7 +969,7 @@ fn emit_macro_def_diagnostics(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>
                 return;
             };
             let krate = HasModule::krate(&m.id, db.upcast());
-            let edition = db.crate_graph()[krate].edition;
+            let edition = krate.data(db).edition;
             emit_def_diagnostic_(
                 db,
                 acc,
@@ -3027,7 +3026,8 @@ impl BuiltinType {
     }
 
     pub fn ty(self, db: &dyn HirDatabase) -> Type {
-        Type::new_for_crate(db.crate_graph().iter().next().unwrap(), TyBuilder::builtin(self.inner))
+        let core = Crate::core(db).map(|core| core.id).unwrap_or_else(|| db.all_crates()[0]);
+        Type::new_for_crate(core, TyBuilder::builtin(self.inner))
     }
 
     pub fn name(self) -> Name {
@@ -3968,7 +3968,7 @@ impl DeriveHelper {
 // FIXME: Wrong name? This is could also be a registered attribute
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub struct BuiltinAttr {
-    krate: Option<CrateId>,
+    krate: Option<base_db::Crate>,
     idx: u32,
 }
 
@@ -4014,7 +4014,7 @@ impl BuiltinAttr {
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub struct ToolModule {
-    krate: CrateId,
+    krate: base_db::Crate,
     idx: u32,
 }
 
@@ -4732,7 +4732,7 @@ impl Type {
         Type { env: environment, ty }
     }
 
-    pub(crate) fn new_for_crate(krate: CrateId, ty: Ty) -> Type {
+    pub(crate) fn new_for_crate(krate: base_db::Crate, ty: Ty) -> Type {
         Type { env: TraitEnvironment::empty(krate), ty }
     }
 
@@ -4794,7 +4794,7 @@ impl Type {
         Type { env: ty.env, ty: TyBuilder::slice(ty.ty) }
     }
 
-    pub fn new_tuple(krate: CrateId, tys: &[Type]) -> Type {
+    pub fn new_tuple(krate: base_db::Crate, tys: &[Type]) -> Type {
         let tys = tys.iter().map(|it| it.ty.clone());
         Type { env: TraitEnvironment::empty(krate), ty: TyBuilder::tuple_with(tys) }
     }
@@ -4826,7 +4826,7 @@ impl Type {
     pub fn contains_reference(&self, db: &dyn HirDatabase) -> bool {
         return go(db, self.env.krate, &self.ty);
 
-        fn go(db: &dyn HirDatabase, krate: CrateId, ty: &Ty) -> bool {
+        fn go(db: &dyn HirDatabase, krate: base_db::Crate, ty: &Ty) -> bool {
             match ty.kind(Interner) {
                 // Reference itself
                 TyKind::Ref(_, _, _) => true,
@@ -6209,7 +6209,7 @@ impl HasContainer for Module {
         let def_map = self.id.def_map(db.upcast());
         match def_map[self.id.local_id].parent {
             Some(parent_id) => ItemContainer::Module(Module { id: def_map.module_id(parent_id) }),
-            None => ItemContainer::Crate(def_map.krate()),
+            None => ItemContainer::Crate(def_map.krate().into()),
         }
     }
 }
@@ -6289,7 +6289,7 @@ pub enum ItemContainer {
     Impl(Impl),
     Module(Module),
     ExternBlock(ExternBlock),
-    Crate(CrateId),
+    Crate(Crate),
 }
 
 /// Subset of `ide_db::Definition` that doc links can resolve to.
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index aeeb3f97909..6378eebd24f 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -318,7 +318,7 @@ impl<'db> SemanticsImpl<'db> {
     pub fn first_crate_or_default(&self, file: FileId) -> Crate {
         match self.file_to_module_defs(file).next() {
             Some(module) => module.krate(),
-            None => (*self.db.crate_graph().crates_in_topological_order().last().unwrap()).into(),
+            None => (*self.db.all_crates().last().unwrap()).into(),
         }
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index fa8153ad216..cc8aa1f1a18 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -77,10 +77,7 @@ impl<'a> SymbolCollector<'a> {
             symbols: Default::default(),
             work: Default::default(),
             current_container_name: None,
-            display_target: DisplayTarget::from_crate(
-                db,
-                *db.crate_graph().crates_in_topological_order().last().unwrap(),
-            ),
+            display_target: DisplayTarget::from_crate(db, *db.all_crates().last().unwrap()),
         }
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
index 9e09f198feb..5a791c58bfd 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
@@ -7,7 +7,7 @@ use hir::{
     sym, FileRange, PathResolution, Semantics, TypeInfo,
 };
 use ide_db::{
-    base_db::CrateId,
+    base_db::Crate,
     defs::Definition,
     imports::insert_use::remove_path_if_in_use_stmt,
     path_transform::PathTransform,
@@ -251,11 +251,11 @@ struct CallInfo {
     node: ast::CallableExpr,
     arguments: Vec<ast::Expr>,
     generic_arg_list: Option<ast::GenericArgList>,
-    krate: CrateId,
+    krate: Crate,
 }
 
 impl CallInfo {
-    fn from_name_ref(name_ref: ast::NameRef, krate: CrateId) -> Option<CallInfo> {
+    fn from_name_ref(name_ref: ast::NameRef, krate: Crate) -> Option<CallInfo> {
         let parent = name_ref.syntax().parent()?;
         if let Some(call) = ast::MethodCallExpr::cast(parent.clone()) {
             let receiver = call.receiver()?;
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
index d8b26567827..4c8940db95c 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
@@ -233,7 +233,13 @@ impl RootDatabase {
             // // SourceDatabase
             // base_db::ParseQuery
             // base_db::ParseErrorsQuery
-            // base_db::CrateGraphQuery
+            // base_db::AllCratesQuery
+            // base_db::InternUniqueCrateDataQuery
+            // base_db::InternUniqueCrateDataLookupQuery
+            // base_db::CrateDataQuery
+            // base_db::ExtraCrateDataQuery
+            // base_db::CrateCfgQuery
+            // base_db::CrateEnvQuery
             // base_db::CrateWorkspaceDataQuery
 
             // // SourceDatabaseExt
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
index af4c10f8ea6..fe4662785af 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
@@ -1,6 +1,6 @@
 //! See [`FamousDefs`].
 
-use base_db::{CrateOrigin, LangCrateOrigin, RootQueryDb as _};
+use base_db::{CrateOrigin, LangCrateOrigin};
 use hir::{Crate, Enum, Function, Macro, Module, ScopeDef, Semantics, Trait};
 
 use crate::RootDatabase;
@@ -198,11 +198,10 @@ impl FamousDefs<'_, '_> {
     fn find_lang_crate(&self, origin: LangCrateOrigin) -> Option<Crate> {
         let krate = self.1;
         let db = self.0.db;
-        let crate_graph = self.0.db.crate_graph();
         let res = krate
             .dependencies(db)
             .into_iter()
-            .find(|dep| crate_graph[dep.krate.into()].origin == CrateOrigin::Lang(origin))?
+            .find(|dep| dep.krate.origin(db) == CrateOrigin::Lang(origin))?
             .krate;
         Some(res)
     }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
index 2516a9d0aa3..414cc6cd18f 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
@@ -51,9 +51,8 @@ use salsa::Durability;
 use std::{fmt, mem::ManuallyDrop};
 
 use base_db::{
-    query_group::{self},
-    FileSourceRootInput, FileText, Files, RootQueryDb, SourceDatabase, SourceRoot, SourceRootId,
-    SourceRootInput, Upcast,
+    query_group, CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Files, RootQueryDb,
+    SourceDatabase, SourceRoot, SourceRootId, SourceRootInput, Upcast,
 };
 use hir::{
     db::{DefDatabase, ExpandDatabase, HirDatabase},
@@ -85,6 +84,7 @@ pub struct RootDatabase {
     // compile times of all `ide_*` and downstream crates suffer greatly.
     storage: ManuallyDrop<salsa::Storage<Self>>,
     files: Arc<Files>,
+    crates_map: Arc<CratesMap>,
 }
 
 impl std::panic::RefUnwindSafe for RootDatabase {}
@@ -102,7 +102,11 @@ impl Drop for RootDatabase {
 
 impl Clone for RootDatabase {
     fn clone(&self) -> Self {
-        Self { storage: self.storage.clone(), files: self.files.clone() }
+        Self {
+            storage: self.storage.clone(),
+            files: self.files.clone(),
+            crates_map: self.crates_map.clone(),
+        }
     }
 }
 
@@ -194,6 +198,10 @@ impl SourceDatabase for RootDatabase {
         let files = Arc::clone(&self.files);
         files.set_file_source_root_with_durability(self, id, source_root_id, durability);
     }
+
+    fn crates_map(&self) -> Arc<CratesMap> {
+        self.crates_map.clone()
+    }
 }
 
 impl Default for RootDatabase {
@@ -207,8 +215,11 @@ impl RootDatabase {
         let mut db = RootDatabase {
             storage: ManuallyDrop::new(salsa::Storage::default()),
             files: Default::default(),
+            crates_map: Default::default(),
         };
-        db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
+        // This needs to be here otherwise `CrateGraphBuilder` will panic.
+        db.set_all_crates(Arc::new(Box::new([])));
+        CrateGraphBuilder::default().set_in_db(&mut db);
         db.set_proc_macros_with_durability(Default::default(), Durability::HIGH);
         db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
         db.set_library_roots_with_durability(Default::default(), Durability::HIGH);
@@ -258,7 +269,11 @@ impl RootDatabase {
     }
 
     pub fn snapshot(&self) -> Self {
-        Self { storage: self.storage.clone(), files: self.files.clone() }
+        Self {
+            storage: self.storage.clone(),
+            files: self.files.clone(),
+            crates_map: self.crates_map.clone(),
+        }
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs
index 74d79cd6955..cd3099fe938 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs
@@ -11,7 +11,7 @@ use itertools::Itertools;
 use salsa::{Cancelled, Database};
 
 use crate::{
-    base_db::{CrateId, RootQueryDb},
+    base_db::{Crate, RootQueryDb},
     symbol_index::SymbolsDatabase,
     FxIndexMap, RootDatabase,
 };
@@ -35,20 +35,22 @@ pub fn parallel_prime_caches(
 ) {
     let _p = tracing::info_span!("parallel_prime_caches").entered();
 
-    let graph = db.crate_graph();
     let mut crates_to_prime = {
+        // FIXME: We already have the crate list topologically sorted (but without the things
+        // `TopologicalSortIter` gives us). Maybe there is a way to avoid using it and rip it out
+        // of the codebase?
         let mut builder = topologic_sort::TopologicalSortIter::builder();
 
-        for crate_id in graph.iter() {
-            builder.add(crate_id, graph[crate_id].dependencies.iter().map(|d| d.crate_id));
+        for &crate_id in db.all_crates().iter() {
+            builder.add(crate_id, crate_id.data(db).dependencies.iter().map(|d| d.crate_id));
         }
 
         builder.build()
     };
 
     enum ParallelPrimeCacheWorkerProgress {
-        BeginCrate { crate_id: CrateId, crate_name: Symbol },
-        EndCrate { crate_id: CrateId },
+        BeginCrate { crate_id: Crate, crate_name: Symbol },
+        EndCrate { crate_id: Crate },
     }
 
     // We split off def map computation from other work,
@@ -108,16 +110,14 @@ pub fn parallel_prime_caches(
     while crates_done < crates_total {
         db.unwind_if_revision_cancelled();
 
-        for crate_id in &mut crates_to_prime {
-            let krate = &graph[crate_id];
-            let name = krate
-                .display_name
-                .as_deref()
-                .cloned()
-                .unwrap_or_else(|| Symbol::integer(crate_id.into_raw().into_u32() as usize));
-            if krate.origin.is_lang() {
-                additional_phases.push((crate_id, name.clone(), PrimingPhase::ImportMap));
-            } else if krate.origin.is_local() {
+        for krate in &mut crates_to_prime {
+            let name = krate.extra_data(db).display_name.as_deref().cloned().unwrap_or_else(|| {
+                Symbol::integer(salsa::plumbing::AsId::as_id(&krate).as_u32() as usize)
+            });
+            let origin = &krate.data(db).origin;
+            if origin.is_lang() {
+                additional_phases.push((krate, name.clone(), PrimingPhase::ImportMap));
+            } else if origin.is_local() {
                 // Compute the symbol search index.
                 // This primes the cache for `ide_db::symbol_index::world_symbols()`.
                 //
@@ -127,10 +127,10 @@ pub fn parallel_prime_caches(
                 // FIXME: We should do it unconditionally if the configuration is set to default to
                 // searching dependencies (rust-analyzer.workspace.symbol.search.scope), but we
                 // would need to pipe that configuration information down here.
-                additional_phases.push((crate_id, name.clone(), PrimingPhase::CrateSymbols));
+                additional_phases.push((krate, name.clone(), PrimingPhase::CrateSymbols));
             }
 
-            work_sender.send((crate_id, name, PrimingPhase::DefMap)).ok();
+            work_sender.send((krate, name, PrimingPhase::DefMap)).ok();
         }
 
         // recv_timeout is somewhat a hack, we need a way to from this thread check to see if the current salsa revision
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index 81df0c0f0f8..4f9ef05b20e 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -162,13 +162,13 @@ impl SearchScope {
     fn crate_graph(db: &RootDatabase) -> SearchScope {
         let mut entries = FxHashMap::default();
 
-        let graph = db.crate_graph();
-        for krate in graph.iter() {
-            let root_file = graph[krate].root_file_id;
-            let source_root = db.file_source_root(root_file).source_root_id(db);
+        let all_crates = db.all_crates();
+        for &krate in all_crates.iter() {
+            let crate_data = krate.data(db);
+            let source_root = db.file_source_root(crate_data.root_file_id).source_root_id(db);
             let source_root = db.source_root(source_root).source_root(db);
             entries.extend(
-                source_root.iter().map(|id| (EditionedFileId::new(id, graph[krate].edition), None)),
+                source_root.iter().map(|id| (EditionedFileId::new(id, crate_data.edition), None)),
             );
         }
         SearchScope { entries }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
index 0cae7f367c2..1e2d4f1ab94 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
@@ -2,7 +2,9 @@
     (
         Module {
             id: ModuleId {
-                krate: Idx::<CrateData>(0),
+                krate: Crate {
+                    [salsa id]: Id(2c00),
+                },
                 block: None,
                 local_id: Idx::<ModuleData>(0),
             },
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index 48de1fb837a..1a77052b18d 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -2,7 +2,9 @@
     (
         Module {
             id: ModuleId {
-                krate: Idx::<CrateData>(0),
+                krate: Crate {
+                    [salsa id]: Id(2c00),
+                },
                 block: None,
                 local_id: Idx::<ModuleData>(0),
             },
@@ -532,7 +534,9 @@
                 def: Module(
                     Module {
                         id: ModuleId {
-                            krate: Idx::<CrateData>(0),
+                            krate: Crate {
+                                [salsa id]: Id(2c00),
+                            },
                             block: None,
                             local_id: Idx::<ModuleData>(1),
                         },
@@ -565,7 +569,9 @@
                 def: Module(
                     Module {
                         id: ModuleId {
-                            krate: Idx::<CrateData>(0),
+                            krate: Crate {
+                                [salsa id]: Id(2c00),
+                            },
                             block: None,
                             local_id: Idx::<ModuleData>(2),
                         },
@@ -827,7 +833,9 @@
     (
         Module {
             id: ModuleId {
-                krate: Idx::<CrateData>(0),
+                krate: Crate {
+                    [salsa id]: Id(2c00),
+                },
                 block: None,
                 local_id: Idx::<ModuleData>(1),
             },
@@ -871,7 +879,9 @@
     (
         Module {
             id: ModuleId {
-                krate: Idx::<CrateData>(0),
+                krate: Crate {
+                    [salsa id]: Id(2c00),
+                },
                 block: None,
                 local_id: Idx::<ModuleData>(2),
             },
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
index 5ce3336eb41..f4ced736b3d 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -389,9 +389,9 @@ pub fn semantic_diagnostics(
         module.and_then(|m| db.toolchain_channel(m.krate().into())),
         Some(ReleaseChannel::Nightly) | None
     );
-    let krate = module.map(|module| module.krate()).unwrap_or_else(|| {
-        (*db.crate_graph().crates_in_topological_order().last().unwrap()).into()
-    });
+    let krate = module
+        .map(|module| module.krate())
+        .unwrap_or_else(|| (*db.all_crates().last().unwrap()).into());
     let display_target = krate.to_display_target(db);
     let ctx = DiagnosticsContext { config, sema, resolve, edition, is_nightly, display_target };
 
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
index d32ba06f1eb..48d2431a11f 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
@@ -626,11 +626,11 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
                 match_error!("Failed to get receiver type for `{}`", expr.syntax().text())
             })?
             .original;
-        let krate = self.sema.scope(expr.syntax()).map(|it| it.krate()).unwrap_or_else(|| {
-            hir::Crate::from(
-                *self.sema.db.crate_graph().crates_in_topological_order().last().unwrap(),
-            )
-        });
+        let krate = self
+            .sema
+            .scope(expr.syntax())
+            .map(|it| it.krate())
+            .unwrap_or_else(|| hir::Crate::from(*self.sema.db.all_crates().last().unwrap()));
         let res = code_type
             .autoderef(self.sema.db)
             .enumerate()
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index 0acb129e939..9bdc2e7d133 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -504,9 +504,7 @@ fn get_doc_base_urls(
 
     let Some(krate) = krate else { return Default::default() };
     let Some(display_name) = krate.display_name(db) else { return Default::default() };
-    let crate_data = &db.crate_graph()[krate.into()];
-
-    let (web_base, local_base) = match &crate_data.origin {
+    let (web_base, local_base) = match krate.origin(db) {
         // std and co do not specify `html_root_url` any longer so we gotta handwrite this ourself.
         // FIXME: Use the toolchains channel instead of nightly
         CrateOrigin::Lang(
diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
index 2347e2e8a36..3771efc17b6 100644
--- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
@@ -1,8 +1,8 @@
 use hir::db::ExpandDatabase;
 use hir::{ExpandResult, InFile, MacroFileIdExt, Semantics};
-use ide_db::base_db::CrateId;
 use ide_db::{
-    helpers::pick_best_token, syntax_helpers::prettify_macro_expansion, FileId, RootDatabase,
+    base_db::Crate, helpers::pick_best_token, syntax_helpers::prettify_macro_expansion, FileId,
+    RootDatabase,
 };
 use span::{Edition, SpanMap, SyntaxContextId, TextRange, TextSize};
 use stdx::format_to;
@@ -208,7 +208,7 @@ fn format(
     file_id: FileId,
     expanded: SyntaxNode,
     span_map: &SpanMap<SyntaxContextId>,
-    krate: CrateId,
+    krate: Crate,
 ) -> String {
     let expansion = prettify_macro_expansion(db, expanded, span_map, krate).to_string();
 
@@ -249,7 +249,7 @@ fn _format(
 
     let upcast_db = ide_db::base_db::Upcast::<dyn ide_db::base_db::RootQueryDb>::upcast(db);
     let &crate_id = upcast_db.relevant_crates(file_id).iter().next()?;
-    let edition = upcast_db.crate_graph()[crate_id].edition;
+    let edition = crate_id.data(upcast_db).edition;
 
     #[allow(clippy::disallowed_methods)]
     let mut cmd = std::process::Command::new(toolchain::Tool::Rustfmt.path());
diff --git a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs
index 0e5bb89b6b7..b682c4bc0f7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs
@@ -20,21 +20,24 @@ pub struct CrateInfo {
 //
 // ![Show Dependency Tree](https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png)
 pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet<CrateInfo> {
-    let crate_graph = db.crate_graph();
-    crate_graph
+    db.all_crates()
         .iter()
-        .map(|crate_id| &crate_graph[crate_id])
-        .filter(|&data| !matches!(data.origin, CrateOrigin::Local { .. }))
-        .map(crate_info)
+        .copied()
+        .map(|crate_id| (crate_id.data(db), crate_id.extra_data(db)))
+        .filter(|(data, _)| !matches!(data.origin, CrateOrigin::Local { .. }))
+        .map(|(data, extra_data)| crate_info(data, extra_data))
         .collect()
 }
 
-fn crate_info(data: &ide_db::base_db::CrateData) -> CrateInfo {
-    let crate_name = crate_name(data);
-    let version = data.version.clone();
+fn crate_info(
+    data: &ide_db::base_db::BuiltCrateData,
+    extra_data: &ide_db::base_db::ExtraCrateData,
+) -> CrateInfo {
+    let crate_name = crate_name(extra_data);
+    let version = extra_data.version.clone();
     CrateInfo { name: crate_name, version, root_file_id: data.root_file_id }
 }
 
-fn crate_name(data: &ide_db::base_db::CrateData) -> Option<String> {
+fn crate_name(data: &ide_db::base_db::ExtraCrateData) -> Option<String> {
     data.display_name.as_ref().map(|it| it.canonical_name().as_str().to_owned())
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index 5d888ceb5ed..1d807fb14c7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -8,7 +8,6 @@ use hir::{
     MethodViolationCode, Name, Semantics, Symbol, Trait, Type, TypeInfo, VariantDef,
 };
 use ide_db::{
-    base_db::RootQueryDb,
     defs::Definition,
     documentation::HasDocs,
     famous_defs::FamousDefs,
@@ -466,8 +465,7 @@ pub(super) fn path(
     item_name: Option<String>,
     edition: Edition,
 ) -> String {
-    let crate_name =
-        db.crate_graph()[module.krate().into()].display_name.as_ref().map(|it| it.to_string());
+    let crate_name = module.krate().display_name(db).as_ref().map(|it| it.to_string());
     let module_path = module
         .path_to_root(db)
         .into_iter()
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index 736d355ef22..afddeb6deaa 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -9252,7 +9252,7 @@ fn main() {
             S
             ```
             ___
-            Implements notable traits: Notable, Future<Output = u32>, Iterator<Item = S>"#]],
+            Implements notable traits: Future<Output = u32>, Iterator<Item = S>, Notable"#]],
     );
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
index 592c8603964..aab8a3f8739 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
@@ -868,15 +868,15 @@ fn main() {
 //- minicore: fn
 fn main() {
     let x = || 2;
-      //^ {closure#26624}
+      //^ {closure#25600}
     let y = |t: i32| x() + t;
-      //^ {closure#26625}
+      //^ {closure#25601}
     let mut t = 5;
           //^ i32
     let z = |k: i32| { t += k; };
-      //^ {closure#26626}
+      //^ {closure#25602}
     let p = (y, z);
-      //^ ({closure#26625}, {closure#26626})
+      //^ ({closure#25601}, {closure#25602})
 }
             "#,
         );
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index dcb170f3f7b..79863f4680d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -57,7 +57,7 @@ mod view_memory_layout;
 mod view_mir;
 mod view_syntax_tree;
 
-use std::{iter, panic::UnwindSafe};
+use std::panic::UnwindSafe;
 
 use cfg::CfgOptions;
 use fetch_crates::CrateInfo;
@@ -125,7 +125,7 @@ pub use ide_completion::{
 };
 pub use ide_db::text_edit::{Indel, TextEdit};
 pub use ide_db::{
-    base_db::{CrateGraph, CrateId, FileChange, SourceRoot, SourceRootId},
+    base_db::{Crate, CrateGraphBuilder, FileChange, SourceRoot, SourceRootId},
     documentation::Documentation,
     label::Label,
     line_index::{LineCol, LineIndex},
@@ -239,7 +239,7 @@ impl Analysis {
 
         let mut change = ChangeWithProcMacros::new();
         change.set_roots(vec![source_root]);
-        let mut crate_graph = CrateGraph::default();
+        let mut crate_graph = CrateGraphBuilder::default();
         // FIXME: cfg options
         // Default to enable test for single file.
         let mut cfg_options = CfgOptions::default();
@@ -255,16 +255,13 @@ impl Analysis {
             CrateOrigin::Local { repo: None, name: None },
             false,
             None,
-        );
-        change.change_file(file_id, Some(text));
-        let ws_data = crate_graph
-            .iter()
-            .zip(iter::repeat(Arc::new(CrateWorkspaceData {
+            Arc::new(CrateWorkspaceData {
                 data_layout: Err("fixture has no layout".into()),
                 toolchain: None,
-            })))
-            .collect();
-        change.set_crate_graph(crate_graph, ws_data);
+            }),
+        );
+        change.change_file(file_id, Some(text));
+        change.set_crate_graph(crate_graph);
 
         host.apply_change(change);
         (host.analysis(), file_id)
@@ -372,7 +369,7 @@ impl Analysis {
         self.with_db(|db| test_explorer::discover_tests_in_crate_by_test_id(db, crate_id))
     }
 
-    pub fn discover_tests_in_crate(&self, crate_id: CrateId) -> Cancellable<Vec<TestItem>> {
+    pub fn discover_tests_in_crate(&self, crate_id: Crate) -> Cancellable<Vec<TestItem>> {
         self.with_db(|db| test_explorer::discover_tests_in_crate(db, crate_id))
     }
 
@@ -602,17 +599,17 @@ impl Analysis {
     }
 
     /// Returns crates that this file belongs to.
-    pub fn crates_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> {
+    pub fn crates_for(&self, file_id: FileId) -> Cancellable<Vec<Crate>> {
         self.with_db(|db| parent_module::crates_for(db, file_id))
     }
 
     /// Returns crates that this file belongs to.
-    pub fn transitive_rev_deps(&self, crate_id: CrateId) -> Cancellable<Vec<CrateId>> {
-        self.with_db(|db| db.crate_graph().transitive_rev_deps(crate_id).collect())
+    pub fn transitive_rev_deps(&self, crate_id: Crate) -> Cancellable<Vec<Crate>> {
+        self.with_db(|db| Vec::from_iter(db.transitive_rev_deps(crate_id)))
     }
 
     /// Returns crates that this file *might* belong to.
-    pub fn relevant_crates_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> {
+    pub fn relevant_crates_for(&self, file_id: FileId) -> Cancellable<Vec<Crate>> {
         self.with_db(|db| {
             let db = Upcast::<dyn RootQueryDb>::upcast(db);
             db.relevant_crates(file_id).iter().copied().collect()
@@ -620,18 +617,23 @@ impl Analysis {
     }
 
     /// Returns the edition of the given crate.
-    pub fn crate_edition(&self, crate_id: CrateId) -> Cancellable<Edition> {
-        self.with_db(|db| db.crate_graph()[crate_id].edition)
+    pub fn crate_edition(&self, crate_id: Crate) -> Cancellable<Edition> {
+        self.with_db(|db| crate_id.data(db).edition)
+    }
+
+    /// Returns whether the given crate is a proc macro.
+    pub fn is_proc_macro_crate(&self, crate_id: Crate) -> Cancellable<bool> {
+        self.with_db(|db| crate_id.data(db).is_proc_macro)
     }
 
     /// Returns true if this crate has `no_std` or `no_core` specified.
-    pub fn is_crate_no_std(&self, crate_id: CrateId) -> Cancellable<bool> {
+    pub fn is_crate_no_std(&self, crate_id: Crate) -> Cancellable<bool> {
         self.with_db(|db| hir::db::DefDatabase::crate_def_map(db, crate_id).is_no_std())
     }
 
     /// Returns the root file of the given crate.
-    pub fn crate_root(&self, crate_id: CrateId) -> Cancellable<FileId> {
-        self.with_db(|db| db.crate_graph()[crate_id].root_file_id)
+    pub fn crate_root(&self, crate_id: Crate) -> Cancellable<FileId> {
+        self.with_db(|db| crate_id.data(db).root_file_id)
     }
 
     /// Returns the set of possible targets to run for the current file.
diff --git a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs
index 90cccca5e80..06ab4750ac5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs
@@ -1,6 +1,6 @@
 use hir::{db::DefDatabase, Semantics};
 use ide_db::{
-    base_db::{CrateId, RootQueryDb, Upcast},
+    base_db::{Crate, RootQueryDb, Upcast},
     FileId, FilePosition, RootDatabase,
 };
 use itertools::Itertools;
@@ -53,7 +53,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
 }
 
 /// This returns `Vec` because a module may be included from several places.
-pub(crate) fn crates_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
+pub(crate) fn crates_for(db: &RootDatabase, file_id: FileId) -> Vec<Crate> {
     let root_db = Upcast::<dyn RootQueryDb>::upcast(db);
     root_db
         .relevant_crates(file_id)
diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
index 33eef6d75c2..baa7ee68973 100644
--- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
@@ -498,9 +498,8 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
     };
     let krate = def.krate(db);
     let edition = krate.map(|it| it.edition(db)).unwrap_or(Edition::CURRENT);
-    let display_target = krate
-        .unwrap_or_else(|| (*db.crate_graph().crates_in_topological_order().last().unwrap()).into())
-        .to_display_target(db);
+    let display_target =
+        krate.unwrap_or_else(|| (*db.all_crates().last().unwrap()).into()).to_display_target(db);
     if !has_runnable_doc_test(&attrs) {
         return None;
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
index ceea34b7213..5a2b09b5137 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -119,9 +119,7 @@ fn documentation_for_definition(
         sema.db,
         famous_defs.as_ref(),
         def.krate(sema.db)
-            .unwrap_or_else(|| {
-                (*sema.db.crate_graph().crates_in_topological_order().last().unwrap()).into()
-            })
+            .unwrap_or_else(|| (*sema.db.all_crates().last().unwrap()).into())
             .to_display_target(sema.db),
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs
index 47ac4ebf20e..52d38041ec1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/status.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/status.rs
@@ -1,4 +1,4 @@
-use ide_db::base_db::{CrateData, RootQueryDb, Upcast};
+use ide_db::base_db::{BuiltCrateData, ExtraCrateData};
 use ide_db::RootDatabase;
 use itertools::Itertools;
 use span::FileId;
@@ -34,28 +34,25 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
         if crates.is_empty() {
             format_to!(buf, "Does not belong to any crate");
         }
-
-        let crate_graph = Upcast::<dyn RootQueryDb>::upcast(db).crate_graph();
         for crate_id in crates {
-            let CrateData {
+            let BuiltCrateData {
                 root_file_id,
                 edition,
-                version,
-                display_name,
-                cfg_options,
-                potential_cfg_options,
-                env,
                 dependencies,
                 origin,
                 is_proc_macro,
                 proc_macro_cwd,
-            } = &crate_graph[crate_id];
+            } = crate_id.data(db);
+            let ExtraCrateData { version, display_name, potential_cfg_options } =
+                crate_id.extra_data(db);
+            let cfg_options = crate_id.cfg_options(db);
+            let env = crate_id.env(db);
             format_to!(
                 buf,
                 "Crate: {}\n",
                 match display_name {
-                    Some(it) => format!("{it}({})", crate_id.into_raw()),
-                    None => format!("{}", crate_id.into_raw()),
+                    Some(it) => format!("{it}({:?})", crate_id),
+                    None => format!("{:?}", crate_id),
                 }
             );
             format_to!(buf, "    Root module file id: {}\n", root_file_id.index());
@@ -69,7 +66,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
             format_to!(buf, "    Proc macro cwd: {:?}\n", proc_macro_cwd);
             let deps = dependencies
                 .iter()
-                .map(|dep| format!("{}={}", dep.name, dep.crate_id.into_raw()))
+                .map(|dep| format!("{}={:?}", dep.name, dep.crate_id))
                 .format(", ");
             format_to!(buf, "    Dependencies: {}\n", deps);
         }
diff --git a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs
index 68f5c72d205..d22133c8565 100644
--- a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs
@@ -1,17 +1,15 @@
 //! Discovers tests
 
 use hir::{Crate, Module, ModuleDef, Semantics};
-use ide_db::{
-    base_db::{CrateGraph, CrateId, RootQueryDb},
-    FileId, RootDatabase,
-};
+use ide_db::base_db;
+use ide_db::{base_db::RootQueryDb, FileId, RootDatabase};
 use syntax::TextRange;
 
 use crate::{runnables::runnable_fn, NavigationTarget, Runnable, TryToNav};
 
 #[derive(Debug)]
 pub enum TestItemKind {
-    Crate(CrateId),
+    Crate(base_db::Crate),
     Module,
     Function,
 }
@@ -28,12 +26,12 @@ pub struct TestItem {
 }
 
 pub(crate) fn discover_test_roots(db: &RootDatabase) -> Vec<TestItem> {
-    let crate_graph = db.crate_graph();
-    crate_graph
+    db.all_crates()
         .iter()
-        .filter(|&id| crate_graph[id].origin.is_local())
+        .copied()
+        .filter(|&id| id.data(db).origin.is_local())
         .filter_map(|id| {
-            let test_id = crate_graph[id].display_name.as_ref()?.to_string();
+            let test_id = id.extra_data(db).display_name.as_ref()?.to_string();
             Some(TestItem {
                 kind: TestItemKind::Crate(id),
                 label: test_id.clone(),
@@ -47,12 +45,12 @@ pub(crate) fn discover_test_roots(db: &RootDatabase) -> Vec<TestItem> {
         .collect()
 }
 
-fn find_crate_by_id(crate_graph: &CrateGraph, crate_id: &str) -> Option<CrateId> {
+fn find_crate_by_id(db: &RootDatabase, crate_id: &str) -> Option<base_db::Crate> {
     // here, we use display_name as the crate id. This is not super ideal, but it works since we
     // only show tests for the local crates.
-    crate_graph.iter().find(|&id| {
-        crate_graph[id].origin.is_local()
-            && crate_graph[id].display_name.as_ref().is_some_and(|x| x.to_string() == crate_id)
+    db.all_crates().iter().copied().find(|&id| {
+        id.data(db).origin.is_local()
+            && id.extra_data(db).display_name.as_ref().is_some_and(|x| x.to_string() == crate_id)
     })
 }
 
@@ -115,8 +113,7 @@ pub(crate) fn discover_tests_in_crate_by_test_id(
     db: &RootDatabase,
     crate_test_id: &str,
 ) -> Vec<TestItem> {
-    let crate_graph = db.crate_graph();
-    let Some(crate_id) = find_crate_by_id(&crate_graph, crate_test_id) else {
+    let Some(crate_id) = find_crate_by_id(db, crate_test_id) else {
         return vec![];
     };
     discover_tests_in_crate(db, crate_id)
@@ -171,12 +168,14 @@ fn find_module_id_and_test_parents(
     Some((r, id))
 }
 
-pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> Vec<TestItem> {
-    let crate_graph = db.crate_graph();
-    if !crate_graph[crate_id].origin.is_local() {
+pub(crate) fn discover_tests_in_crate(
+    db: &RootDatabase,
+    crate_id: base_db::Crate,
+) -> Vec<TestItem> {
+    if !crate_id.data(db).origin.is_local() {
         return vec![];
     }
-    let Some(crate_test_id) = &crate_graph[crate_id].display_name else {
+    let Some(crate_test_id) = &crate_id.extra_data(db).display_name else {
         return vec![];
     };
     let kind = TestItemKind::Crate(crate_id);
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
index eeb65ac0389..09f21ecfe41 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
@@ -1,9 +1,10 @@
 use dot::{Id, LabelText};
 use ide_db::{
-    base_db::{CrateGraph, CrateId, Dependency, RootQueryDb, SourceDatabase, Upcast},
-    FxHashSet, RootDatabase,
+    base_db::{
+        BuiltCrateData, BuiltDependency, Crate, ExtraCrateData, RootQueryDb, SourceDatabase,
+    },
+    FxHashMap, RootDatabase,
 };
-use triomphe::Arc;
 
 // Feature: View Crate Graph
 //
@@ -16,77 +17,80 @@ use triomphe::Arc;
 // |---------|-------------|
 // | VS Code | **rust-analyzer: View Crate Graph** |
 pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String, String> {
-    let crate_graph = Upcast::<dyn RootQueryDb>::upcast(db).crate_graph();
-    let crates_to_render = crate_graph
+    let all_crates = db.all_crates();
+    let crates_to_render = all_crates
         .iter()
-        .filter(|krate| {
+        .copied()
+        .map(|krate| (krate, (krate.data(db), krate.extra_data(db))))
+        .filter(|(_, (crate_data, _))| {
             if full {
                 true
             } else {
                 // Only render workspace crates
-                let root_id =
-                    db.file_source_root(crate_graph[*krate].root_file_id).source_root_id(db);
+                let root_id = db.file_source_root(crate_data.root_file_id).source_root_id(db);
                 !db.source_root(root_id).source_root(db).is_library
             }
         })
         .collect();
-    let graph = DotCrateGraph { graph: crate_graph, crates_to_render };
+    let graph = DotCrateGraph { crates_to_render };
 
     let mut dot = Vec::new();
     dot::render(&graph, &mut dot).unwrap();
     Ok(String::from_utf8(dot).unwrap())
 }
 
-struct DotCrateGraph {
-    graph: Arc<CrateGraph>,
-    crates_to_render: FxHashSet<CrateId>,
+struct DotCrateGraph<'db> {
+    crates_to_render: FxHashMap<Crate, (&'db BuiltCrateData, &'db ExtraCrateData)>,
 }
 
-type Edge<'a> = (CrateId, &'a Dependency);
+type Edge<'a> = (Crate, &'a BuiltDependency);
 
-impl<'a> dot::GraphWalk<'a, CrateId, Edge<'a>> for DotCrateGraph {
-    fn nodes(&'a self) -> dot::Nodes<'a, CrateId> {
-        self.crates_to_render.iter().copied().collect()
+impl<'a> dot::GraphWalk<'a, Crate, Edge<'a>> for DotCrateGraph<'_> {
+    fn nodes(&'a self) -> dot::Nodes<'a, Crate> {
+        self.crates_to_render.keys().copied().collect()
     }
 
     fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> {
         self.crates_to_render
             .iter()
-            .flat_map(|krate| {
-                self.graph[*krate]
+            .flat_map(|(krate, (crate_data, _))| {
+                crate_data
                     .dependencies
                     .iter()
-                    .filter(|dep| self.crates_to_render.contains(&dep.crate_id))
+                    .filter(|dep| self.crates_to_render.contains_key(&dep.crate_id))
                     .map(move |dep| (*krate, dep))
             })
             .collect()
     }
 
-    fn source(&'a self, edge: &Edge<'a>) -> CrateId {
+    fn source(&'a self, edge: &Edge<'a>) -> Crate {
         edge.0
     }
 
-    fn target(&'a self, edge: &Edge<'a>) -> CrateId {
+    fn target(&'a self, edge: &Edge<'a>) -> Crate {
         edge.1.crate_id
     }
 }
 
-impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph {
+impl<'a> dot::Labeller<'a, Crate, Edge<'a>> for DotCrateGraph<'_> {
     fn graph_id(&'a self) -> Id<'a> {
         Id::new("rust_analyzer_crate_graph").unwrap()
     }
 
-    fn node_id(&'a self, n: &CrateId) -> Id<'a> {
-        Id::new(format!("_{}", u32::from(n.into_raw()))).unwrap()
+    fn node_id(&'a self, n: &Crate) -> Id<'a> {
+        Id::new(format!("_{:?}", n)).unwrap()
     }
 
-    fn node_shape(&'a self, _node: &CrateId) -> Option<LabelText<'a>> {
+    fn node_shape(&'a self, _node: &Crate) -> Option<LabelText<'a>> {
         Some(LabelText::LabelStr("box".into()))
     }
 
-    fn node_label(&'a self, n: &CrateId) -> LabelText<'a> {
-        let name =
-            self.graph[*n].display_name.as_ref().map_or("(unnamed crate)", |name| name.as_str());
+    fn node_label(&'a self, n: &Crate) -> LabelText<'a> {
+        let name = self.crates_to_render[n]
+            .1
+            .display_name
+            .as_ref()
+            .map_or("(unnamed crate)", |name| name.as_str());
         LabelText::LabelStr(name.into())
     }
 }
diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs
index 0fa6701ca3f..f6a74d97416 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs
@@ -42,6 +42,18 @@ struct TaggedArcPtr {
 unsafe impl Send for TaggedArcPtr {}
 unsafe impl Sync for TaggedArcPtr {}
 
+impl Ord for TaggedArcPtr {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.as_str().cmp(other.as_str())
+    }
+}
+
+impl PartialOrd for TaggedArcPtr {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
 impl TaggedArcPtr {
     const BOOL_BITS: usize = true as usize;
 
@@ -113,7 +125,7 @@ impl TaggedArcPtr {
     }
 }
 
-#[derive(PartialEq, Eq, Hash)]
+#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct Symbol {
     repr: TaggedArcPtr,
 }
diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
index fbb0043ad2d..01d29d88df4 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -2,15 +2,15 @@
 //! for incorporating changes.
 // Note, don't remove any public api from this. This API is consumed by external tools
 // to run rust-analyzer as a library.
-use std::{collections::hash_map::Entry, iter, mem, path::Path, sync};
+use std::{collections::hash_map::Entry, mem, path::Path, sync};
 
 use crossbeam_channel::{unbounded, Receiver};
 use hir_expand::proc_macro::{
     ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacroLoadResult,
-    ProcMacros,
+    ProcMacrosBuilder,
 };
 use ide_db::{
-    base_db::{CrateGraph, CrateWorkspaceData, Env, SourceRoot, SourceRootId},
+    base_db::{CrateGraphBuilder, Env, SourceRoot, SourceRootId},
     prime_caches, ChangeWithProcMacros, FxHashMap, RootDatabase,
 };
 use itertools::Itertools;
@@ -139,7 +139,6 @@ pub fn load_workspace(
     });
 
     let db = load_crate_graph(
-        &ws,
         crate_graph,
         proc_macros,
         project_folders.source_root_config,
@@ -418,15 +417,12 @@ pub fn load_proc_macro(
 }
 
 fn load_crate_graph(
-    ws: &ProjectWorkspace,
-    crate_graph: CrateGraph,
-    proc_macros: ProcMacros,
+    crate_graph: CrateGraphBuilder,
+    proc_macros: ProcMacrosBuilder,
     source_root_config: SourceRootConfig,
     vfs: &mut vfs::Vfs,
     receiver: &Receiver<vfs::loader::Message>,
 ) -> RootDatabase {
-    let ProjectWorkspace { toolchain, target_layout, .. } = ws;
-
     let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<u16>().ok());
     let mut db = RootDatabase::new(lru_cap);
     let mut analysis_change = ChangeWithProcMacros::new();
@@ -461,14 +457,7 @@ fn load_crate_graph(
     let source_roots = source_root_config.partition(vfs);
     analysis_change.set_roots(source_roots);
 
-    let ws_data = crate_graph
-        .iter()
-        .zip(iter::repeat(From::from(CrateWorkspaceData {
-            data_layout: target_layout.clone(),
-            toolchain: toolchain.clone(),
-        })))
-        .collect();
-    analysis_change.set_crate_graph(crate_graph, ws_data);
+    analysis_change.set_crate_graph(crate_graph);
     analysis_change.set_proc_macros(proc_macros);
 
     db.apply_change(analysis_change);
@@ -494,7 +483,7 @@ fn expander_to_proc_macro(
     }
 }
 
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Eq)]
 struct Expander(proc_macro_api::ProcMacro);
 
 impl ProcMacroExpander for Expander {
@@ -522,6 +511,10 @@ impl ProcMacroExpander for Expander {
             Err(err) => Err(ProcMacroExpansionError::System(err.to_string())),
         }
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.as_any().downcast_ref::<Self>().is_some_and(|other| self == other)
+    }
 }
 
 #[cfg(test)]
@@ -543,7 +536,7 @@ mod tests {
         let (db, _vfs, _proc_macro) =
             load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {}).unwrap();
 
-        let n_crates = db.crate_graph().iter().count();
+        let n_crates = db.all_crates().len();
         // RA has quite a few crates, but the exact count doesn't matter
         assert!(n_crates > 20);
     }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index dc3328ebcda..571ceaabe62 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -13,7 +13,7 @@ mod process;
 
 use paths::{AbsPath, AbsPathBuf};
 use span::Span;
-use std::{fmt, io, sync::Arc};
+use std::{fmt, io, sync::Arc, time::SystemTime};
 
 use crate::{
     legacy_protocol::msg::{
@@ -66,6 +66,7 @@ pub struct ProcMacro {
     dylib_path: Arc<AbsPathBuf>,
     name: Box<str>,
     kind: ProcMacroKind,
+    dylib_last_modified: Option<SystemTime>,
 }
 
 impl Eq for ProcMacro {}
@@ -73,7 +74,8 @@ impl PartialEq for ProcMacro {
     fn eq(&self, other: &Self) -> bool {
         self.name == other.name
             && self.kind == other.kind
-            && Arc::ptr_eq(&self.dylib_path, &other.dylib_path)
+            && self.dylib_path == other.dylib_path
+            && self.dylib_last_modified == other.dylib_last_modified
             && Arc::ptr_eq(&self.process, &other.process)
     }
 }
@@ -116,6 +118,9 @@ impl ProcMacroClient {
         let macros = self.process.find_proc_macros(&dylib.path)?;
 
         let dylib_path = Arc::new(dylib.path);
+        let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
+            .ok()
+            .and_then(|metadata| metadata.modified().ok());
         match macros {
             Ok(macros) => Ok(macros
                 .into_iter()
@@ -124,6 +129,7 @@ impl ProcMacroClient {
                     name: name.into(),
                     kind,
                     dylib_path: dylib_path.clone(),
+                    dylib_last_modified,
                 })
                 .collect()),
             Err(message) => Err(ServerError { message, io: None }),
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
index 15de88ea656..716d351271e 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
@@ -12,7 +12,7 @@ fn test_derive_empty() {
         "DeriveEmpty",
         r#"struct S;"#,
         expect!["SUBTREE $$ 1 1"],
-        expect!["SUBTREE $$ 42:2@0..100#2 42:2@0..100#2"],
+        expect!["SUBTREE $$ 42:2@0..100#4294967037 42:2@0..100#4294967037"],
     );
 }
 
@@ -29,12 +29,12 @@ fn test_derive_error() {
                 LITERAL Str #[derive(DeriveError)] struct S ; 1
               PUNCH   ; [alone] 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
-              IDENT   compile_error 42:2@0..100#2
-              PUNCH   ! [alone] 42:2@0..100#2
-              SUBTREE () 42:2@0..100#2 42:2@0..100#2
-                LITERAL Str #[derive(DeriveError)] struct S ; 42:2@0..100#2
-              PUNCH   ; [alone] 42:2@0..100#2"#]],
+            SUBTREE $$ 42:2@0..100#4294967037 42:2@0..100#4294967037
+              IDENT   compile_error 42:2@0..100#4294967037
+              PUNCH   ! [alone] 42:2@0..100#4294967037
+              SUBTREE () 42:2@0..100#4294967037 42:2@0..100#4294967037
+                LITERAL Str #[derive(DeriveError)] struct S ; 42:2@0..100#4294967037
+              PUNCH   ; [alone] 42:2@0..100#4294967037"#]],
     );
 }
 
@@ -53,14 +53,14 @@ fn test_fn_like_macro_noop() {
               PUNCH   , [alone] 1
               SUBTREE [] 1 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
-              IDENT   ident 42:2@0..5#2
-              PUNCH   , [alone] 42:2@5..6#2
-              LITERAL Integer 0 42:2@7..8#2
-              PUNCH   , [alone] 42:2@8..9#2
-              LITERAL Integer 1 42:2@10..11#2
-              PUNCH   , [alone] 42:2@11..12#2
-              SUBTREE [] 42:2@13..14#2 42:2@14..15#2"#]],
+            SUBTREE $$ 42:2@0..100#4294967037 42:2@0..100#4294967037
+              IDENT   ident 42:2@0..5#4294967037
+              PUNCH   , [alone] 42:2@5..6#4294967037
+              LITERAL Integer 0 42:2@7..8#4294967037
+              PUNCH   , [alone] 42:2@8..9#4294967037
+              LITERAL Integer 1 42:2@10..11#4294967037
+              PUNCH   , [alone] 42:2@11..12#4294967037
+              SUBTREE [] 42:2@13..14#4294967037 42:2@14..15#4294967037"#]],
     );
 }
 
@@ -75,10 +75,10 @@ fn test_fn_like_macro_clone_ident_subtree() {
               PUNCH   , [alone] 1
               SUBTREE [] 1 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
-              IDENT   ident 42:2@0..5#2
-              PUNCH   , [alone] 42:2@5..6#2
-              SUBTREE [] 42:2@7..8#2 42:2@7..8#2"#]],
+            SUBTREE $$ 42:2@0..100#4294967037 42:2@0..100#4294967037
+              IDENT   ident 42:2@0..5#4294967037
+              PUNCH   , [alone] 42:2@5..6#4294967037
+              SUBTREE [] 42:2@7..8#4294967037 42:2@7..8#4294967037"#]],
     );
 }
 
@@ -91,8 +91,8 @@ fn test_fn_like_macro_clone_raw_ident() {
             SUBTREE $$ 1 1
               IDENT   r#async 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
-              IDENT   r#async 42:2@0..7#2"#]],
+            SUBTREE $$ 42:2@0..100#4294967037 42:2@0..100#4294967037
+              IDENT   r#async 42:2@0..7#4294967037"#]],
     );
 }
 
@@ -105,8 +105,8 @@ fn test_fn_like_fn_like_span_join() {
             SUBTREE $$ 1 1
               IDENT   r#joined 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
-              IDENT   r#joined 42:2@0..11#2"#]],
+            SUBTREE $$ 42:2@0..100#4294967037 42:2@0..100#4294967037
+              IDENT   r#joined 42:2@0..11#4294967037"#]],
     );
 }
 
@@ -121,10 +121,10 @@ fn test_fn_like_fn_like_span_ops() {
               IDENT   resolved_at_def_site 1
               IDENT   start_span 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
-              IDENT   set_def_site 41:1@0..150#2
-              IDENT   resolved_at_def_site 42:2@13..33#2
-              IDENT   start_span 42:2@34..34#2"#]],
+            SUBTREE $$ 42:2@0..100#4294967037 42:2@0..100#4294967037
+              IDENT   set_def_site 41:1@0..150#4294967037
+              IDENT   resolved_at_def_site 42:2@13..33#4294967037
+              IDENT   start_span 42:2@34..34#4294967037"#]],
     );
 }
 
@@ -143,14 +143,14 @@ fn test_fn_like_mk_literals() {
               LITERAL Integer 123i64 1
               LITERAL Integer 123 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
-              LITERAL ByteStr byte_string 42:2@0..100#2
-              LITERAL Char c 42:2@0..100#2
-              LITERAL Str string 42:2@0..100#2
-              LITERAL Float 3.14f64 42:2@0..100#2
-              LITERAL Float 3.14 42:2@0..100#2
-              LITERAL Integer 123i64 42:2@0..100#2
-              LITERAL Integer 123 42:2@0..100#2"#]],
+            SUBTREE $$ 42:2@0..100#4294967037 42:2@0..100#4294967037
+              LITERAL ByteStr byte_string 42:2@0..100#4294967037
+              LITERAL Char c 42:2@0..100#4294967037
+              LITERAL Str string 42:2@0..100#4294967037
+              LITERAL Float 3.14f64 42:2@0..100#4294967037
+              LITERAL Float 3.14 42:2@0..100#4294967037
+              LITERAL Integer 123i64 42:2@0..100#4294967037
+              LITERAL Integer 123 42:2@0..100#4294967037"#]],
     );
 }
 
@@ -164,9 +164,9 @@ fn test_fn_like_mk_idents() {
               IDENT   standard 1
               IDENT   r#raw 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
-              IDENT   standard 42:2@0..100#2
-              IDENT   r#raw 42:2@0..100#2"#]],
+            SUBTREE $$ 42:2@0..100#4294967037 42:2@0..100#4294967037
+              IDENT   standard 42:2@0..100#4294967037
+              IDENT   r#raw 42:2@0..100#4294967037"#]],
     );
 }
 
@@ -198,27 +198,27 @@ fn test_fn_like_macro_clone_literals() {
               PUNCH   , [alone] 1
               LITERAL CStr null 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
-              LITERAL Integer 1u16 42:2@0..4#2
-              PUNCH   , [alone] 42:2@4..5#2
-              LITERAL Integer 2_u32 42:2@6..11#2
-              PUNCH   , [alone] 42:2@11..12#2
-              PUNCH   - [alone] 42:2@13..14#2
-              LITERAL Integer 4i64 42:2@14..18#2
-              PUNCH   , [alone] 42:2@18..19#2
-              LITERAL Float 3.14f32 42:2@20..27#2
-              PUNCH   , [alone] 42:2@27..28#2
-              LITERAL Str hello bridge 42:2@29..43#2
-              PUNCH   , [alone] 42:2@43..44#2
-              LITERAL Str suffixedsuffix 42:2@45..61#2
-              PUNCH   , [alone] 42:2@61..62#2
-              LITERAL StrRaw(2) raw 42:2@63..73#2
-              PUNCH   , [alone] 42:2@73..74#2
-              LITERAL Char a 42:2@75..78#2
-              PUNCH   , [alone] 42:2@78..79#2
-              LITERAL Byte b 42:2@80..84#2
-              PUNCH   , [alone] 42:2@84..85#2
-              LITERAL CStr null 42:2@86..93#2"#]],
+            SUBTREE $$ 42:2@0..100#4294967037 42:2@0..100#4294967037
+              LITERAL Integer 1u16 42:2@0..4#4294967037
+              PUNCH   , [alone] 42:2@4..5#4294967037
+              LITERAL Integer 2_u32 42:2@6..11#4294967037
+              PUNCH   , [alone] 42:2@11..12#4294967037
+              PUNCH   - [alone] 42:2@13..14#4294967037
+              LITERAL Integer 4i64 42:2@14..18#4294967037
+              PUNCH   , [alone] 42:2@18..19#4294967037
+              LITERAL Float 3.14f32 42:2@20..27#4294967037
+              PUNCH   , [alone] 42:2@27..28#4294967037
+              LITERAL Str hello bridge 42:2@29..43#4294967037
+              PUNCH   , [alone] 42:2@43..44#4294967037
+              LITERAL Str suffixedsuffix 42:2@45..61#4294967037
+              PUNCH   , [alone] 42:2@61..62#4294967037
+              LITERAL StrRaw(2) raw 42:2@63..73#4294967037
+              PUNCH   , [alone] 42:2@73..74#4294967037
+              LITERAL Char a 42:2@75..78#4294967037
+              PUNCH   , [alone] 42:2@78..79#4294967037
+              LITERAL Byte b 42:2@80..84#4294967037
+              PUNCH   , [alone] 42:2@84..85#4294967037
+              LITERAL CStr null 42:2@86..93#4294967037"#]],
     );
 }
 
@@ -239,12 +239,12 @@ fn test_attr_macro() {
                 LITERAL Str #[attr_error(some arguments)] mod m {} 1
               PUNCH   ; [alone] 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
-              IDENT   compile_error 42:2@0..100#2
-              PUNCH   ! [alone] 42:2@0..100#2
-              SUBTREE () 42:2@0..100#2 42:2@0..100#2
-                LITERAL Str #[attr_error(some arguments)] mod m {} 42:2@0..100#2
-              PUNCH   ; [alone] 42:2@0..100#2"#]],
+            SUBTREE $$ 42:2@0..100#4294967037 42:2@0..100#4294967037
+              IDENT   compile_error 42:2@0..100#4294967037
+              PUNCH   ! [alone] 42:2@0..100#4294967037
+              SUBTREE () 42:2@0..100#4294967037 42:2@0..100#4294967037
+                LITERAL Str #[attr_error(some arguments)] mod m {} 42:2@0..100#4294967037
+              PUNCH   ; [alone] 42:2@0..100#4294967037"#]],
     );
 }
 
diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
index b2df8e4703a..3c14e6e6277 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
@@ -452,7 +452,7 @@ pub enum TargetKindData {
 }
 /// Identifies a crate by position in the crates array.
 ///
-/// This will differ from `CrateId` when multiple `ProjectJson`
+/// This will differ from `Crate` when multiple `ProjectJson`
 /// workspaces are loaded.
 #[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq, Hash)]
 #[serde(transparent)]
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index 83740622732..d4c93b0e9bc 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -1,4 +1,4 @@
-use base_db::{CrateGraph, ProcMacroPaths};
+use base_db::{CrateGraphBuilder, ProcMacroPaths};
 use cargo_metadata::Metadata;
 use cfg::{CfgAtom, CfgDiff};
 use expect_test::{expect_file, ExpectFile};
@@ -15,7 +15,7 @@ use crate::{
     Sysroot, WorkspaceBuildScripts,
 };
 
-fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) {
+fn load_cargo(file: &str) -> (CrateGraphBuilder, ProcMacroPaths) {
     let project_workspace = load_workspace_from_metadata(file);
     to_crate_graph(project_workspace, &mut Default::default())
 }
@@ -23,7 +23,7 @@ fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) {
 fn load_cargo_with_overrides(
     file: &str,
     cfg_overrides: CfgOverrides,
-) -> (CrateGraph, ProcMacroPaths) {
+) -> (CrateGraphBuilder, ProcMacroPaths) {
     let project_workspace =
         ProjectWorkspace { cfg_overrides, ..load_workspace_from_metadata(file) };
     to_crate_graph(project_workspace, &mut Default::default())
@@ -51,7 +51,7 @@ fn load_workspace_from_metadata(file: &str) -> ProjectWorkspace {
     }
 }
 
-fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) {
+fn load_rust_project(file: &str) -> (CrateGraphBuilder, ProcMacroPaths) {
     let data = get_test_json_file(file);
     let project = rooted_project_json(data);
     let sysroot = get_fake_sysroot();
@@ -142,7 +142,7 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
 fn to_crate_graph(
     project_workspace: ProjectWorkspace,
     file_map: &mut FxHashMap<AbsPathBuf, FileId>,
-) -> (CrateGraph, ProcMacroPaths) {
+) -> (CrateGraphBuilder, ProcMacroPaths) {
     project_workspace.to_crate_graph(
         &mut {
             |path| {
@@ -154,7 +154,7 @@ fn to_crate_graph(
     )
 }
 
-fn check_crate_graph(crate_graph: CrateGraph, expect: ExpectFile) {
+fn check_crate_graph(crate_graph: CrateGraphBuilder, expect: ExpectFile) {
     let mut crate_graph = format!("{crate_graph:#?}");
 
     replace_root(&mut crate_graph, false);
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index 62c13c7d9ec..114c2551e1c 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -6,8 +6,9 @@ use std::{collections::VecDeque, fmt, fs, iter, ops::Deref, sync, thread};
 
 use anyhow::Context;
 use base_db::{
-    CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env,
-    LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult,
+    CrateBuilderId, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin,
+    CrateWorkspaceData, DependencyBuilder, Env, LangCrateOrigin, ProcMacroPaths,
+    TargetLayoutLoadResult,
 };
 use cfg::{CfgAtom, CfgDiff, CfgOptions};
 use intern::{sym, Symbol};
@@ -848,10 +849,14 @@ impl ProjectWorkspace {
         &self,
         load: FileLoader<'_>,
         extra_env: &FxHashMap<String, String>,
-    ) -> (CrateGraph, ProcMacroPaths) {
+    ) -> (CrateGraphBuilder, ProcMacroPaths) {
         let _p = tracing::info_span!("ProjectWorkspace::to_crate_graph").entered();
 
         let Self { kind, sysroot, cfg_overrides, rustc_cfg, .. } = self;
+        let crate_ws_data = Arc::new(CrateWorkspaceData {
+            toolchain: self.toolchain.clone(),
+            data_layout: self.target_layout.clone(),
+        });
         let (crate_graph, proc_macros) = match kind {
             ProjectWorkspaceKind::Json(project) => project_json_to_crate_graph(
                 rustc_cfg.clone(),
@@ -861,6 +866,7 @@ impl ProjectWorkspace {
                 extra_env,
                 cfg_overrides,
                 self.set_test,
+                crate_ws_data,
             ),
             ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _ } => {
                 cargo_to_crate_graph(
@@ -872,6 +878,7 @@ impl ProjectWorkspace {
                     cfg_overrides,
                     build_scripts,
                     self.set_test,
+                    crate_ws_data,
                 )
             }
             ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => {
@@ -885,6 +892,7 @@ impl ProjectWorkspace {
                         cfg_overrides,
                         build_scripts,
                         self.set_test,
+                        crate_ws_data,
                     )
                 } else {
                     detached_file_to_crate_graph(
@@ -894,6 +902,7 @@ impl ProjectWorkspace {
                         sysroot,
                         cfg_overrides,
                         self.set_test,
+                        crate_ws_data,
                     )
                 }
             }
@@ -959,15 +968,22 @@ fn project_json_to_crate_graph(
     extra_env: &FxHashMap<String, String>,
     override_cfg: &CfgOverrides,
     set_test: bool,
-) -> (CrateGraph, ProcMacroPaths) {
-    let mut res = (CrateGraph::default(), ProcMacroPaths::default());
+    crate_ws_data: Arc<CrateWorkspaceData>,
+) -> (CrateGraphBuilder, ProcMacroPaths) {
+    let mut res = (CrateGraphBuilder::default(), ProcMacroPaths::default());
     let (crate_graph, proc_macros) = &mut res;
-    let (public_deps, libproc_macro) =
-        sysroot_to_crate_graph(crate_graph, sysroot, rustc_cfg.clone(), load);
+    let (public_deps, libproc_macro) = sysroot_to_crate_graph(
+        crate_graph,
+        sysroot,
+        rustc_cfg.clone(),
+        load,
+        // FIXME: This looks incorrect but I don't think this matters.
+        crate_ws_data.clone(),
+    );
 
     let mut cfg_cache: FxHashMap<&str, Vec<CfgAtom>> = FxHashMap::default();
 
-    let idx_to_crate_id: FxHashMap<CrateArrayIdx, CrateId> = project
+    let idx_to_crate_id: FxHashMap<CrateArrayIdx, _> = project
         .crates()
         .filter_map(|(idx, krate)| Some((idx, krate, load(&krate.root_module)?)))
         .map(
@@ -1042,6 +1058,7 @@ fn project_json_to_crate_graph(
                     },
                     *is_proc_macro,
                     proc_macro_cwd.clone(),
+                    crate_ws_data.clone(),
                 );
                 debug!(
                     ?crate_graph_crate_id,
@@ -1092,12 +1109,19 @@ fn cargo_to_crate_graph(
     override_cfg: &CfgOverrides,
     build_scripts: &WorkspaceBuildScripts,
     set_test: bool,
-) -> (CrateGraph, ProcMacroPaths) {
+    crate_ws_data: Arc<CrateWorkspaceData>,
+) -> (CrateGraphBuilder, ProcMacroPaths) {
     let _p = tracing::info_span!("cargo_to_crate_graph").entered();
-    let mut res = (CrateGraph::default(), ProcMacroPaths::default());
+    let mut res = (CrateGraphBuilder::default(), ProcMacroPaths::default());
     let (crate_graph, proc_macros) = &mut res;
-    let (public_deps, libproc_macro) =
-        sysroot_to_crate_graph(crate_graph, sysroot, rustc_cfg.clone(), load);
+    let (public_deps, libproc_macro) = sysroot_to_crate_graph(
+        crate_graph,
+        sysroot,
+        rustc_cfg.clone(),
+        load,
+        // FIXME: This looks incorrect but I don't think this causes problems.
+        crate_ws_data.clone(),
+    );
 
     let cfg_options = CfgOptions::from_iter(rustc_cfg);
 
@@ -1163,6 +1187,7 @@ fn cargo_to_crate_graph(
                         name: Symbol::intern(&pkg_data.name),
                     }
                 },
+                crate_ws_data.clone(),
             );
             if let TargetKind::Lib { .. } = kind {
                 lib_tgt = Some((crate_id, name.clone()));
@@ -1267,6 +1292,8 @@ fn cargo_to_crate_graph(
                 } else {
                     rustc_build_scripts
                 },
+                // FIXME: This looks incorrect but I don't think this causes problems.
+                crate_ws_data,
             );
         }
     }
@@ -1280,11 +1307,18 @@ fn detached_file_to_crate_graph(
     sysroot: &Sysroot,
     override_cfg: &CfgOverrides,
     set_test: bool,
-) -> (CrateGraph, ProcMacroPaths) {
+    crate_ws_data: Arc<CrateWorkspaceData>,
+) -> (CrateGraphBuilder, ProcMacroPaths) {
     let _p = tracing::info_span!("detached_file_to_crate_graph").entered();
-    let mut crate_graph = CrateGraph::default();
-    let (public_deps, _libproc_macro) =
-        sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
+    let mut crate_graph = CrateGraphBuilder::default();
+    let (public_deps, _libproc_macro) = sysroot_to_crate_graph(
+        &mut crate_graph,
+        sysroot,
+        rustc_cfg.clone(),
+        load,
+        // FIXME: This looks incorrect but I don't think this causes problems.
+        crate_ws_data.clone(),
+    );
 
     let mut cfg_options = CfgOptions::from_iter(rustc_cfg);
     if set_test {
@@ -1316,6 +1350,7 @@ fn detached_file_to_crate_graph(
         },
         false,
         None,
+        crate_ws_data,
     );
 
     public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate);
@@ -1323,18 +1358,19 @@ fn detached_file_to_crate_graph(
 }
 
 fn handle_rustc_crates(
-    crate_graph: &mut CrateGraph,
+    crate_graph: &mut CrateGraphBuilder,
     proc_macros: &mut ProcMacroPaths,
-    pkg_to_lib_crate: &mut FxHashMap<Package, CrateId>,
+    pkg_to_lib_crate: &mut FxHashMap<Package, CrateBuilderId>,
     load: FileLoader<'_>,
     rustc_workspace: &CargoWorkspace,
     cargo: &CargoWorkspace,
     public_deps: &SysrootPublicDeps,
-    libproc_macro: Option<CrateId>,
-    pkg_crates: &FxHashMap<Package, Vec<(CrateId, TargetKind)>>,
+    libproc_macro: Option<CrateBuilderId>,
+    pkg_crates: &FxHashMap<Package, Vec<(CrateBuilderId, TargetKind)>>,
     cfg_options: &CfgOptions,
     override_cfg: &CfgOverrides,
     build_scripts: &WorkspaceBuildScripts,
+    crate_ws_data: Arc<CrateWorkspaceData>,
 ) {
     let mut rustc_pkg_crates = FxHashMap::default();
     // The root package of the rustc-dev component is rustc_driver, so we match that
@@ -1377,6 +1413,7 @@ fn handle_rustc_crates(
                         &rustc_workspace[tgt].name,
                         kind,
                         CrateOrigin::Rustc { name: Symbol::intern(&rustc_workspace[pkg].name) },
+                        crate_ws_data.clone(),
                     );
                     pkg_to_lib_crate.insert(pkg, crate_id);
                     // Add dependencies on core / std / alloc for this crate
@@ -1417,7 +1454,7 @@ fn handle_rustc_crates(
                     // This avoids the situation where `from` depends on e.g. `arrayvec`, but
                     // `rust_analyzer` thinks that it should use the one from the `rustc_source`
                     // instead of the one from `crates.io`
-                    if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
+                    if !crate_graph[*from].basic.dependencies.iter().any(|d| d.name == name) {
                         add_dep(crate_graph, *from, name.clone(), to);
                     }
                 }
@@ -1427,7 +1464,7 @@ fn handle_rustc_crates(
 }
 
 fn add_target_crate_root(
-    crate_graph: &mut CrateGraph,
+    crate_graph: &mut CrateGraphBuilder,
     proc_macros: &mut ProcMacroPaths,
     cargo: &CargoWorkspace,
     pkg: &PackageData,
@@ -1437,7 +1474,8 @@ fn add_target_crate_root(
     cargo_name: &str,
     kind: TargetKind,
     origin: CrateOrigin,
-) -> CrateId {
+    crate_ws_data: Arc<CrateWorkspaceData>,
+) -> CrateBuilderId {
     let edition = pkg.edition;
     let potential_cfg_options = if pkg.features.is_empty() {
         None
@@ -1474,7 +1512,7 @@ fn add_target_crate_root(
         Some(CrateDisplayName::from_canonical_name(cargo_name)),
         Some(pkg.version.to_string()),
         Arc::new(cfg_options),
-        potential_cfg_options.map(Arc::new),
+        potential_cfg_options,
         env,
         origin,
         matches!(kind, TargetKind::Lib { is_proc_macro: true }),
@@ -1483,6 +1521,7 @@ fn add_target_crate_root(
         } else {
             pkg.manifest.parent().to_path_buf()
         }),
+        crate_ws_data,
     );
     if let TargetKind::Lib { is_proc_macro: true } = kind {
         let proc_macro = match build_data {
@@ -1503,12 +1542,12 @@ fn add_target_crate_root(
 
 #[derive(Default, Debug)]
 struct SysrootPublicDeps {
-    deps: Vec<(CrateName, CrateId, bool)>,
+    deps: Vec<(CrateName, CrateBuilderId, bool)>,
 }
 
 impl SysrootPublicDeps {
     /// Makes `from` depend on the public sysroot crates.
-    fn add_to_crate_graph(&self, crate_graph: &mut CrateGraph, from: CrateId) {
+    fn add_to_crate_graph(&self, crate_graph: &mut CrateGraphBuilder, from: CrateBuilderId) {
         for (name, krate, prelude) in &self.deps {
             add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude, true);
         }
@@ -1516,10 +1555,10 @@ impl SysrootPublicDeps {
 }
 
 fn extend_crate_graph_with_sysroot(
-    crate_graph: &mut CrateGraph,
-    mut sysroot_crate_graph: CrateGraph,
+    crate_graph: &mut CrateGraphBuilder,
+    mut sysroot_crate_graph: CrateGraphBuilder,
     mut sysroot_proc_macros: ProcMacroPaths,
-) -> (SysrootPublicDeps, Option<CrateId>) {
+) -> (SysrootPublicDeps, Option<CrateBuilderId>) {
     let mut pub_deps = vec![];
     let mut libproc_macro = None;
     let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag(sym::test.clone())]);
@@ -1527,11 +1566,11 @@ fn extend_crate_graph_with_sysroot(
         // uninject `test` flag so `core` keeps working.
         Arc::make_mut(&mut c.cfg_options).apply_diff(diff.clone());
         // patch the origin
-        if c.origin.is_local() {
+        if c.basic.origin.is_local() {
             let lang_crate = LangCrateOrigin::from(
-                c.display_name.as_ref().map_or("", |it| it.canonical_name().as_str()),
+                c.extra.display_name.as_ref().map_or("", |it| it.canonical_name().as_str()),
             );
-            c.origin = CrateOrigin::Lang(lang_crate);
+            c.basic.origin = CrateOrigin::Lang(lang_crate);
             match lang_crate {
                 LangCrateOrigin::Test
                 | LangCrateOrigin::Alloc
@@ -1579,11 +1618,12 @@ fn extend_crate_graph_with_sysroot(
 }
 
 fn sysroot_to_crate_graph(
-    crate_graph: &mut CrateGraph,
+    crate_graph: &mut CrateGraphBuilder,
     sysroot: &Sysroot,
     rustc_cfg: Vec<CfgAtom>,
     load: FileLoader<'_>,
-) -> (SysrootPublicDeps, Option<CrateId>) {
+    crate_ws_data: Arc<CrateWorkspaceData>,
+) -> (SysrootPublicDeps, Option<CrateBuilderId>) {
     let _p = tracing::info_span!("sysroot_to_crate_graph").entered();
     match sysroot.workspace() {
         RustLibSrcWorkspace::Workspace(cargo) => {
@@ -1605,6 +1645,7 @@ fn sysroot_to_crate_graph(
                 },
                 &WorkspaceBuildScripts::default(),
                 false,
+                crate_ws_data,
             );
 
             extend_crate_graph_with_sysroot(crate_graph, cg, pm)
@@ -1627,6 +1668,7 @@ fn sysroot_to_crate_graph(
                     ..Default::default()
                 },
                 false,
+                crate_ws_data,
             );
 
             extend_crate_graph_with_sysroot(crate_graph, cg, pm)
@@ -1639,29 +1681,31 @@ fn sysroot_to_crate_graph(
                 cfg_options.insert_atom(sym::miri.clone());
                 cfg_options
             });
-            let sysroot_crates: FxHashMap<crate::sysroot::stitched::RustLibSrcCrate, CrateId> =
-                stitched
-                    .crates()
-                    .filter_map(|krate| {
-                        let file_id = load(&stitched[krate].root)?;
-
-                        let display_name =
-                            CrateDisplayName::from_canonical_name(&stitched[krate].name);
-                        let crate_id = crate_graph.add_crate_root(
-                            file_id,
-                            Edition::CURRENT_FIXME,
-                            Some(display_name),
-                            None,
-                            cfg_options.clone(),
-                            None,
-                            Env::default(),
-                            CrateOrigin::Lang(LangCrateOrigin::from(&*stitched[krate].name)),
-                            false,
-                            None,
-                        );
-                        Some((krate, crate_id))
-                    })
-                    .collect();
+            let sysroot_crates: FxHashMap<
+                crate::sysroot::stitched::RustLibSrcCrate,
+                CrateBuilderId,
+            > = stitched
+                .crates()
+                .filter_map(|krate| {
+                    let file_id = load(&stitched[krate].root)?;
+
+                    let display_name = CrateDisplayName::from_canonical_name(&stitched[krate].name);
+                    let crate_id = crate_graph.add_crate_root(
+                        file_id,
+                        Edition::CURRENT_FIXME,
+                        Some(display_name),
+                        None,
+                        cfg_options.clone(),
+                        None,
+                        Env::default(),
+                        CrateOrigin::Lang(LangCrateOrigin::from(&*stitched[krate].name)),
+                        false,
+                        None,
+                        crate_ws_data.clone(),
+                    );
+                    Some((krate, crate_id))
+                })
+                .collect();
 
             for from in stitched.crates() {
                 for &to in stitched[from].deps.iter() {
@@ -1691,22 +1735,32 @@ fn sysroot_to_crate_graph(
     }
 }
 
-fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
-    add_dep_inner(graph, from, Dependency::new(name, to))
+fn add_dep(
+    graph: &mut CrateGraphBuilder,
+    from: CrateBuilderId,
+    name: CrateName,
+    to: CrateBuilderId,
+) {
+    add_dep_inner(graph, from, DependencyBuilder::new(name, to))
 }
 
 fn add_dep_with_prelude(
-    graph: &mut CrateGraph,
-    from: CrateId,
+    graph: &mut CrateGraphBuilder,
+    from: CrateBuilderId,
     name: CrateName,
-    to: CrateId,
+    to: CrateBuilderId,
     prelude: bool,
     sysroot: bool,
 ) {
-    add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude, sysroot))
+    add_dep_inner(graph, from, DependencyBuilder::with_prelude(name, to, prelude, sysroot))
 }
 
-fn add_proc_macro_dep(crate_graph: &mut CrateGraph, from: CrateId, to: CrateId, prelude: bool) {
+fn add_proc_macro_dep(
+    crate_graph: &mut CrateGraphBuilder,
+    from: CrateBuilderId,
+    to: CrateBuilderId,
+    prelude: bool,
+) {
     add_dep_with_prelude(
         crate_graph,
         from,
@@ -1717,7 +1771,7 @@ fn add_proc_macro_dep(crate_graph: &mut CrateGraph, from: CrateId, to: CrateId,
     );
 }
 
-fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
+fn add_dep_inner(graph: &mut CrateGraphBuilder, from: CrateBuilderId, dep: DependencyBuilder) {
     if let Err(err) = graph.add_dep(from, dep) {
         tracing::warn!("{}", err)
     }
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
index fae0b6fcca4..ae842bf704d 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
@@ -1,20 +1,47 @@
 {
-    0: CrateData {
-        root_file_id: FileId(
-            1,
-        ),
-        edition: Edition2018,
-        version: Some(
-            "0.1.0",
-        ),
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "hello_world",
+    0: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                1,
+            ),
+            edition: Edition2018,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(4),
+                    name: CrateName(
+                        "libc",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Local {
+                repo: None,
+                name: Some(
+                    "hello-world",
                 ),
-                canonical_name: "hello-world",
             },
-        ),
+            is_proc_macro: false,
+            proc_macro_cwd: Some(
+                AbsPathBuf(
+                    "$ROOT$hello-world",
+                ),
+            ),
+        },
+        extra: ExtraCrateData {
+            version: Some(
+                "0.1.0",
+            ),
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "hello_world",
+                    ),
+                    canonical_name: "hello-world",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "rust_analyzer",
@@ -22,7 +49,6 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {
                 "CARGO": "$CARGO$",
@@ -44,45 +70,64 @@
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(4),
-                name: CrateName(
-                    "libc",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Local {
-            repo: None,
-            name: Some(
-                "hello-world",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "target_data_layout not loaded",
             ),
+            toolchain: None,
         },
-        is_proc_macro: false,
-        proc_macro_cwd: Some(
-            AbsPathBuf(
-                "$ROOT$hello-world",
-            ),
-        ),
     },
-    1: CrateData {
-        root_file_id: FileId(
-            2,
-        ),
-        edition: Edition2018,
-        version: Some(
-            "0.1.0",
-        ),
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "hello_world",
+    1: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                2,
+            ),
+            edition: Edition2018,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(0),
+                    name: CrateName(
+                        "hello_world",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(4),
+                    name: CrateName(
+                        "libc",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Local {
+                repo: None,
+                name: Some(
+                    "hello-world",
                 ),
-                canonical_name: "hello-world",
             },
-        ),
+            is_proc_macro: false,
+            proc_macro_cwd: Some(
+                AbsPathBuf(
+                    "$ROOT$hello-world",
+                ),
+            ),
+        },
+        extra: ExtraCrateData {
+            version: Some(
+                "0.1.0",
+            ),
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "hello_world",
+                    ),
+                    canonical_name: "hello-world",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "rust_analyzer",
@@ -90,7 +135,6 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {
                 "CARGO": "$CARGO$",
@@ -112,53 +156,64 @@
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(0),
-                name: CrateName(
-                    "hello_world",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "target_data_layout not loaded",
+            ),
+            toolchain: None,
+        },
+    },
+    2: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                3,
+            ),
+            edition: Edition2018,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(0),
+                    name: CrateName(
+                        "hello_world",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(4),
+                    name: CrateName(
+                        "libc",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Local {
+                repo: None,
+                name: Some(
+                    "hello-world",
                 ),
-                prelude: true,
-                sysroot: false,
             },
-            Dependency {
-                crate_id: Idx::<CrateData>(4),
-                name: CrateName(
-                    "libc",
+            is_proc_macro: false,
+            proc_macro_cwd: Some(
+                AbsPathBuf(
+                    "$ROOT$hello-world",
                 ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Local {
-            repo: None,
-            name: Some(
-                "hello-world",
             ),
         },
-        is_proc_macro: false,
-        proc_macro_cwd: Some(
-            AbsPathBuf(
-                "$ROOT$hello-world",
+        extra: ExtraCrateData {
+            version: Some(
+                "0.1.0",
             ),
-        ),
-    },
-    2: CrateData {
-        root_file_id: FileId(
-            3,
-        ),
-        edition: Edition2018,
-        version: Some(
-            "0.1.0",
-        ),
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "an_example",
-                ),
-                canonical_name: "an-example",
-            },
-        ),
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "an_example",
+                    ),
+                    canonical_name: "an-example",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "rust_analyzer",
@@ -166,7 +221,6 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {
                 "CARGO": "$CARGO$",
@@ -188,53 +242,64 @@
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(0),
-                name: CrateName(
-                    "hello_world",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "target_data_layout not loaded",
+            ),
+            toolchain: None,
+        },
+    },
+    3: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                4,
+            ),
+            edition: Edition2018,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(0),
+                    name: CrateName(
+                        "hello_world",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(4),
+                    name: CrateName(
+                        "libc",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Local {
+                repo: None,
+                name: Some(
+                    "hello-world",
                 ),
-                prelude: true,
-                sysroot: false,
             },
-            Dependency {
-                crate_id: Idx::<CrateData>(4),
-                name: CrateName(
-                    "libc",
+            is_proc_macro: false,
+            proc_macro_cwd: Some(
+                AbsPathBuf(
+                    "$ROOT$hello-world",
                 ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Local {
-            repo: None,
-            name: Some(
-                "hello-world",
             ),
         },
-        is_proc_macro: false,
-        proc_macro_cwd: Some(
-            AbsPathBuf(
-                "$ROOT$hello-world",
+        extra: ExtraCrateData {
+            version: Some(
+                "0.1.0",
             ),
-        ),
-    },
-    3: CrateData {
-        root_file_id: FileId(
-            4,
-        ),
-        edition: Edition2018,
-        version: Some(
-            "0.1.0",
-        ),
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "it",
-                ),
-                canonical_name: "it",
-            },
-        ),
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "it",
+                    ),
+                    canonical_name: "it",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "rust_analyzer",
@@ -242,7 +307,6 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {
                 "CARGO": "$CARGO$",
@@ -264,53 +328,60 @@
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(0),
-                name: CrateName(
-                    "hello_world",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "target_data_layout not loaded",
+            ),
+            toolchain: None,
+        },
+    },
+    4: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                5,
+            ),
+            edition: Edition2015,
+            dependencies: [],
+            origin: Library {
+                repo: Some(
+                    "https://github.com/rust-lang/libc",
                 ),
-                prelude: true,
-                sysroot: false,
+                name: "libc",
             },
-            Dependency {
-                crate_id: Idx::<CrateData>(4),
-                name: CrateName(
-                    "libc",
+            is_proc_macro: false,
+            proc_macro_cwd: Some(
+                AbsPathBuf(
+                    "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
                 ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Local {
-            repo: None,
-            name: Some(
-                "hello-world",
             ),
         },
-        is_proc_macro: false,
-        proc_macro_cwd: Some(
-            AbsPathBuf(
-                "$ROOT$hello-world",
+        extra: ExtraCrateData {
+            version: Some(
+                "0.2.98",
             ),
-        ),
-    },
-    4: CrateData {
-        root_file_id: FileId(
-            5,
-        ),
-        edition: Edition2015,
-        version: Some(
-            "0.2.98",
-        ),
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "libc",
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "libc",
+                    ),
+                    canonical_name: "libc",
+                },
+            ),
+            potential_cfg_options: Some(
+                CfgOptions(
+                    [
+                        "feature=align",
+                        "feature=const-extern-fn",
+                        "feature=default",
+                        "feature=extra_traits",
+                        "feature=rustc-dep-of-std",
+                        "feature=std",
+                        "feature=use_std",
+                        "true",
+                    ],
                 ),
-                canonical_name: "libc",
-            },
-        ),
+            ),
+        },
         cfg_options: CfgOptions(
             [
                 "feature=default",
@@ -318,20 +389,6 @@
                 "true",
             ],
         ),
-        potential_cfg_options: Some(
-            CfgOptions(
-                [
-                    "feature=align",
-                    "feature=const-extern-fn",
-                    "feature=default",
-                    "feature=extra_traits",
-                    "feature=rustc-dep-of-std",
-                    "feature=std",
-                    "feature=use_std",
-                    "true",
-                ],
-            ),
-        ),
         env: Env {
             entries: {
                 "CARGO": "$CARGO$",
@@ -353,18 +410,11 @@
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
-        dependencies: [],
-        origin: Library {
-            repo: Some(
-                "https://github.com/rust-lang/libc",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "target_data_layout not loaded",
             ),
-            name: "libc",
+            toolchain: None,
         },
-        is_proc_macro: false,
-        proc_macro_cwd: Some(
-            AbsPathBuf(
-                "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
-            ),
-        ),
     },
 }
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
index fae0b6fcca4..ae842bf704d 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
@@ -1,20 +1,47 @@
 {
-    0: CrateData {
-        root_file_id: FileId(
-            1,
-        ),
-        edition: Edition2018,
-        version: Some(
-            "0.1.0",
-        ),
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "hello_world",
+    0: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                1,
+            ),
+            edition: Edition2018,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(4),
+                    name: CrateName(
+                        "libc",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Local {
+                repo: None,
+                name: Some(
+                    "hello-world",
                 ),
-                canonical_name: "hello-world",
             },
-        ),
+            is_proc_macro: false,
+            proc_macro_cwd: Some(
+                AbsPathBuf(
+                    "$ROOT$hello-world",
+                ),
+            ),
+        },
+        extra: ExtraCrateData {
+            version: Some(
+                "0.1.0",
+            ),
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "hello_world",
+                    ),
+                    canonical_name: "hello-world",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "rust_analyzer",
@@ -22,7 +49,6 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {
                 "CARGO": "$CARGO$",
@@ -44,45 +70,64 @@
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(4),
-                name: CrateName(
-                    "libc",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Local {
-            repo: None,
-            name: Some(
-                "hello-world",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "target_data_layout not loaded",
             ),
+            toolchain: None,
         },
-        is_proc_macro: false,
-        proc_macro_cwd: Some(
-            AbsPathBuf(
-                "$ROOT$hello-world",
-            ),
-        ),
     },
-    1: CrateData {
-        root_file_id: FileId(
-            2,
-        ),
-        edition: Edition2018,
-        version: Some(
-            "0.1.0",
-        ),
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "hello_world",
+    1: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                2,
+            ),
+            edition: Edition2018,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(0),
+                    name: CrateName(
+                        "hello_world",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(4),
+                    name: CrateName(
+                        "libc",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Local {
+                repo: None,
+                name: Some(
+                    "hello-world",
                 ),
-                canonical_name: "hello-world",
             },
-        ),
+            is_proc_macro: false,
+            proc_macro_cwd: Some(
+                AbsPathBuf(
+                    "$ROOT$hello-world",
+                ),
+            ),
+        },
+        extra: ExtraCrateData {
+            version: Some(
+                "0.1.0",
+            ),
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "hello_world",
+                    ),
+                    canonical_name: "hello-world",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "rust_analyzer",
@@ -90,7 +135,6 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {
                 "CARGO": "$CARGO$",
@@ -112,53 +156,64 @@
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(0),
-                name: CrateName(
-                    "hello_world",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "target_data_layout not loaded",
+            ),
+            toolchain: None,
+        },
+    },
+    2: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                3,
+            ),
+            edition: Edition2018,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(0),
+                    name: CrateName(
+                        "hello_world",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(4),
+                    name: CrateName(
+                        "libc",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Local {
+                repo: None,
+                name: Some(
+                    "hello-world",
                 ),
-                prelude: true,
-                sysroot: false,
             },
-            Dependency {
-                crate_id: Idx::<CrateData>(4),
-                name: CrateName(
-                    "libc",
+            is_proc_macro: false,
+            proc_macro_cwd: Some(
+                AbsPathBuf(
+                    "$ROOT$hello-world",
                 ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Local {
-            repo: None,
-            name: Some(
-                "hello-world",
             ),
         },
-        is_proc_macro: false,
-        proc_macro_cwd: Some(
-            AbsPathBuf(
-                "$ROOT$hello-world",
+        extra: ExtraCrateData {
+            version: Some(
+                "0.1.0",
             ),
-        ),
-    },
-    2: CrateData {
-        root_file_id: FileId(
-            3,
-        ),
-        edition: Edition2018,
-        version: Some(
-            "0.1.0",
-        ),
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "an_example",
-                ),
-                canonical_name: "an-example",
-            },
-        ),
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "an_example",
+                    ),
+                    canonical_name: "an-example",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "rust_analyzer",
@@ -166,7 +221,6 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {
                 "CARGO": "$CARGO$",
@@ -188,53 +242,64 @@
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(0),
-                name: CrateName(
-                    "hello_world",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "target_data_layout not loaded",
+            ),
+            toolchain: None,
+        },
+    },
+    3: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                4,
+            ),
+            edition: Edition2018,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(0),
+                    name: CrateName(
+                        "hello_world",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(4),
+                    name: CrateName(
+                        "libc",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Local {
+                repo: None,
+                name: Some(
+                    "hello-world",
                 ),
-                prelude: true,
-                sysroot: false,
             },
-            Dependency {
-                crate_id: Idx::<CrateData>(4),
-                name: CrateName(
-                    "libc",
+            is_proc_macro: false,
+            proc_macro_cwd: Some(
+                AbsPathBuf(
+                    "$ROOT$hello-world",
                 ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Local {
-            repo: None,
-            name: Some(
-                "hello-world",
             ),
         },
-        is_proc_macro: false,
-        proc_macro_cwd: Some(
-            AbsPathBuf(
-                "$ROOT$hello-world",
+        extra: ExtraCrateData {
+            version: Some(
+                "0.1.0",
             ),
-        ),
-    },
-    3: CrateData {
-        root_file_id: FileId(
-            4,
-        ),
-        edition: Edition2018,
-        version: Some(
-            "0.1.0",
-        ),
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "it",
-                ),
-                canonical_name: "it",
-            },
-        ),
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "it",
+                    ),
+                    canonical_name: "it",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "rust_analyzer",
@@ -242,7 +307,6 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {
                 "CARGO": "$CARGO$",
@@ -264,53 +328,60 @@
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(0),
-                name: CrateName(
-                    "hello_world",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "target_data_layout not loaded",
+            ),
+            toolchain: None,
+        },
+    },
+    4: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                5,
+            ),
+            edition: Edition2015,
+            dependencies: [],
+            origin: Library {
+                repo: Some(
+                    "https://github.com/rust-lang/libc",
                 ),
-                prelude: true,
-                sysroot: false,
+                name: "libc",
             },
-            Dependency {
-                crate_id: Idx::<CrateData>(4),
-                name: CrateName(
-                    "libc",
+            is_proc_macro: false,
+            proc_macro_cwd: Some(
+                AbsPathBuf(
+                    "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
                 ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Local {
-            repo: None,
-            name: Some(
-                "hello-world",
             ),
         },
-        is_proc_macro: false,
-        proc_macro_cwd: Some(
-            AbsPathBuf(
-                "$ROOT$hello-world",
+        extra: ExtraCrateData {
+            version: Some(
+                "0.2.98",
             ),
-        ),
-    },
-    4: CrateData {
-        root_file_id: FileId(
-            5,
-        ),
-        edition: Edition2015,
-        version: Some(
-            "0.2.98",
-        ),
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "libc",
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "libc",
+                    ),
+                    canonical_name: "libc",
+                },
+            ),
+            potential_cfg_options: Some(
+                CfgOptions(
+                    [
+                        "feature=align",
+                        "feature=const-extern-fn",
+                        "feature=default",
+                        "feature=extra_traits",
+                        "feature=rustc-dep-of-std",
+                        "feature=std",
+                        "feature=use_std",
+                        "true",
+                    ],
                 ),
-                canonical_name: "libc",
-            },
-        ),
+            ),
+        },
         cfg_options: CfgOptions(
             [
                 "feature=default",
@@ -318,20 +389,6 @@
                 "true",
             ],
         ),
-        potential_cfg_options: Some(
-            CfgOptions(
-                [
-                    "feature=align",
-                    "feature=const-extern-fn",
-                    "feature=default",
-                    "feature=extra_traits",
-                    "feature=rustc-dep-of-std",
-                    "feature=std",
-                    "feature=use_std",
-                    "true",
-                ],
-            ),
-        ),
         env: Env {
             entries: {
                 "CARGO": "$CARGO$",
@@ -353,18 +410,11 @@
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
-        dependencies: [],
-        origin: Library {
-            repo: Some(
-                "https://github.com/rust-lang/libc",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "target_data_layout not loaded",
             ),
-            name: "libc",
+            toolchain: None,
         },
-        is_proc_macro: false,
-        proc_macro_cwd: Some(
-            AbsPathBuf(
-                "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
-            ),
-        ),
     },
 }
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
index 566174882dd..272cd69cb14 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
@@ -1,27 +1,53 @@
 {
-    0: CrateData {
-        root_file_id: FileId(
-            1,
-        ),
-        edition: Edition2018,
-        version: Some(
-            "0.1.0",
-        ),
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "hello_world",
+    0: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                1,
+            ),
+            edition: Edition2018,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(4),
+                    name: CrateName(
+                        "libc",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Local {
+                repo: None,
+                name: Some(
+                    "hello-world",
                 ),
-                canonical_name: "hello-world",
             },
-        ),
+            is_proc_macro: false,
+            proc_macro_cwd: Some(
+                AbsPathBuf(
+                    "$ROOT$hello-world",
+                ),
+            ),
+        },
+        extra: ExtraCrateData {
+            version: Some(
+                "0.1.0",
+            ),
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "hello_world",
+                    ),
+                    canonical_name: "hello-world",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "rust_analyzer",
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {
                 "CARGO": "$CARGO$",
@@ -43,52 +69,70 @@
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(4),
-                name: CrateName(
-                    "libc",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Local {
-            repo: None,
-            name: Some(
-                "hello-world",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "target_data_layout not loaded",
             ),
+            toolchain: None,
         },
-        is_proc_macro: false,
-        proc_macro_cwd: Some(
-            AbsPathBuf(
-                "$ROOT$hello-world",
-            ),
-        ),
     },
-    1: CrateData {
-        root_file_id: FileId(
-            2,
-        ),
-        edition: Edition2018,
-        version: Some(
-            "0.1.0",
-        ),
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "hello_world",
+    1: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                2,
+            ),
+            edition: Edition2018,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(0),
+                    name: CrateName(
+                        "hello_world",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(4),
+                    name: CrateName(
+                        "libc",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Local {
+                repo: None,
+                name: Some(
+                    "hello-world",
                 ),
-                canonical_name: "hello-world",
             },
-        ),
+            is_proc_macro: false,
+            proc_macro_cwd: Some(
+                AbsPathBuf(
+                    "$ROOT$hello-world",
+                ),
+            ),
+        },
+        extra: ExtraCrateData {
+            version: Some(
+                "0.1.0",
+            ),
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "hello_world",
+                    ),
+                    canonical_name: "hello-world",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "rust_analyzer",
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {
                 "CARGO": "$CARGO$",
@@ -110,60 +154,70 @@
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(0),
-                name: CrateName(
-                    "hello_world",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "target_data_layout not loaded",
+            ),
+            toolchain: None,
+        },
+    },
+    2: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                3,
+            ),
+            edition: Edition2018,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(0),
+                    name: CrateName(
+                        "hello_world",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(4),
+                    name: CrateName(
+                        "libc",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Local {
+                repo: None,
+                name: Some(
+                    "hello-world",
                 ),
-                prelude: true,
-                sysroot: false,
             },
-            Dependency {
-                crate_id: Idx::<CrateData>(4),
-                name: CrateName(
-                    "libc",
+            is_proc_macro: false,
+            proc_macro_cwd: Some(
+                AbsPathBuf(
+                    "$ROOT$hello-world",
                 ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Local {
-            repo: None,
-            name: Some(
-                "hello-world",
             ),
         },
-        is_proc_macro: false,
-        proc_macro_cwd: Some(
-            AbsPathBuf(
-                "$ROOT$hello-world",
+        extra: ExtraCrateData {
+            version: Some(
+                "0.1.0",
             ),
-        ),
-    },
-    2: CrateData {
-        root_file_id: FileId(
-            3,
-        ),
-        edition: Edition2018,
-        version: Some(
-            "0.1.0",
-        ),
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "an_example",
-                ),
-                canonical_name: "an-example",
-            },
-        ),
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "an_example",
+                    ),
+                    canonical_name: "an-example",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "rust_analyzer",
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {
                 "CARGO": "$CARGO$",
@@ -185,60 +239,70 @@
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(0),
-                name: CrateName(
-                    "hello_world",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "target_data_layout not loaded",
+            ),
+            toolchain: None,
+        },
+    },
+    3: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                4,
+            ),
+            edition: Edition2018,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(0),
+                    name: CrateName(
+                        "hello_world",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(4),
+                    name: CrateName(
+                        "libc",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Local {
+                repo: None,
+                name: Some(
+                    "hello-world",
                 ),
-                prelude: true,
-                sysroot: false,
             },
-            Dependency {
-                crate_id: Idx::<CrateData>(4),
-                name: CrateName(
-                    "libc",
+            is_proc_macro: false,
+            proc_macro_cwd: Some(
+                AbsPathBuf(
+                    "$ROOT$hello-world",
                 ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Local {
-            repo: None,
-            name: Some(
-                "hello-world",
             ),
         },
-        is_proc_macro: false,
-        proc_macro_cwd: Some(
-            AbsPathBuf(
-                "$ROOT$hello-world",
+        extra: ExtraCrateData {
+            version: Some(
+                "0.1.0",
             ),
-        ),
-    },
-    3: CrateData {
-        root_file_id: FileId(
-            4,
-        ),
-        edition: Edition2018,
-        version: Some(
-            "0.1.0",
-        ),
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "it",
-                ),
-                canonical_name: "it",
-            },
-        ),
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "it",
+                    ),
+                    canonical_name: "it",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "rust_analyzer",
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {
                 "CARGO": "$CARGO$",
@@ -260,53 +324,60 @@
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(0),
-                name: CrateName(
-                    "hello_world",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "target_data_layout not loaded",
+            ),
+            toolchain: None,
+        },
+    },
+    4: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                5,
+            ),
+            edition: Edition2015,
+            dependencies: [],
+            origin: Library {
+                repo: Some(
+                    "https://github.com/rust-lang/libc",
                 ),
-                prelude: true,
-                sysroot: false,
+                name: "libc",
             },
-            Dependency {
-                crate_id: Idx::<CrateData>(4),
-                name: CrateName(
-                    "libc",
+            is_proc_macro: false,
+            proc_macro_cwd: Some(
+                AbsPathBuf(
+                    "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
                 ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Local {
-            repo: None,
-            name: Some(
-                "hello-world",
             ),
         },
-        is_proc_macro: false,
-        proc_macro_cwd: Some(
-            AbsPathBuf(
-                "$ROOT$hello-world",
+        extra: ExtraCrateData {
+            version: Some(
+                "0.2.98",
             ),
-        ),
-    },
-    4: CrateData {
-        root_file_id: FileId(
-            5,
-        ),
-        edition: Edition2015,
-        version: Some(
-            "0.2.98",
-        ),
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "libc",
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "libc",
+                    ),
+                    canonical_name: "libc",
+                },
+            ),
+            potential_cfg_options: Some(
+                CfgOptions(
+                    [
+                        "feature=align",
+                        "feature=const-extern-fn",
+                        "feature=default",
+                        "feature=extra_traits",
+                        "feature=rustc-dep-of-std",
+                        "feature=std",
+                        "feature=use_std",
+                        "true",
+                    ],
                 ),
-                canonical_name: "libc",
-            },
-        ),
+            ),
+        },
         cfg_options: CfgOptions(
             [
                 "feature=default",
@@ -314,20 +385,6 @@
                 "true",
             ],
         ),
-        potential_cfg_options: Some(
-            CfgOptions(
-                [
-                    "feature=align",
-                    "feature=const-extern-fn",
-                    "feature=default",
-                    "feature=extra_traits",
-                    "feature=rustc-dep-of-std",
-                    "feature=std",
-                    "feature=use_std",
-                    "true",
-                ],
-            ),
-        ),
         env: Env {
             entries: {
                 "CARGO": "$CARGO$",
@@ -349,18 +406,11 @@
                 "CARGO_PKG_VERSION_PRE": "",
             },
         },
-        dependencies: [],
-        origin: Library {
-            repo: Some(
-                "https://github.com/rust-lang/libc",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "target_data_layout not loaded",
             ),
-            name: "libc",
+            toolchain: None,
         },
-        is_proc_macro: false,
-        proc_macro_cwd: Some(
-            AbsPathBuf(
-                "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
-            ),
-        ),
     },
 }
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt
index 9b4be19c41c..2b8e521f344 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt
@@ -1,18 +1,38 @@
 {
-    0: CrateData {
-        root_file_id: FileId(
-            1,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "alloc",
-                ),
-                canonical_name: "alloc",
-            },
-        ),
+    0: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                1,
+            ),
+            edition: Edition2021,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(1),
+                    name: CrateName(
+                        "core",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Lang(
+                Alloc,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "alloc",
+                    ),
+                    canonical_name: "alloc",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -20,40 +40,41 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(1),
-                name: CrateName(
-                    "core",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Lang(
-            Alloc,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    1: CrateData {
-        root_file_id: FileId(
-            2,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "core",
-                ),
-                canonical_name: "core",
-            },
-        ),
+    1: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                2,
+            ),
+            edition: Edition2021,
+            dependencies: [],
+            origin: Lang(
+                Core,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "core",
+                    ),
+                    canonical_name: "core",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -61,31 +82,41 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [],
-        origin: Lang(
-            Core,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    2: CrateData {
-        root_file_id: FileId(
-            3,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "panic_abort",
-                ),
-                canonical_name: "panic_abort",
-            },
-        ),
+    2: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                3,
+            ),
+            edition: Edition2021,
+            dependencies: [],
+            origin: Lang(
+                Other,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "panic_abort",
+                    ),
+                    canonical_name: "panic_abort",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -93,31 +124,41 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [],
-        origin: Lang(
-            Other,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    3: CrateData {
-        root_file_id: FileId(
-            4,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "panic_unwind",
-                ),
-                canonical_name: "panic_unwind",
-            },
-        ),
+    3: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                4,
+            ),
+            edition: Edition2021,
+            dependencies: [],
+            origin: Lang(
+                Other,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "panic_unwind",
+                    ),
+                    canonical_name: "panic_unwind",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -125,31 +166,58 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [],
-        origin: Lang(
-            Other,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    4: CrateData {
-        root_file_id: FileId(
-            5,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "proc_macro",
-                ),
-                canonical_name: "proc_macro",
-            },
-        ),
+    4: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                5,
+            ),
+            edition: Edition2021,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(6),
+                    name: CrateName(
+                        "std",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(1),
+                    name: CrateName(
+                        "core",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Lang(
+                ProcMacro,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "proc_macro",
+                    ),
+                    canonical_name: "proc_macro",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -157,48 +225,41 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(6),
-                name: CrateName(
-                    "std",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(1),
-                name: CrateName(
-                    "core",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Lang(
-            ProcMacro,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    5: CrateData {
-        root_file_id: FileId(
-            6,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "profiler_builtins",
-                ),
-                canonical_name: "profiler_builtins",
-            },
-        ),
+    5: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                6,
+            ),
+            edition: Edition2021,
+            dependencies: [],
+            origin: Lang(
+                Other,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "profiler_builtins",
+                    ),
+                    canonical_name: "profiler_builtins",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -206,31 +267,106 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [],
-        origin: Lang(
-            Other,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    6: CrateData {
-        root_file_id: FileId(
-            7,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "std",
-                ),
-                canonical_name: "std",
-            },
-        ),
+    6: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                7,
+            ),
+            edition: Edition2021,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(0),
+                    name: CrateName(
+                        "alloc",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(3),
+                    name: CrateName(
+                        "panic_unwind",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(2),
+                    name: CrateName(
+                        "panic_abort",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(1),
+                    name: CrateName(
+                        "core",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(5),
+                    name: CrateName(
+                        "profiler_builtins",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(9),
+                    name: CrateName(
+                        "unwind",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(7),
+                    name: CrateName(
+                        "std_detect",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(8),
+                    name: CrateName(
+                        "test",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Lang(
+                Std,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "std",
+                    ),
+                    canonical_name: "std",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -238,96 +374,41 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(0),
-                name: CrateName(
-                    "alloc",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(3),
-                name: CrateName(
-                    "panic_unwind",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(2),
-                name: CrateName(
-                    "panic_abort",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(1),
-                name: CrateName(
-                    "core",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(5),
-                name: CrateName(
-                    "profiler_builtins",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(9),
-                name: CrateName(
-                    "unwind",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(7),
-                name: CrateName(
-                    "std_detect",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(8),
-                name: CrateName(
-                    "test",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Lang(
-            Std,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    7: CrateData {
-        root_file_id: FileId(
-            8,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "std_detect",
-                ),
-                canonical_name: "std_detect",
-            },
-        ),
+    7: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                8,
+            ),
+            edition: Edition2021,
+            dependencies: [],
+            origin: Lang(
+                Other,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "std_detect",
+                    ),
+                    canonical_name: "std_detect",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -335,31 +416,41 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [],
-        origin: Lang(
-            Other,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    8: CrateData {
-        root_file_id: FileId(
-            9,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "test",
-                ),
-                canonical_name: "test",
-            },
-        ),
+    8: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                9,
+            ),
+            edition: Edition2021,
+            dependencies: [],
+            origin: Lang(
+                Test,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "test",
+                    ),
+                    canonical_name: "test",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -367,31 +458,41 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [],
-        origin: Lang(
-            Test,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    9: CrateData {
-        root_file_id: FileId(
-            10,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "unwind",
-                ),
-                canonical_name: "unwind",
-            },
-        ),
+    9: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                10,
+            ),
+            edition: Edition2021,
+            dependencies: [],
+            origin: Lang(
+                Other,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "unwind",
+                    ),
+                    canonical_name: "unwind",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -399,31 +500,85 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [],
-        origin: Lang(
-            Other,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    10: CrateData {
-        root_file_id: FileId(
-            11,
-        ),
-        edition: Edition2018,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
+    10: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                11,
+            ),
+            edition: Edition2018,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(1),
+                    name: CrateName(
+                        "core",
+                    ),
+                    prelude: true,
+                    sysroot: true,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(0),
+                    name: CrateName(
+                        "alloc",
+                    ),
+                    prelude: false,
+                    sysroot: true,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(6),
+                    name: CrateName(
+                        "std",
+                    ),
+                    prelude: true,
+                    sysroot: true,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(8),
+                    name: CrateName(
+                        "test",
+                    ),
+                    prelude: false,
+                    sysroot: true,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(4),
+                    name: CrateName(
+                        "proc_macro",
+                    ),
+                    prelude: false,
+                    sysroot: true,
+                },
+            ],
+            origin: Local {
+                repo: None,
+                name: Some(
                     "hello_world",
                 ),
-                canonical_name: "hello_world",
             },
-        ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "hello_world",
+                    ),
+                    canonical_name: "hello_world",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "group1_cfg=some_config",
@@ -434,75 +589,85 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(1),
-                name: CrateName(
-                    "core",
-                ),
-                prelude: true,
-                sysroot: true,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(0),
-                name: CrateName(
-                    "alloc",
-                ),
-                prelude: false,
-                sysroot: true,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(6),
-                name: CrateName(
-                    "std",
-                ),
-                prelude: true,
-                sysroot: true,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(8),
-                name: CrateName(
-                    "test",
-                ),
-                prelude: false,
-                sysroot: true,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(4),
-                name: CrateName(
-                    "proc_macro",
-                ),
-                prelude: false,
-                sysroot: true,
-            },
-        ],
-        origin: Local {
-            repo: None,
-            name: Some(
-                "hello_world",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
             ),
+            toolchain: None,
         },
-        is_proc_macro: false,
-        proc_macro_cwd: None,
     },
-    11: CrateData {
-        root_file_id: FileId(
-            11,
-        ),
-        edition: Edition2018,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
+    11: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                11,
+            ),
+            edition: Edition2018,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(1),
+                    name: CrateName(
+                        "core",
+                    ),
+                    prelude: true,
+                    sysroot: true,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(0),
+                    name: CrateName(
+                        "alloc",
+                    ),
+                    prelude: false,
+                    sysroot: true,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(6),
+                    name: CrateName(
+                        "std",
+                    ),
+                    prelude: true,
+                    sysroot: true,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(8),
+                    name: CrateName(
+                        "test",
+                    ),
+                    prelude: false,
+                    sysroot: true,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(4),
+                    name: CrateName(
+                        "proc_macro",
+                    ),
+                    prelude: false,
+                    sysroot: true,
+                },
+            ],
+            origin: Local {
+                repo: None,
+                name: Some(
                     "other_crate",
                 ),
-                canonical_name: "other_crate",
             },
-        ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "other_crate",
+                    ),
+                    canonical_name: "other_crate",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "group2_cfg=fourth_config",
@@ -513,59 +678,14 @@
                 "unrelated_cfg",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(1),
-                name: CrateName(
-                    "core",
-                ),
-                prelude: true,
-                sysroot: true,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(0),
-                name: CrateName(
-                    "alloc",
-                ),
-                prelude: false,
-                sysroot: true,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(6),
-                name: CrateName(
-                    "std",
-                ),
-                prelude: true,
-                sysroot: true,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(8),
-                name: CrateName(
-                    "test",
-                ),
-                prelude: false,
-                sysroot: true,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(4),
-                name: CrateName(
-                    "proc_macro",
-                ),
-                prelude: false,
-                sysroot: true,
-            },
-        ],
-        origin: Local {
-            repo: None,
-            name: Some(
-                "other_crate",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
             ),
+            toolchain: None,
         },
-        is_proc_macro: false,
-        proc_macro_cwd: None,
     },
 }
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
index 4c8e66e8e96..053e9473cf2 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
@@ -1,18 +1,38 @@
 {
-    0: CrateData {
-        root_file_id: FileId(
-            1,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "alloc",
-                ),
-                canonical_name: "alloc",
-            },
-        ),
+    0: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                1,
+            ),
+            edition: Edition2021,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(1),
+                    name: CrateName(
+                        "core",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Lang(
+                Alloc,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "alloc",
+                    ),
+                    canonical_name: "alloc",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -20,40 +40,41 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(1),
-                name: CrateName(
-                    "core",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Lang(
-            Alloc,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    1: CrateData {
-        root_file_id: FileId(
-            2,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "core",
-                ),
-                canonical_name: "core",
-            },
-        ),
+    1: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                2,
+            ),
+            edition: Edition2021,
+            dependencies: [],
+            origin: Lang(
+                Core,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "core",
+                    ),
+                    canonical_name: "core",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -61,31 +82,41 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [],
-        origin: Lang(
-            Core,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    2: CrateData {
-        root_file_id: FileId(
-            3,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "panic_abort",
-                ),
-                canonical_name: "panic_abort",
-            },
-        ),
+    2: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                3,
+            ),
+            edition: Edition2021,
+            dependencies: [],
+            origin: Lang(
+                Other,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "panic_abort",
+                    ),
+                    canonical_name: "panic_abort",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -93,31 +124,41 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [],
-        origin: Lang(
-            Other,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    3: CrateData {
-        root_file_id: FileId(
-            4,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "panic_unwind",
-                ),
-                canonical_name: "panic_unwind",
-            },
-        ),
+    3: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                4,
+            ),
+            edition: Edition2021,
+            dependencies: [],
+            origin: Lang(
+                Other,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "panic_unwind",
+                    ),
+                    canonical_name: "panic_unwind",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -125,31 +166,58 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [],
-        origin: Lang(
-            Other,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    4: CrateData {
-        root_file_id: FileId(
-            5,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "proc_macro",
-                ),
-                canonical_name: "proc_macro",
-            },
-        ),
+    4: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                5,
+            ),
+            edition: Edition2021,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(6),
+                    name: CrateName(
+                        "std",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(1),
+                    name: CrateName(
+                        "core",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Lang(
+                ProcMacro,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "proc_macro",
+                    ),
+                    canonical_name: "proc_macro",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -157,48 +225,41 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(6),
-                name: CrateName(
-                    "std",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(1),
-                name: CrateName(
-                    "core",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Lang(
-            ProcMacro,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    5: CrateData {
-        root_file_id: FileId(
-            6,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "profiler_builtins",
-                ),
-                canonical_name: "profiler_builtins",
-            },
-        ),
+    5: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                6,
+            ),
+            edition: Edition2021,
+            dependencies: [],
+            origin: Lang(
+                Other,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "profiler_builtins",
+                    ),
+                    canonical_name: "profiler_builtins",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -206,31 +267,106 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [],
-        origin: Lang(
-            Other,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    6: CrateData {
-        root_file_id: FileId(
-            7,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "std",
-                ),
-                canonical_name: "std",
-            },
-        ),
+    6: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                7,
+            ),
+            edition: Edition2021,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(0),
+                    name: CrateName(
+                        "alloc",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(3),
+                    name: CrateName(
+                        "panic_unwind",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(2),
+                    name: CrateName(
+                        "panic_abort",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(1),
+                    name: CrateName(
+                        "core",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(5),
+                    name: CrateName(
+                        "profiler_builtins",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(9),
+                    name: CrateName(
+                        "unwind",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(7),
+                    name: CrateName(
+                        "std_detect",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(8),
+                    name: CrateName(
+                        "test",
+                    ),
+                    prelude: true,
+                    sysroot: false,
+                },
+            ],
+            origin: Lang(
+                Std,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "std",
+                    ),
+                    canonical_name: "std",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -238,96 +374,41 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(0),
-                name: CrateName(
-                    "alloc",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(3),
-                name: CrateName(
-                    "panic_unwind",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(2),
-                name: CrateName(
-                    "panic_abort",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(1),
-                name: CrateName(
-                    "core",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(5),
-                name: CrateName(
-                    "profiler_builtins",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(9),
-                name: CrateName(
-                    "unwind",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(7),
-                name: CrateName(
-                    "std_detect",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(8),
-                name: CrateName(
-                    "test",
-                ),
-                prelude: true,
-                sysroot: false,
-            },
-        ],
-        origin: Lang(
-            Std,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    7: CrateData {
-        root_file_id: FileId(
-            8,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "std_detect",
-                ),
-                canonical_name: "std_detect",
-            },
-        ),
+    7: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                8,
+            ),
+            edition: Edition2021,
+            dependencies: [],
+            origin: Lang(
+                Other,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "std_detect",
+                    ),
+                    canonical_name: "std_detect",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -335,31 +416,41 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [],
-        origin: Lang(
-            Other,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    8: CrateData {
-        root_file_id: FileId(
-            9,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "test",
-                ),
-                canonical_name: "test",
-            },
-        ),
+    8: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                9,
+            ),
+            edition: Edition2021,
+            dependencies: [],
+            origin: Lang(
+                Test,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "test",
+                    ),
+                    canonical_name: "test",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -367,31 +458,41 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [],
-        origin: Lang(
-            Test,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    9: CrateData {
-        root_file_id: FileId(
-            10,
-        ),
-        edition: Edition2021,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
-                    "unwind",
-                ),
-                canonical_name: "unwind",
-            },
-        ),
+    9: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                10,
+            ),
+            edition: Edition2021,
+            dependencies: [],
+            origin: Lang(
+                Other,
+            ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "unwind",
+                    ),
+                    canonical_name: "unwind",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "debug_assertions",
@@ -399,31 +500,85 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [],
-        origin: Lang(
-            Other,
-        ),
-        is_proc_macro: false,
-        proc_macro_cwd: None,
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
+            ),
+            toolchain: None,
+        },
     },
-    10: CrateData {
-        root_file_id: FileId(
-            11,
-        ),
-        edition: Edition2018,
-        version: None,
-        display_name: Some(
-            CrateDisplayName {
-                crate_name: CrateName(
+    10: CrateBuilder {
+        basic: CrateData {
+            root_file_id: FileId(
+                11,
+            ),
+            edition: Edition2018,
+            dependencies: [
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(1),
+                    name: CrateName(
+                        "core",
+                    ),
+                    prelude: true,
+                    sysroot: true,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(0),
+                    name: CrateName(
+                        "alloc",
+                    ),
+                    prelude: false,
+                    sysroot: true,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(6),
+                    name: CrateName(
+                        "std",
+                    ),
+                    prelude: true,
+                    sysroot: true,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(8),
+                    name: CrateName(
+                        "test",
+                    ),
+                    prelude: false,
+                    sysroot: true,
+                },
+                Dependency {
+                    crate_id: Idx::<CrateBuilder>(4),
+                    name: CrateName(
+                        "proc_macro",
+                    ),
+                    prelude: false,
+                    sysroot: true,
+                },
+            ],
+            origin: Local {
+                repo: None,
+                name: Some(
                     "hello_world",
                 ),
-                canonical_name: "hello_world",
             },
-        ),
+            is_proc_macro: false,
+            proc_macro_cwd: None,
+        },
+        extra: ExtraCrateData {
+            version: None,
+            display_name: Some(
+                CrateDisplayName {
+                    crate_name: CrateName(
+                        "hello_world",
+                    ),
+                    canonical_name: "hello_world",
+                },
+            ),
+            potential_cfg_options: None,
+        },
         cfg_options: CfgOptions(
             [
                 "rust_analyzer",
@@ -431,59 +586,14 @@
                 "true",
             ],
         ),
-        potential_cfg_options: None,
         env: Env {
             entries: {},
         },
-        dependencies: [
-            Dependency {
-                crate_id: Idx::<CrateData>(1),
-                name: CrateName(
-                    "core",
-                ),
-                prelude: true,
-                sysroot: true,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(0),
-                name: CrateName(
-                    "alloc",
-                ),
-                prelude: false,
-                sysroot: true,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(6),
-                name: CrateName(
-                    "std",
-                ),
-                prelude: true,
-                sysroot: true,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(8),
-                name: CrateName(
-                    "test",
-                ),
-                prelude: false,
-                sysroot: true,
-            },
-            Dependency {
-                crate_id: Idx::<CrateData>(4),
-                name: CrateName(
-                    "proc_macro",
-                ),
-                prelude: false,
-                sysroot: true,
-            },
-        ],
-        origin: Local {
-            repo: None,
-            name: Some(
-                "hello_world",
+        ws_data: CrateWorkspaceData {
+            data_layout: Err(
+                "test has no data layout",
             ),
+            toolchain: None,
         },
-        is_proc_macro: false,
-        proc_macro_cwd: None,
     },
 }
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index f114a4454e0..a6aa0376b0c 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -8,7 +8,7 @@ use std::{ops::Not as _, time::Instant};
 use crossbeam_channel::{unbounded, Receiver, Sender};
 use hir::ChangeWithProcMacros;
 use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId};
-use ide_db::base_db::{CrateId, ProcMacroPaths, SourceDatabase};
+use ide_db::base_db::{Crate, ProcMacroPaths, SourceDatabase};
 use itertools::Itertools;
 use load_cargo::SourceRootConfig;
 use lsp_types::{SemanticTokens, Url};
@@ -158,7 +158,7 @@ pub(crate) struct GlobalState {
     // op queues
     pub(crate) fetch_workspaces_queue: OpQueue<FetchWorkspaceRequest, FetchWorkspaceResponse>,
     pub(crate) fetch_build_data_queue: OpQueue<(), FetchBuildDataResponse>,
-    pub(crate) fetch_proc_macros_queue: OpQueue<Vec<ProcMacroPaths>, bool>,
+    pub(crate) fetch_proc_macros_queue: OpQueue<(ChangeWithProcMacros, Vec<ProcMacroPaths>), bool>,
     pub(crate) prime_caches_queue: OpQueue,
     pub(crate) discover_workspace_queue: OpQueue,
 
@@ -714,7 +714,7 @@ impl GlobalStateSnapshot {
         self.vfs_read().file_path(file_id).clone()
     }
 
-    pub(crate) fn target_spec_for_crate(&self, crate_id: CrateId) -> Option<TargetSpec> {
+    pub(crate) fn target_spec_for_crate(&self, crate_id: Crate) -> Option<TargetSpec> {
         let file_id = self.analysis.crate_root(crate_id).ok()?;
         let path = self.vfs_read().file_path(file_id).clone();
         let path = path.as_path()?;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index b47a126424a..7244fbc4aaa 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -10,7 +10,7 @@ use std::{
 
 use always_assert::always;
 use crossbeam_channel::{select, Receiver};
-use ide_db::base_db::{RootQueryDb, SourceDatabase, VfsPath};
+use ide_db::base_db::{SourceDatabase, VfsPath};
 use lsp_server::{Connection, Notification, Request};
 use lsp_types::{notification::Notification as _, TextDocumentIdentifier};
 use stdx::thread::ThreadIntent;
@@ -504,8 +504,10 @@ impl GlobalState {
         if !self.fetch_workspaces_queue.op_in_progress() {
             if let Some((cause, ())) = self.fetch_build_data_queue.should_start_op() {
                 self.fetch_build_data(cause);
-            } else if let Some((cause, paths)) = self.fetch_proc_macros_queue.should_start_op() {
-                self.fetch_proc_macros(cause, paths);
+            } else if let Some((cause, (change, paths))) =
+                self.fetch_proc_macros_queue.should_start_op()
+            {
+                self.fetch_proc_macros(cause, change, paths);
             }
         }
 
@@ -804,9 +806,10 @@ impl GlobalState {
                 let (state, msg) = match progress {
                     ProcMacroProgress::Begin => (Some(Progress::Begin), None),
                     ProcMacroProgress::Report(msg) => (Some(Progress::Report), Some(msg)),
-                    ProcMacroProgress::End(proc_macro_load_result) => {
+                    ProcMacroProgress::End(change) => {
                         self.fetch_proc_macros_queue.op_completed(true);
-                        self.set_proc_macros(proc_macro_load_result);
+                        self.analysis_host.apply_change(change);
+                        self.finish_loading_crate_graph();
                         (Some(Progress::End), None)
                     }
                 };
@@ -909,16 +912,15 @@ impl GlobalState {
                 });
             }
             QueuedTask::CheckProcMacroSources(modified_rust_files) => {
-                let crate_graph = self.analysis_host.raw_database().crate_graph();
                 let analysis = AssertUnwindSafe(self.snapshot().analysis);
                 self.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, {
                     move |sender| {
                         if modified_rust_files.into_iter().any(|file_id| {
                             // FIXME: Check whether these files could be build script related
                             match analysis.crates_for(file_id) {
-                                Ok(crates) => {
-                                    crates.iter().any(|&krate| crate_graph[krate].is_proc_macro)
-                                }
+                                Ok(crates) => crates.iter().any(|&krate| {
+                                    analysis.is_proc_macro_crate(krate).is_ok_and(|it| it)
+                                }),
                                 _ => false,
                             }
                         }) {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index 1a60fa06c9e..d6a99fbe55a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -15,10 +15,9 @@
 // FIXME: This is a mess that needs some untangling work
 use std::{iter, mem};
 
-use hir::{db::DefDatabase, ChangeWithProcMacros, ProcMacros, ProcMacrosBuilder};
-use ide::CrateId;
+use hir::{db::DefDatabase, ChangeWithProcMacros, ProcMacrosBuilder};
 use ide_db::{
-    base_db::{salsa::Durability, CrateGraph, CrateWorkspaceData, ProcMacroPaths},
+    base_db::{salsa::Durability, CrateGraphBuilder, ProcMacroPaths, RootQueryDb},
     FxHashMap,
 };
 use itertools::Itertools;
@@ -60,7 +59,7 @@ pub(crate) enum BuildDataProgress {
 pub(crate) enum ProcMacroProgress {
     Begin,
     Report(String),
-    End(ProcMacros),
+    End(ChangeWithProcMacros),
 }
 
 impl GlobalState {
@@ -387,7 +386,12 @@ impl GlobalState {
         });
     }
 
-    pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec<ProcMacroPaths>) {
+    pub(crate) fn fetch_proc_macros(
+        &mut self,
+        cause: Cause,
+        mut change: ChangeWithProcMacros,
+        paths: Vec<ProcMacroPaths>,
+    ) {
         info!(%cause, "will load proc macros");
         let ignored_proc_macros = self.config.ignored_proc_macros(None).clone();
         let proc_macro_clients = self.proc_macro_clients.clone();
@@ -440,16 +444,11 @@ impl GlobalState {
                     .for_each(|(krate, res)| builder.insert(krate, res));
             }
 
-            sender.send(Task::LoadProcMacros(ProcMacroProgress::End(builder.build()))).unwrap();
+            change.set_proc_macros(builder);
+            sender.send(Task::LoadProcMacros(ProcMacroProgress::End(change))).unwrap();
         });
     }
 
-    pub(crate) fn set_proc_macros(&mut self, proc_macros: ProcMacros) {
-        let mut change = ChangeWithProcMacros::new();
-        change.set_proc_macros(proc_macros);
-        self.analysis_host.apply_change(change);
-    }
-
     pub(crate) fn switch_workspaces(&mut self, cause: Cause) {
         let _p = tracing::info_span!("GlobalState::switch_workspaces").entered();
         tracing::info!(%cause, "will switch workspaces");
@@ -528,6 +527,14 @@ impl GlobalState {
             if self.config.run_build_scripts(None) {
                 self.build_deps_changed = false;
                 self.fetch_build_data_queue.request_op("workspace updated".to_owned(), ());
+
+                let initial_build = self.analysis_host.raw_database().all_crates().is_empty();
+                if !initial_build {
+                    // `switch_workspaces()` will be called again when build scripts already run, which should
+                    // take a short time. If we update the workspace now we will invalidate proc macros and cfgs,
+                    // and then when build scripts complete we will invalidate them again.
+                    return;
+                }
             }
         }
 
@@ -711,7 +718,7 @@ impl GlobalState {
             })
             .collect();
 
-        let (crate_graph, proc_macro_paths, ws_data) = {
+        let (crate_graph, proc_macro_paths) = {
             // Create crate graph from all the workspaces
             let vfs = &self.vfs.read().0;
             let load = |path: &AbsPath| {
@@ -725,24 +732,35 @@ impl GlobalState {
             ws_to_crate_graph(&self.workspaces, self.config.extra_env(None), load)
         };
         let mut change = ChangeWithProcMacros::new();
-        if self.config.expand_proc_macros() {
-            change.set_proc_macros(
-                crate_graph
-                    .iter()
-                    .map(|id| (id, Err(("proc-macro has not been built yet".to_owned(), true))))
-                    .collect(),
-            );
-            self.fetch_proc_macros_queue.request_op(cause, proc_macro_paths);
-        } else {
-            change.set_proc_macros(
-                crate_graph
-                    .iter()
-                    .map(|id| (id, Err(("proc-macro expansion is disabled".to_owned(), false))))
-                    .collect(),
-            );
+        let initial_build = self.analysis_host.raw_database().all_crates().is_empty();
+        if initial_build || !self.config.expand_proc_macros() {
+            if self.config.expand_proc_macros() {
+                change.set_proc_macros(
+                    crate_graph
+                        .iter()
+                        .map(|id| (id, Err(("proc-macro has not been built yet".to_owned(), true))))
+                        .collect(),
+                );
+            } else {
+                change.set_proc_macros(
+                    crate_graph
+                        .iter()
+                        .map(|id| (id, Err(("proc-macro expansion is disabled".to_owned(), false))))
+                        .collect(),
+                );
+            }
+
+            change.set_crate_graph(crate_graph);
+            self.analysis_host.apply_change(change);
+
+            self.finish_loading_crate_graph();
+            return;
         }
-        change.set_crate_graph(crate_graph, ws_data);
-        self.analysis_host.apply_change(change);
+        change.set_crate_graph(crate_graph);
+        self.fetch_proc_macros_queue.request_op(cause, (change, proc_macro_paths));
+    }
+
+    pub(crate) fn finish_loading_crate_graph(&mut self) {
         self.report_progress(
             "Building CrateGraph",
             crate::lsp::utils::Progress::End,
@@ -883,26 +901,19 @@ pub fn ws_to_crate_graph(
     workspaces: &[ProjectWorkspace],
     extra_env: &FxHashMap<String, String>,
     mut load: impl FnMut(&AbsPath) -> Option<vfs::FileId>,
-) -> (CrateGraph, Vec<ProcMacroPaths>, FxHashMap<CrateId, Arc<CrateWorkspaceData>>) {
-    let mut crate_graph = CrateGraph::default();
+) -> (CrateGraphBuilder, Vec<ProcMacroPaths>) {
+    let mut crate_graph = CrateGraphBuilder::default();
     let mut proc_macro_paths = Vec::default();
-    let mut ws_data = FxHashMap::default();
     for ws in workspaces {
         let (other, mut crate_proc_macros) = ws.to_crate_graph(&mut load, extra_env);
-        let ProjectWorkspace { toolchain, target_layout, .. } = ws;
-
-        let mapping = crate_graph.extend(other, &mut crate_proc_macros);
-        // Populate the side tables for the newly merged crates
-        ws_data.extend(mapping.values().copied().zip(iter::repeat(Arc::new(CrateWorkspaceData {
-            toolchain: toolchain.clone(),
-            data_layout: target_layout.clone(),
-        }))));
+
+        crate_graph.extend(other, &mut crate_proc_macros);
         proc_macro_paths.push(crate_proc_macros);
     }
 
     crate_graph.shrink_to_fit();
     proc_macro_paths.shrink_to_fit();
-    (crate_graph, proc_macro_paths, ws_data)
+    (crate_graph, proc_macro_paths)
 }
 
 pub(crate) fn should_refresh_for_change(
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs
index b28567fe09b..abb70ebe36f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs
@@ -4,7 +4,7 @@ use std::mem;
 
 use cfg::{CfgAtom, CfgExpr};
 use hir::sym;
-use ide::{Cancellable, CrateId, FileId, RunnableKind, TestId};
+use ide::{Cancellable, Crate, FileId, RunnableKind, TestId};
 use project_model::project_json::Runnable;
 use project_model::{CargoFeatures, ManifestPath, TargetKind};
 use rustc_hash::FxHashSet;
@@ -54,7 +54,7 @@ pub(crate) struct CargoTargetSpec {
     pub(crate) package: String,
     pub(crate) target: String,
     pub(crate) target_kind: TargetKind,
-    pub(crate) crate_id: CrateId,
+    pub(crate) crate_id: Crate,
     pub(crate) required_features: Vec<String>,
     pub(crate) features: FxHashSet<String>,
     pub(crate) sysroot_root: Option<vfs::AbsPathBuf>,
diff --git a/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml b/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml
index 95f4cb9d67e..2547a02a291 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml
@@ -18,6 +18,7 @@ rustc-hash.workspace = true
 span.workspace = true
 stdx.workspace = true
 intern.workspace = true
+triomphe.workspace = true
 
 [lints]
 workspace = true
diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
index 4b5c2ff4cf7..51d6179a62b 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
@@ -1,10 +1,10 @@
 //! A set of high-level utility fixture methods to use in tests.
-use std::{iter, mem, str::FromStr, sync};
+use std::{mem, str::FromStr, sync};
 
 use base_db::{
-    CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, CrateWorkspaceData, Dependency,
-    Env, FileChange, FileSet, LangCrateOrigin, RootQueryDb, SourceDatabase, SourceRoot, Version,
-    VfsPath,
+    Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData,
+    DependencyBuilder, Env, FileChange, FileSet, LangCrateOrigin, SourceDatabase, SourceRoot,
+    Version, VfsPath,
 };
 use cfg::CfgOptions;
 use hir_expand::{
@@ -26,6 +26,7 @@ use test_utils::{
     extract_range_or_offset, Fixture, FixtureWithProjectMeta, RangeOrOffset, CURSOR_MARKER,
     ESCAPED_CURSOR_MARKER,
 };
+use triomphe::Arc;
 
 pub const WORKSPACE: base_db::SourceRootId = base_db::SourceRootId(0);
 
@@ -101,14 +102,8 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static {
         (db, file_id, range_or_offset)
     }
 
-    fn test_crate(&self) -> CrateId {
-        let crate_graph = RootQueryDb::crate_graph(self);
-        let mut it = crate_graph.iter();
-        let mut res = it.next().unwrap();
-        while crate_graph[res].origin.is_lang() {
-            res = it.next().unwrap();
-        }
-        res
+    fn test_crate(&self) -> Crate {
+        self.all_crates().iter().copied().find(|&krate| !krate.data(self).origin.is_lang()).unwrap()
     }
 }
 
@@ -146,7 +141,7 @@ impl ChangeFixture {
         let mut source_change = FileChange::new();
 
         let mut files = Vec::new();
-        let mut crate_graph = CrateGraph::default();
+        let mut crate_graph = CrateGraphBuilder::default();
         let mut crates = FxHashMap::default();
         let mut crate_deps = Vec::new();
         let mut default_crate_root: Option<FileId> = None;
@@ -163,6 +158,9 @@ impl ChangeFixture {
 
         let mut file_position = None;
 
+        let crate_ws_data =
+            Arc::new(CrateWorkspaceData { data_layout: target_data_layout, toolchain });
+
         for entry in fixture {
             let mut range_or_offset = None;
             let text = if entry.text.contains(CURSOR_MARKER) {
@@ -210,11 +208,12 @@ impl ChangeFixture {
                     Some(crate_name.clone().into()),
                     version,
                     From::from(meta.cfg.clone()),
-                    Some(From::from(meta.cfg)),
+                    Some(meta.cfg),
                     meta.env,
                     origin,
                     false,
                     None,
+                    crate_ws_data.clone(),
                 );
                 let prev = crates.insert(crate_name.clone(), crate_id);
                 assert!(prev.is_none(), "multiple crates with same name: {crate_name}");
@@ -249,19 +248,23 @@ impl ChangeFixture {
                 Some(CrateName::new("ra_test_fixture").unwrap().into()),
                 None,
                 From::from(default_cfg.clone()),
-                Some(From::from(default_cfg)),
+                Some(default_cfg),
                 default_env,
                 CrateOrigin::Local { repo: None, name: None },
                 false,
                 None,
+                crate_ws_data.clone(),
             );
         } else {
             for (from, to, prelude) in crate_deps {
                 let from_id = crates[&from];
                 let to_id = crates[&to];
-                let sysroot = crate_graph[to_id].origin.is_lang();
+                let sysroot = crate_graph[to_id].basic.origin.is_lang();
                 crate_graph
-                    .add_dep(from_id, Dependency::with_prelude(to.clone(), to_id, prelude, sysroot))
+                    .add_dep(
+                        from_id,
+                        DependencyBuilder::with_prelude(to.clone(), to_id, prelude, sysroot),
+                    )
                     .unwrap();
             }
         }
@@ -276,7 +279,7 @@ impl ChangeFixture {
 
             source_change.change_file(core_file, Some(mini_core.source_code()));
 
-            let all_crates = crate_graph.crates_in_topological_order();
+            let all_crates = crate_graph.iter().collect::<Vec<_>>();
 
             let core_crate = crate_graph.add_crate_root(
                 core_file,
@@ -292,13 +295,14 @@ impl ChangeFixture {
                 CrateOrigin::Lang(LangCrateOrigin::Core),
                 false,
                 None,
+                crate_ws_data.clone(),
             );
 
             for krate in all_crates {
                 crate_graph
                     .add_dep(
                         krate,
-                        Dependency::with_prelude(
+                        DependencyBuilder::with_prelude(
                             CrateName::new("core").unwrap(),
                             core_crate,
                             true,
@@ -324,7 +328,7 @@ impl ChangeFixture {
 
             source_change.change_file(proc_lib_file, Some(source));
 
-            let all_crates = crate_graph.crates_in_topological_order();
+            let all_crates = crate_graph.iter().collect::<Vec<_>>();
 
             let proc_macros_crate = crate_graph.add_crate_root(
                 proc_lib_file,
@@ -340,6 +344,7 @@ impl ChangeFixture {
                 CrateOrigin::Local { repo: None, name: None },
                 true,
                 None,
+                crate_ws_data,
             );
             proc_macros.insert(proc_macros_crate, Ok(proc_macro));
 
@@ -347,7 +352,10 @@ impl ChangeFixture {
                 crate_graph
                     .add_dep(
                         krate,
-                        Dependency::new(CrateName::new("proc_macros").unwrap(), proc_macros_crate),
+                        DependencyBuilder::new(
+                            CrateName::new("proc_macros").unwrap(),
+                            proc_macros_crate,
+                        ),
                     )
                     .unwrap();
             }
@@ -359,19 +367,9 @@ impl ChangeFixture {
         };
         roots.push(root);
 
-        let mut change =
-            ChangeWithProcMacros { source_change, proc_macros: Some(proc_macros.build()) };
+        let mut change = ChangeWithProcMacros { source_change, proc_macros: Some(proc_macros) };
 
         change.source_change.set_roots(roots);
-        change.source_change.set_ws_data(
-            crate_graph
-                .iter()
-                .zip(iter::repeat(From::from(CrateWorkspaceData {
-                    data_layout: target_data_layout,
-                    toolchain,
-                })))
-                .collect(),
-        );
         change.source_change.set_crate_graph(crate_graph);
 
         ChangeFixture { file_position, files, change }
@@ -654,6 +652,10 @@ impl ProcMacroExpander for IdentityProcMacroExpander {
     ) -> Result<TopSubtree, ProcMacroExpansionError> {
         Ok(subtree.clone())
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.as_any().type_id() == std::any::TypeId::of::<Self>()
+    }
 }
 
 // Expands to a macro_rules! macro, for issue #18089.
@@ -685,6 +687,10 @@ impl ProcMacroExpander for Issue18089ProcMacroExpander {
             #subtree
         })
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.as_any().type_id() == std::any::TypeId::of::<Self>()
+    }
 }
 
 // Pastes the attribute input as its output
@@ -705,6 +711,10 @@ impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander {
             .cloned()
             .ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into()))
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.as_any().type_id() == std::any::TypeId::of::<Self>()
+    }
 }
 
 #[derive(Debug)]
@@ -736,6 +746,10 @@ impl ProcMacroExpander for Issue18840ProcMacroExpander {
         top_subtree_delimiter_mut.close = def_site;
         Ok(result)
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.as_any().type_id() == std::any::TypeId::of::<Self>()
+    }
 }
 
 #[derive(Debug)]
@@ -767,6 +781,10 @@ impl ProcMacroExpander for MirrorProcMacroExpander {
         traverse(&mut builder, input.iter());
         Ok(builder.build())
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.as_any().type_id() == std::any::TypeId::of::<Self>()
+    }
 }
 
 // Replaces every literal with an empty string literal and every identifier with its first letter,
@@ -807,6 +825,10 @@ impl ProcMacroExpander for ShortenProcMacroExpander {
             }
         }
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.as_any().type_id() == std::any::TypeId::of::<Self>()
+    }
 }
 
 // Reads ident type within string quotes, for issue #17479.
@@ -832,6 +854,10 @@ impl ProcMacroExpander for Issue17479ProcMacroExpander {
             #symbol()
         })
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.as_any().type_id() == std::any::TypeId::of::<Self>()
+    }
 }
 
 // Reads ident type within string quotes, for issue #17479.
@@ -883,6 +909,10 @@ impl ProcMacroExpander for Issue18898ProcMacroExpander {
             }
         })
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.as_any().type_id() == std::any::TypeId::of::<Self>()
+    }
 }
 
 // Reads ident type within string quotes, for issue #17479.
@@ -910,4 +940,8 @@ impl ProcMacroExpander for DisallowCfgProcMacroExpander {
         }
         Ok(subtree.clone())
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.as_any().type_id() == std::any::TypeId::of::<Self>()
+    }
 }