diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index c4ada9c76539..80da41222a15 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -386,6 +386,37 @@ impl CrateGraph { self.arena.alloc(data) } + /// Remove the crate from crate graph. If any crates depend on this crate, the dependency would be replaced + /// with the second input. + pub fn remove_and_replace( + &mut self, + id: CrateId, + replace_with: CrateId, + ) -> Result<(), CyclicDependenciesError> { + for (x, data) in self.arena.iter() { + if x == id { + continue; + } + for edge in &data.dependencies { + if edge.crate_id == id { + self.check_cycle_after_dependency(edge.crate_id, replace_with)?; + } + } + } + // if everything was ok, start to replace + for (x, data) in self.arena.iter_mut() { + if x == id { + continue; + } + for edge in &mut data.dependencies { + if edge.crate_id == id { + edge.crate_id = replace_with; + } + } + } + Ok(()) + } + pub fn add_dep( &mut self, from: CrateId, @@ -393,17 +424,26 @@ impl CrateGraph { ) -> Result<(), CyclicDependenciesError> { let _p = profile::span("add_dep"); - // Check if adding a dep from `from` to `to` creates a cycle. To figure - // 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) { + self.check_cycle_after_dependency(from, dep.crate_id)?; + + self.arena[from].add_dep(dep); + Ok(()) + } + + /// Check if adding a dep from `from` to `to` creates a cycle. To figure + /// that out, look for a path in the *opposite* direction, from `to` to + /// `from`. + fn check_cycle_after_dependency( + &self, + from: CrateId, + to: CrateId, + ) -> Result<(), CyclicDependenciesError> { + if let Some(path) = self.find_path(&mut FxHashSet::default(), to, from) { let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect(); let err = CyclicDependenciesError { path }; - assert!(err.from().0 == from && err.to().0 == dep.crate_id); + assert!(err.from().0 == from && err.to().0 == to); return Err(err); } - - self.arena[from].add_dep(dep); Ok(()) } diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index 6c468f5ee66a..03665eb20913 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -12,13 +12,14 @@ use la_arena::{Arena, Idx}; use paths::{AbsPath, AbsPathBuf}; use rustc_hash::FxHashMap; -use crate::{utf8_stdout, ManifestPath}; +use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath}; #[derive(Debug, Clone, Eq, PartialEq)] pub struct Sysroot { root: AbsPathBuf, src_root: AbsPathBuf, crates: Arena, + pub cargo_workspace: Option, } pub(crate) type SysrootCrate = Idx; @@ -125,9 +126,31 @@ impl Sysroot { Ok(Sysroot::load(sysroot_dir, sysroot_src_dir)) } - pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Sysroot { - let mut sysroot = - Sysroot { root: sysroot_dir, src_root: sysroot_src_dir, crates: Arena::default() }; + pub fn load(sysroot_dir: AbsPathBuf, mut sysroot_src_dir: AbsPathBuf) -> Sysroot { + // FIXME: Remove this `cargo_workspace` field completely once we support sysroot dependencies + let cargo_workspace = if let Ok(path) = std::env::var("RA_UNSTABLE_SYSROOT_HACK") { + let cargo_toml = ManifestPath::try_from( + AbsPathBuf::try_from(&*format!("{path}/Cargo.toml")).unwrap(), + ) + .unwrap(); + sysroot_src_dir = AbsPathBuf::try_from(&*path).unwrap().join("library"); + CargoWorkspace::fetch_metadata( + &cargo_toml, + &AbsPathBuf::try_from("/home/hamid/oss/rust-analyzer").unwrap(), + &CargoConfig::default(), + &|_| (), + ) + .map(CargoWorkspace::new) + .ok() + } else { + None + }; + let mut sysroot = Sysroot { + root: sysroot_dir, + src_root: sysroot_src_dir, + crates: Arena::default(), + cargo_workspace, + }; for path in SYSROOT_CRATES.trim().lines() { let name = path.split('/').last().unwrap(); diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index c8e83a687e02..1c1621b83ab6 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -615,20 +615,27 @@ impl ProjectWorkspace { build_scripts, toolchain, target_layout, - } => cargo_to_crate_graph( - load, - rustc.as_ref().ok(), - cargo, - sysroot.as_ref().ok(), - rustc_cfg.clone(), - cfg_overrides, - build_scripts, - match target_layout.as_ref() { - Ok(it) => Ok(Arc::from(it.as_str())), - Err(it) => Err(Arc::from(it.as_str())), - }, - toolchain.as_ref().and_then(|it| ReleaseChannel::from_str(it.pre.as_str())), - ), + } => { + let mut res = (CrateGraph::default(), ProcMacroPaths::default()); + cargo_to_crate_graph( + &mut res.0, + &mut res.1, + load, + rustc.as_ref().ok(), + cargo, + sysroot.as_ref().ok(), + rustc_cfg.clone(), + cfg_overrides, + None, + build_scripts, + match target_layout.as_ref() { + Ok(it) => Ok(Arc::from(it.as_str())), + Err(it) => Err(Arc::from(it.as_str())), + }, + toolchain.as_ref().and_then(|it| ReleaseChannel::from_str(it.pre.as_str())), + ); + res + } ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => { detached_files_to_crate_graph( rustc_cfg.clone(), @@ -815,20 +822,21 @@ fn project_json_to_crate_graph( } fn cargo_to_crate_graph( + crate_graph: &mut CrateGraph, + proc_macros: &mut ProcMacroPaths, load: &mut dyn FnMut(&AbsPath) -> Option, rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>, cargo: &CargoWorkspace, sysroot: Option<&Sysroot>, rustc_cfg: Vec, override_cfg: &CfgOverrides, + // Don't compute cfg and use this if present + forced_cfg: Option, build_scripts: &WorkspaceBuildScripts, target_layout: TargetLayoutLoadResult, channel: Option, -) -> (CrateGraph, ProcMacroPaths) { +) { let _p = profile::span("cargo_to_crate_graph"); - let mut res = (CrateGraph::default(), ProcMacroPaths::default()); - let crate_graph = &mut res.0; - let proc_macros = &mut res.1; let (public_deps, libproc_macro) = match sysroot { Some(sysroot) => sysroot_to_crate_graph( crate_graph, @@ -858,7 +866,7 @@ fn cargo_to_crate_graph( for pkg in cargo.packages() { has_private |= cargo[pkg].metadata.rustc_private; - let cfg_options = { + let cfg_options = forced_cfg.clone().unwrap_or_else(|| { let mut cfg_options = cfg_options.clone(); // Add test cfg for local crates @@ -882,7 +890,7 @@ fn cargo_to_crate_graph( cfg_options.apply_diff(overrides.clone()); }; cfg_options - }; + }); let mut lib_tgt = None; for &tgt in cargo[pkg].targets.iter() { @@ -989,7 +997,6 @@ fn cargo_to_crate_graph( ); } } - res } fn detached_files_to_crate_graph( @@ -1280,31 +1287,81 @@ fn sysroot_to_crate_graph( ) -> (SysrootPublicDeps, Option) { let _p = profile::span("sysroot_to_crate_graph"); let mut cfg_options = CfgOptions::default(); - cfg_options.extend(rustc_cfg); - let sysroot_crates: FxHashMap = sysroot - .crates() - .filter_map(|krate| { - let file_id = load(&sysroot[krate].root)?; - - let env = Env::default(); - let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone()); - let crate_id = crate_graph.add_crate_root( - file_id, - Edition::CURRENT, - Some(display_name), + cfg_options.extend(rustc_cfg.clone()); + let sysroot_crates: FxHashMap = + if let Some(cargo) = &sysroot.cargo_workspace { + cargo_to_crate_graph( + crate_graph, + &mut Default::default(), + load, None, - cfg_options.clone(), + cargo, None, - env, - false, - CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)), - target_layout.clone(), + rustc_cfg, + &CfgOverrides::default(), + Some(cfg_options), + &WorkspaceBuildScripts::default(), + target_layout, channel, ); - Some((krate, crate_id)) - }) - .collect(); + for crate_name in ["std", "alloc", "core"] { + let original = crate_graph + .iter() + .find(|x| { + crate_graph[*x] + .display_name + .as_ref() + .map(|x| x.canonical_name() == crate_name) + .unwrap_or(false) + }) + .unwrap(); + let fake_crate_name = format!("rustc-std-workspace-{}", crate_name); + let fake = crate_graph + .iter() + .find(|x| { + crate_graph[*x] + .display_name + .as_ref() + .map(|x| x.canonical_name() == fake_crate_name) + .unwrap_or(false) + }) + .unwrap(); + crate_graph.remove_and_replace(fake, original).unwrap(); + } + sysroot + .crates() + .filter_map(|krate| { + let file_id = load(&sysroot[krate].root)?; + let crate_id = crate_graph.crate_id_for_crate_root(file_id)?; + Some((krate, crate_id)) + }) + .collect() + } else { + sysroot + .crates() + .filter_map(|krate| { + let file_id = load(&sysroot[krate].root)?; + let env = Env::default(); + let display_name = + CrateDisplayName::from_canonical_name(sysroot[krate].name.clone()); + let crate_id = crate_graph.add_crate_root( + file_id, + Edition::CURRENT, + Some(display_name), + None, + cfg_options.clone(), + None, + env, + false, + CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)), + target_layout.clone(), + channel, + ); + Some((krate, crate_id)) + }) + .collect() + }; for from in sysroot.crates() { for &to in sysroot[from].deps.iter() { let name = CrateName::new(&sysroot[to].name).unwrap();