Skip to content

Commit a440fcb

Browse files
authored
Merge pull request #18806 from LHolten/deduplicate-crate-graph
fix: Deduplicate crate graph
2 parents fd2cb64 + 8115394 commit a440fcb

File tree

7 files changed

+19299
-35
lines changed

7 files changed

+19299
-35
lines changed

crates/base-db/src/input.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -490,21 +490,25 @@ impl CrateGraph {
490490
}
491491
}
492492

493-
pub fn sort_deps(&mut self) {
494-
self.arena
495-
.iter_mut()
496-
.for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id));
497-
}
498-
499493
/// Extends this crate graph by adding a complete second crate
500494
/// graph and adjust the ids in the [`ProcMacroPaths`] accordingly.
501495
///
496+
/// This will deduplicate the crates of the graph where possible.
497+
/// Furthermore dependencies are sorted by crate id to make deduplication easier.
498+
///
502499
/// Returns a map mapping `other`'s IDs to the new IDs in `self`.
503500
pub fn extend(
504501
&mut self,
505502
mut other: CrateGraph,
506503
proc_macros: &mut ProcMacroPaths,
507504
) -> FxHashMap<CrateId, CrateId> {
505+
// Sorting here is a bit pointless because the input is likely already sorted.
506+
// However, the overhead is small and it makes the `extend` method harder to misuse.
507+
self.arena
508+
.iter_mut()
509+
.for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id));
510+
511+
let m = self.len();
508512
let topo = other.crates_in_topological_order();
509513
let mut id_map: FxHashMap<CrateId, CrateId> = FxHashMap::default();
510514
for topo in topo {
@@ -513,7 +517,8 @@ impl CrateGraph {
513517
crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]);
514518
crate_data.dependencies.sort_by_key(|dep| dep.crate_id);
515519

516-
let new_id = self.arena.alloc(crate_data.clone());
520+
let find = self.arena.iter().take(m).find_map(|(k, v)| (v == crate_data).then_some(k));
521+
let new_id = find.unwrap_or_else(|| self.arena.alloc(crate_data.clone()));
517522
id_map.insert(topo, new_id);
518523
}
519524

crates/project-model/src/env.rs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use paths::Utf8Path;
44
use rustc_hash::FxHashMap;
55
use toolchain::Tool;
66

7-
use crate::{utf8_stdout, CargoWorkspace, ManifestPath, PackageData, Sysroot, TargetKind};
7+
use crate::{utf8_stdout, ManifestPath, PackageData, Sysroot, TargetKind};
88

99
/// Recreates the compile-time environment variables that Cargo sets.
1010
///
@@ -51,23 +51,13 @@ pub(crate) fn inject_cargo_env(env: &mut Env) {
5151
env.set("CARGO", Tool::Cargo.path().to_string());
5252
}
5353

54-
pub(crate) fn inject_rustc_tool_env(
55-
env: &mut Env,
56-
cargo: &CargoWorkspace,
57-
cargo_name: &str,
58-
kind: TargetKind,
59-
) {
54+
pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: TargetKind) {
6055
_ = kind;
6156
// FIXME
6257
// if kind.is_executable() {
6358
// env.set("CARGO_BIN_NAME", cargo_name);
6459
// }
6560
env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_"));
66-
// NOTE: Technically we should set this for all crates, but that will worsen the deduplication
67-
// logic so for now just keeping it proc-macros ought to be fine.
68-
if kind.is_proc_macro() {
69-
env.set("CARGO_RUSTC_CURRENT_DIR", cargo.manifest_path().parent().to_string());
70-
}
7161
}
7262

7363
pub(crate) fn cargo_config_env(

crates/project-model/src/tests.rs

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,38 @@ use crate::{
1818
};
1919

2020
fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) {
21-
load_cargo_with_overrides(file, CfgOverrides::default())
21+
let project_workspace = load_workspace_from_metadata(file);
22+
to_crate_graph(project_workspace, &mut Default::default())
2223
}
2324

2425
fn load_cargo_with_overrides(
2526
file: &str,
2627
cfg_overrides: CfgOverrides,
2728
) -> (CrateGraph, ProcMacroPaths) {
29+
let project_workspace =
30+
ProjectWorkspace { cfg_overrides, ..load_workspace_from_metadata(file) };
31+
to_crate_graph(project_workspace, &mut Default::default())
32+
}
33+
34+
fn load_workspace_from_metadata(file: &str) -> ProjectWorkspace {
2835
let meta: Metadata = get_test_json_file(file);
2936
let manifest_path =
3037
ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap();
3138
let cargo_workspace = CargoWorkspace::new(meta, manifest_path, Default::default());
32-
let project_workspace = ProjectWorkspace {
39+
ProjectWorkspace {
3340
kind: ProjectWorkspaceKind::Cargo {
3441
cargo: cargo_workspace,
3542
build_scripts: WorkspaceBuildScripts::default(),
3643
rustc: Err(None),
3744
error: None,
3845
set_test: true,
3946
},
40-
cfg_overrides,
47+
cfg_overrides: Default::default(),
4148
sysroot: Sysroot::empty(),
4249
rustc_cfg: Vec::new(),
4350
toolchain: None,
4451
target_layout: Err("target_data_layout not loaded".into()),
45-
};
46-
to_crate_graph(project_workspace)
52+
}
4753
}
4854

4955
fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) {
@@ -58,7 +64,7 @@ fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) {
5864
target_layout: Err(Arc::from("test has no data layout")),
5965
cfg_overrides: Default::default(),
6066
};
61-
to_crate_graph(project_workspace)
67+
to_crate_graph(project_workspace, &mut Default::default())
6268
}
6369

6470
fn get_test_json_file<T: DeserializeOwned>(file: &str) -> T {
@@ -127,13 +133,15 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
127133
ProjectJson::new(None, base, data)
128134
}
129135

130-
fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacroPaths) {
136+
fn to_crate_graph(
137+
project_workspace: ProjectWorkspace,
138+
file_map: &mut FxHashMap<AbsPathBuf, FileId>,
139+
) -> (CrateGraph, ProcMacroPaths) {
131140
project_workspace.to_crate_graph(
132141
&mut {
133-
let mut counter = 0;
134-
move |_path| {
135-
counter += 1;
136-
Some(FileId::from_raw(counter))
142+
|path| {
143+
let len = file_map.len() + 1;
144+
Some(*file_map.entry(path.to_path_buf()).or_insert(FileId::from_raw(len as u32)))
137145
}
138146
},
139147
&Default::default(),
@@ -221,6 +229,33 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() {
221229
crate_data.dependencies.iter().find(|&dep| dep.name.deref() == "proc_macro").unwrap();
222230
}
223231

232+
#[test]
233+
fn crate_graph_dedup_identical() {
234+
let (mut crate_graph, proc_macros) = load_cargo("regex-metadata.json");
235+
236+
let (d_crate_graph, mut d_proc_macros) = (crate_graph.clone(), proc_macros.clone());
237+
238+
crate_graph.extend(d_crate_graph.clone(), &mut d_proc_macros);
239+
assert!(crate_graph.iter().eq(d_crate_graph.iter()));
240+
assert_eq!(proc_macros, d_proc_macros);
241+
}
242+
243+
#[test]
244+
fn crate_graph_dedup() {
245+
let mut file_map = Default::default();
246+
247+
let ripgrep_workspace = load_workspace_from_metadata("ripgrep-metadata.json");
248+
let (mut crate_graph, _proc_macros) = to_crate_graph(ripgrep_workspace, &mut file_map);
249+
assert_eq!(crate_graph.iter().count(), 71);
250+
251+
let regex_workspace = load_workspace_from_metadata("regex-metadata.json");
252+
let (regex_crate_graph, mut regex_proc_macros) = to_crate_graph(regex_workspace, &mut file_map);
253+
assert_eq!(regex_crate_graph.iter().count(), 50);
254+
255+
crate_graph.extend(regex_crate_graph, &mut regex_proc_macros);
256+
assert_eq!(crate_graph.iter().count(), 108);
257+
}
258+
224259
#[test]
225260
fn smoke_test_real_sysroot_cargo() {
226261
let file_map = &mut FxHashMap::<AbsPathBuf, FileId>::default();

crates/project-model/src/workspace.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,12 +1362,10 @@ fn add_target_crate_root(
13621362
let mut env = cargo.env().clone();
13631363
inject_cargo_package_env(&mut env, pkg);
13641364
inject_cargo_env(&mut env);
1365-
inject_rustc_tool_env(&mut env, cargo, cargo_name, kind);
1365+
inject_rustc_tool_env(&mut env, cargo_name, kind);
13661366

13671367
if let Some(envs) = build_data.map(|(it, _)| &it.envs) {
1368-
for (k, v) in envs {
1369-
env.set(k, v.clone());
1370-
}
1368+
env.extend_from_other(envs);
13711369
}
13721370
let crate_id = crate_graph.add_crate_root(
13731371
file_id,

crates/project-model/test_data/output/rust_project_cfg_groups.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@
479479
},
480480
11: CrateData {
481481
root_file_id: FileId(
482-
12,
482+
11,
483483
),
484484
edition: Edition2018,
485485
version: None,

0 commit comments

Comments
 (0)