diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index c4ada9c7653..80da41222a1 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -386,6 +386,37 @@ pub fn add_crate_root( 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,20 +424,29 @@ pub fn add_dep( ) -> 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) { - 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); - return Err(err); - } + 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 == to); + return Err(err); + } + Ok(()) + } + pub fn is_empty(&self) -> bool { self.arena.is_empty() } diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index 6c468f5ee66..e3a2de927c9 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -12,13 +12,15 @@ 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, + /// Stores the result of `cargo metadata` of the `RA_UNSTABLE_SYSROOT_HACK` workspace. + pub hack_cargo_workspace: Option, } pub(crate) type SysrootCrate = Idx; @@ -125,9 +127,31 @@ pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result { 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 `hack_cargo_workspace` field completely once we support sysroot dependencies + let hack_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("/").unwrap(), + &CargoConfig::default(), + &|_| (), + ) + .map(CargoWorkspace::new) + .ok() + } else { + None + }; + let mut sysroot = Sysroot { + root: sysroot_dir, + src_root: sysroot_src_dir, + crates: Arena::default(), + hack_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 c8e83a687e0..d69ff98f5d6 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -622,6 +622,7 @@ pub fn to_crate_graph( 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())), @@ -821,6 +822,8 @@ fn cargo_to_crate_graph( 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, @@ -858,7 +861,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 +885,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() { @@ -1280,31 +1283,43 @@ 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), - None, - cfg_options.clone(), - None, - env, - false, - CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)), - target_layout.clone(), - channel, - ); - Some((krate, crate_id)) - }) - .collect(); + cfg_options.extend(rustc_cfg.clone()); + let sysroot_crates: FxHashMap = match &sysroot.hack_cargo_workspace { + Some(cargo) => handle_hack_cargo_workspace( + load, + cargo, + rustc_cfg, + cfg_options, + target_layout, + channel, + crate_graph, + sysroot, + ), + None => 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(); @@ -1325,6 +1340,63 @@ fn sysroot_to_crate_graph( (public_deps, libproc_macro) } +fn handle_hack_cargo_workspace( + load: &mut dyn FnMut(&AbsPath) -> Option, + cargo: &CargoWorkspace, + rustc_cfg: Vec, + cfg_options: CfgOptions, + target_layout: Result, Arc>, + channel: Option, + crate_graph: &mut CrateGraph, + sysroot: &Sysroot, +) -> FxHashMap { + let (cg, mut pm) = cargo_to_crate_graph( + load, + None, + cargo, + None, + rustc_cfg, + &CfgOverrides::default(), + Some(cfg_options), + &WorkspaceBuildScripts::default(), + target_layout, + channel, + ); + crate_graph.extend(cg, &mut pm); + 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() +} + fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) { add_dep_inner(graph, from, Dependency::new(name, to)) }