Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #16356 - Veykril:sysroot-metadata, r=Veykril
internal: Add unstable config for loading the sysroot sources via `cargo metadata` cc https://github.com/rust-lang/rust-analyzer/issues/7637 This takes the approach of having `cargo metadata` generate a lock file that we then delete again, hence why this is behind a flag. If people need this for their workflow they can just enable it, if not, they are probably better off keeping it disabled. [example](https://dreampuf.github.io/GraphvizOnline/#digraph%20rust_analyzer_crate_graph%20%7B%0A%20%20%20%20_0%5Blabel%3D%22core%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_17%5Blabel%3D%22ra_playground%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_14%5Blabel%3D%22getopts%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_11%5Blabel%3D%22std_detect%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_8%5Blabel%3D%22unwind%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_5%5Blabel%3D%22hashbrown%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_2%5Blabel%3D%22alloc%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_16%5Blabel%3D%22test%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_13%5Blabel%3D%22unicode_width%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_10%5Blabel%3D%22rustc_demangle%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_7%5Blabel%3D%22panic_abort%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_4%5Blabel%3D%22cfg_if%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_1%5Blabel%3D%22compiler_builtins%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_18%5Blabel%3D%22build_script_build%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_15%5Blabel%3D%22proc_macro%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_12%5Blabel%3D%22std%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_9%5Blabel%3D%22panic_unwind%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_6%5Blabel%3D%22libc%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_3%5Blabel%3D%22allocator_api2%22%5D%5Bshape%3D%22box%22%5D%3B%0A%20%20%20%20_17%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_17%20-%3E%20_2%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_17%20-%3E%20_12%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_17%20-%3E%20_15%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_17%20-%3E%20_16%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_14%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_14%20-%3E%20_12%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_14%20-%3E%20_13%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_11%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_11%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_11%20-%3E%20_2%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_11%20-%3E%20_4%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_11%20-%3E%20_6%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_8%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_8%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_8%20-%3E%20_4%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_8%20-%3E%20_6%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_5%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_5%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_5%20-%3E%20_2%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_5%20-%3E%20_3%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_2%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_2%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_16%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_16%20-%3E%20_7%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_16%20-%3E%20_9%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_16%20-%3E%20_12%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_16%20-%3E%20_14%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_13%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_13%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_13%20-%3E%20_12%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_10%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_10%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_7%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_7%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_7%20-%3E%20_2%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_7%20-%3E%20_4%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_7%20-%3E%20_6%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_1%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_18%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_18%20-%3E%20_2%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_18%20-%3E%20_12%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_18%20-%3E%20_15%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_18%20-%3E%20_16%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_15%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_15%20-%3E%20_12%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_2%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_4%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_4%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_5%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_6%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_7%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_8%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_9%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_10%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_12%20-%3E%20_11%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_9%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_9%20-%3E%20_1%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_9%20-%3E%20_2%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_9%20-%3E%20_4%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_9%20-%3E%20_6%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_9%20-%3E%20_8%5Blabel%3D%22%22%5D%3B%0A%20%20%20%20_6%20-%3E%20_0%5Blabel%3D%22%22%5D%3B%0A%7D%0A) ![image](https://github.com/rust-lang/rust-analyzer/assets/3757771/7709bb38-d948-4106-82c2-9b76677620bd) hashbrown resolves as a dependency now
bors 2024-01-15
parent 0b19e48 · parent c7eb52d · commit a616c4d
-rw-r--r--crates/base-db/src/input.rs42
-rw-r--r--crates/project-model/src/cargo_workspace.rs2
-rw-r--r--crates/project-model/src/sysroot.rs275
-rw-r--r--crates/project-model/src/tests.rs63
-rw-r--r--crates/project-model/src/workspace.rs317
-rw-r--r--crates/project-model/test_data/output/rust_project_hello_world_project_model.txt2
-rw-r--r--crates/rust-analyzer/src/cli/rustc_tests.rs9
-rw-r--r--crates/rust-analyzer/src/config.rs9
-rw-r--r--crates/rust-analyzer/src/reload.rs2
-rw-r--r--docs/user/generated_config.adoc10
-rw-r--r--editors/code/package.json5
11 files changed, 497 insertions, 239 deletions
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index e45a81238a..8c43f97b91 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -9,7 +9,7 @@
use std::{fmt, mem, ops, str::FromStr};
use cfg::CfgOptions;
-use la_arena::{Arena, Idx};
+use la_arena::{Arena, Idx, RawIdx};
use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version;
use syntax::SmolStr;
@@ -157,6 +157,10 @@ impl CrateOrigin {
pub fn is_lib(&self) -> bool {
matches!(self, CrateOrigin::Library { .. })
}
+
+ pub fn is_lang(&self) -> bool {
+ matches!(self, CrateOrigin::Lang { .. })
+ }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -174,7 +178,7 @@ impl From<&str> for LangCrateOrigin {
match s {
"alloc" => LangCrateOrigin::Alloc,
"core" => LangCrateOrigin::Core,
- "proc-macro" => LangCrateOrigin::ProcMacro,
+ "proc-macro" | "proc_macro" => LangCrateOrigin::ProcMacro,
"std" => LangCrateOrigin::Std,
"test" => LangCrateOrigin::Test,
_ => LangCrateOrigin::Other,
@@ -522,7 +526,7 @@ impl CrateGraph {
self.arena.iter().map(|(idx, _)| idx)
}
- // FIXME: used for `handle_hack_cargo_workspace`, should be removed later
+ // 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()
@@ -619,7 +623,12 @@ impl CrateGraph {
/// This will deduplicate the crates of the graph where possible.
/// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id.
/// If the crate dependencies were sorted, the resulting graph from this `extend` call will also have the crate dependencies sorted.
- pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) {
+ pub fn extend(
+ &mut self,
+ mut other: CrateGraph,
+ proc_macros: &mut ProcMacroPaths,
+ on_finished: impl FnOnce(&FxHashMap<CrateId, CrateId>),
+ ) {
let topo = other.crates_in_topological_order();
let mut id_map: FxHashMap<CrateId, CrateId> = FxHashMap::default();
for topo in topo {
@@ -670,6 +679,8 @@ impl CrateGraph {
*proc_macros =
mem::take(proc_macros).into_iter().map(|(id, macros)| (id_map[&id], macros)).collect();
+
+ on_finished(&id_map);
}
fn find_path(
@@ -721,6 +732,29 @@ impl CrateGraph {
fn hacky_find_crate<'a>(&'a self, display_name: &'a str) -> impl Iterator<Item = CrateId> + 'a {
self.iter().filter(move |it| self[*it].display_name.as_deref() == Some(display_name))
}
+
+ /// 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>> {
+ let mut id_map = vec![None; self.arena.len()];
+ self.arena = std::mem::take(&mut self.arena)
+ .into_iter()
+ .filter_map(|(id, data)| if to_keep.contains(&id) { Some((id, data)) } else { None })
+ .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)));
+ data
+ })
+ .collect();
+ for (_, data) in self.arena.iter_mut() {
+ data.dependencies.iter_mut().for_each(|dep| {
+ dep.crate_id =
+ id_map[dep.crate_id.into_raw().into_u32() as usize].expect("crate was filtered")
+ });
+ }
+ id_map
+ }
}
impl ops::Index<CrateId> for CrateGraph {
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs
index bc1fcd08e2..dba9edab3c 100644
--- a/crates/project-model/src/cargo_workspace.rs
+++ b/crates/project-model/src/cargo_workspace.rs
@@ -82,6 +82,8 @@ pub struct CargoConfig {
pub target: Option<String>,
/// Sysroot loading behavior
pub sysroot: Option<RustLibSource>,
+ /// Whether to invoke `cargo metadata` on the sysroot crate.
+ pub sysroot_query_metadata: bool,
pub sysroot_src: Option<AbsPathBuf>,
/// rustc private crate source
pub rustc_source: Option<RustLibSource>,
diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs
index d52e448d74..fb5e8c365a 100644
--- a/crates/project-model/src/sysroot.rs
+++ b/crates/project-model/src/sysroot.rs
@@ -8,6 +8,7 @@ use std::{env, fs, iter, ops, path::PathBuf, process::Command};
use anyhow::{format_err, Context, Result};
use base_db::CrateName;
+use itertools::Itertools;
use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashMap;
@@ -18,42 +19,29 @@ use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath};
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>,
+ mode: SysrootMode,
}
-pub(crate) type SysrootCrate = Idx<SysrootCrateData>;
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub(crate) enum SysrootMode {
+ Workspace(CargoWorkspace),
+ Stitched(Stitched),
+}
#[derive(Debug, Clone, Eq, PartialEq)]
-pub struct SysrootCrateData {
- pub name: String,
- pub root: ManifestPath,
- pub deps: Vec<SysrootCrate>,
+pub(crate) struct Stitched {
+ crates: Arena<SysrootCrateData>,
}
-impl ops::Index<SysrootCrate> for Sysroot {
+impl ops::Index<SysrootCrate> for Stitched {
type Output = SysrootCrateData;
fn index(&self, index: SysrootCrate) -> &SysrootCrateData {
&self.crates[index]
}
}
-impl Sysroot {
- /// Returns sysroot "root" directory, where `bin/`, `etc/`, `lib/`, `libexec/`
- /// subfolder live, like:
- /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu`
- pub fn root(&self) -> &AbsPath {
- &self.root
- }
-
- /// Returns the sysroot "source" directory, where stdlib sources are located, like:
- /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library`
- pub fn src_root(&self) -> &AbsPath {
- &self.src_root
- }
-
- pub fn public_deps(&self) -> impl Iterator<Item = (CrateName, SysrootCrate, bool)> + '_ {
+impl Stitched {
+ pub(crate) fn public_deps(&self) -> impl Iterator<Item = (CrateName, SysrootCrate, bool)> + '_ {
// core is added as a dependency before std in order to
// mimic rustcs dependency order
["core", "alloc", "std"]
@@ -65,20 +53,56 @@ impl Sysroot {
})
}
- pub fn proc_macro(&self) -> Option<SysrootCrate> {
+ pub(crate) fn proc_macro(&self) -> Option<SysrootCrate> {
self.by_name("proc_macro")
}
- pub fn crates(&self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + '_ {
+ pub(crate) fn crates(&self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + '_ {
self.crates.iter().map(|(id, _data)| id)
}
+ fn by_name(&self, name: &str) -> Option<SysrootCrate> {
+ let (id, _data) = self.crates.iter().find(|(_id, data)| data.name == name)?;
+ Some(id)
+ }
+}
+
+pub(crate) type SysrootCrate = Idx<SysrootCrateData>;
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub(crate) struct SysrootCrateData {
+ pub(crate) name: String,
+ pub(crate) root: ManifestPath,
+ pub(crate) deps: Vec<SysrootCrate>,
+}
+
+impl Sysroot {
+ /// Returns sysroot "root" directory, where `bin/`, `etc/`, `lib/`, `libexec/`
+ /// subfolder live, like:
+ /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu`
+ pub fn root(&self) -> &AbsPath {
+ &self.root
+ }
+
+ /// Returns the sysroot "source" directory, where stdlib sources are located, like:
+ /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library`
+ pub fn src_root(&self) -> &AbsPath {
+ &self.src_root
+ }
+
pub fn is_empty(&self) -> bool {
- self.crates.is_empty()
+ match &self.mode {
+ SysrootMode::Workspace(ws) => ws.packages().next().is_none(),
+ SysrootMode::Stitched(stitched) => stitched.crates.is_empty(),
+ }
}
pub fn loading_warning(&self) -> Option<String> {
- if self.by_name("core").is_none() {
+ let has_core = match &self.mode {
+ SysrootMode::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"),
+ SysrootMode::Stitched(stitched) => stitched.by_name("core").is_some(),
+ };
+ if !has_core {
let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
" (`RUST_SRC_PATH` might be incorrect, try unsetting it)"
} else {
@@ -92,27 +116,43 @@ impl Sysroot {
None
}
}
+
+ pub fn num_packages(&self) -> usize {
+ match &self.mode {
+ SysrootMode::Workspace(ws) => ws.packages().count(),
+ SysrootMode::Stitched(c) => c.crates().count(),
+ }
+ }
+
+ pub(crate) fn mode(&self) -> &SysrootMode {
+ &self.mode
+ }
}
// FIXME: Expose a builder api as loading the sysroot got way too modular and complicated.
impl Sysroot {
/// Attempts to discover the toolchain's sysroot from the given `dir`.
- pub fn discover(dir: &AbsPath, extra_env: &FxHashMap<String, String>) -> Result<Sysroot> {
+ pub fn discover(
+ dir: &AbsPath,
+ extra_env: &FxHashMap<String, String>,
+ metadata: bool,
+ ) -> Result<Sysroot> {
tracing::debug!("discovering sysroot for {dir}");
let sysroot_dir = discover_sysroot_dir(dir, extra_env)?;
let sysroot_src_dir =
discover_sysroot_src_dir_or_add_component(&sysroot_dir, dir, extra_env)?;
- Ok(Sysroot::load(sysroot_dir, sysroot_src_dir))
+ Ok(Sysroot::load(sysroot_dir, sysroot_src_dir, metadata))
}
pub fn discover_with_src_override(
current_dir: &AbsPath,
extra_env: &FxHashMap<String, String>,
src: AbsPathBuf,
+ metadata: bool,
) -> Result<Sysroot> {
tracing::debug!("discovering sysroot for {current_dir}");
let sysroot_dir = discover_sysroot_dir(current_dir, extra_env)?;
- Ok(Sysroot::load(sysroot_dir, src))
+ Ok(Sysroot::load(sysroot_dir, src, metadata))
}
pub fn discover_rustc_src(&self) -> Option<ManifestPath> {
@@ -131,49 +171,132 @@ impl Sysroot {
}
}
- pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result<Sysroot> {
+ pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf, metadata: bool) -> Result<Sysroot> {
let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir).ok_or_else(|| {
format_err!("can't load standard library from sysroot path {sysroot_dir}")
})?;
- Ok(Sysroot::load(sysroot_dir, sysroot_src_dir))
+ Ok(Sysroot::load(sysroot_dir, sysroot_src_dir, metadata))
}
- 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,
- };
+ pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf, metadata: bool) -> Sysroot {
+ if metadata {
+ let sysroot: Option<_> = (|| {
+ let sysroot_cargo_toml = ManifestPath::try_from(
+ AbsPathBuf::try_from(&*format!("{sysroot_src_dir}/sysroot/Cargo.toml")).ok()?,
+ )
+ .ok()?;
+ let current_dir =
+ AbsPathBuf::try_from(&*format!("{sysroot_src_dir}/sysroot")).ok()?;
+ let res = CargoWorkspace::fetch_metadata(
+ &sysroot_cargo_toml,
+ &current_dir,
+ &CargoConfig::default(),
+ &|_| (),
+ )
+ .map_err(|e| {
+ tracing::error!(
+ "failed to load sysroot `{sysroot_src_dir}/sysroot/Cargo.toml`: {}",
+ e
+ );
+ e
+ });
+ if let Err(e) =
+ std::fs::remove_file(&format!("{sysroot_src_dir}/sysroot/Cargo.lock"))
+ {
+ tracing::error!(
+ "failed to remove sysroot `{sysroot_src_dir}/sysroot/Cargo.lock`: {}",
+ e
+ )
+ }
+ let mut res = res.ok()?;
+
+ // Patch out `rustc-std-workspace-*` crates to point to the real crates.
+ // This is done prior to `CrateGraph` construction to avoid having duplicate `std` targets.
+
+ let mut fake_core = None;
+ let mut fake_alloc = None;
+ let mut fake_std = None;
+ let mut real_core = None;
+ let mut real_alloc = None;
+ let mut real_std = None;
+ res.packages.iter().enumerate().for_each(|(idx, package)| {
+ match package.name.strip_prefix("rustc-std-workspace-") {
+ Some("core") => fake_core = Some((idx, package.id.clone())),
+ Some("alloc") => fake_alloc = Some((idx, package.id.clone())),
+ Some("std") => fake_std = Some((idx, package.id.clone())),
+ Some(_) => {
+ tracing::warn!("unknown rustc-std-workspace-* crate: {}", package.name)
+ }
+ None => match &*package.name {
+ "core" => real_core = Some(package.id.clone()),
+ "alloc" => real_alloc = Some(package.id.clone()),
+ "std" => real_std = Some(package.id.clone()),
+ _ => (),
+ },
+ }
+ });
+
+ let patches =
+ [fake_core.zip(real_core), fake_alloc.zip(real_alloc), fake_std.zip(real_std)]
+ .into_iter()
+ .flatten();
+
+ let resolve = res.resolve.as_mut().expect("metadata executed with deps");
+ let mut remove_nodes = vec![];
+ for (idx, node) in resolve.nodes.iter_mut().enumerate() {
+ // Replace them in the dependency list
+ node.deps.iter_mut().for_each(|dep| {
+ if let Some((_, real)) =
+ patches.clone().find(|((_, fake_id), _)| *fake_id == dep.pkg)
+ {
+ dep.pkg = real;
+ }
+ });
+ if patches.clone().any(|((_, fake), _)| fake == node.id) {
+ remove_nodes.push(idx);
+ }
+ }
+ // Remove the fake ones from the resolve data
+ remove_nodes.into_iter().rev().for_each(|r| {
+ resolve.nodes.remove(r);
+ });
+ // Remove the fake ones from the packages
+ patches.map(|((r, _), _)| r).sorted().rev().for_each(|r| {
+ res.packages.remove(r);
+ });
+
+ res.workspace_members = res
+ .packages
+ .iter()
+ .filter_map(|package| {
+ RELEVANT_SYSROOT_CRATES
+ .contains(&&*package.name)
+ .then(|| package.id.clone())
+ })
+ .collect();
+ let cargo_workspace = CargoWorkspace::new(res);
+ Some(Sysroot {
+ root: sysroot_dir.clone(),
+ src_root: sysroot_src_dir.clone(),
+ mode: SysrootMode::Workspace(cargo_workspace),
+ })
+ })();
+ if let Some(sysroot) = sysroot {
+ return sysroot;
+ }
+ }
+ let mut stitched = Stitched { crates: Arena::default() };
for path in SYSROOT_CRATES.trim().lines() {
let name = path.split('/').last().unwrap();
let root = [format!("{path}/src/lib.rs"), format!("lib{path}/lib.rs")]
.into_iter()
- .map(|it| sysroot.src_root.join(it))
+ .map(|it| sysroot_src_dir.join(it))
.filter_map(|it| ManifestPath::try_from(it).ok())
.find(|it| fs::metadata(it).is_ok());
if let Some(root) = root {
- sysroot.crates.alloc(SysrootCrateData {
+ stitched.crates.alloc(SysrootCrateData {
name: name.into(),
root,
deps: Vec::new(),
@@ -181,36 +304,34 @@ impl Sysroot {
}
}
- if let Some(std) = sysroot.by_name("std") {
+ if let Some(std) = stitched.by_name("std") {
for dep in STD_DEPS.trim().lines() {
- if let Some(dep) = sysroot.by_name(dep) {
- sysroot.crates[std].deps.push(dep)
+ if let Some(dep) = stitched.by_name(dep) {
+ stitched.crates[std].deps.push(dep)
}
}
}
- if let Some(alloc) = sysroot.by_name("alloc") {
+ if let Some(alloc) = stitched.by_name("alloc") {
for dep in ALLOC_DEPS.trim().lines() {
- if let Some(dep) = sysroot.by_name(dep) {
- sysroot.crates[alloc].deps.push(dep)
+ if let Some(dep) = stitched.by_name(dep) {
+ stitched.crates[alloc].deps.push(dep)
}
}
}
- if let Some(proc_macro) = sysroot.by_name("proc_macro") {
+ if let Some(proc_macro) = stitched.by_name("proc_macro") {
for dep in PROC_MACRO_DEPS.trim().lines() {
- if let Some(dep) = sysroot.by_name(dep) {
- sysroot.crates[proc_macro].deps.push(dep)
+ if let Some(dep) = stitched.by_name(dep) {
+ stitched.crates[proc_macro].deps.push(dep)
}
}
}
-
- sysroot
- }
-
- fn by_name(&self, name: &str) -> Option<SysrootCrate> {
- let (id, _data) = self.crates.iter().find(|(_id, data)| data.name == name)?;
- Some(id)
+ Sysroot {
+ root: sysroot_dir,
+ src_root: sysroot_src_dir,
+ mode: SysrootMode::Stitched(stitched),
+ }
}
}
@@ -318,3 +439,5 @@ test";
const PROC_MACRO_DEPS: &str = "
std
core";
+
+const RELEVANT_SYSROOT_CRATES: &[&str] = &["core", "alloc", "std", "test", "proc_macro"];
diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs
index 4887b29815..9e6b00d938 100644
--- a/crates/project-model/src/tests.rs
+++ b/crates/project-model/src/tests.rs
@@ -38,7 +38,7 @@ fn load_cargo_with_overrides(
to_crate_graph(project_workspace)
}
-fn load_cargo_with_sysroot(
+fn load_cargo_with_fake_sysroot(
file_map: &mut FxHashMap<AbsPathBuf, FileId>,
file: &str,
) -> (CrateGraph, ProcMacroPaths) {
@@ -125,7 +125,7 @@ fn get_fake_sysroot() -> Sysroot {
// fake sysroot, so we give them both the same path:
let sysroot_dir = AbsPathBuf::assert(sysroot_path);
let sysroot_src_dir = sysroot_dir.clone();
- Sysroot::load(sysroot_dir, sysroot_src_dir)
+ Sysroot::load(sysroot_dir, sysroot_src_dir, false)
}
fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
@@ -225,12 +225,12 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() {
#[test]
fn crate_graph_dedup_identical() {
let (mut crate_graph, proc_macros) =
- load_cargo_with_sysroot(&mut Default::default(), "regex-metadata.json");
+ load_cargo_with_fake_sysroot(&mut Default::default(), "regex-metadata.json");
crate_graph.sort_deps();
let (d_crate_graph, mut d_proc_macros) = (crate_graph.clone(), proc_macros.clone());
- crate_graph.extend(d_crate_graph.clone(), &mut d_proc_macros);
+ crate_graph.extend(d_crate_graph.clone(), &mut d_proc_macros, |_| ());
assert!(crate_graph.iter().eq(d_crate_graph.iter()));
assert_eq!(proc_macros, d_proc_macros);
}
@@ -239,14 +239,14 @@ fn crate_graph_dedup_identical() {
fn crate_graph_dedup() {
let path_map = &mut Default::default();
let (mut crate_graph, _proc_macros) =
- load_cargo_with_sysroot(path_map, "ripgrep-metadata.json");
+ load_cargo_with_fake_sysroot(path_map, "ripgrep-metadata.json");
assert_eq!(crate_graph.iter().count(), 81);
crate_graph.sort_deps();
let (regex_crate_graph, mut regex_proc_macros) =
- load_cargo_with_sysroot(path_map, "regex-metadata.json");
+ load_cargo_with_fake_sysroot(path_map, "regex-metadata.json");
assert_eq!(regex_crate_graph.iter().count(), 60);
- crate_graph.extend(regex_crate_graph, &mut regex_proc_macros);
+ crate_graph.extend(regex_crate_graph, &mut regex_proc_macros, |_| ());
assert_eq!(crate_graph.iter().count(), 118);
}
@@ -254,12 +254,12 @@ fn crate_graph_dedup() {
fn test_deduplicate_origin_dev() {
let path_map = &mut Default::default();
let (mut crate_graph, _proc_macros) =
- load_cargo_with_sysroot(path_map, "deduplication_crate_graph_A.json");
+ load_cargo_with_fake_sysroot(path_map, "deduplication_crate_graph_A.json");
crate_graph.sort_deps();
let (crate_graph_1, mut _proc_macros_2) =
- load_cargo_with_sysroot(path_map, "deduplication_crate_graph_B.json");
+ load_cargo_with_fake_sysroot(path_map, "deduplication_crate_graph_B.json");
- crate_graph.extend(crate_graph_1, &mut _proc_macros_2);
+ crate_graph.extend(crate_graph_1, &mut _proc_macros_2, |_| ());
let mut crates_named_p2 = vec![];
for id in crate_graph.iter() {
@@ -280,12 +280,12 @@ fn test_deduplicate_origin_dev() {
fn test_deduplicate_origin_dev_rev() {
let path_map = &mut Default::default();
let (mut crate_graph, _proc_macros) =
- load_cargo_with_sysroot(path_map, "deduplication_crate_graph_B.json");
+ load_cargo_with_fake_sysroot(path_map, "deduplication_crate_graph_B.json");
crate_graph.sort_deps();
let (crate_graph_1, mut _proc_macros_2) =
- load_cargo_with_sysroot(path_map, "deduplication_crate_graph_A.json");
+ load_cargo_with_fake_sysroot(path_map, "deduplication_crate_graph_A.json");
- crate_graph.extend(crate_graph_1, &mut _proc_macros_2);
+ crate_graph.extend(crate_graph_1, &mut _proc_macros_2, |_| ());
let mut crates_named_p2 = vec![];
for id in crate_graph.iter() {
@@ -301,3 +301,40 @@ fn test_deduplicate_origin_dev_rev() {
let p2 = crates_named_p2[0];
assert!(p2.origin.is_local());
}
+
+#[test]
+fn smoke_test_real_sysroot_cargo() {
+ if std::env::var("SYSROOT_CARGO_METADATA").is_err() {
+ return;
+ }
+ let file_map = &mut FxHashMap::<AbsPathBuf, FileId>::default();
+ let meta = get_test_json_file("hello-world-metadata.json");
+
+ let cargo_workspace = CargoWorkspace::new(meta);
+ let sysroot = Ok(Sysroot::discover(
+ AbsPath::assert(Path::new(env!("CARGO_MANIFEST_DIR"))),
+ &Default::default(),
+ true,
+ )
+ .unwrap());
+
+ let project_workspace = ProjectWorkspace::Cargo {
+ cargo: cargo_workspace,
+ build_scripts: WorkspaceBuildScripts::default(),
+ sysroot,
+ rustc: Err(None),
+ rustc_cfg: Vec::new(),
+ cfg_overrides: Default::default(),
+ toolchain: None,
+ target_layout: Err("target_data_layout not loaded".into()),
+ };
+ project_workspace.to_crate_graph(
+ &mut {
+ |path| {
+ let len = file_map.len();
+ Some(*file_map.entry(path.to_path_buf()).or_insert(FileId::from_raw(len as u32)))
+ }
+ },
+ &Default::default(),
+ );
+}
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index c04eddc56f..679f219dcc 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -9,7 +9,7 @@ use base_db::{
CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, DependencyKind,
Edition, Env, FileId, LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult,
};
-use cfg::{CfgDiff, CfgOptions};
+use cfg::{CfgAtom, CfgDiff, CfgOptions};
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version;
@@ -22,7 +22,7 @@ use crate::{
cfg_flag::CfgFlag,
project_json::Crate,
rustc_cfg::{self, RustcCfgConfig},
- sysroot::SysrootCrate,
+ sysroot::{SysrootCrate, SysrootMode},
target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath,
Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
};
@@ -130,7 +130,7 @@ impl fmt::Debug for ProjectWorkspace {
let mut debug_struct = f.debug_struct("Json");
debug_struct.field("n_crates", &project.n_crates());
if let Ok(sysroot) = sysroot {
- debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
+ debug_struct.field("n_sysroot_crates", &sysroot.num_packages());
}
debug_struct.field("toolchain", &toolchain);
debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
@@ -208,23 +208,23 @@ impl ProjectWorkspace {
let sysroot = match (&config.sysroot, &config.sysroot_src) {
(Some(RustLibSource::Path(path)), None) => {
- Sysroot::with_sysroot_dir(path.clone()).map_err(|e| {
+ Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata).map_err(|e| {
Some(format!("Failed to find sysroot at {path}:{e}"))
})
}
(Some(RustLibSource::Discover), None) => {
- Sysroot::discover(cargo_toml.parent(), &config.extra_env).map_err(|e| {
+ Sysroot::discover(cargo_toml.parent(), &config.extra_env, config.sysroot_query_metadata).map_err(|e| {
Some(format!("Failed to find sysroot for Cargo.toml file {cargo_toml}. Is rust-src installed? {e}"))
})
}
(Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => {
- Ok(Sysroot::load(sysroot.clone(), sysroot_src.clone()))
+ Ok(Sysroot::load(sysroot.clone(), sysroot_src.clone(), config.sysroot_query_metadata))
}
(Some(RustLibSource::Discover), Some(sysroot_src)) => {
Sysroot::discover_with_src_override(
cargo_toml.parent(),
&config.extra_env,
- sysroot_src.clone(),
+ sysroot_src.clone(), config.sysroot_query_metadata,
).map_err(|e| {
Some(format!("Failed to find sysroot for Cargo.toml file {cargo_toml}. Is rust-src installed? {e}"))
})
@@ -317,12 +317,12 @@ impl ProjectWorkspace {
toolchain: Option<Version>,
) -> ProjectWorkspace {
let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
- (Some(sysroot), Some(sysroot_src)) => Ok(Sysroot::load(sysroot, sysroot_src)),
+ (Some(sysroot), Some(sysroot_src)) => Ok(Sysroot::load(sysroot, sysroot_src, false)),
(Some(sysroot), None) => {
// assume sysroot is structured like rustup's and guess `sysroot_src`
let sysroot_src =
sysroot.join("lib").join("rustlib").join("src").join("rust").join("library");
- Ok(Sysroot::load(sysroot, sysroot_src))
+ Ok(Sysroot::load(sysroot, sysroot_src, false))
}
(None, Some(sysroot_src)) => {
// assume sysroot is structured like rustup's and guess `sysroot`
@@ -330,7 +330,7 @@ impl ProjectWorkspace {
for _ in 0..5 {
sysroot.pop();
}
- Ok(Sysroot::load(sysroot, sysroot_src))
+ Ok(Sysroot::load(sysroot, sysroot_src, false))
}
(None, None) => Err(None),
};
@@ -354,16 +354,22 @@ impl ProjectWorkspace {
config: &CargoConfig,
) -> anyhow::Result<ProjectWorkspace> {
let sysroot = match &config.sysroot {
- Some(RustLibSource::Path(path)) => Sysroot::with_sysroot_dir(path.clone())
- .map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}"))),
+ Some(RustLibSource::Path(path)) => {
+ Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata)
+ .map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}")))
+ }
Some(RustLibSource::Discover) => {
let dir = &detached_files
.first()
.and_then(|it| it.parent())
.ok_or_else(|| format_err!("No detached files to load"))?;
- Sysroot::discover(dir, &config.extra_env).map_err(|e| {
- Some(format!("Failed to find sysroot for {dir}. Is rust-src installed? {e}"))
- })
+ Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata).map_err(
+ |e| {
+ Some(format!(
+ "Failed to find sysroot for {dir}. Is rust-src installed? {e}"
+ ))
+ },
+ )
}
None => Err(None),
};
@@ -494,13 +500,43 @@ impl ProjectWorkspace {
/// The return type contains the path and whether or not
/// the root is a member of the current workspace
pub fn to_roots(&self) -> Vec<PackageRoot> {
- let mk_sysroot = |sysroot: Result<&Sysroot, _>, project_root: Option<&AbsPath>| {
- sysroot.map(|sysroot| PackageRoot {
- // mark the sysroot as mutable if it is located inside of the project
- is_local: project_root
- .map_or(false, |project_root| sysroot.src_root().starts_with(project_root)),
- include: vec![sysroot.src_root().to_path_buf()],
- exclude: Vec::new(),
+ let mk_sysroot = |sysroot: Result<_, _>, project_root: Option<&AbsPath>| {
+ let project_root = project_root.map(ToOwned::to_owned);
+ sysroot.into_iter().flat_map(move |sysroot: &Sysroot| {
+ let mut r = match sysroot.mode() {
+ SysrootMode::Workspace(ws) => ws
+ .packages()
+ .filter_map(|pkg| {
+ if ws[pkg].is_local {
+ // the local ones are included in the main `PackageRoot`` below
+ return None;
+ }
+ let pkg_root = ws[pkg].manifest.parent().to_path_buf();
+
+ let include = vec![pkg_root.clone()];
+
+ let exclude = vec![
+ pkg_root.join(".git"),
+ pkg_root.join("target"),
+ pkg_root.join("tests"),
+ pkg_root.join("examples"),
+ pkg_root.join("benches"),
+ ];
+ Some(PackageRoot { is_local: false, include, exclude })
+ })
+ .collect(),
+ SysrootMode::Stitched(_) => vec![],
+ };
+
+ r.push(PackageRoot {
+ // mark the sysroot as mutable if it is located inside of the project
+ is_local: project_root
+ .as_ref()
+ .map_or(false, |project_root| sysroot.src_root().starts_with(project_root)),
+ include: vec![sysroot.src_root().to_path_buf()],
+ exclude: Vec::new(),
+ });
+ r
})
};
match self {
@@ -588,16 +624,16 @@ impl ProjectWorkspace {
pub fn n_packages(&self) -> usize {
match self {
ProjectWorkspace::Json { project, sysroot, .. } => {
- let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
+ let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
sysroot_package_len + project.n_crates()
}
ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
let rustc_package_len = rustc.as_ref().map_or(0, |(it, _)| it.packages().len());
- let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
+ let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
cargo.packages().len() + sysroot_package_len + rustc_package_len
}
ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
- let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
+ let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
sysroot_package_len + files.len()
}
}
@@ -638,7 +674,6 @@ 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())),
@@ -849,8 +884,6 @@ fn cargo_to_crate_graph(
sysroot: Option<&Sysroot>,
rustc_cfg: Vec<CfgFlag>,
override_cfg: &CfgOverrides,
- // Don't compute cfg and use this if present, only used for the sysroot experiment hack
- forced_cfg: Option<CfgOptions>,
build_scripts: &WorkspaceBuildScripts,
target_layout: TargetLayoutLoadResult,
toolchain: Option<&Version>,
@@ -883,7 +916,7 @@ fn cargo_to_crate_graph(
for pkg in cargo.packages() {
has_private |= cargo[pkg].metadata.rustc_private;
- let cfg_options = forced_cfg.clone().unwrap_or_else(|| {
+ let cfg_options = {
let mut cfg_options = cfg_options.clone();
// Add test cfg for local crates
@@ -908,7 +941,7 @@ fn cargo_to_crate_graph(
cfg_options.apply_diff(diff.clone());
};
cfg_options
- });
+ };
let mut lib_tgt = None;
for &tgt in cargo[pkg].targets.iter() {
@@ -1349,124 +1382,126 @@ fn sysroot_to_crate_graph(
toolchain: Option<&Version>,
) -> (SysrootPublicDeps, Option<CrateId>) {
let _p = profile::span("sysroot_to_crate_graph");
- let cfg_options = create_cfg_options(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,
- toolchain,
- 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(),
- toolchain.cloned(),
- );
- 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();
- if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
- add_dep(crate_graph, from, name, to, DependencyKind::Normal);
+ match sysroot.mode() {
+ SysrootMode::Workspace(cargo) => {
+ let (mut cg, mut pm) = cargo_to_crate_graph(
+ load,
+ None,
+ cargo,
+ None,
+ rustc_cfg,
+ &CfgOverrides::default(),
+ &WorkspaceBuildScripts::default(),
+ target_layout,
+ toolchain,
+ );
+
+ let mut pub_deps = vec![];
+ let mut libproc_macro = None;
+ let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap();
+ for (cid, c) in cg.iter_mut() {
+ // uninject `test` flag so `core` keeps working.
+ c.cfg_options.apply_diff(diff.clone());
+ // patch the origin
+ if c.origin.is_local() {
+ let lang_crate = LangCrateOrigin::from(
+ c.display_name.as_ref().map_or("", |it| it.canonical_name()),
+ );
+ c.origin = CrateOrigin::Lang(lang_crate);
+ match lang_crate {
+ LangCrateOrigin::Test
+ | LangCrateOrigin::Alloc
+ | LangCrateOrigin::Core
+ | LangCrateOrigin::Std => pub_deps.push((
+ CrateName::normalize_dashes(&lang_crate.to_string()),
+ cid,
+ !matches!(lang_crate, LangCrateOrigin::Test),
+ )),
+ LangCrateOrigin::ProcMacro => libproc_macro = Some(cid),
+ LangCrateOrigin::Other => (),
+ }
+ }
+ }
+
+ let mut marker_set = vec![];
+ for &(_, cid, _) in pub_deps.iter() {
+ marker_set.extend(cg.transitive_deps(cid));
}
+ if let Some(cid) = libproc_macro {
+ marker_set.extend(cg.transitive_deps(cid));
+ }
+
+ marker_set.sort();
+ marker_set.dedup();
+
+ // Remove all crates except the ones we are interested in to keep the sysroot graph small.
+ let removed_mapping = cg.remove_crates_except(&marker_set);
+
+ crate_graph.extend(cg, &mut pm, |mapping| {
+ // Map the id through the removal mapping first, then through the crate graph extension mapping.
+ pub_deps.iter_mut().for_each(|(_, cid, _)| {
+ *cid = mapping[&removed_mapping[cid.into_raw().into_u32() as usize].unwrap()]
+ });
+ if let Some(libproc_macro) = &mut libproc_macro {
+ *libproc_macro = mapping
+ [&removed_mapping[libproc_macro.into_raw().into_u32() as usize].unwrap()];
+ }
+ });
+
+ (SysrootPublicDeps { deps: pub_deps }, libproc_macro)
}
- }
+ SysrootMode::Stitched(stitched) => {
+ let cfg_options = create_cfg_options(rustc_cfg.clone());
+ let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = stitched
+ .crates()
+ .filter_map(|krate| {
+ let file_id = load(&stitched[krate].root)?;
- let public_deps = SysrootPublicDeps {
- deps: sysroot
- .public_deps()
- .filter_map(|(name, idx, prelude)| Some((name, *sysroot_crates.get(&idx)?, prelude)))
- .collect::<Vec<_>>(),
- };
+ let env = Env::default();
+ let display_name =
+ CrateDisplayName::from_canonical_name(stitched[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(&*stitched[krate].name)),
+ target_layout.clone(),
+ toolchain.cloned(),
+ );
+ Some((krate, crate_id))
+ })
+ .collect();
+
+ for from in stitched.crates() {
+ for &to in stitched[from].deps.iter() {
+ let name = CrateName::new(&stitched[to].name).unwrap();
+ if let (Some(&from), Some(&to)) =
+ (sysroot_crates.get(&from), sysroot_crates.get(&to))
+ {
+ add_dep(crate_graph, from, name, to, DependencyKind::Normal);
+ }
+ }
+ }
- let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
- (public_deps, libproc_macro)
-}
+ let public_deps = SysrootPublicDeps {
+ deps: stitched
+ .public_deps()
+ .filter_map(|(name, idx, prelude)| {
+ Some((name, *sysroot_crates.get(&idx)?, prelude))
+ })
+ .collect::<Vec<_>>(),
+ };
-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>>,
- toolchain: Option<&Version>,
- 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,
- toolchain,
- );
- 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();
- }
- for (_, c) in crate_graph.iter_mut() {
- if c.origin.is_local() {
- // LangCrateOrigin::Other is good enough for a hack.
- c.origin = CrateOrigin::Lang(LangCrateOrigin::Other);
+ let libproc_macro =
+ stitched.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
+ (public_deps, libproc_macro)
}
}
- 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(
diff --git a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
index e35f0fc732..0df99534c5 100644
--- a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
+++ b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
@@ -182,7 +182,7 @@
},
],
origin: Lang(
- Other,
+ ProcMacro,
),
is_proc_macro: false,
target_layout: Err(
diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs
index b8f6138161..87bb3cbd34 100644
--- a/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -61,9 +61,12 @@ impl Tester {
cargo_config.sysroot = Some(RustLibSource::Discover);
let workspace = ProjectWorkspace::DetachedFiles {
files: vec![tmp_file.clone()],
- sysroot: Ok(
- Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env).unwrap()
- ),
+ sysroot: Ok(Sysroot::discover(
+ tmp_file.parent().unwrap(),
+ &cargo_config.extra_env,
+ false,
+ )
+ .unwrap()),
rustc_cfg: vec![],
};
let load_cargo_config = LoadCargoConfig {
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index fe009f82a7..3ec5d86966 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -135,6 +135,13 @@ config_data! {
///
/// This option does not take effect until rust-analyzer is restarted.
cargo_sysroot: Option<String> = "\"discover\"",
+ /// Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze
+ /// third-party dependencies of the standard libraries.
+ ///
+ /// This will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer
+ /// will attempt to clean up afterwards, but nevertheless requires the location to be
+ /// writable to.
+ cargo_sysrootQueryMetadata: bool = "false",
/// Relative path to the sysroot library sources. If left unset, this will default to
/// `{cargo.sysroot}/lib/rustlib/src/rust/library`.
///
@@ -1233,6 +1240,7 @@ impl Config {
});
let sysroot_src =
self.data.cargo_sysrootSrc.as_ref().map(|sysroot| self.root_path.join(sysroot));
+ let sysroot_query_metadata = self.data.cargo_sysrootQueryMetadata;
CargoConfig {
features: match &self.data.cargo_features {
@@ -1244,6 +1252,7 @@ impl Config {
},
target: self.data.cargo_target.clone(),
sysroot,
+ sysroot_query_metadata,
sysroot_src,
rustc_source,
cfg_overrides: project_model::CfgOverrides {
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 8e3fa7fa4d..24af5fb49c 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -516,7 +516,7 @@ impl GlobalState {
for ws in &**self.workspaces {
let (other, mut crate_proc_macros) =
ws.to_crate_graph(&mut load, &self.config.extra_env());
- crate_graph.extend(other, &mut crate_proc_macros);
+ crate_graph.extend(other, &mut crate_proc_macros, |_| {});
proc_macros.push(crate_proc_macros);
}
(crate_graph, proc_macros, crate_graph_file_dependencies)
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index ecc90abff1..1a2791954e 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -121,6 +121,16 @@ Unsetting this disables sysroot loading.
This option does not take effect until rust-analyzer is restarted.
--
+[[rust-analyzer.cargo.sysrootQueryMetadata]]rust-analyzer.cargo.sysrootQueryMetadata (default: `false`)::
++
+--
+Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze
+third-party dependencies of the standard libraries.
+
+This will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer
+will attempt to clean up afterwards, but nevertheless requires the location to be
+writable to.
+--
[[rust-analyzer.cargo.sysrootSrc]]rust-analyzer.cargo.sysrootSrc (default: `null`)::
+
--
diff --git a/editors/code/package.json b/editors/code/package.json
index 8307f6833e..e7ceee165c 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -648,6 +648,11 @@
"string"
]
},
+ "rust-analyzer.cargo.sysrootQueryMetadata": {
+ "markdownDescription": "Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze\nthird-party dependencies of the standard libraries.\n\nThis will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer\nwill attempt to clean up afterwards, but nevertheless requires the location to be\nwritable to.",
+ "default": false,
+ "type": "boolean"
+ },
"rust-analyzer.cargo.sysrootSrc": {
"markdownDescription": "Relative path to the sysroot library sources. If left unset, this will default to\n`{cargo.sysroot}/lib/rustlib/src/rust/library`.\n\nThis option does not take effect until rust-analyzer is restarted.",
"default": null,