Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/project-model/src/workspace.rs')
-rw-r--r--crates/project-model/src/workspace.rs651
1 files changed, 404 insertions, 247 deletions
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index d1e53e12ee..0aca620a67 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -2,53 +2,43 @@
//! metadata` or `rust-project.json`) into representation stored in the salsa
//! database -- `CrateGraph`.
-use std::{collections::VecDeque, fmt, fs, process::Command, sync::Arc};
+use std::{collections::VecDeque, fmt, fs, process::Command, sync};
use anyhow::{format_err, Context, Result};
use base_db::{
CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env,
- FileId, LangCrateOrigin, ProcMacroLoadResult, TargetLayoutLoadResult,
+ FileId, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, TargetLayoutLoadResult,
};
use cfg::{CfgDiff, CfgOptions};
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version;
-use stdx::{always, hash::NoHashHashMap};
+use stdx::always;
+use triomphe::Arc;
use crate::{
build_scripts::BuildScriptOutput,
cargo_workspace::{DepKind, PackageData, RustLibSource},
cfg_flag::CfgFlag,
+ project_json::Crate,
rustc_cfg,
sysroot::SysrootCrate,
target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath,
- Package, ProjectJson, ProjectManifest, Sysroot, TargetKind, WorkspaceBuildScripts,
+ Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
};
/// A set of cfg-overrides per crate.
-///
-/// `Wildcard(..)` is useful e.g. disabling `#[cfg(test)]` on all crates,
-/// without having to first obtain a list of all crates.
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub enum CfgOverrides {
- /// A single global set of overrides matching all crates.
- Wildcard(CfgDiff),
+#[derive(Default, Debug, Clone, Eq, PartialEq)]
+pub struct CfgOverrides {
+ /// A global set of overrides matching all crates.
+ pub global: CfgDiff,
/// A set of overrides matching specific crates.
- Selective(FxHashMap<String, CfgDiff>),
-}
-
-impl Default for CfgOverrides {
- fn default() -> Self {
- Self::Selective(FxHashMap::default())
- }
+ pub selective: FxHashMap<String, CfgDiff>,
}
impl CfgOverrides {
pub fn len(&self) -> usize {
- match self {
- CfgOverrides::Wildcard(_) => 1,
- CfgOverrides::Selective(hash_map) => hash_map.len(),
- }
+ self.global.len() + self.selective.iter().map(|(_, it)| it.len()).sum::<usize>()
}
}
@@ -82,7 +72,14 @@ pub enum ProjectWorkspace {
target_layout: Result<String, String>,
},
/// Project workspace was manually specified using a `rust-project.json` file.
- Json { project: ProjectJson, sysroot: Result<Sysroot, Option<String>>, rustc_cfg: Vec<CfgFlag> },
+ Json {
+ project: ProjectJson,
+ sysroot: Result<Sysroot, Option<String>>,
+ /// Holds cfg flags for the current target. We get those by running
+ /// `rustc --print cfg`.
+ rustc_cfg: Vec<CfgFlag>,
+ toolchain: Option<Version>,
+ },
// FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
// That's not the end user experience we should strive for.
// Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
@@ -96,6 +93,8 @@ pub enum ProjectWorkspace {
DetachedFiles {
files: Vec<AbsPathBuf>,
sysroot: Result<Sysroot, Option<String>>,
+ /// Holds cfg flags for the current target. We get those by running
+ /// `rustc --print cfg`.
rustc_cfg: Vec<CfgFlag>,
},
}
@@ -127,12 +126,13 @@ impl fmt::Debug for ProjectWorkspace {
.field("toolchain", &toolchain)
.field("data_layout", &data_layout)
.finish(),
- ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
+ ProjectWorkspace::Json { project, sysroot, rustc_cfg, toolchain } => {
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("toolchain", &toolchain);
debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
debug_struct.finish()
}
@@ -152,6 +152,19 @@ impl ProjectWorkspace {
config: &CargoConfig,
progress: &dyn Fn(String),
) -> Result<ProjectWorkspace> {
+ let version = |current_dir, cmd_path, prefix: &str| {
+ let cargo_version = utf8_stdout({
+ let mut cmd = Command::new(cmd_path);
+ cmd.envs(&config.extra_env);
+ cmd.arg("--version").current_dir(current_dir);
+ cmd
+ })?;
+ anyhow::Ok(
+ cargo_version
+ .get(prefix.len()..)
+ .and_then(|it| Version::parse(it.split_whitespace().next()?).ok()),
+ )
+ };
let res = match manifest {
ProjectManifest::ProjectJson(project_json) => {
let file = fs::read_to_string(&project_json).with_context(|| {
@@ -161,24 +174,17 @@ impl ProjectWorkspace {
format!("Failed to deserialize json file {}", project_json.display())
})?;
let project_location = project_json.parent().to_path_buf();
+ let toolchain = version(&*project_location, toolchain::rustc(), "rustc ")?;
let project_json = ProjectJson::new(&project_location, data);
ProjectWorkspace::load_inline(
project_json,
config.target.as_deref(),
&config.extra_env,
+ toolchain,
)
}
ProjectManifest::CargoToml(cargo_toml) => {
- let cargo_version = utf8_stdout({
- let mut cmd = Command::new(toolchain::cargo());
- cmd.envs(&config.extra_env);
- cmd.arg("--version");
- cmd
- })?;
- let toolchain = cargo_version
- .get("cargo ".len()..)
- .and_then(|it| Version::parse(it.split_whitespace().next()?).ok());
-
+ let toolchain = version(cargo_toml.parent(), toolchain::cargo(), "cargo ")?;
let meta = CargoWorkspace::fetch_metadata(
&cargo_toml,
cargo_toml.parent(),
@@ -274,7 +280,7 @@ impl ProjectWorkspace {
let rustc_cfg =
rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env);
- let cfg_overrides = config.cfg_overrides();
+ let cfg_overrides = config.cfg_overrides.clone();
let data_layout = target_data_layout::get(
Some(&cargo_toml),
config.target.as_deref(),
@@ -303,6 +309,7 @@ impl ProjectWorkspace {
project_json: ProjectJson,
target: Option<&str>,
extra_env: &FxHashMap<String, String>,
+ 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)),
@@ -327,7 +334,7 @@ impl ProjectWorkspace {
}
let rustc_cfg = rustc_cfg::get(None, target, extra_env);
- ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }
+ ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg, toolchain }
}
pub fn load_detached_files(
@@ -403,7 +410,7 @@ impl ProjectWorkspace {
let outputs = &mut match WorkspaceBuildScripts::run_once(config, &cargo_ws, progress) {
Ok(it) => Ok(it.into_iter()),
// io::Error is not Clone?
- Err(e) => Err(Arc::new(e)),
+ Err(e) => Err(sync::Arc::new(e)),
};
workspaces
@@ -440,18 +447,35 @@ impl ProjectWorkspace {
}
}
- pub fn find_sysroot_proc_macro_srv(&self) -> Option<AbsPathBuf> {
+ pub fn find_sysroot_proc_macro_srv(&self) -> Result<AbsPathBuf> {
match self {
ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. }
- | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. } => {
+ | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. }
+ | ProjectWorkspace::DetachedFiles { sysroot: Ok(sysroot), .. } => {
let standalone_server_name =
format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
["libexec", "lib"]
.into_iter()
.map(|segment| sysroot.root().join(segment).join(&standalone_server_name))
.find(|server_path| std::fs::metadata(server_path).is_ok())
+ .ok_or_else(|| {
+ anyhow::anyhow!(
+ "cannot find proc-macro server in sysroot `{}`",
+ sysroot.root().display()
+ )
+ })
+ }
+ ProjectWorkspace::DetachedFiles { .. } => {
+ Err(anyhow::anyhow!("cannot find proc-macro server, no sysroot was found"))
}
- _ => None,
+ ProjectWorkspace::Cargo { cargo, .. } => Err(anyhow::anyhow!(
+ "cannot find proc-macro-srv, the workspace `{}` is missing a sysroot",
+ cargo.workspace_root().display()
+ )),
+ ProjectWorkspace::Json { project, .. } => Err(anyhow::anyhow!(
+ "cannot find proc-macro-srv, the workspace `{}` is missing a sysroot",
+ project.path().display()
+ )),
}
}
@@ -469,7 +493,7 @@ impl ProjectWorkspace {
})
};
match self {
- ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
+ ProjectWorkspace::Json { project, sysroot, rustc_cfg: _, toolchain: _ } => project
.crates()
.map(|(_, krate)| PackageRoot {
is_local: krate.is_workspace_member,
@@ -570,22 +594,23 @@ impl ProjectWorkspace {
pub fn to_crate_graph(
&self,
- load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
extra_env: &FxHashMap<String, String>,
- ) -> CrateGraph {
+ ) -> (CrateGraph, ProcMacroPaths) {
let _p = profile::span("ProjectWorkspace::to_crate_graph");
- let mut crate_graph = match self {
- ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
- rustc_cfg.clone(),
- load_proc_macro,
- load,
- project,
- sysroot.as_ref().ok(),
- extra_env,
- Err("rust-project.json projects have no target layout set".into()),
- ),
+ let (mut crate_graph, proc_macros) = match self {
+ ProjectWorkspace::Json { project, sysroot, rustc_cfg, toolchain } => {
+ project_json_to_crate_graph(
+ rustc_cfg.clone(),
+ load,
+ project,
+ sysroot.as_ref().ok(),
+ extra_env,
+ Err("rust-project.json projects have no target layout set".into()),
+ toolchain.as_ref().and_then(|it| ReleaseChannel::from_str(it.pre.as_str())),
+ )
+ }
ProjectWorkspace::Cargo {
cargo,
sysroot,
@@ -593,21 +618,22 @@ impl ProjectWorkspace {
rustc_cfg,
cfg_overrides,
build_scripts,
- toolchain: _,
+ toolchain,
target_layout,
} => cargo_to_crate_graph(
- load_proc_macro,
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())),
),
ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
detached_files_to_crate_graph(
@@ -624,7 +650,7 @@ impl ProjectWorkspace {
} else {
tracing::debug!("Did not patch std to depend on cfg-if")
}
- crate_graph
+ (crate_graph, proc_macros)
}
pub fn eq_ignore_build_data(&self, other: &Self) -> bool {
@@ -659,9 +685,19 @@ impl ProjectWorkspace {
&& sysroot == o_sysroot
}
(
- Self::Json { project, sysroot, rustc_cfg },
- Self::Json { project: o_project, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg },
- ) => project == o_project && rustc_cfg == o_rustc_cfg && sysroot == o_sysroot,
+ Self::Json { project, sysroot, rustc_cfg, toolchain },
+ Self::Json {
+ project: o_project,
+ sysroot: o_sysroot,
+ rustc_cfg: o_rustc_cfg,
+ toolchain: o_toolchain,
+ },
+ ) => {
+ project == o_project
+ && rustc_cfg == o_rustc_cfg
+ && sysroot == o_sysroot
+ && toolchain == o_toolchain
+ }
(
Self::DetachedFiles { files, sysroot, rustc_cfg },
Self::DetachedFiles { files: o_files, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg },
@@ -669,130 +705,146 @@ impl ProjectWorkspace {
_ => false,
}
}
+
+ /// Returns `true` if the project workspace is [`Json`].
+ ///
+ /// [`Json`]: ProjectWorkspace::Json
+ #[must_use]
+ pub fn is_json(&self) -> bool {
+ matches!(self, Self::Json { .. })
+ }
}
fn project_json_to_crate_graph(
rustc_cfg: Vec<CfgFlag>,
- load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
project: &ProjectJson,
sysroot: Option<&Sysroot>,
extra_env: &FxHashMap<String, String>,
target_layout: TargetLayoutLoadResult,
-) -> CrateGraph {
- let mut crate_graph = CrateGraph::default();
+ channel: Option<ReleaseChannel>,
+) -> (CrateGraph, ProcMacroPaths) {
+ let mut res = (CrateGraph::default(), ProcMacroPaths::default());
+ let (crate_graph, proc_macros) = &mut res;
let sysroot_deps = sysroot.as_ref().map(|sysroot| {
sysroot_to_crate_graph(
- &mut crate_graph,
+ crate_graph,
sysroot,
rustc_cfg.clone(),
target_layout.clone(),
load,
+ channel,
)
});
let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
- let crates: NoHashHashMap<CrateId, CrateId> = project
+ let crates: FxHashMap<CrateId, CrateId> = project
.crates()
- .filter_map(|(crate_id, krate)| {
- let file_path = &krate.root_module;
- let file_id = load(file_path)?;
- Some((crate_id, krate, file_id))
- })
- .map(|(crate_id, krate, file_id)| {
- let env = krate.env.clone().into_iter().collect();
- let proc_macro = match krate.proc_macro_dylib_path.clone() {
- Some(it) => load_proc_macro(
- krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""),
- &it,
- ),
- None => Err("no proc macro dylib present".into()),
- };
-
- let target_cfgs = match krate.target.as_deref() {
- Some(target) => cfg_cache
- .entry(target)
- .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)),
- None => &rustc_cfg,
- };
-
- let mut cfg_options = CfgOptions::default();
- cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
- (
+ .filter_map(|(crate_id, krate)| Some((crate_id, krate, load(&krate.root_module)?)))
+ .map(
+ |(
crate_id,
- crate_graph.add_crate_root(
+ Crate {
+ display_name,
+ edition,
+ version,
+ cfg,
+ target,
+ env,
+ proc_macro_dylib_path,
+ is_proc_macro,
+ repository,
+ ..
+ },
+ file_id,
+ )| {
+ let env = env.clone().into_iter().collect();
+
+ let target_cfgs = match target.as_deref() {
+ Some(target) => cfg_cache
+ .entry(target)
+ .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)),
+ None => &rustc_cfg,
+ };
+
+ let crate_graph_crate_id = crate_graph.add_crate_root(
file_id,
- krate.edition,
- krate.display_name.clone(),
- krate.version.clone(),
- cfg_options.clone(),
- cfg_options,
+ *edition,
+ display_name.clone(),
+ version.clone(),
+ target_cfgs.iter().chain(cfg.iter()).cloned().collect(),
+ None,
env,
- proc_macro,
- krate.is_proc_macro,
- if krate.display_name.is_some() {
- CrateOrigin::CratesIo {
- repo: krate.repository.clone(),
- name: krate
- .display_name
- .clone()
- .map(|n| n.canonical_name().to_string()),
+ *is_proc_macro,
+ if let Some(name) = display_name.clone() {
+ CrateOrigin::Local {
+ repo: repository.clone(),
+ name: Some(name.canonical_name().to_string()),
}
} else {
- CrateOrigin::CratesIo { repo: None, name: None }
+ CrateOrigin::Local { repo: None, name: None }
},
target_layout.clone(),
- ),
- )
- })
+ channel,
+ );
+ if *is_proc_macro {
+ if let Some(path) = proc_macro_dylib_path.clone() {
+ let node = Ok((
+ display_name.as_ref().map(|it| it.canonical_name().to_owned()),
+ path,
+ ));
+ proc_macros.insert(crate_graph_crate_id, node);
+ }
+ }
+ (crate_id, crate_graph_crate_id)
+ },
+ )
.collect();
for (from, krate) in project.crates() {
if let Some(&from) = crates.get(&from) {
if let Some((public_deps, libproc_macro)) = &sysroot_deps {
- public_deps.add_to_crate_graph(&mut crate_graph, from);
- if krate.is_proc_macro {
- if let Some(proc_macro) = libproc_macro {
- add_dep(
- &mut crate_graph,
- from,
- CrateName::new("proc_macro").unwrap(),
- *proc_macro,
- );
- }
+ public_deps.add_to_crate_graph(crate_graph, from);
+ if let Some(proc_macro) = libproc_macro {
+ add_proc_macro_dep(crate_graph, from, *proc_macro, krate.is_proc_macro);
}
}
for dep in &krate.deps {
if let Some(&to) = crates.get(&dep.crate_id) {
- add_dep(&mut crate_graph, from, dep.name.clone(), to)
+ add_dep(crate_graph, from, dep.name.clone(), to)
}
}
}
}
- crate_graph
+ res
}
fn cargo_to_crate_graph(
- load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>,
cargo: &CargoWorkspace,
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,
-) -> CrateGraph {
+ channel: Option<ReleaseChannel>,
+) -> (CrateGraph, ProcMacroPaths) {
let _p = profile::span("cargo_to_crate_graph");
- let mut crate_graph = CrateGraph::default();
+ 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(
- &mut crate_graph,
+ crate_graph,
sysroot,
rustc_cfg.clone(),
target_layout.clone(),
load,
+ channel,
),
None => (SysrootPublicDeps::default(), None),
};
@@ -804,37 +856,40 @@ fn cargo_to_crate_graph(
cfg_options
};
+ // Mapping of a package to its library target
let mut pkg_to_lib_crate = FxHashMap::default();
-
let mut pkg_crates = FxHashMap::default();
// Does any crate signal to rust-analyzer that they need the rustc_private crates?
let mut has_private = false;
+
// Next, create crates for each package, target pair
for pkg in cargo.packages() {
- let mut cfg_options = cfg_options.clone();
+ has_private |= cargo[pkg].metadata.rustc_private;
- let overrides = match override_cfg {
- CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
- CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
- };
+ let cfg_options = forced_cfg.clone().unwrap_or_else(|| {
+ let mut cfg_options = cfg_options.clone();
- // Add test cfg for local crates
- if cargo[pkg].is_local {
- cfg_options.insert_atom("test".into());
- }
+ // Add test cfg for local crates
+ if cargo[pkg].is_local {
+ cfg_options.insert_atom("test".into());
+ }
- if let Some(overrides) = overrides {
- // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
- // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
- // working on rust-lang/rust as that's the only time it appears outside sysroot).
- //
- // A more ideal solution might be to reanalyze crates based on where the cursor is and
- // figure out the set of cfgs that would have to apply to make it active.
+ if !override_cfg.global.is_empty() {
+ cfg_options.apply_diff(override_cfg.global.clone());
+ };
+ if let Some(diff) = override_cfg.selective.get(&cargo[pkg].name) {
+ // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
+ // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
+ // working on rust-lang/rust as that's the only time it appears outside sysroot).
+ //
+ // A more ideal solution might be to reanalyze crates based on where the cursor is and
+ // figure out the set of cfgs that would have to apply to make it active.
- cfg_options.apply_diff(overrides.clone());
- };
+ cfg_options.apply_diff(diff.clone());
+ };
+ cfg_options
+ });
- has_private |= cargo[pkg].metadata.rustc_private;
let mut lib_tgt = None;
for &tgt in cargo[pkg].targets.iter() {
if cargo[tgt].kind != TargetKind::Lib && !cargo[pkg].is_member {
@@ -845,44 +900,57 @@ fn cargo_to_crate_graph(
// https://github.com/rust-lang/rust-analyzer/issues/11300
continue;
}
+ let &TargetData { ref name, kind, is_proc_macro, ref root, .. } = &cargo[tgt];
- if let Some(file_id) = load(&cargo[tgt].root) {
- let crate_id = add_target_crate_root(
- &mut crate_graph,
- &cargo[pkg],
- build_scripts.get_output(pkg),
- cfg_options.clone(),
- &mut |path| load_proc_macro(&cargo[tgt].name, path),
- file_id,
- &cargo[tgt].name,
- cargo[tgt].is_proc_macro,
- target_layout.clone(),
- );
- if cargo[tgt].kind == TargetKind::Lib {
- lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
+ if kind == TargetKind::Lib
+ && sysroot.map_or(false, |sysroot| root.starts_with(sysroot.src_root()))
+ {
+ if let Some(&(_, crate_id, _)) =
+ public_deps.deps.iter().find(|(dep_name, ..)| dep_name.as_smol_str() == name)
+ {
+ pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, kind));
+
+ lib_tgt = Some((crate_id, name.clone()));
pkg_to_lib_crate.insert(pkg, crate_id);
+ // sysroot is inside the workspace, prevent the sysroot crates from being duplicated here
+ continue;
}
- // Even crates that don't set proc-macro = true are allowed to depend on proc_macro
- // (just none of the APIs work when called outside of a proc macro).
- if let Some(proc_macro) = libproc_macro {
- add_dep_with_prelude(
- &mut crate_graph,
- crate_id,
- CrateName::new("proc_macro").unwrap(),
- proc_macro,
- cargo[tgt].is_proc_macro,
- );
- }
+ }
- pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
+ let Some(file_id) = load(root) else { continue };
+
+ let crate_id = add_target_crate_root(
+ crate_graph,
+ proc_macros,
+ &cargo[pkg],
+ build_scripts.get_output(pkg),
+ cfg_options.clone(),
+ file_id,
+ name,
+ is_proc_macro,
+ target_layout.clone(),
+ false,
+ channel,
+ );
+ if kind == TargetKind::Lib {
+ lib_tgt = Some((crate_id, name.clone()));
+ pkg_to_lib_crate.insert(pkg, crate_id);
}
+ // Even crates that don't set proc-macro = true are allowed to depend on proc_macro
+ // (just none of the APIs work when called outside of a proc macro).
+ if let Some(proc_macro) = libproc_macro {
+ add_proc_macro_dep(crate_graph, crate_id, proc_macro, is_proc_macro);
+ }
+
+ pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, kind));
}
// Set deps to the core, std and to the lib target of the current package
for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
// Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
- public_deps.add_to_crate_graph(&mut crate_graph, from);
+ public_deps.add_to_crate_graph(crate_graph, from);
+ // Add dep edge of all targets to the package's lib target
if let Some((to, name)) = lib_tgt.clone() {
if to != from && kind != TargetKind::BuildScript {
// (build script can not depend on its library target)
@@ -891,7 +959,7 @@ fn cargo_to_crate_graph(
// cargo metadata does not do any normalization,
// so we do it ourselves currently
let name = CrateName::normalize_dashes(&name);
- add_dep(&mut crate_graph, from, name, to);
+ add_dep(crate_graph, from, name, to);
}
}
}
@@ -900,21 +968,18 @@ fn cargo_to_crate_graph(
// Now add a dep edge from all targets of upstream to the lib
// target of downstream.
for pkg in cargo.packages() {
- for dep in cargo[pkg].dependencies.iter() {
- let name = CrateName::new(&dep.name).unwrap();
- if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
- for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
- if dep.kind == DepKind::Build && kind != TargetKind::BuildScript {
- // Only build scripts may depend on build dependencies.
- continue;
- }
- if dep.kind != DepKind::Build && kind == TargetKind::BuildScript {
- // Build scripts may only depend on build dependencies.
- continue;
- }
+ for dep in &cargo[pkg].dependencies {
+ let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) else { continue };
+ let Some(targets) = pkg_crates.get(&pkg) else { continue };
- add_dep(&mut crate_graph, from, name.clone(), to)
+ let name = CrateName::new(&dep.name).unwrap();
+ for &(from, kind) in targets {
+ // Build scripts may only depend on build dependencies.
+ if (dep.kind == DepKind::Build) != (kind == TargetKind::BuildScript) {
+ continue;
}
+
+ add_dep(crate_graph, from, name.clone(), to)
}
}
}
@@ -924,10 +989,10 @@ fn cargo_to_crate_graph(
// and create dependencies on them for the crates which opt-in to that
if let Some((rustc_workspace, rustc_build_scripts)) = rustc {
handle_rustc_crates(
- &mut crate_graph,
+ crate_graph,
+ proc_macros,
&mut pkg_to_lib_crate,
load,
- load_proc_macro,
rustc_workspace,
cargo,
&public_deps,
@@ -943,10 +1008,11 @@ fn cargo_to_crate_graph(
rustc_build_scripts
},
target_layout,
+ channel,
);
}
}
- crate_graph
+ res
}
fn detached_files_to_crate_graph(
@@ -955,7 +1021,7 @@ fn detached_files_to_crate_graph(
detached_files: &[AbsPathBuf],
sysroot: Option<&Sysroot>,
target_layout: TargetLayoutLoadResult,
-) -> CrateGraph {
+) -> (CrateGraph, ProcMacroPaths) {
let _p = profile::span("detached_files_to_crate_graph");
let mut crate_graph = CrateGraph::default();
let (public_deps, _libproc_macro) = match sysroot {
@@ -965,6 +1031,7 @@ fn detached_files_to_crate_graph(
rustc_cfg.clone(),
target_layout.clone(),
load,
+ None,
),
None => (SysrootPublicDeps::default(), None),
};
@@ -990,27 +1057,27 @@ fn detached_files_to_crate_graph(
display_name.clone(),
None,
cfg_options.clone(),
- cfg_options.clone(),
+ None,
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo {
+ CrateOrigin::Local {
repo: None,
name: display_name.map(|n| n.canonical_name().to_string()),
},
target_layout.clone(),
+ None,
);
public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate);
}
- crate_graph
+ (crate_graph, FxHashMap::default())
}
fn handle_rustc_crates(
crate_graph: &mut CrateGraph,
+ proc_macros: &mut ProcMacroPaths,
pkg_to_lib_crate: &mut FxHashMap<Package, CrateId>,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
- load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
rustc_workspace: &CargoWorkspace,
cargo: &CargoWorkspace,
public_deps: &SysrootPublicDeps,
@@ -1020,6 +1087,7 @@ fn handle_rustc_crates(
override_cfg: &CfgOverrides,
build_scripts: &WorkspaceBuildScripts,
target_layout: TargetLayoutLoadResult,
+ channel: Option<ReleaseChannel>,
) {
let mut rustc_pkg_crates = FxHashMap::default();
// The root package of the rustc-dev component is rustc_driver, so we match that
@@ -1044,14 +1112,10 @@ fn handle_rustc_crates(
let mut cfg_options = cfg_options.clone();
- let overrides = match override_cfg {
- CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
- CfgOverrides::Selective(cfg_overrides) => {
- cfg_overrides.get(&rustc_workspace[pkg].name)
- }
+ if !override_cfg.global.is_empty() {
+ cfg_options.apply_diff(override_cfg.global.clone());
};
-
- if let Some(overrides) = overrides {
+ if let Some(diff) = override_cfg.selective.get(&rustc_workspace[pkg].name) {
// FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
// in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
// working on rust-lang/rust as that's the only time it appears outside sysroot).
@@ -1059,7 +1123,7 @@ fn handle_rustc_crates(
// A more ideal solution might be to reanalyze crates based on where the cursor is and
// figure out the set of cfgs that would have to apply to make it active.
- cfg_options.apply_diff(overrides.clone());
+ cfg_options.apply_diff(diff.clone());
};
for &tgt in rustc_workspace[pkg].targets.iter() {
@@ -1069,23 +1133,24 @@ fn handle_rustc_crates(
if let Some(file_id) = load(&rustc_workspace[tgt].root) {
let crate_id = add_target_crate_root(
crate_graph,
+ proc_macros,
&rustc_workspace[pkg],
build_scripts.get_output(pkg),
cfg_options.clone(),
- &mut |path| load_proc_macro(&rustc_workspace[tgt].name, path),
file_id,
&rustc_workspace[tgt].name,
rustc_workspace[tgt].is_proc_macro,
target_layout.clone(),
+ true,
+ channel,
);
pkg_to_lib_crate.insert(pkg, crate_id);
// Add dependencies on core / std / alloc for this crate
public_deps.add_to_crate_graph(crate_graph, crate_id);
if let Some(proc_macro) = libproc_macro {
- add_dep_with_prelude(
+ add_proc_macro_dep(
crate_graph,
crate_id,
- CrateName::new("proc_macro").unwrap(),
proc_macro,
rustc_workspace[tgt].is_proc_macro,
);
@@ -1134,22 +1199,29 @@ fn handle_rustc_crates(
fn add_target_crate_root(
crate_graph: &mut CrateGraph,
+ proc_macros: &mut ProcMacroPaths,
pkg: &PackageData,
build_data: Option<&BuildScriptOutput>,
cfg_options: CfgOptions,
- load_proc_macro: &mut dyn FnMut(&AbsPath) -> ProcMacroLoadResult,
file_id: FileId,
cargo_name: &str,
is_proc_macro: bool,
target_layout: TargetLayoutLoadResult,
+ rustc_crate: bool,
+ channel: Option<ReleaseChannel>,
) -> CrateId {
let edition = pkg.edition;
- let mut potential_cfg_options = cfg_options.clone();
- potential_cfg_options.extend(
- pkg.features
- .iter()
- .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
- );
+ let potential_cfg_options = if pkg.features.is_empty() {
+ None
+ } else {
+ let mut potential_cfg_options = cfg_options.clone();
+ potential_cfg_options.extend(
+ pkg.features
+ .iter()
+ .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
+ );
+ Some(potential_cfg_options)
+ };
let cfg_options = {
let mut opts = cfg_options;
for feature in pkg.active_features.iter() {
@@ -1170,14 +1242,8 @@ fn add_target_crate_root(
}
}
- let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) {
- Some(Some(it)) => load_proc_macro(it),
- Some(None) => Err("no proc macro dylib present".into()),
- None => Err("crate has not (yet) been built".into()),
- };
-
let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
- crate_graph.add_crate_root(
+ let crate_id = crate_graph.add_crate_root(
file_id,
edition,
Some(display_name),
@@ -1185,11 +1251,28 @@ fn add_target_crate_root(
cfg_options,
potential_cfg_options,
env,
- proc_macro,
is_proc_macro,
- CrateOrigin::CratesIo { repo: pkg.repository.clone(), name: Some(pkg.name.clone()) },
+ if rustc_crate {
+ CrateOrigin::Rustc { name: pkg.name.clone() }
+ } else if pkg.is_member {
+ CrateOrigin::Local { repo: pkg.repository.clone(), name: Some(pkg.name.clone()) }
+ } else {
+ CrateOrigin::Library { repo: pkg.repository.clone(), name: pkg.name.clone() }
+ },
target_layout,
- )
+ channel,
+ );
+ if is_proc_macro {
+ let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) {
+ Some(it) => it.cloned().map(|path| Ok((Some(cargo_name.to_owned()), path))),
+ None => Some(Err("crate has not yet been built".to_owned())),
+ };
+ if let Some(proc_macro) = proc_macro {
+ proc_macros.insert(crate_id, proc_macro);
+ }
+ }
+
+ crate_id
}
#[derive(Default)]
@@ -1212,34 +1295,47 @@ fn sysroot_to_crate_graph(
rustc_cfg: Vec<CfgFlag>,
target_layout: TargetLayoutLoadResult,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+ channel: Option<ReleaseChannel>,
) -> (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(),
- cfg_options.clone(),
- env,
- Err("no proc macro loaded for sysroot crate".into()),
- false,
- CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
- target_layout.clone(),
- );
- 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();
@@ -1260,6 +1356,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))
}
@@ -1274,6 +1427,10 @@ fn add_dep_with_prelude(
add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude))
}
+fn add_proc_macro_dep(crate_graph: &mut CrateGraph, from: CrateId, to: CrateId, prelude: bool) {
+ add_dep_with_prelude(crate_graph, from, CrateName::new("proc_macro").unwrap(), to, prelude);
+}
+
fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
if let Err(err) = graph.add_dep(from, dep) {
tracing::error!("{}", err)