Skip to content

Commit cccc7ca

Browse files
committed
Auto merge of #15754 - alibektas:15656/linked_projects_are_local_too, r=Veykril
fix: Dedup duplicate crates with differing origins in CrateGraph construction Partially fixes #15656 . Until now the condition for deduplication in crate graphs were the strict equality of two crates. One problem that arises from this is that in certain conditions when we see the same crate having different `CrateOrigin`s the first occurrence would be kept. 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.
2 parents 7ceefc7 + ba1b080 commit cccc7ca

13 files changed

+542
-48
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

+161-28
Original file line numberDiff line numberDiff line change
@@ -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,62 @@ pub struct CrateData {
324328
pub channel: Option<ReleaseChannel>,
325329
}
326330

331+
impl CrateData {
332+
/// Check if [`other`] is almost equal to [`self`] ignoring `CrateOrigin` value.
333+
pub fn eq_ignoring_origin_and_deps(&self, other: &CrateData, ignore_dev_deps: bool) -> bool {
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+
if self.root_file_id != other.root_file_id {
337+
return false;
338+
}
339+
340+
if self.display_name != other.display_name {
341+
return false;
342+
}
343+
344+
if self.is_proc_macro != other.is_proc_macro {
345+
return false;
346+
}
347+
348+
if self.edition != other.edition {
349+
return false;
350+
}
351+
352+
if self.version != other.version {
353+
return false;
354+
}
355+
356+
let mut opts = self.cfg_options.difference(&other.cfg_options);
357+
if let Some(it) = opts.next() {
358+
// Don't care if rust_analyzer CfgAtom is the only cfg in the difference set of self's and other's cfgs.
359+
// https://github.com/rust-lang/rust-analyzer/blob/0840038f02daec6ba3238f05d8caa037d28701a0/crates/project-model/src/workspace.rs#L894
360+
if it.to_string() != "rust_analyzer" {
361+
return false;
362+
}
363+
364+
if let Some(_) = opts.next() {
365+
return false;
366+
}
367+
}
368+
369+
if self.env != other.env {
370+
return false;
371+
}
372+
373+
let slf_deps = self.dependencies.iter();
374+
let other_deps = other.dependencies.iter();
375+
376+
if ignore_dev_deps {
377+
return slf_deps
378+
.clone()
379+
.filter(|it| it.kind != DependencyKind::Dev)
380+
.eq(other_deps.clone().filter(|it| it.kind != DependencyKind::Dev));
381+
}
382+
383+
slf_deps.eq(other_deps)
384+
}
385+
}
386+
327387
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
328388
pub enum Edition {
329389
Edition2015,
@@ -351,26 +411,43 @@ impl Env {
351411
}
352412
}
353413

414+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
415+
pub enum DependencyKind {
416+
Normal,
417+
Dev,
418+
Build,
419+
}
420+
354421
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
355422
pub struct Dependency {
356423
pub crate_id: CrateId,
357424
pub name: CrateName,
425+
kind: DependencyKind,
358426
prelude: bool,
359427
}
360428

361429
impl Dependency {
362-
pub fn new(name: CrateName, crate_id: CrateId) -> Self {
363-
Self { name, crate_id, prelude: true }
430+
pub fn new(name: CrateName, crate_id: CrateId, kind: DependencyKind) -> Self {
431+
Self { name, crate_id, prelude: true, kind }
364432
}
365433

366-
pub fn with_prelude(name: CrateName, crate_id: CrateId, prelude: bool) -> Self {
367-
Self { name, crate_id, prelude }
434+
pub fn with_prelude(
435+
name: CrateName,
436+
crate_id: CrateId,
437+
prelude: bool,
438+
kind: DependencyKind,
439+
) -> Self {
440+
Self { name, crate_id, prelude, kind }
368441
}
369442

370443
/// Whether this dependency is to be added to the depending crate's extern prelude.
371444
pub fn is_prelude(&self) -> bool {
372445
self.prelude
373446
}
447+
448+
pub fn kind(&self) -> DependencyKind {
449+
self.kind
450+
}
374451
}
375452

376453
impl CrateGraph {
@@ -574,23 +651,46 @@ impl CrateGraph {
574651
pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) {
575652
let topo = other.crates_in_topological_order();
576653
let mut id_map: FxHashMap<CrateId, CrateId> = FxHashMap::default();
577-
578654
for topo in topo {
579655
let crate_data = &mut other.arena[topo];
656+
580657
crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]);
581658
crate_data.dependencies.sort_by_key(|dep| dep.crate_id);
582-
583-
let res = self.arena.iter().find_map(
584-
|(id, data)| {
585-
if data == crate_data {
586-
Some(id)
587-
} else {
588-
None
659+
let res = self.arena.iter().find_map(|(id, data)| {
660+
match (&data.origin, &crate_data.origin) {
661+
(a, b) if a == b => {
662+
if data.eq_ignoring_origin_and_deps(&crate_data, false) {
663+
return Some((id, false));
664+
}
665+
}
666+
(a @ CrateOrigin::Local { .. }, CrateOrigin::Library { .. })
667+
| (a @ CrateOrigin::Library { .. }, CrateOrigin::Local { .. }) => {
668+
// If the origins differ, check if the two crates are equal without
669+
// considering the dev dependencies, if they are, they most likely are in
670+
// different loaded workspaces which may cause issues. We keep the local
671+
// version and discard the library one as the local version may have
672+
// dev-dependencies that we want to keep resolving. See #15656 for more
673+
// information.
674+
if data.eq_ignoring_origin_and_deps(&crate_data, true) {
675+
return Some((id, if a.is_local() { false } else { true }));
676+
}
589677
}
590-
},
591-
);
592-
if let Some(res) = res {
678+
(_, _) => return None,
679+
}
680+
681+
None
682+
});
683+
684+
if let Some((res, should_update_lib_to_local)) = res {
593685
id_map.insert(topo, res);
686+
if should_update_lib_to_local {
687+
assert!(self.arena[res].origin.is_lib());
688+
assert!(crate_data.origin.is_local());
689+
self.arena[res].origin = crate_data.origin.clone();
690+
691+
// Move local's dev dependencies into the newly-local-formerly-lib crate.
692+
self.arena[res].dependencies = crate_data.dependencies.clone();
693+
}
594694
} else {
595695
let id = self.arena.alloc(crate_data.clone());
596696
id_map.insert(topo, id);
@@ -636,9 +736,11 @@ impl CrateGraph {
636736
match (cfg_if, std) {
637737
(Some(cfg_if), Some(std)) => {
638738
self.arena[cfg_if].dependencies.clear();
639-
self.arena[std]
640-
.dependencies
641-
.push(Dependency::new(CrateName::new("cfg_if").unwrap(), cfg_if));
739+
self.arena[std].dependencies.push(Dependency::new(
740+
CrateName::new("cfg_if").unwrap(),
741+
cfg_if,
742+
DependencyKind::Normal,
743+
));
642744
true
643745
}
644746
_ => false,
@@ -658,6 +760,8 @@ impl ops::Index<CrateId> for CrateGraph {
658760
}
659761

660762
impl CrateData {
763+
/// Add a dependency to `self` without checking if the dependency
764+
// is existent among `self.dependencies`.
661765
fn add_dep(&mut self, dep: Dependency) {
662766
self.dependencies.push(dep)
663767
}
@@ -759,7 +863,7 @@ impl fmt::Display for CyclicDependenciesError {
759863

760864
#[cfg(test)]
761865
mod tests {
762-
use crate::CrateOrigin;
866+
use crate::{CrateOrigin, DependencyKind};
763867

764868
use super::{CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
765869

@@ -806,13 +910,22 @@ mod tests {
806910
None,
807911
);
808912
assert!(graph
809-
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
913+
.add_dep(
914+
crate1,
915+
Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal)
916+
)
810917
.is_ok());
811918
assert!(graph
812-
.add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3))
919+
.add_dep(
920+
crate2,
921+
Dependency::new(CrateName::new("crate3").unwrap(), crate3, DependencyKind::Normal)
922+
)
813923
.is_ok());
814924
assert!(graph
815-
.add_dep(crate3, Dependency::new(CrateName::new("crate1").unwrap(), crate1))
925+
.add_dep(
926+
crate3,
927+
Dependency::new(CrateName::new("crate1").unwrap(), crate1, DependencyKind::Normal)
928+
)
816929
.is_err());
817930
}
818931

@@ -846,10 +959,16 @@ mod tests {
846959
None,
847960
);
848961
assert!(graph
849-
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
962+
.add_dep(
963+
crate1,
964+
Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal)
965+
)
850966
.is_ok());
851967
assert!(graph
852-
.add_dep(crate2, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
968+
.add_dep(
969+
crate2,
970+
Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal)
971+
)
853972
.is_err());
854973
}
855974

@@ -896,10 +1015,16 @@ mod tests {
8961015
None,
8971016
);
8981017
assert!(graph
899-
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
1018+
.add_dep(
1019+
crate1,
1020+
Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal)
1021+
)
9001022
.is_ok());
9011023
assert!(graph
902-
.add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3))
1024+
.add_dep(
1025+
crate2,
1026+
Dependency::new(CrateName::new("crate3").unwrap(), crate3, DependencyKind::Normal)
1027+
)
9031028
.is_ok());
9041029
}
9051030

@@ -935,12 +1060,20 @@ mod tests {
9351060
assert!(graph
9361061
.add_dep(
9371062
crate1,
938-
Dependency::new(CrateName::normalize_dashes("crate-name-with-dashes"), crate2)
1063+
Dependency::new(
1064+
CrateName::normalize_dashes("crate-name-with-dashes"),
1065+
crate2,
1066+
DependencyKind::Normal
1067+
)
9391068
)
9401069
.is_ok());
9411070
assert_eq!(
9421071
graph[crate1].dependencies,
943-
vec![Dependency::new(CrateName::new("crate_name_with_dashes").unwrap(), crate2)]
1072+
vec![Dependency::new(
1073+
CrateName::new("crate_name_with_dashes").unwrap(),
1074+
crate2,
1075+
DependencyKind::Normal
1076+
)]
9441077
);
9451078
}
9461079
}

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::{

crates/cfg/src/lib.rs

+7
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ impl CfgOptions {
5858
self.enabled.insert(CfgAtom::KeyValue { key, value });
5959
}
6060

61+
pub fn difference<'a>(
62+
&'a self,
63+
other: &'a CfgOptions,
64+
) -> impl Iterator<Item = &'a CfgAtom> + 'a {
65+
self.enabled.difference(&other.enabled)
66+
}
67+
6168
pub fn apply_diff(&mut self, diff: CfgDiff) {
6269
for atom in diff.enable {
6370
self.enabled.insert(atom);

crates/project-model/src/project_json.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
//! user explores them belongs to that extension (it's totally valid to change
5050
//! rust-project.json over time via configuration request!)
5151
52-
use base_db::{CrateDisplayName, CrateId, CrateName, Dependency, Edition};
52+
use base_db::{CrateDisplayName, CrateId, CrateName, Dependency, DependencyKind, Edition};
5353
use la_arena::RawIdx;
5454
use paths::{AbsPath, AbsPathBuf};
5555
use rustc_hash::FxHashMap;
@@ -135,6 +135,7 @@ impl ProjectJson {
135135
Dependency::new(
136136
dep_data.name,
137137
CrateId::from_raw(RawIdx::from(dep_data.krate as u32)),
138+
DependencyKind::Normal,
138139
)
139140
})
140141
.collect::<Vec<_>>(),

0 commit comments

Comments
 (0)