Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #14599 - HKalbasi:dev2, r=HKalbasi
Detect sysroot dependencies using symlink copy cc #7637 It is currently in a proof of concept stage, and it doesn't generates a copy. You need to provide your own sysroot copy in `/tmp/ra-sysroot-hack` in a way that `/tmp/ra-sysroot-hack/library/std/lib.rs` exists and `/tmp/ra-sysroot-hack/Cargo.toml` is [the one from this comment](https://github.com/rust-lang/rust-analyzer/issues/7637#issuecomment-1495008329). I will add the symlink code if we decide that this approach is not a dead end. It seems to somehow work on my system. Go to definition into std dependencies works, type checking can look through fields if I make them public and `cfg_if` appears to work (I tested it by hovering both sides and seeing that the correct one is enabled). Though finding layout of `HashMap` didn't work. Please try it and let me know if I should go forward in this direction or not.
bors 2023-04-21
parent 5b66550 · parent 39715ce · commit 0289dfa
-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 a38ab4f628..dfd0b2abc3 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 6c468f5ee6..e3a2de927c 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 9439d94f03..5518b6bc7f 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))
}