Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/base-db/src/input.rs')
-rw-r--r--crates/base-db/src/input.rs616
1 files changed, 416 insertions, 200 deletions
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index bd08387b58..499c9b3716 100644
--- a/crates/base-db/src/input.rs
+++ b/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::DashMap;
+use dashmap::mapref::entry::Entry;
use intern::Symbol;
use la_arena::{Arena, Idx, RawIdx};
-use rustc_hash::{FxHashMap, FxHashSet};
-use span::{Edition, EditionedFileId};
+use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
+use salsa::{Durability, Setter};
+use span::Edition;
use triomphe::Arc;
-use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath};
+use vfs::{AbsPathBuf, AnchoredPath, FileId, VfsPath, file_set::FileSet};
-pub type ProcMacroPaths = FxHashMap<CrateId, Result<(String, AbsPathBuf), String>>;
+use crate::{CrateWorkspaceData, EditionedFileId, 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>,
+}
+
+pub type CrateBuilderId = Idx<CrateBuilder>;
+
+impl ops::Index<CrateBuilderId> for CrateGraphBuilder {
+ type Output = CrateBuilder;
+
+ fn index(&self, index: CrateBuilderId) -> &Self::Output {
+ &self.arena[index]
+ }
}
-impl fmt::Debug for CrateGraph {
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct CrateBuilder {
+ pub basic: CrateDataBuilder,
+ pub extra: ExtraCrateData,
+ pub cfg_options: 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);
@@ -105,11 +110,7 @@ impl CrateName {
/// Dashes are not allowed in the crate names,
/// hence the input string is returned as `Err` for those cases.
pub fn new(name: &str) -> Result<CrateName, &str> {
- if name.contains('-') {
- Err(name)
- } else {
- Ok(Self(Symbol::intern(name)))
- }
+ if name.contains('-') { Err(name) } else { Ok(Self(Symbol::intern(name))) }
}
/// Creates a crate name, unconditionally replacing the dashes with underscores.
@@ -272,10 +273,49 @@ 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,
+ /// 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<Id>>,
+ pub origin: CrateOrigin,
+ pub is_proc_macro: bool,
+ /// The working directory to run proc-macros in invoked in the context of this crate.
+ /// This is the workspace root of the cargo workspace for workspace members, the crate manifest
+ /// dir otherwise.
+ // FIXME: This ought to be a `VfsPath` or something opaque.
+ pub proc_macro_cwd: Arc<AbsPathBuf>,
+}
+
+pub type CrateDataBuilder = CrateData<CrateBuilderId>;
+pub type BuiltCrateData = CrateData<Crate>;
+
+/// Crate data unrelated to analysis.
+#[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
@@ -284,21 +324,8 @@ pub struct CrateData {
/// 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 origin: CrateOrigin,
- pub is_proc_macro: bool,
- /// The working directory to run proc-macros in. This is the workspace root of the cargo workspace
- /// for workspace members, the crate manifest dir otherwise.
- pub proc_macro_cwd: Option<AbsPathBuf>,
+ pub potential_cfg_options: Option<CfgOptions>,
}
#[derive(Default, Clone, PartialEq, Eq)]
@@ -326,22 +353,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,41 +390,71 @@ impl Dependency {
}
}
-impl CrateGraph {
+pub type CratesIdMap = FxHashMap<CrateBuilderId, Crate>;
+
+#[salsa::input]
+#[derive(Debug)]
+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>,
+ #[return_ref]
+ pub cfg_options: 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,
edition: Edition,
display_name: Option<CrateDisplayName>,
version: Option<String>,
- cfg_options: Arc<CfgOptions>,
- potential_cfg_options: Option<Arc<CfgOptions>>,
+ mut cfg_options: CfgOptions,
+ mut potential_cfg_options: Option<CfgOptions>,
mut env: Env,
origin: CrateOrigin,
is_proc_macro: bool,
- proc_macro_cwd: Option<AbsPathBuf>,
- ) -> CrateId {
+ proc_macro_cwd: Arc<AbsPathBuf>,
+ ws_data: Arc<CrateWorkspaceData>,
+ ) -> CrateBuilderId {
env.entries.shrink_to_fit();
- let data = CrateData {
- root_file_id,
- edition,
- version,
- display_name,
+ cfg_options.shrink_to_fit();
+ if let Some(potential_cfg_options) = &mut potential_cfg_options {
+ potential_cfg_options.shrink_to_fit();
+ }
+ 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 +462,154 @@ 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();
- pub fn iter(&self) -> impl Iterator<Item = CrateId> + '_ {
- self.arena.iter().map(|(idx, _)| idx)
+ 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::MEDIUM,
+ );
+ }
+
+ 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::MEDIUM).to(crate_data);
+ }
+ if krate.extra != *old_crate.extra_data(db) {
+ old_crate
+ .set_extra_data(db)
+ .with_durability(Durability::MEDIUM)
+ .to(krate.extra.clone());
+ }
+ if krate.cfg_options != *old_crate.cfg_options(db) {
+ old_crate
+ .set_cfg_options(db)
+ .with_durability(Durability::MEDIUM)
+ .to(krate.cfg_options.clone());
+ }
+ if krate.env != *old_crate.env(db) {
+ old_crate
+ .set_env(db)
+ .with_durability(Durability::MEDIUM)
+ .to(krate.env.clone());
+ }
+ if krate.ws_data != *old_crate.workspace_data(db) {
+ old_crate
+ .set_workspace_data(db)
+ .with_durability(Durability::MEDIUM)
+ .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::MEDIUM)
+ .new(db);
+ entry.insert(input);
+ input
+ }
+ };
+ all_crates.push(crate_input);
+ visited.insert(source, crate_input);
+ crate_input
+ }
}
- // 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)> + '_ {
- self.arena.iter_mut()
+ pub fn iter(&self) -> impl Iterator<Item = CrateBuilderId> + '_ {
+ self.arena.iter().map(|(idx, _)| idx)
}
/// 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 +618,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 +637,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 +661,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 +695,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 +707,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 +720,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 +731,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,22 +749,36 @@ 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
+}
- pub fn root_file_id(&self) -> EditionedFileId {
- EditionedFileId::new(self.root_file_id, self.edition)
+impl BuiltCrateData {
+ pub fn root_file_id(&self, db: &dyn salsa::Database) -> EditionedFileId {
+ EditionedFileId::new(db, self.root_file_id, self.edition)
}
}
@@ -657,21 +835,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 +866,20 @@ impl fmt::Display for CyclicDependenciesError {
#[cfg(test)]
mod tests {
- use crate::CrateOrigin;
+ use triomphe::Arc;
+ use vfs::AbsPathBuf;
+
+ 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,
@@ -705,7 +890,8 @@ mod tests {
Env::default(),
CrateOrigin::Local { repo: None, name: None },
false,
- None,
+ Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
+ empty_ws_data(),
);
let crate2 = graph.add_crate_root(
FileId::from_raw(2u32),
@@ -717,7 +903,8 @@ mod tests {
Env::default(),
CrateOrigin::Local { repo: None, name: None },
false,
- None,
+ Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
+ empty_ws_data(),
);
let crate3 = graph.add_crate_root(
FileId::from_raw(3u32),
@@ -729,22 +916,29 @@ mod tests {
Env::default(),
CrateOrigin::Local { repo: None, name: None },
false,
- None,
+ Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
+ empty_ws_data(),
+ );
+ assert!(
+ graph
+ .add_dep(crate1, DependencyBuilder::new(CrateName::new("crate2").unwrap(), crate2,))
+ .is_ok()
+ );
+ assert!(
+ graph
+ .add_dep(crate2, DependencyBuilder::new(CrateName::new("crate3").unwrap(), crate3,))
+ .is_ok()
+ );
+ assert!(
+ graph
+ .add_dep(crate3, DependencyBuilder::new(CrateName::new("crate1").unwrap(), crate1,))
+ .is_err()
);
- assert!(graph
- .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2,))
- .is_ok());
- assert!(graph
- .add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3,))
- .is_ok());
- assert!(graph
- .add_dep(crate3, Dependency::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,
@@ -755,7 +949,8 @@ mod tests {
Env::default(),
CrateOrigin::Local { repo: None, name: None },
false,
- None,
+ Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
+ empty_ws_data(),
);
let crate2 = graph.add_crate_root(
FileId::from_raw(2u32),
@@ -767,19 +962,24 @@ mod tests {
Env::default(),
CrateOrigin::Local { repo: None, name: None },
false,
- None,
+ Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
+ empty_ws_data(),
+ );
+ assert!(
+ graph
+ .add_dep(crate1, DependencyBuilder::new(CrateName::new("crate2").unwrap(), crate2,))
+ .is_ok()
+ );
+ assert!(
+ graph
+ .add_dep(crate2, DependencyBuilder::new(CrateName::new("crate2").unwrap(), crate2,))
+ .is_err()
);
- assert!(graph
- .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2,))
- .is_ok());
- assert!(graph
- .add_dep(crate2, Dependency::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,
@@ -790,7 +990,8 @@ mod tests {
Env::default(),
CrateOrigin::Local { repo: None, name: None },
false,
- None,
+ Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
+ empty_ws_data(),
);
let crate2 = graph.add_crate_root(
FileId::from_raw(2u32),
@@ -802,7 +1003,8 @@ mod tests {
Env::default(),
CrateOrigin::Local { repo: None, name: None },
false,
- None,
+ Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
+ empty_ws_data(),
);
let crate3 = graph.add_crate_root(
FileId::from_raw(3u32),
@@ -814,19 +1016,24 @@ mod tests {
Env::default(),
CrateOrigin::Local { repo: None, name: None },
false,
- None,
+ Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
+ empty_ws_data(),
+ );
+ assert!(
+ graph
+ .add_dep(crate1, DependencyBuilder::new(CrateName::new("crate2").unwrap(), crate2,))
+ .is_ok()
+ );
+ assert!(
+ graph
+ .add_dep(crate2, DependencyBuilder::new(CrateName::new("crate3").unwrap(), crate3,))
+ .is_ok()
);
- assert!(graph
- .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2,))
- .is_ok());
- assert!(graph
- .add_dep(crate2, Dependency::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,
@@ -837,7 +1044,8 @@ mod tests {
Env::default(),
CrateOrigin::Local { repo: None, name: None },
false,
- None,
+ Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
+ empty_ws_data(),
);
let crate2 = graph.add_crate_root(
FileId::from_raw(2u32),
@@ -849,17 +1057,25 @@ mod tests {
Env::default(),
CrateOrigin::Local { repo: None, name: None },
false,
- None,
+ Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
+ empty_ws_data(),
+ );
+ assert!(
+ graph
+ .add_dep(
+ crate1,
+ DependencyBuilder::new(
+ CrateName::normalize_dashes("crate-name-with-dashes"),
+ crate2,
+ )
+ )
+ .is_ok()
);
- assert!(graph
- .add_dep(
- crate1,
- Dependency::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,)
+ ]
);
}
}