1 //! Handles lowering of build-system specific workspace information (`cargo
2 //! metadata` or `rust-project.json`) into representation stored in the salsa
3 //! database -- `CrateGraph`.
5 use std::{collections::VecDeque, fmt, fs, process::Command};
7 use anyhow::{format_err, Context, Result};
9 CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env,
10 FileId, LangCrateOrigin, ProcMacroLoadResult,
12 use cfg::{CfgDiff, CfgOptions};
13 use paths::{AbsPath, AbsPathBuf};
14 use rustc_hash::{FxHashMap, FxHashSet};
16 use stdx::{always, hash::NoHashHashMap};
19 build_scripts::BuildScriptOutput,
20 cargo_workspace::{DepKind, PackageData, RustcSource},
23 sysroot::SysrootCrate,
24 utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, ProjectJson, ProjectManifest, Sysroot,
25 TargetKind, WorkspaceBuildScripts,
28 /// A set of cfg-overrides per crate.
30 /// `Wildcard(..)` is useful e.g. disabling `#[cfg(test)]` on all crates,
31 /// without having to first obtain a list of all crates.
32 #[derive(Debug, Clone, Eq, PartialEq)]
33 pub enum CfgOverrides {
34 /// A single global set of overrides matching all crates.
36 /// A set of overrides matching specific crates.
37 Selective(FxHashMap<String, CfgDiff>),
40 impl Default for CfgOverrides {
41 fn default() -> Self {
42 Self::Selective(FxHashMap::default())
47 pub fn len(&self) -> usize {
49 CfgOverrides::Wildcard(_) => 1,
50 CfgOverrides::Selective(hash_map) => hash_map.len(),
55 /// `PackageRoot` describes a package root folder.
56 /// Which may be an external dependency, or a member of
57 /// the current workspace.
58 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
59 pub struct PackageRoot {
60 /// Is from the local filesystem and may be edited
62 pub include: Vec<AbsPathBuf>,
63 pub exclude: Vec<AbsPathBuf>,
66 #[derive(Clone, Eq, PartialEq)]
67 pub enum ProjectWorkspace {
68 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
70 cargo: CargoWorkspace,
71 build_scripts: WorkspaceBuildScripts,
72 sysroot: Option<Sysroot>,
73 rustc: Option<CargoWorkspace>,
74 /// Holds cfg flags for the current target. We get those by running
75 /// `rustc --print cfg`.
77 /// FIXME: make this a per-crate map, as, eg, build.rs might have a
79 rustc_cfg: Vec<CfgFlag>,
80 cfg_overrides: CfgOverrides,
81 toolchain: Option<Version>,
83 /// Project workspace was manually specified using a `rust-project.json` file.
84 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
86 // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
87 // That's not the end user experience we should strive for.
88 // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
89 // That needs some changes on the salsa-level though.
90 // In particular, we should split the unified CrateGraph (which currently has maximal durability) into proper crate graph, and a set of ad hoc roots (with minimal durability).
91 // Then, we need to hide the graph behind the queries such that most queries look only at the proper crate graph, and fall back to ad hoc roots only if there's no results.
92 // After this, we should be able to tweak the logic in reload.rs to add newly opened files, which don't belong to any existing crates, to the set of the detached files.
94 /// Project with a set of disjoint files, not belonging to any particular workspace.
95 /// Backed by basic sysroot crates for basic completion and highlighting.
96 DetachedFiles { files: Vec<AbsPathBuf>, sysroot: Sysroot, rustc_cfg: Vec<CfgFlag> },
99 impl fmt::Debug for ProjectWorkspace {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 // Make sure this isn't too verbose.
103 ProjectWorkspace::Cargo {
112 .debug_struct("Cargo")
113 .field("root", &cargo.workspace_root().file_name())
114 .field("n_packages", &cargo.packages().len())
115 .field("sysroot", &sysroot.is_some())
117 "n_rustc_compiler_crates",
118 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
120 .field("n_rustc_cfg", &rustc_cfg.len())
121 .field("n_cfg_overrides", &cfg_overrides.len())
122 .field("toolchain", &toolchain)
124 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
125 let mut debug_struct = f.debug_struct("Json");
126 debug_struct.field("n_crates", &project.n_crates());
127 if let Some(sysroot) = sysroot {
128 debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
130 debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
131 debug_struct.finish()
133 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
134 .debug_struct("DetachedFiles")
135 .field("n_files", &files.len())
136 .field("n_sysroot_crates", &sysroot.crates().len())
137 .field("n_rustc_cfg", &rustc_cfg.len())
143 impl ProjectWorkspace {
145 manifest: ProjectManifest,
146 config: &CargoConfig,
147 progress: &dyn Fn(String),
148 ) -> Result<ProjectWorkspace> {
149 let res = match manifest {
150 ProjectManifest::ProjectJson(project_json) => {
151 let file = fs::read_to_string(&project_json).with_context(|| {
152 format!("Failed to read json file {}", project_json.display())
154 let data = serde_json::from_str(&file).with_context(|| {
155 format!("Failed to deserialize json file {}", project_json.display())
157 let project_location = project_json.parent().to_path_buf();
158 let project_json = ProjectJson::new(&project_location, data);
159 ProjectWorkspace::load_inline(project_json, config.target.as_deref(), config)?
161 ProjectManifest::CargoToml(cargo_toml) => {
162 let cargo_version = utf8_stdout({
163 let mut cmd = Command::new(toolchain::cargo());
164 cmd.envs(&config.extra_env);
165 cmd.arg("--version");
168 let toolchain = cargo_version
169 .get("cargo ".len()..)
170 .and_then(|it| Version::parse(it.split_whitespace().next()?).ok());
172 let meta = CargoWorkspace::fetch_metadata(
180 "Failed to read Cargo metadata from Cargo.toml file {}, {:?}",
181 cargo_toml.display(),
185 let cargo = CargoWorkspace::new(meta);
187 let sysroot = if config.no_sysroot {
190 Some(Sysroot::discover(cargo_toml.parent(), config).with_context(|| {
192 "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
198 let rustc_dir = match &config.rustc_source {
199 Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
200 Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml, config),
204 let rustc = match rustc_dir {
205 Some(rustc_dir) => Some({
206 let meta = CargoWorkspace::fetch_metadata(
213 "Failed to read Cargo metadata for Rust sources".to_string()
215 CargoWorkspace::new(meta)
220 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), config);
222 let cfg_overrides = config.cfg_overrides();
223 ProjectWorkspace::Cargo {
225 build_scripts: WorkspaceBuildScripts::default(),
239 project_json: ProjectJson,
240 target: Option<&str>,
241 config: &CargoConfig,
242 ) -> Result<ProjectWorkspace> {
243 let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
244 (Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)?),
245 (Some(sysroot), None) => {
246 // assume sysroot is structured like rustup's and guess `sysroot_src`
248 sysroot.join("lib").join("rustlib").join("src").join("rust").join("library");
250 Some(Sysroot::load(sysroot, sysroot_src)?)
252 (None, Some(sysroot_src)) => {
253 // assume sysroot is structured like rustup's and guess `sysroot`
254 let mut sysroot = sysroot_src.clone();
258 Some(Sysroot::load(sysroot, sysroot_src)?)
260 (None, None) => None,
263 let rustc_cfg = rustc_cfg::get(None, target, config);
264 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
267 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
268 let sysroot = Sysroot::discover(
271 .and_then(|it| it.parent())
272 .ok_or_else(|| format_err!("No detached files to load"))?,
273 &CargoConfig::default(),
275 let rustc_cfg = rustc_cfg::get(None, None, &CargoConfig::default());
276 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
279 pub fn run_build_scripts(
281 config: &CargoConfig,
282 progress: &dyn Fn(String),
283 ) -> Result<WorkspaceBuildScripts> {
285 ProjectWorkspace::Cargo { cargo, toolchain, .. } => {
286 WorkspaceBuildScripts::run(config, cargo, progress, toolchain).with_context(|| {
287 format!("Failed to run build scripts for {}", &cargo.workspace_root().display())
290 ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
291 Ok(WorkspaceBuildScripts::default())
296 pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
298 ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
300 always!(bs == WorkspaceBuildScripts::default());
305 /// Returns the roots for the current `ProjectWorkspace`
306 /// The return type contains the path and whether or not
307 /// the root is a member of the current workspace
308 pub fn to_roots(&self) -> Vec<PackageRoot> {
310 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
312 .map(|(_, krate)| PackageRoot {
313 is_local: krate.is_workspace_member,
314 include: krate.include.clone(),
315 exclude: krate.exclude.clone(),
317 .collect::<FxHashSet<_>>()
319 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
320 sysroot.crates().map(move |krate| PackageRoot {
322 include: vec![sysroot[krate].root.parent().to_path_buf()],
326 .collect::<Vec<_>>(),
327 ProjectWorkspace::Cargo {
339 let is_local = cargo[pkg].is_local;
340 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
342 let mut include = vec![pkg_root.clone()];
344 build_scripts.get_output(pkg).and_then(|it| it.out_dir.clone());
345 include.extend(out_dir);
347 // In case target's path is manually set in Cargo.toml to be
348 // outside the package root, add its parent as an extra include.
349 // An example of this situation would look like this:
353 // path = "../../src/lib.rs"
355 let extra_targets = cargo[pkg]
358 .filter(|&&tgt| cargo[tgt].kind == TargetKind::Lib)
359 .filter_map(|&tgt| cargo[tgt].root.parent())
360 .map(|tgt| tgt.normalize().to_path_buf())
361 .filter(|path| !path.starts_with(&pkg_root));
362 include.extend(extra_targets);
364 let mut exclude = vec![pkg_root.join(".git")];
366 exclude.push(pkg_root.join("target"));
368 exclude.push(pkg_root.join("tests"));
369 exclude.push(pkg_root.join("examples"));
370 exclude.push(pkg_root.join("benches"));
372 PackageRoot { is_local, include, exclude }
374 .chain(sysroot.iter().map(|sysroot| PackageRoot {
376 include: vec![sysroot.src_root().to_path_buf()],
379 .chain(rustc.iter().flat_map(|rustc| {
380 rustc.packages().map(move |krate| PackageRoot {
382 include: vec![rustc[krate].manifest.parent().to_path_buf()],
388 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
390 .map(|detached_file| PackageRoot {
392 include: vec![detached_file.clone()],
395 .chain(sysroot.crates().map(|krate| PackageRoot {
397 include: vec![sysroot[krate].root.parent().to_path_buf()],
404 pub fn n_packages(&self) -> usize {
406 ProjectWorkspace::Json { project, .. } => project.n_crates(),
407 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
408 let rustc_package_len = rustc.as_ref().map_or(0, |it| it.packages().len());
409 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
410 cargo.packages().len() + sysroot_package_len + rustc_package_len
412 ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
413 sysroot.crates().len() + files.len()
418 pub fn to_crate_graph(
420 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
421 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
422 config: &CargoConfig,
424 let _p = profile::span("ProjectWorkspace::to_crate_graph");
426 let mut crate_graph = match self {
427 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
435 ProjectWorkspace::Cargo {
443 } => cargo_to_crate_graph(
453 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
454 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
457 if crate_graph.patch_cfg_if() {
458 tracing::debug!("Patched std to depend on cfg-if")
460 tracing::debug!("Did not patch std to depend on cfg-if")
466 fn project_json_to_crate_graph(
467 rustc_cfg: Vec<CfgFlag>,
468 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
469 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
470 project: &ProjectJson,
471 sysroot: &Option<Sysroot>,
472 config: &CargoConfig,
474 let mut crate_graph = CrateGraph::default();
475 let sysroot_deps = sysroot
477 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
479 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
480 let crates: NoHashHashMap<CrateId, CrateId> = project
482 .filter_map(|(crate_id, krate)| {
483 let file_path = &krate.root_module;
484 let file_id = load(file_path)?;
485 Some((crate_id, krate, file_id))
487 .map(|(crate_id, krate, file_id)| {
488 let env = krate.env.clone().into_iter().collect();
489 let proc_macro = match krate.proc_macro_dylib_path.clone() {
490 Some(it) => load_proc_macro(
491 krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""),
494 None => Err("no proc macro dylib present".into()),
497 let target_cfgs = match krate.target.as_deref() {
498 Some(target) => cfg_cache
500 .or_insert_with(|| rustc_cfg::get(None, Some(target), config)),
504 let mut cfg_options = CfgOptions::default();
505 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
508 crate_graph.add_crate_root(
511 krate.display_name.clone(),
512 krate.version.clone(),
518 if krate.display_name.is_some() {
519 CrateOrigin::CratesIo { repo: krate.repository.clone() }
521 CrateOrigin::CratesIo { repo: None }
528 for (from, krate) in project.crates() {
529 if let Some(&from) = crates.get(&from) {
530 if let Some((public_deps, libproc_macro)) = &sysroot_deps {
531 public_deps.add(from, &mut crate_graph);
532 if krate.is_proc_macro {
533 if let Some(proc_macro) = libproc_macro {
537 CrateName::new("proc_macro").unwrap(),
544 for dep in &krate.deps {
545 if let Some(&to) = crates.get(&dep.crate_id) {
546 add_dep(&mut crate_graph, from, dep.name.clone(), to)
554 fn cargo_to_crate_graph(
555 rustc_cfg: Vec<CfgFlag>,
556 override_cfg: &CfgOverrides,
557 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
558 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
559 cargo: &CargoWorkspace,
560 build_scripts: &WorkspaceBuildScripts,
561 sysroot: Option<&Sysroot>,
562 rustc: &Option<CargoWorkspace>,
564 let _p = profile::span("cargo_to_crate_graph");
565 let mut crate_graph = CrateGraph::default();
566 let (public_deps, libproc_macro) = match sysroot {
567 Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load),
568 None => (SysrootPublicDeps::default(), None),
571 let mut cfg_options = CfgOptions::default();
572 cfg_options.extend(rustc_cfg);
574 let mut pkg_to_lib_crate = FxHashMap::default();
576 cfg_options.insert_atom("debug_assertions".into());
578 let mut pkg_crates = FxHashMap::default();
579 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
580 let mut has_private = false;
581 // Next, create crates for each package, target pair
582 for pkg in cargo.packages() {
583 let mut cfg_options = cfg_options.clone();
585 let overrides = match override_cfg {
586 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
587 CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
590 // Add test cfg for local crates
591 if cargo[pkg].is_local {
592 cfg_options.insert_atom("test".into());
595 if let Some(overrides) = overrides {
596 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
597 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
598 // working on rust-lang/rust as that's the only time it appears outside sysroot).
600 // A more ideal solution might be to reanalyze crates based on where the cursor is and
601 // figure out the set of cfgs that would have to apply to make it active.
603 cfg_options.apply_diff(overrides.clone());
606 has_private |= cargo[pkg].metadata.rustc_private;
607 let mut lib_tgt = None;
608 for &tgt in cargo[pkg].targets.iter() {
609 if cargo[tgt].kind != TargetKind::Lib && !cargo[pkg].is_member {
610 // For non-workspace-members, Cargo does not resolve dev-dependencies, so we don't
611 // add any targets except the library target, since those will not work correctly if
612 // they use dev-dependencies.
613 // In fact, they can break quite badly if multiple client workspaces get merged:
614 // https://github.com/rust-lang/rust-analyzer/issues/11300
618 if let Some(file_id) = load(&cargo[tgt].root) {
619 let crate_id = add_target_crate_root(
622 build_scripts.get_output(pkg),
624 &mut |path| load_proc_macro(&cargo[tgt].name, path),
627 cargo[tgt].is_proc_macro,
629 if cargo[tgt].kind == TargetKind::Lib {
630 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
631 pkg_to_lib_crate.insert(pkg, crate_id);
633 if let Some(proc_macro) = libproc_macro {
634 add_dep_with_prelude(
637 CrateName::new("proc_macro").unwrap(),
639 cargo[tgt].is_proc_macro,
643 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
647 // Set deps to the core, std and to the lib target of the current package
648 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
649 // Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
650 public_deps.add(*from, &mut crate_graph);
652 if let Some((to, name)) = lib_tgt.clone() {
653 if to != *from && *kind != TargetKind::BuildScript {
654 // (build script can not depend on its library target)
656 // For root projects with dashes in their name,
657 // cargo metadata does not do any normalization,
658 // so we do it ourselves currently
659 let name = CrateName::normalize_dashes(&name);
660 add_dep(&mut crate_graph, *from, name, to);
666 // Now add a dep edge from all targets of upstream to the lib
667 // target of downstream.
668 for pkg in cargo.packages() {
669 for dep in cargo[pkg].dependencies.iter() {
670 let name = CrateName::new(&dep.name).unwrap();
671 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
672 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
673 if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
674 // Only build scripts may depend on build dependencies.
677 if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
678 // Build scripts may only depend on build dependencies.
682 add_dep(&mut crate_graph, *from, name.clone(), to)
689 // If the user provided a path to rustc sources, we add all the rustc_private crates
690 // and create dependencies on them for the crates which opt-in to that
691 if let Some(rustc_workspace) = rustc {
699 &mut pkg_to_lib_crate,
710 fn detached_files_to_crate_graph(
711 rustc_cfg: Vec<CfgFlag>,
712 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
713 detached_files: &[AbsPathBuf],
716 let _p = profile::span("detached_files_to_crate_graph");
717 let mut crate_graph = CrateGraph::default();
718 let (public_deps, _libproc_macro) =
719 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
721 let mut cfg_options = CfgOptions::default();
722 cfg_options.extend(rustc_cfg);
724 for detached_file in detached_files {
725 let file_id = match load(detached_file) {
726 Some(file_id) => file_id,
728 tracing::error!("Failed to load detached file {:?}", detached_file);
732 let display_name = detached_file
734 .and_then(|os_str| os_str.to_str())
735 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
736 let detached_file_crate = crate_graph.add_crate_root(
746 CrateOrigin::CratesIo { repo: None },
749 public_deps.add(detached_file_crate, &mut crate_graph);
754 fn handle_rustc_crates(
755 rustc_workspace: &CargoWorkspace,
756 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
757 crate_graph: &mut CrateGraph,
758 cfg_options: &CfgOptions,
759 override_cfg: &CfgOverrides,
760 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
761 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
762 public_deps: &SysrootPublicDeps,
763 cargo: &CargoWorkspace,
764 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
765 build_scripts: &WorkspaceBuildScripts,
767 let mut rustc_pkg_crates = FxHashMap::default();
768 // The root package of the rustc-dev component is rustc_driver, so we match that
770 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
771 // The rustc workspace might be incomplete (such as if rustc-dev is not
772 // installed for the current toolchain) and `rustc_source` is set to discover.
773 if let Some(root_pkg) = root_pkg {
774 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
775 let mut queue = VecDeque::new();
776 queue.push_back(root_pkg);
777 while let Some(pkg) = queue.pop_front() {
778 // Don't duplicate packages if they are dependended on a diamond pattern
779 // N.B. if this line is omitted, we try to analyse over 4_800_000 crates
780 // which is not ideal
781 if rustc_pkg_crates.contains_key(&pkg) {
784 for dep in &rustc_workspace[pkg].dependencies {
785 queue.push_back(dep.pkg);
788 let mut cfg_options = cfg_options.clone();
790 let overrides = match override_cfg {
791 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
792 CfgOverrides::Selective(cfg_overrides) => {
793 cfg_overrides.get(&rustc_workspace[pkg].name)
797 if let Some(overrides) = overrides {
798 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
799 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
800 // working on rust-lang/rust as that's the only time it appears outside sysroot).
802 // A more ideal solution might be to reanalyze crates based on where the cursor is and
803 // figure out the set of cfgs that would have to apply to make it active.
805 cfg_options.apply_diff(overrides.clone());
808 for &tgt in rustc_workspace[pkg].targets.iter() {
809 if rustc_workspace[tgt].kind != TargetKind::Lib {
812 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
813 let crate_id = add_target_crate_root(
815 &rustc_workspace[pkg],
816 build_scripts.get_output(pkg),
818 &mut |path| load_proc_macro(&rustc_workspace[tgt].name, path),
820 &rustc_workspace[tgt].name,
821 rustc_workspace[tgt].is_proc_macro,
823 pkg_to_lib_crate.insert(pkg, crate_id);
824 // Add dependencies on core / std / alloc for this crate
825 public_deps.add(crate_id, crate_graph);
826 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
831 // Now add a dep edge from all targets of upstream to the lib
832 // target of downstream.
833 for pkg in rustc_pkg_crates.keys().copied() {
834 for dep in rustc_workspace[pkg].dependencies.iter() {
835 let name = CrateName::new(&dep.name).unwrap();
836 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
837 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
838 add_dep(crate_graph, from, name.clone(), to);
843 // Add a dependency on the rustc_private crates for all targets of each package
845 for dep in rustc_workspace.packages() {
846 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
848 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
849 for pkg in cargo.packages() {
850 let package = &cargo[pkg];
851 if !package.metadata.rustc_private {
854 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
855 // Avoid creating duplicate dependencies
856 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
857 // `rust_analyzer` thinks that it should use the one from the `rustc_source`
858 // instead of the one from `crates.io`
859 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
860 add_dep(crate_graph, *from, name.clone(), to);
868 fn add_target_crate_root(
869 crate_graph: &mut CrateGraph,
871 build_data: Option<&BuildScriptOutput>,
872 cfg_options: CfgOptions,
873 load_proc_macro: &mut dyn FnMut(&AbsPath) -> ProcMacroLoadResult,
878 let edition = pkg.edition;
879 let mut potential_cfg_options = cfg_options.clone();
880 potential_cfg_options.extend(
883 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
886 let mut opts = cfg_options;
887 for feature in pkg.active_features.iter() {
888 opts.insert_key_value("feature".into(), feature.into());
890 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
891 opts.extend(cfgs.iter().cloned());
896 let mut env = Env::default();
897 inject_cargo_env(pkg, &mut env);
899 if let Some(envs) = build_data.map(|it| &it.envs) {
901 env.set(k, v.clone());
905 let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) {
906 Some(Some(it)) => load_proc_macro(it),
907 Some(None) => Err("no proc macro dylib present".into()),
908 None => Err("crate has not (yet) been built".into()),
911 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
912 crate_graph.add_crate_root(
916 Some(pkg.version.to_string()),
918 potential_cfg_options,
922 CrateOrigin::CratesIo { repo: pkg.repository.clone() },
927 struct SysrootPublicDeps {
928 deps: Vec<(CrateName, CrateId, bool)>,
931 impl SysrootPublicDeps {
932 /// Makes `from` depend on the public sysroot crates.
933 fn add(&self, from: CrateId, crate_graph: &mut CrateGraph) {
934 for (name, krate, prelude) in &self.deps {
935 add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude);
940 fn sysroot_to_crate_graph(
941 crate_graph: &mut CrateGraph,
943 rustc_cfg: Vec<CfgFlag>,
944 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
945 ) -> (SysrootPublicDeps, Option<CrateId>) {
946 let _p = profile::span("sysroot_to_crate_graph");
947 let mut cfg_options = CfgOptions::default();
948 cfg_options.extend(rustc_cfg);
949 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
951 .filter_map(|krate| {
952 let file_id = load(&sysroot[krate].root)?;
954 let env = Env::default();
955 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
956 let crate_id = crate_graph.add_crate_root(
964 Err("no proc macro loaded for sysroot crate".into()),
966 CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
968 Some((krate, crate_id))
972 for from in sysroot.crates() {
973 for &to in sysroot[from].deps.iter() {
974 let name = CrateName::new(&sysroot[to].name).unwrap();
975 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
976 add_dep(crate_graph, from, name, to);
981 let public_deps = SysrootPublicDeps {
984 .map(|(name, idx, prelude)| {
985 (CrateName::new(name).unwrap(), sysroot_crates[&idx], prelude)
987 .collect::<Vec<_>>(),
990 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
991 (public_deps, libproc_macro)
994 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
995 add_dep_inner(graph, from, Dependency::new(name, to))
998 fn add_dep_with_prelude(
999 graph: &mut CrateGraph,
1005 add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude))
1008 fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
1009 if let Err(err) = graph.add_dep(from, dep) {
1010 tracing::error!("{}", err)
1014 /// Recreates the compile-time environment variables that Cargo sets.
1016 /// Should be synced with
1017 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
1019 /// FIXME: ask Cargo to provide this data instead of re-deriving.
1020 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
1021 // FIXME: Missing variables:
1022 // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
1024 let manifest_dir = package.manifest.parent();
1025 env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned());
1027 // Not always right, but works for common cases.
1028 env.set("CARGO", "cargo".into());
1030 env.set("CARGO_PKG_VERSION", package.version.to_string());
1031 env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
1032 env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
1033 env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
1034 env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
1036 env.set("CARGO_PKG_AUTHORS", String::new());
1038 env.set("CARGO_PKG_NAME", package.name.clone());
1039 // FIXME: This isn't really correct (a package can have many crates with different names), but
1040 // it's better than leaving the variable unset.
1041 env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string());
1042 env.set("CARGO_PKG_DESCRIPTION", String::new());
1043 env.set("CARGO_PKG_HOMEPAGE", String::new());
1044 env.set("CARGO_PKG_REPOSITORY", String::new());
1045 env.set("CARGO_PKG_LICENSE", String::new());
1047 env.set("CARGO_PKG_LICENSE_FILE", String::new());