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())?
161 ProjectManifest::CargoToml(cargo_toml) => {
162 let cargo_version = utf8_stdout({
163 let mut cmd = Command::new(toolchain::cargo());
164 cmd.arg("--version");
167 let toolchain = cargo_version
168 .get("cargo ".len()..)
169 .and_then(|it| Version::parse(it.split_whitespace().next()?).ok());
171 let meta = CargoWorkspace::fetch_metadata(
179 "Failed to read Cargo metadata from Cargo.toml file {}, {:?}",
180 cargo_toml.display(),
184 let cargo = CargoWorkspace::new(meta);
186 let sysroot = if config.no_sysroot {
189 Some(Sysroot::discover(cargo_toml.parent()).with_context(|| {
191 "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
197 let rustc_dir = match &config.rustc_source {
198 Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
199 Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml),
203 let rustc = match rustc_dir {
204 Some(rustc_dir) => Some({
205 let meta = CargoWorkspace::fetch_metadata(
212 "Failed to read Cargo metadata for Rust sources".to_string()
214 CargoWorkspace::new(meta)
219 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
221 let cfg_overrides = config.cfg_overrides();
222 ProjectWorkspace::Cargo {
224 build_scripts: WorkspaceBuildScripts::default(),
238 project_json: ProjectJson,
239 target: Option<&str>,
240 ) -> Result<ProjectWorkspace> {
241 let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
242 (Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)?),
243 (Some(sysroot), None) => {
244 // assume sysroot is structured like rustup's and guess `sysroot_src`
246 sysroot.join("lib").join("rustlib").join("src").join("rust").join("library");
248 Some(Sysroot::load(sysroot, sysroot_src)?)
250 (None, Some(sysroot_src)) => {
251 // assume sysroot is structured like rustup's and guess `sysroot`
252 let mut sysroot = sysroot_src.clone();
256 Some(Sysroot::load(sysroot, sysroot_src)?)
258 (None, None) => None,
261 let rustc_cfg = rustc_cfg::get(None, target);
262 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
265 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
266 let sysroot = Sysroot::discover(
269 .and_then(|it| it.parent())
270 .ok_or_else(|| format_err!("No detached files to load"))?,
272 let rustc_cfg = rustc_cfg::get(None, None);
273 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
276 pub fn run_build_scripts(
278 config: &CargoConfig,
279 progress: &dyn Fn(String),
280 ) -> Result<WorkspaceBuildScripts> {
282 ProjectWorkspace::Cargo { cargo, toolchain, .. } => {
283 WorkspaceBuildScripts::run(config, cargo, progress, toolchain).with_context(|| {
284 format!("Failed to run build scripts for {}", &cargo.workspace_root().display())
287 ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
288 Ok(WorkspaceBuildScripts::default())
293 pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
295 ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
297 always!(bs == WorkspaceBuildScripts::default());
302 /// Returns the roots for the current `ProjectWorkspace`
303 /// The return type contains the path and whether or not
304 /// the root is a member of the current workspace
305 pub fn to_roots(&self) -> Vec<PackageRoot> {
307 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
309 .map(|(_, krate)| PackageRoot {
310 is_local: krate.is_workspace_member,
311 include: krate.include.clone(),
312 exclude: krate.exclude.clone(),
314 .collect::<FxHashSet<_>>()
316 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
317 sysroot.crates().map(move |krate| PackageRoot {
319 include: vec![sysroot[krate].root.parent().to_path_buf()],
323 .collect::<Vec<_>>(),
324 ProjectWorkspace::Cargo {
336 let is_local = cargo[pkg].is_local;
337 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
339 let mut include = vec![pkg_root.clone()];
341 build_scripts.get_output(pkg).and_then(|it| it.out_dir.clone());
342 include.extend(out_dir);
344 // In case target's path is manually set in Cargo.toml to be
345 // outside the package root, add its parent as an extra include.
346 // An example of this situation would look like this:
350 // path = "../../src/lib.rs"
352 let extra_targets = cargo[pkg]
355 .filter(|&&tgt| cargo[tgt].kind == TargetKind::Lib)
356 .filter_map(|&tgt| cargo[tgt].root.parent())
357 .map(|tgt| tgt.normalize().to_path_buf())
358 .filter(|path| !path.starts_with(&pkg_root));
359 include.extend(extra_targets);
361 let mut exclude = vec![pkg_root.join(".git")];
363 exclude.push(pkg_root.join("target"));
365 exclude.push(pkg_root.join("tests"));
366 exclude.push(pkg_root.join("examples"));
367 exclude.push(pkg_root.join("benches"));
369 PackageRoot { is_local, include, exclude }
371 .chain(sysroot.iter().map(|sysroot| PackageRoot {
373 include: vec![sysroot.src_root().to_path_buf()],
376 .chain(rustc.iter().flat_map(|rustc| {
377 rustc.packages().map(move |krate| PackageRoot {
379 include: vec![rustc[krate].manifest.parent().to_path_buf()],
385 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
387 .map(|detached_file| PackageRoot {
389 include: vec![detached_file.clone()],
392 .chain(sysroot.crates().map(|krate| PackageRoot {
394 include: vec![sysroot[krate].root.parent().to_path_buf()],
401 pub fn n_packages(&self) -> usize {
403 ProjectWorkspace::Json { project, .. } => project.n_crates(),
404 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
405 let rustc_package_len = rustc.as_ref().map_or(0, |it| it.packages().len());
406 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
407 cargo.packages().len() + sysroot_package_len + rustc_package_len
409 ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
410 sysroot.crates().len() + files.len()
415 pub fn to_crate_graph(
417 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
418 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
420 let _p = profile::span("ProjectWorkspace::to_crate_graph");
422 let mut crate_graph = match self {
423 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
430 ProjectWorkspace::Cargo {
438 } => cargo_to_crate_graph(
448 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
449 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
452 if crate_graph.patch_cfg_if() {
453 tracing::debug!("Patched std to depend on cfg-if")
455 tracing::debug!("Did not patch std to depend on cfg-if")
461 fn project_json_to_crate_graph(
462 rustc_cfg: Vec<CfgFlag>,
463 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
464 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
465 project: &ProjectJson,
466 sysroot: &Option<Sysroot>,
468 let mut crate_graph = CrateGraph::default();
469 let sysroot_deps = sysroot
471 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
473 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
474 let crates: NoHashHashMap<CrateId, CrateId> = project
476 .filter_map(|(crate_id, krate)| {
477 let file_path = &krate.root_module;
478 let file_id = load(file_path)?;
479 Some((crate_id, krate, file_id))
481 .map(|(crate_id, krate, file_id)| {
482 let env = krate.env.clone().into_iter().collect();
483 let proc_macro = match krate.proc_macro_dylib_path.clone() {
484 Some(it) => load_proc_macro(
485 krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""),
488 None => Err("no proc macro dylib present".into()),
491 let target_cfgs = match krate.target.as_deref() {
493 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
498 let mut cfg_options = CfgOptions::default();
499 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
502 crate_graph.add_crate_root(
505 krate.display_name.clone(),
506 krate.version.clone(),
512 if krate.display_name.is_some() {
513 CrateOrigin::CratesIo { repo: krate.repository.clone() }
515 CrateOrigin::CratesIo { repo: None }
522 for (from, krate) in project.crates() {
523 if let Some(&from) = crates.get(&from) {
524 if let Some((public_deps, libproc_macro)) = &sysroot_deps {
525 public_deps.add(from, &mut crate_graph);
526 if krate.is_proc_macro {
527 if let Some(proc_macro) = libproc_macro {
531 CrateName::new("proc_macro").unwrap(),
538 for dep in &krate.deps {
539 if let Some(&to) = crates.get(&dep.crate_id) {
540 add_dep(&mut crate_graph, from, dep.name.clone(), to)
548 fn cargo_to_crate_graph(
549 rustc_cfg: Vec<CfgFlag>,
550 override_cfg: &CfgOverrides,
551 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
552 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
553 cargo: &CargoWorkspace,
554 build_scripts: &WorkspaceBuildScripts,
555 sysroot: Option<&Sysroot>,
556 rustc: &Option<CargoWorkspace>,
558 let _p = profile::span("cargo_to_crate_graph");
559 let mut crate_graph = CrateGraph::default();
560 let (public_deps, libproc_macro) = match sysroot {
561 Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load),
562 None => (SysrootPublicDeps::default(), None),
565 let mut cfg_options = CfgOptions::default();
566 cfg_options.extend(rustc_cfg);
568 let mut pkg_to_lib_crate = FxHashMap::default();
570 cfg_options.insert_atom("debug_assertions".into());
572 let mut pkg_crates = FxHashMap::default();
573 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
574 let mut has_private = false;
575 // Next, create crates for each package, target pair
576 for pkg in cargo.packages() {
577 let mut cfg_options = cfg_options.clone();
579 let overrides = match override_cfg {
580 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
581 CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
584 // Add test cfg for local crates
585 if cargo[pkg].is_local {
586 cfg_options.insert_atom("test".into());
589 if let Some(overrides) = overrides {
590 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
591 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
592 // working on rust-lang/rust as that's the only time it appears outside sysroot).
594 // A more ideal solution might be to reanalyze crates based on where the cursor is and
595 // figure out the set of cfgs that would have to apply to make it active.
597 cfg_options.apply_diff(overrides.clone());
600 has_private |= cargo[pkg].metadata.rustc_private;
601 let mut lib_tgt = None;
602 for &tgt in cargo[pkg].targets.iter() {
603 if cargo[tgt].kind != TargetKind::Lib && !cargo[pkg].is_member {
604 // For non-workspace-members, Cargo does not resolve dev-dependencies, so we don't
605 // add any targets except the library target, since those will not work correctly if
606 // they use dev-dependencies.
607 // In fact, they can break quite badly if multiple client workspaces get merged:
608 // https://github.com/rust-lang/rust-analyzer/issues/11300
612 if let Some(file_id) = load(&cargo[tgt].root) {
613 let crate_id = add_target_crate_root(
616 build_scripts.get_output(pkg),
618 &mut |path| load_proc_macro(&cargo[tgt].name, path),
621 cargo[tgt].is_proc_macro,
623 if cargo[tgt].kind == TargetKind::Lib {
624 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
625 pkg_to_lib_crate.insert(pkg, crate_id);
627 if let Some(proc_macro) = libproc_macro {
628 add_dep_with_prelude(
631 CrateName::new("proc_macro").unwrap(),
633 cargo[tgt].is_proc_macro,
637 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
641 // Set deps to the core, std and to the lib target of the current package
642 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
643 // Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
644 public_deps.add(*from, &mut crate_graph);
646 if let Some((to, name)) = lib_tgt.clone() {
647 if to != *from && *kind != TargetKind::BuildScript {
648 // (build script can not depend on its library target)
650 // For root projects with dashes in their name,
651 // cargo metadata does not do any normalization,
652 // so we do it ourselves currently
653 let name = CrateName::normalize_dashes(&name);
654 add_dep(&mut crate_graph, *from, name, to);
660 // Now add a dep edge from all targets of upstream to the lib
661 // target of downstream.
662 for pkg in cargo.packages() {
663 for dep in cargo[pkg].dependencies.iter() {
664 let name = CrateName::new(&dep.name).unwrap();
665 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
666 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
667 if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
668 // Only build scripts may depend on build dependencies.
671 if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
672 // Build scripts may only depend on build dependencies.
676 add_dep(&mut crate_graph, *from, name.clone(), to)
683 // If the user provided a path to rustc sources, we add all the rustc_private crates
684 // and create dependencies on them for the crates which opt-in to that
685 if let Some(rustc_workspace) = rustc {
693 &mut pkg_to_lib_crate,
704 fn detached_files_to_crate_graph(
705 rustc_cfg: Vec<CfgFlag>,
706 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
707 detached_files: &[AbsPathBuf],
710 let _p = profile::span("detached_files_to_crate_graph");
711 let mut crate_graph = CrateGraph::default();
712 let (public_deps, _libproc_macro) =
713 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
715 let mut cfg_options = CfgOptions::default();
716 cfg_options.extend(rustc_cfg);
718 for detached_file in detached_files {
719 let file_id = match load(detached_file) {
720 Some(file_id) => file_id,
722 tracing::error!("Failed to load detached file {:?}", detached_file);
726 let display_name = detached_file
728 .and_then(|os_str| os_str.to_str())
729 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
730 let detached_file_crate = crate_graph.add_crate_root(
740 CrateOrigin::CratesIo { repo: None },
743 public_deps.add(detached_file_crate, &mut crate_graph);
748 fn handle_rustc_crates(
749 rustc_workspace: &CargoWorkspace,
750 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
751 crate_graph: &mut CrateGraph,
752 cfg_options: &CfgOptions,
753 override_cfg: &CfgOverrides,
754 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
755 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
756 public_deps: &SysrootPublicDeps,
757 cargo: &CargoWorkspace,
758 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
759 build_scripts: &WorkspaceBuildScripts,
761 let mut rustc_pkg_crates = FxHashMap::default();
762 // The root package of the rustc-dev component is rustc_driver, so we match that
764 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
765 // The rustc workspace might be incomplete (such as if rustc-dev is not
766 // installed for the current toolchain) and `rustc_source` is set to discover.
767 if let Some(root_pkg) = root_pkg {
768 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
769 let mut queue = VecDeque::new();
770 queue.push_back(root_pkg);
771 while let Some(pkg) = queue.pop_front() {
772 // Don't duplicate packages if they are dependended on a diamond pattern
773 // N.B. if this line is omitted, we try to analyse over 4_800_000 crates
774 // which is not ideal
775 if rustc_pkg_crates.contains_key(&pkg) {
778 for dep in &rustc_workspace[pkg].dependencies {
779 queue.push_back(dep.pkg);
782 let mut cfg_options = cfg_options.clone();
784 let overrides = match override_cfg {
785 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
786 CfgOverrides::Selective(cfg_overrides) => {
787 cfg_overrides.get(&rustc_workspace[pkg].name)
791 if let Some(overrides) = overrides {
792 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
793 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
794 // working on rust-lang/rust as that's the only time it appears outside sysroot).
796 // A more ideal solution might be to reanalyze crates based on where the cursor is and
797 // figure out the set of cfgs that would have to apply to make it active.
799 cfg_options.apply_diff(overrides.clone());
802 for &tgt in rustc_workspace[pkg].targets.iter() {
803 if rustc_workspace[tgt].kind != TargetKind::Lib {
806 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
807 let crate_id = add_target_crate_root(
809 &rustc_workspace[pkg],
810 build_scripts.get_output(pkg),
812 &mut |path| load_proc_macro(&rustc_workspace[tgt].name, path),
814 &rustc_workspace[tgt].name,
815 rustc_workspace[tgt].is_proc_macro,
817 pkg_to_lib_crate.insert(pkg, crate_id);
818 // Add dependencies on core / std / alloc for this crate
819 public_deps.add(crate_id, crate_graph);
820 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
825 // Now add a dep edge from all targets of upstream to the lib
826 // target of downstream.
827 for pkg in rustc_pkg_crates.keys().copied() {
828 for dep in rustc_workspace[pkg].dependencies.iter() {
829 let name = CrateName::new(&dep.name).unwrap();
830 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
831 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
832 add_dep(crate_graph, from, name.clone(), to);
837 // Add a dependency on the rustc_private crates for all targets of each package
839 for dep in rustc_workspace.packages() {
840 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
842 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
843 for pkg in cargo.packages() {
844 let package = &cargo[pkg];
845 if !package.metadata.rustc_private {
848 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
849 // Avoid creating duplicate dependencies
850 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
851 // `rust_analyzer` thinks that it should use the one from the `rustc_source`
852 // instead of the one from `crates.io`
853 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
854 add_dep(crate_graph, *from, name.clone(), to);
862 fn add_target_crate_root(
863 crate_graph: &mut CrateGraph,
865 build_data: Option<&BuildScriptOutput>,
866 cfg_options: CfgOptions,
867 load_proc_macro: &mut dyn FnMut(&AbsPath) -> ProcMacroLoadResult,
872 let edition = pkg.edition;
873 let mut potential_cfg_options = cfg_options.clone();
874 potential_cfg_options.extend(
877 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
880 let mut opts = cfg_options;
881 for feature in pkg.active_features.iter() {
882 opts.insert_key_value("feature".into(), feature.into());
884 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
885 opts.extend(cfgs.iter().cloned());
890 let mut env = Env::default();
891 inject_cargo_env(pkg, &mut env);
893 if let Some(envs) = build_data.map(|it| &it.envs) {
895 env.set(k, v.clone());
899 let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) {
900 Some(Some(it)) => load_proc_macro(it),
901 Some(None) => Err("no proc macro dylib present".into()),
902 None => Err("crate has not (yet) been built".into()),
905 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
906 crate_graph.add_crate_root(
910 Some(pkg.version.to_string()),
912 potential_cfg_options,
916 CrateOrigin::CratesIo { repo: pkg.repository.clone() },
921 struct SysrootPublicDeps {
922 deps: Vec<(CrateName, CrateId, bool)>,
925 impl SysrootPublicDeps {
926 /// Makes `from` depend on the public sysroot crates.
927 fn add(&self, from: CrateId, crate_graph: &mut CrateGraph) {
928 for (name, krate, prelude) in &self.deps {
929 add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude);
934 fn sysroot_to_crate_graph(
935 crate_graph: &mut CrateGraph,
937 rustc_cfg: Vec<CfgFlag>,
938 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
939 ) -> (SysrootPublicDeps, Option<CrateId>) {
940 let _p = profile::span("sysroot_to_crate_graph");
941 let mut cfg_options = CfgOptions::default();
942 cfg_options.extend(rustc_cfg);
943 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
945 .filter_map(|krate| {
946 let file_id = load(&sysroot[krate].root)?;
948 let env = Env::default();
949 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
950 let crate_id = crate_graph.add_crate_root(
958 Err("no proc macro loaded for sysroot crate".into()),
960 CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
962 Some((krate, crate_id))
966 for from in sysroot.crates() {
967 for &to in sysroot[from].deps.iter() {
968 let name = CrateName::new(&sysroot[to].name).unwrap();
969 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
970 add_dep(crate_graph, from, name, to);
975 let public_deps = SysrootPublicDeps {
978 .map(|(name, idx, prelude)| {
979 (CrateName::new(name).unwrap(), sysroot_crates[&idx], prelude)
981 .collect::<Vec<_>>(),
984 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
985 (public_deps, libproc_macro)
988 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
989 add_dep_inner(graph, from, Dependency::new(name, to))
992 fn add_dep_with_prelude(
993 graph: &mut CrateGraph,
999 add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude))
1002 fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
1003 if let Err(err) = graph.add_dep(from, dep) {
1004 tracing::error!("{}", err)
1008 /// Recreates the compile-time environment variables that Cargo sets.
1010 /// Should be synced with
1011 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
1013 /// FIXME: ask Cargo to provide this data instead of re-deriving.
1014 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
1015 // FIXME: Missing variables:
1016 // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
1018 let manifest_dir = package.manifest.parent();
1019 env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned());
1021 // Not always right, but works for common cases.
1022 env.set("CARGO", "cargo".into());
1024 env.set("CARGO_PKG_VERSION", package.version.to_string());
1025 env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
1026 env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
1027 env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
1028 env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
1030 env.set("CARGO_PKG_AUTHORS", String::new());
1032 env.set("CARGO_PKG_NAME", package.name.clone());
1033 // FIXME: This isn't really correct (a package can have many crates with different names), but
1034 // it's better than leaving the variable unset.
1035 env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string());
1036 env.set("CARGO_PKG_DESCRIPTION", String::new());
1037 env.set("CARGO_PKG_HOMEPAGE", String::new());
1038 env.set("CARGO_PKG_REPOSITORY", String::new());
1039 env.set("CARGO_PKG_LICENSE", String::new());
1041 env.set("CARGO_PKG_LICENSE_FILE", String::new());