Skip to content

Commit 797c2f1

Browse files
committed
Auto merge of #14659 - Veykril:dedup-crates, r=Veykril
Deduplicate crates when extending crate graphs This is quadratic in runtime per deduplication attempt, but I don't think that'll be a problem for the workload here. Don't be scared of the diff, the actual diff is +42 -22, the rest is tests and test data. Fixes #14476
2 parents e68e47f + 968850d commit 797c2f1

8 files changed

+20834
-1518
lines changed

crates/base-db/src/input.rs

+42-22
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
use std::{fmt, mem, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc};
1010

1111
use cfg::CfgOptions;
12-
use la_arena::{Arena, Idx, RawIdx};
12+
use la_arena::{Arena, Idx};
1313
use rustc_hash::{FxHashMap, FxHashSet};
1414
use syntax::SmolStr;
1515
use tt::token_id::Subtree;
@@ -534,28 +534,46 @@ impl CrateGraph {
534534
Some(crate_id)
535535
}
536536

537+
pub fn sort_deps(&mut self) {
538+
self.arena
539+
.iter_mut()
540+
.for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id));
541+
}
542+
537543
/// Extends this crate graph by adding a complete disjoint second crate
538544
/// graph and adjust the ids in the [`ProcMacroPaths`] accordingly.
539545
///
540-
/// The ids of the crates in the `other` graph are shifted by the return
541-
/// amount.
542-
pub fn extend(&mut self, other: CrateGraph, proc_macros: &mut ProcMacroPaths) -> u32 {
543-
let start = self.arena.len() as u32;
544-
self.arena.extend(other.arena.into_iter().map(|(_, mut data)| {
545-
for dep in &mut data.dependencies {
546-
dep.crate_id =
547-
CrateId::from_raw(RawIdx::from(u32::from(dep.crate_id.into_raw()) + start));
546+
/// This will deduplicate the crates of the graph where possible.
547+
/// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id.
548+
/// If the crate dependencies were sorted, the resulting graph from this `extend` call will also have the crate dependencies sorted.
549+
pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) {
550+
let topo = other.crates_in_topological_order();
551+
let mut id_map: FxHashMap<CrateId, CrateId> = FxHashMap::default();
552+
553+
for topo in topo {
554+
let crate_data = &mut other.arena[topo];
555+
crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]);
556+
crate_data.dependencies.sort_by_key(|dep| dep.crate_id);
557+
558+
let res = self.arena.iter().find_map(
559+
|(id, data)| {
560+
if data == crate_data {
561+
Some(id)
562+
} else {
563+
None
564+
}
565+
},
566+
);
567+
if let Some(res) = res {
568+
id_map.insert(topo, res);
569+
} else {
570+
let id = self.arena.alloc(crate_data.clone());
571+
id_map.insert(topo, id);
548572
}
549-
data
550-
}));
573+
}
551574

552-
*proc_macros = mem::take(proc_macros)
553-
.into_iter()
554-
.map(|(id, macros)| {
555-
(CrateId::from_raw(RawIdx::from(u32::from(id.into_raw()) + start)), macros)
556-
})
557-
.collect();
558-
start
575+
*proc_macros =
576+
mem::take(proc_macros).into_iter().map(|(id, macros)| (id_map[&id], macros)).collect();
559577
}
560578

561579
fn find_path(
@@ -586,8 +604,10 @@ impl CrateGraph {
586604
// Work around for https://github.com/rust-lang/rust-analyzer/issues/6038.
587605
// As hacky as it gets.
588606
pub fn patch_cfg_if(&mut self) -> bool {
589-
let cfg_if = self.hacky_find_crate("cfg_if");
590-
let std = self.hacky_find_crate("std");
607+
// we stupidly max by version in an attempt to have all duplicated std's depend on the same cfg_if so that deduplication still works
608+
let cfg_if =
609+
self.hacky_find_crate("cfg_if").max_by_key(|&it| self.arena[it].version.clone());
610+
let std = self.hacky_find_crate("std").next();
591611
match (cfg_if, std) {
592612
(Some(cfg_if), Some(std)) => {
593613
self.arena[cfg_if].dependencies.clear();
@@ -600,8 +620,8 @@ impl CrateGraph {
600620
}
601621
}
602622

603-
fn hacky_find_crate(&self, display_name: &str) -> Option<CrateId> {
604-
self.iter().find(|it| self[*it].display_name.as_deref() == Some(display_name))
623+
fn hacky_find_crate<'a>(&'a self, display_name: &'a str) -> impl Iterator<Item = CrateId> + 'a {
624+
self.iter().filter(move |it| self[*it].display_name.as_deref() == Some(display_name))
605625
}
606626
}
607627

0 commit comments

Comments
 (0)