about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/base-db/src/input.rs54
-rw-r--r--crates/project-model/src/sysroot.rs32
-rw-r--r--crates/project-model/src/workspace.rs126
3 files changed, 174 insertions, 38 deletions
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index a38ab4f6286..dfd0b2abc33 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 6c468f5ee66..e3a2de927c9 100644
--- a/crates/project-model/src/sysroot.rs
+++ b/crates/project-model/src/sysroot.rs
@@ -12,13 +12,15 @@ 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<SysrootCrateData>,
+    /// Stores the result of `cargo metadata` of the `RA_UNSTABLE_SYSROOT_HACK` workspace.
+    pub hack_cargo_workspace: Option<CargoWorkspace>,
 }
 
 pub(crate) type SysrootCrate = Idx<SysrootCrateData>;
@@ -125,9 +127,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 `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 9439d94f031..5518b6bc7f5 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -622,6 +622,7 @@ impl ProjectWorkspace {
                 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<CfgFlag>,
     override_cfg: &CfgOverrides,
+    // Don't compute cfg and use this if present
+    forced_cfg: Option<CfgOptions>,
     build_scripts: &WorkspaceBuildScripts,
     target_layout: TargetLayoutLoadResult,
     channel: Option<ReleaseChannel>,
@@ -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<CrateId>) {
     let _p = profile::span("sysroot_to_crate_graph");
     let mut cfg_options = CfgOptions::default();
-    cfg_options.extend(rustc_cfg);
-    let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = 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<SysrootCrate, CrateId> = 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<FileId>,
+    cargo: &CargoWorkspace,
+    rustc_cfg: Vec<CfgFlag>,
+    cfg_options: CfgOptions,
+    target_layout: Result<Arc<str>, Arc<str>>,
+    channel: Option<ReleaseChannel>,
+    crate_graph: &mut CrateGraph,
+    sysroot: &Sysroot,
+) -> FxHashMap<SysrootCrate, CrateId> {
+    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))
 }