Skip to content

Commit 886eaa0

Browse files
committed
Relaxation for crate graph mergin
Partially fixes #15656 . When a crate graph is extended which is the case when new workspaces are added to the project the rules for deduplication were too strict. One problem that arises from this is that in certain conditions when we see the same crate having different `CrateOrigin`s the first form would be maintained. This approach however results in some unwanted results such as making renaming forbidden as this has been recently only made available for local crates. The given example in #15656 can still not be resolved with this PR as that involves taking inconsistencies between dependencies into consideration. This will be addressed in a future PR.
1 parent 255eed4 commit 886eaa0

11 files changed

+515
-54
lines changed

crates/base-db/src/fixture.rs

+22-6
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ use vfs::{file_set::FileSet, VfsPath};
1313

1414
use crate::{
1515
input::{CrateName, CrateOrigin, LangCrateOrigin},
16-
Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition,
17-
FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros, ReleaseChannel,
18-
SourceDatabaseExt, SourceRoot, SourceRootId,
16+
Change, CrateDisplayName, CrateGraph, CrateId, Dependency, DependencyKind, Edition, Env,
17+
FileId, FilePosition, FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError,
18+
ProcMacros, ReleaseChannel, SourceDatabaseExt, SourceRoot, SourceRootId,
1919
};
2020

2121
pub const WORKSPACE: SourceRootId = SourceRootId(0);
@@ -237,7 +237,12 @@ impl ChangeFixture {
237237
crate_graph
238238
.add_dep(
239239
from_id,
240-
Dependency::with_prelude(CrateName::new(&to).unwrap(), to_id, prelude),
240+
Dependency::with_prelude(
241+
CrateName::new(&to).unwrap(),
242+
to_id,
243+
prelude,
244+
DependencyKind::Normal,
245+
),
241246
)
242247
.unwrap();
243248
}
@@ -275,7 +280,14 @@ impl ChangeFixture {
275280

276281
for krate in all_crates {
277282
crate_graph
278-
.add_dep(krate, Dependency::new(CrateName::new("core").unwrap(), core_crate))
283+
.add_dep(
284+
krate,
285+
Dependency::new(
286+
CrateName::new("core").unwrap(),
287+
core_crate,
288+
DependencyKind::Normal,
289+
),
290+
)
279291
.unwrap();
280292
}
281293
}
@@ -317,7 +329,11 @@ impl ChangeFixture {
317329
crate_graph
318330
.add_dep(
319331
krate,
320-
Dependency::new(CrateName::new("proc_macros").unwrap(), proc_macros_crate),
332+
Dependency::new(
333+
CrateName::new("proc_macros").unwrap(),
334+
proc_macros_crate,
335+
DependencyKind::Normal,
336+
),
321337
)
322338
.unwrap();
323339
}

crates/base-db/src/input.rs

+191-30
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
99
use std::{fmt, mem, ops, panic::RefUnwindSafe, str::FromStr, sync};
1010

11-
use cfg::CfgOptions;
11+
use cfg::{CfgDiff, CfgOptions};
1212
use la_arena::{Arena, Idx};
1313
use rustc_hash::{FxHashMap, FxHashSet};
1414
use syntax::SmolStr;
@@ -155,6 +155,10 @@ impl CrateOrigin {
155155
pub fn is_local(&self) -> bool {
156156
matches!(self, CrateOrigin::Local { .. })
157157
}
158+
159+
pub fn is_lib(&self) -> bool {
160+
matches!(self, CrateOrigin::Library { .. })
161+
}
158162
}
159163

160164
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -324,6 +328,99 @@ pub struct CrateData {
324328
pub channel: Option<ReleaseChannel>,
325329
}
326330

331+
impl CrateData {
332+
/**
333+
Check if [`other`] is almost equal to [`self`].
334+
This method has some obscure bits. These are mostly there to be compliant with
335+
some patches. References to the patches are given.
336+
*/
337+
pub fn almost_eq(&self, other: &CrateData) -> bool {
338+
if self.root_file_id != other.root_file_id {
339+
return false;
340+
}
341+
342+
if self.display_name != other.display_name {
343+
return false;
344+
}
345+
346+
if self.is_proc_macro != other.is_proc_macro {
347+
return false;
348+
}
349+
350+
if self.edition != other.edition {
351+
return false;
352+
}
353+
354+
if self.version != other.version {
355+
return false;
356+
}
357+
358+
let mut opts = self.cfg_options.clone();
359+
opts.apply_diff(CfgDiff {
360+
disable: other.cfg_options.clone().into_iter().collect(),
361+
enable: vec![],
362+
});
363+
364+
let mut cfgs = opts.into_iter();
365+
if let Some(cfg) = cfgs.next() {
366+
// Don't care if rust_analyzer CfgAtom is the only cfg in the difference set of self's and other's cfgs.
367+
// https://github.com/rust-lang/rust-analyzer/blob/0840038f02daec6ba3238f05d8caa037d28701a0/crates/project-model/src/workspace.rs#L894
368+
if !cfgs.next().is_none() || cfg.to_string() != "rust_analyzer" {
369+
return false;
370+
}
371+
}
372+
373+
let mut itself = self.dependencies.iter();
374+
let mut otself = other.dependencies.iter();
375+
let (mut anx, mut bnx) = (itself.next(), otself.next());
376+
loop {
377+
match (anx, bnx) {
378+
(None, None) => {
379+
break;
380+
}
381+
(None, Some(b)) => {
382+
if b.kind != DependencyKind::Normal {
383+
bnx = otself.next();
384+
} else {
385+
break;
386+
}
387+
}
388+
(Some(a), None) => {
389+
if a.kind != DependencyKind::Normal {
390+
anx = itself.next();
391+
} else {
392+
break;
393+
}
394+
}
395+
(Some(a), Some(b)) => {
396+
if a.kind != DependencyKind::Normal {
397+
anx = itself.next();
398+
continue;
399+
}
400+
401+
if b.kind != DependencyKind::Normal {
402+
bnx = otself.next();
403+
continue;
404+
}
405+
406+
if a != b {
407+
return false;
408+
}
409+
410+
anx = itself.next();
411+
bnx = otself.next();
412+
}
413+
}
414+
}
415+
416+
if self.env != other.env {
417+
return false;
418+
}
419+
420+
true
421+
}
422+
}
423+
327424
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
328425
pub enum Edition {
329426
Edition2015,
@@ -351,26 +448,43 @@ impl Env {
351448
}
352449
}
353450

451+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
452+
pub enum DependencyKind {
453+
Normal,
454+
Dev,
455+
Build,
456+
}
457+
354458
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
355459
pub struct Dependency {
356460
pub crate_id: CrateId,
357461
pub name: CrateName,
462+
kind: DependencyKind,
358463
prelude: bool,
359464
}
360465

361466
impl Dependency {
362-
pub fn new(name: CrateName, crate_id: CrateId) -> Self {
363-
Self { name, crate_id, prelude: true }
467+
pub fn new(name: CrateName, crate_id: CrateId, kind: DependencyKind) -> Self {
468+
Self { name, crate_id, prelude: true, kind }
364469
}
365470

366-
pub fn with_prelude(name: CrateName, crate_id: CrateId, prelude: bool) -> Self {
367-
Self { name, crate_id, prelude }
471+
pub fn with_prelude(
472+
name: CrateName,
473+
crate_id: CrateId,
474+
prelude: bool,
475+
kind: DependencyKind,
476+
) -> Self {
477+
Self { name, crate_id, prelude, kind }
368478
}
369479

370480
/// Whether this dependency is to be added to the depending crate's extern prelude.
371481
pub fn is_prelude(&self) -> bool {
372482
self.prelude
373483
}
484+
485+
pub fn kind(&self) -> &DependencyKind {
486+
&self.kind
487+
}
374488
}
375489

376490
impl CrateGraph {
@@ -572,25 +686,41 @@ impl CrateGraph {
572686
/// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id.
573687
/// If the crate dependencies were sorted, the resulting graph from this `extend` call will also have the crate dependencies sorted.
574688
pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) {
689+
enum ExtendStrategy {
690+
Dedup(CrateId),
691+
Replace(CrateId),
692+
}
693+
575694
let topo = other.crates_in_topological_order();
576695
let mut id_map: FxHashMap<CrateId, CrateId> = FxHashMap::default();
577-
578696
for topo in topo {
579697
let crate_data = &mut other.arena[topo];
580-
crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]);
581-
crate_data.dependencies.sort_by_key(|dep| dep.crate_id);
582698

583-
let res = self.arena.iter().find_map(
584-
|(id, data)| {
585-
if data == crate_data {
586-
Some(id)
587-
} else {
588-
None
699+
crate_data.dependencies.iter_mut().for_each(|dep| {
700+
dep.crate_id = id_map[&dep.crate_id];
701+
});
702+
crate_data.dependencies.sort_by_key(|dep| dep.crate_id);
703+
let res = self.arena.iter().find_map(|(id, data)| {
704+
if data.almost_eq(crate_data) {
705+
if data.origin.is_lib() && crate_data.origin.is_local() {
706+
// See #15656 for a relevant example.
707+
return Some(ExtendStrategy::Replace(id));
589708
}
590-
},
591-
);
709+
710+
return Some(ExtendStrategy::Dedup(id));
711+
}
712+
None
713+
});
714+
592715
if let Some(res) = res {
593-
id_map.insert(topo, res);
716+
match res {
717+
ExtendStrategy::Dedup(res) => id_map.insert(topo, res),
718+
ExtendStrategy::Replace(res) => {
719+
let id = self.arena.alloc(crate_data.clone());
720+
let _ = self.remove_and_replace(res, id);
721+
id_map.insert(topo, id)
722+
}
723+
};
594724
} else {
595725
let id = self.arena.alloc(crate_data.clone());
596726
id_map.insert(topo, id);
@@ -636,9 +766,11 @@ impl CrateGraph {
636766
match (cfg_if, std) {
637767
(Some(cfg_if), Some(std)) => {
638768
self.arena[cfg_if].dependencies.clear();
639-
self.arena[std]
640-
.dependencies
641-
.push(Dependency::new(CrateName::new("cfg_if").unwrap(), cfg_if));
769+
self.arena[std].dependencies.push(Dependency::new(
770+
CrateName::new("cfg_if").unwrap(),
771+
cfg_if,
772+
DependencyKind::Normal,
773+
));
642774
true
643775
}
644776
_ => false,
@@ -759,7 +891,7 @@ impl fmt::Display for CyclicDependenciesError {
759891

760892
#[cfg(test)]
761893
mod tests {
762-
use crate::CrateOrigin;
894+
use crate::{CrateOrigin, DependencyKind};
763895

764896
use super::{CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
765897

@@ -806,13 +938,22 @@ mod tests {
806938
None,
807939
);
808940
assert!(graph
809-
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
941+
.add_dep(
942+
crate1,
943+
Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal)
944+
)
810945
.is_ok());
811946
assert!(graph
812-
.add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3))
947+
.add_dep(
948+
crate2,
949+
Dependency::new(CrateName::new("crate3").unwrap(), crate3, DependencyKind::Normal)
950+
)
813951
.is_ok());
814952
assert!(graph
815-
.add_dep(crate3, Dependency::new(CrateName::new("crate1").unwrap(), crate1))
953+
.add_dep(
954+
crate3,
955+
Dependency::new(CrateName::new("crate1").unwrap(), crate1, DependencyKind::Normal)
956+
)
816957
.is_err());
817958
}
818959

@@ -846,10 +987,16 @@ mod tests {
846987
None,
847988
);
848989
assert!(graph
849-
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
990+
.add_dep(
991+
crate1,
992+
Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal)
993+
)
850994
.is_ok());
851995
assert!(graph
852-
.add_dep(crate2, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
996+
.add_dep(
997+
crate2,
998+
Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal)
999+
)
8531000
.is_err());
8541001
}
8551002

@@ -896,10 +1043,16 @@ mod tests {
8961043
None,
8971044
);
8981045
assert!(graph
899-
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
1046+
.add_dep(
1047+
crate1,
1048+
Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal)
1049+
)
9001050
.is_ok());
9011051
assert!(graph
902-
.add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3))
1052+
.add_dep(
1053+
crate2,
1054+
Dependency::new(CrateName::new("crate3").unwrap(), crate3, DependencyKind::Normal)
1055+
)
9031056
.is_ok());
9041057
}
9051058

@@ -935,12 +1088,20 @@ mod tests {
9351088
assert!(graph
9361089
.add_dep(
9371090
crate1,
938-
Dependency::new(CrateName::normalize_dashes("crate-name-with-dashes"), crate2)
1091+
Dependency::new(
1092+
CrateName::normalize_dashes("crate-name-with-dashes"),
1093+
crate2,
1094+
DependencyKind::Normal
1095+
)
9391096
)
9401097
.is_ok());
9411098
assert_eq!(
9421099
graph[crate1].dependencies,
943-
vec![Dependency::new(CrateName::new("crate_name_with_dashes").unwrap(), crate2)]
1100+
vec![Dependency::new(
1101+
CrateName::new("crate_name_with_dashes").unwrap(),
1102+
crate2,
1103+
DependencyKind::Normal
1104+
)]
9441105
);
9451106
}
9461107
}

crates/base-db/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_hash::FxHashSet;
1212
use syntax::{ast, Parse, SourceFile, TextRange, TextSize};
1313
use triomphe::Arc;
1414

15+
pub use crate::input::DependencyKind;
1516
pub use crate::{
1617
change::Change,
1718
input::{

0 commit comments

Comments
 (0)