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, sync::Arc};
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 target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath,
25 Package, ProjectJson, ProjectManifest, Sysroot, 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>,
82 target_layout: Option<String>,
84 /// Project workspace was manually specified using a `rust-project.json` file.
85 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
87 // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
88 // That's not the end user experience we should strive for.
89 // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
90 // That needs some changes on the salsa-level though.
91 // 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).
92 // 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.
93 // 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.
95 /// Project with a set of disjoint files, not belonging to any particular workspace.
96 /// Backed by basic sysroot crates for basic completion and highlighting.
97 DetachedFiles { files: Vec<AbsPathBuf>, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
100 impl fmt::Debug for ProjectWorkspace {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 // Make sure this isn't too verbose.
104 ProjectWorkspace::Cargo {
112 target_layout: data_layout,
114 .debug_struct("Cargo")
115 .field("root", &cargo.workspace_root().file_name())
116 .field("n_packages", &cargo.packages().len())
117 .field("sysroot", &sysroot.is_some())
119 "n_rustc_compiler_crates",
120 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
122 .field("n_rustc_cfg", &rustc_cfg.len())
123 .field("n_cfg_overrides", &cfg_overrides.len())
124 .field("toolchain", &toolchain)
125 .field("data_layout", &data_layout)
127 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
128 let mut debug_struct = f.debug_struct("Json");
129 debug_struct.field("n_crates", &project.n_crates());
130 if let Some(sysroot) = sysroot {
131 debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
133 debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
134 debug_struct.finish()
136 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
137 .debug_struct("DetachedFiles")
138 .field("n_files", &files.len())
139 .field("sysroot", &sysroot.is_some())
140 .field("n_rustc_cfg", &rustc_cfg.len())
146 impl ProjectWorkspace {
148 manifest: ProjectManifest,
149 config: &CargoConfig,
150 progress: &dyn Fn(String),
151 ) -> Result<ProjectWorkspace> {
152 let res = match manifest {
153 ProjectManifest::ProjectJson(project_json) => {
154 let file = fs::read_to_string(&project_json).with_context(|| {
155 format!("Failed to read json file {}", project_json.display())
157 let data = serde_json::from_str(&file).with_context(|| {
158 format!("Failed to deserialize json file {}", project_json.display())
160 let project_location = project_json.parent().to_path_buf();
161 let project_json = ProjectJson::new(&project_location, data);
162 ProjectWorkspace::load_inline(
164 config.target.as_deref(),
168 ProjectManifest::CargoToml(cargo_toml) => {
169 let cargo_version = utf8_stdout({
170 let mut cmd = Command::new(toolchain::cargo());
171 cmd.envs(&config.extra_env);
172 cmd.arg("--version");
175 let toolchain = cargo_version
176 .get("cargo ".len()..)
177 .and_then(|it| Version::parse(it.split_whitespace().next()?).ok());
179 let meta = CargoWorkspace::fetch_metadata(
187 "Failed to read Cargo metadata from Cargo.toml file {}, {:?}",
188 cargo_toml.display(),
192 let cargo = CargoWorkspace::new(meta);
194 let sysroot = match &config.sysroot {
195 Some(RustcSource::Path(path)) => {
196 Some(Sysroot::with_sysroot_dir(path.clone()).with_context(|| {
197 format!("Failed to find sysroot at {}.", path.display())
200 Some(RustcSource::Discover) => Some(
201 Sysroot::discover(cargo_toml.parent(), &config.extra_env).with_context(
204 "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
212 if let Some(sysroot) = &sysroot {
213 tracing::info!(src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot");
216 let rustc_dir = match &config.rustc_source {
217 Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
218 Some(RustcSource::Discover) => {
219 Sysroot::discover_rustc(&cargo_toml, &config.extra_env)
223 if let Some(rustc_dir) = &rustc_dir {
224 tracing::info!(rustc_dir = %rustc_dir.display(), "Using rustc source");
227 let rustc = match rustc_dir {
228 Some(rustc_dir) => Some({
229 let meta = CargoWorkspace::fetch_metadata(
236 "Failed to read Cargo metadata for Rust sources".to_string()
238 CargoWorkspace::new(meta)
244 rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env);
246 let cfg_overrides = config.cfg_overrides();
247 let data_layout = target_data_layout::get(
249 config.target.as_deref(),
252 ProjectWorkspace::Cargo {
254 build_scripts: WorkspaceBuildScripts::default(),
260 target_layout: data_layout,
269 project_json: ProjectJson,
270 target: Option<&str>,
271 extra_env: &FxHashMap<String, String>,
272 ) -> Result<ProjectWorkspace> {
273 let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
274 (Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)?),
275 (Some(sysroot), None) => {
276 // assume sysroot is structured like rustup's and guess `sysroot_src`
278 sysroot.join("lib").join("rustlib").join("src").join("rust").join("library");
280 Some(Sysroot::load(sysroot, sysroot_src)?)
282 (None, Some(sysroot_src)) => {
283 // assume sysroot is structured like rustup's and guess `sysroot`
284 let mut sysroot = sysroot_src.clone();
288 Some(Sysroot::load(sysroot, sysroot_src)?)
290 (None, None) => None,
292 if let Some(sysroot) = &sysroot {
293 tracing::info!(src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot");
296 let rustc_cfg = rustc_cfg::get(None, target, extra_env);
297 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
300 pub fn load_detached_files(
301 detached_files: Vec<AbsPathBuf>,
302 config: &CargoConfig,
303 ) -> Result<ProjectWorkspace> {
304 let sysroot = match &config.sysroot {
305 Some(RustcSource::Path(path)) => Some(
306 Sysroot::with_sysroot_dir(path.clone())
307 .with_context(|| format!("Failed to find sysroot at {}.", path.display()))?,
309 Some(RustcSource::Discover) => {
310 let dir = &detached_files
312 .and_then(|it| it.parent())
313 .ok_or_else(|| format_err!("No detached files to load"))?;
314 Some(Sysroot::discover(dir, &config.extra_env).with_context(|| {
315 format!("Failed to find sysroot in {}. Is rust-src installed?", dir.display())
320 if let Some(sysroot) = &sysroot {
321 tracing::info!(src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot");
323 let rustc_cfg = rustc_cfg::get(None, None, &Default::default());
324 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
327 /// Runs the build scripts for this [`ProjectWorkspace`].
328 pub fn run_build_scripts(
330 config: &CargoConfig,
331 progress: &dyn Fn(String),
332 ) -> Result<WorkspaceBuildScripts> {
334 ProjectWorkspace::Cargo { cargo, toolchain, .. } => {
335 WorkspaceBuildScripts::run_for_workspace(config, cargo, progress, toolchain)
338 "Failed to run build scripts for {}",
339 &cargo.workspace_root().display()
343 ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
344 Ok(WorkspaceBuildScripts::default())
349 /// Runs the build scripts for the given [`ProjectWorkspace`]s. Depending on the invocation
350 /// strategy this may run a single build process for all project workspaces.
351 pub fn run_all_build_scripts(
352 workspaces: &[ProjectWorkspace],
353 config: &CargoConfig,
354 progress: &dyn Fn(String),
355 ) -> Vec<Result<WorkspaceBuildScripts>> {
356 if matches!(config.invocation_strategy, InvocationStrategy::PerWorkspace)
357 || config.run_build_script_command.is_none()
359 return workspaces.iter().map(|it| it.run_build_scripts(config, progress)).collect();
362 let cargo_ws: Vec<_> = workspaces
364 .filter_map(|it| match it {
365 ProjectWorkspace::Cargo { cargo, .. } => Some(cargo),
369 let ref mut outputs = match WorkspaceBuildScripts::run_once(config, &cargo_ws, progress) {
370 Ok(it) => Ok(it.into_iter()),
371 // io::Error is not Clone?
372 Err(e) => Err(Arc::new(e)),
378 ProjectWorkspace::Cargo { cargo, .. } => match outputs {
379 Ok(outputs) => Ok(outputs.next().unwrap()),
380 Err(e) => Err(e.clone()).with_context(|| {
382 "Failed to run build scripts for {}",
383 &cargo.workspace_root().display()
387 _ => Ok(WorkspaceBuildScripts::default()),
392 pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
394 ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
396 always!(bs == WorkspaceBuildScripts::default());
401 pub fn find_sysroot_proc_macro_srv(&self) -> Option<AbsPathBuf> {
403 ProjectWorkspace::Cargo { sysroot: Some(sysroot), .. }
404 | ProjectWorkspace::Json { sysroot: Some(sysroot), .. } => {
405 let standalone_server_name =
406 format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
409 .map(|segment| sysroot.root().join(segment).join(&standalone_server_name))
410 .find(|server_path| std::fs::metadata(server_path).is_ok())
416 /// Returns the roots for the current `ProjectWorkspace`
417 /// The return type contains the path and whether or not
418 /// the root is a member of the current workspace
419 pub fn to_roots(&self) -> Vec<PackageRoot> {
420 let mk_sysroot = |sysroot: Option<&Sysroot>| {
421 sysroot.map(|sysroot| PackageRoot {
423 include: vec![sysroot.src_root().to_path_buf()],
428 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
430 .map(|(_, krate)| PackageRoot {
431 is_local: krate.is_workspace_member,
432 include: krate.include.clone(),
433 exclude: krate.exclude.clone(),
435 .collect::<FxHashSet<_>>()
437 .chain(mk_sysroot(sysroot.as_ref()))
438 .collect::<Vec<_>>(),
439 ProjectWorkspace::Cargo {
452 let is_local = cargo[pkg].is_local;
453 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
455 let mut include = vec![pkg_root.clone()];
457 build_scripts.get_output(pkg).and_then(|it| it.out_dir.clone());
458 include.extend(out_dir);
460 // In case target's path is manually set in Cargo.toml to be
461 // outside the package root, add its parent as an extra include.
462 // An example of this situation would look like this:
466 // path = "../../src/lib.rs"
468 let extra_targets = cargo[pkg]
471 .filter(|&&tgt| cargo[tgt].kind == TargetKind::Lib)
472 .filter_map(|&tgt| cargo[tgt].root.parent())
473 .map(|tgt| tgt.normalize().to_path_buf())
474 .filter(|path| !path.starts_with(&pkg_root));
475 include.extend(extra_targets);
477 let mut exclude = vec![pkg_root.join(".git")];
479 exclude.push(pkg_root.join("target"));
481 exclude.push(pkg_root.join("tests"));
482 exclude.push(pkg_root.join("examples"));
483 exclude.push(pkg_root.join("benches"));
485 PackageRoot { is_local, include, exclude }
487 .chain(mk_sysroot(sysroot.as_ref()))
488 .chain(rustc.iter().flat_map(|rustc| {
489 rustc.packages().map(move |krate| PackageRoot {
491 include: vec![rustc[krate].manifest.parent().to_path_buf()],
497 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
499 .map(|detached_file| PackageRoot {
501 include: vec![detached_file.clone()],
504 .chain(mk_sysroot(sysroot.as_ref()))
509 pub fn n_packages(&self) -> usize {
511 ProjectWorkspace::Json { project, sysroot, .. } => {
512 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
513 sysroot_package_len + project.n_crates()
515 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
516 let rustc_package_len = rustc.as_ref().map_or(0, |it| it.packages().len());
517 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
518 cargo.packages().len() + sysroot_package_len + rustc_package_len
520 ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
521 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
522 sysroot_package_len + files.len()
527 pub fn to_crate_graph(
529 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
530 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
531 extra_env: &FxHashMap<String, String>,
533 let _p = profile::span("ProjectWorkspace::to_crate_graph");
535 let mut crate_graph = match self {
536 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
545 ProjectWorkspace::Cargo {
554 } => cargo_to_crate_graph(
563 target_layout.as_deref().map(Arc::from),
565 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
566 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot, None)
569 if crate_graph.patch_cfg_if() {
570 tracing::debug!("Patched std to depend on cfg-if")
572 tracing::debug!("Did not patch std to depend on cfg-if")
578 fn project_json_to_crate_graph(
579 rustc_cfg: Vec<CfgFlag>,
580 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
581 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
582 project: &ProjectJson,
583 sysroot: &Option<Sysroot>,
584 extra_env: &FxHashMap<String, String>,
585 target_layout: Option<Arc<str>>,
587 let mut crate_graph = CrateGraph::default();
588 let sysroot_deps = sysroot.as_ref().map(|sysroot| {
589 sysroot_to_crate_graph(
593 target_layout.clone(),
598 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
599 let crates: NoHashHashMap<CrateId, CrateId> = project
601 .filter_map(|(crate_id, krate)| {
602 let file_path = &krate.root_module;
603 let file_id = load(file_path)?;
604 Some((crate_id, krate, file_id))
606 .map(|(crate_id, krate, file_id)| {
607 let env = krate.env.clone().into_iter().collect();
608 let proc_macro = match krate.proc_macro_dylib_path.clone() {
609 Some(it) => load_proc_macro(
610 krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""),
613 None => Err("no proc macro dylib present".into()),
616 let target_cfgs = match krate.target.as_deref() {
617 Some(target) => cfg_cache
619 .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)),
623 let mut cfg_options = CfgOptions::default();
624 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
627 crate_graph.add_crate_root(
630 krate.display_name.clone(),
631 krate.version.clone(),
637 if krate.display_name.is_some() {
638 CrateOrigin::CratesIo {
639 repo: krate.repository.clone(),
643 .map(|n| n.canonical_name().to_string()),
646 CrateOrigin::CratesIo { repo: None, name: None }
648 target_layout.clone(),
654 for (from, krate) in project.crates() {
655 if let Some(&from) = crates.get(&from) {
656 if let Some((public_deps, libproc_macro)) = &sysroot_deps {
657 public_deps.add_to_crate_graph(&mut crate_graph, from);
658 if krate.is_proc_macro {
659 if let Some(proc_macro) = libproc_macro {
663 CrateName::new("proc_macro").unwrap(),
670 for dep in &krate.deps {
671 if let Some(&to) = crates.get(&dep.crate_id) {
672 add_dep(&mut crate_graph, from, dep.name.clone(), to)
680 fn cargo_to_crate_graph(
681 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
682 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
683 rustc: &Option<CargoWorkspace>,
684 cargo: &CargoWorkspace,
685 sysroot: Option<&Sysroot>,
686 rustc_cfg: Vec<CfgFlag>,
687 override_cfg: &CfgOverrides,
688 build_scripts: &WorkspaceBuildScripts,
689 target_layout: Option<Arc<str>>,
691 let _p = profile::span("cargo_to_crate_graph");
692 let mut crate_graph = CrateGraph::default();
693 let (public_deps, libproc_macro) = match sysroot {
694 Some(sysroot) => sysroot_to_crate_graph(
698 target_layout.clone(),
701 None => (SysrootPublicDeps::default(), None),
705 let mut cfg_options = CfgOptions::default();
706 cfg_options.extend(rustc_cfg);
707 cfg_options.insert_atom("debug_assertions".into());
711 let mut pkg_to_lib_crate = FxHashMap::default();
713 let mut pkg_crates = FxHashMap::default();
714 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
715 let mut has_private = false;
716 // Next, create crates for each package, target pair
717 for pkg in cargo.packages() {
718 let mut cfg_options = cfg_options.clone();
720 let overrides = match override_cfg {
721 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
722 CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
725 // Add test cfg for local crates
726 if cargo[pkg].is_local {
727 cfg_options.insert_atom("test".into());
730 if let Some(overrides) = overrides {
731 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
732 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
733 // working on rust-lang/rust as that's the only time it appears outside sysroot).
735 // A more ideal solution might be to reanalyze crates based on where the cursor is and
736 // figure out the set of cfgs that would have to apply to make it active.
738 cfg_options.apply_diff(overrides.clone());
741 has_private |= cargo[pkg].metadata.rustc_private;
742 let mut lib_tgt = None;
743 for &tgt in cargo[pkg].targets.iter() {
744 if cargo[tgt].kind != TargetKind::Lib && !cargo[pkg].is_member {
745 // For non-workspace-members, Cargo does not resolve dev-dependencies, so we don't
746 // add any targets except the library target, since those will not work correctly if
747 // they use dev-dependencies.
748 // In fact, they can break quite badly if multiple client workspaces get merged:
749 // https://github.com/rust-lang/rust-analyzer/issues/11300
753 if let Some(file_id) = load(&cargo[tgt].root) {
754 let crate_id = add_target_crate_root(
757 build_scripts.get_output(pkg),
759 &mut |path| load_proc_macro(&cargo[tgt].name, path),
762 cargo[tgt].is_proc_macro,
763 target_layout.clone(),
765 if cargo[tgt].kind == TargetKind::Lib {
766 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
767 pkg_to_lib_crate.insert(pkg, crate_id);
769 // Even crates that don't set proc-macro = true are allowed to depend on proc_macro
770 // (just none of the APIs work when called outside of a proc macro).
771 if let Some(proc_macro) = libproc_macro {
772 add_dep_with_prelude(
775 CrateName::new("proc_macro").unwrap(),
777 cargo[tgt].is_proc_macro,
781 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
785 // Set deps to the core, std and to the lib target of the current package
786 for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
787 // Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
788 public_deps.add_to_crate_graph(&mut crate_graph, from);
790 if let Some((to, name)) = lib_tgt.clone() {
791 if to != from && kind != TargetKind::BuildScript {
792 // (build script can not depend on its library target)
794 // For root projects with dashes in their name,
795 // cargo metadata does not do any normalization,
796 // so we do it ourselves currently
797 let name = CrateName::normalize_dashes(&name);
798 add_dep(&mut crate_graph, from, name, to);
804 // Now add a dep edge from all targets of upstream to the lib
805 // target of downstream.
806 for pkg in cargo.packages() {
807 for dep in cargo[pkg].dependencies.iter() {
808 let name = CrateName::new(&dep.name).unwrap();
809 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
810 for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
811 if dep.kind == DepKind::Build && kind != TargetKind::BuildScript {
812 // Only build scripts may depend on build dependencies.
815 if dep.kind != DepKind::Build && kind == TargetKind::BuildScript {
816 // Build scripts may only depend on build dependencies.
820 add_dep(&mut crate_graph, from, name.clone(), to)
827 // If the user provided a path to rustc sources, we add all the rustc_private crates
828 // and create dependencies on them for the crates which opt-in to that
829 if let Some(rustc_workspace) = rustc {
832 &mut pkg_to_lib_crate,
850 fn detached_files_to_crate_graph(
851 rustc_cfg: Vec<CfgFlag>,
852 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
853 detached_files: &[AbsPathBuf],
854 sysroot: &Option<Sysroot>,
855 target_layout: Option<Arc<str>>,
857 let _p = profile::span("detached_files_to_crate_graph");
858 let mut crate_graph = CrateGraph::default();
859 let (public_deps, _libproc_macro) = match sysroot {
860 Some(sysroot) => sysroot_to_crate_graph(
864 target_layout.clone(),
867 None => (SysrootPublicDeps::default(), None),
870 let mut cfg_options = CfgOptions::default();
871 cfg_options.extend(rustc_cfg);
873 for detached_file in detached_files {
874 let file_id = match load(detached_file) {
875 Some(file_id) => file_id,
877 tracing::error!("Failed to load detached file {:?}", detached_file);
881 let display_name = detached_file
883 .and_then(|os_str| os_str.to_str())
884 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
885 let detached_file_crate = crate_graph.add_crate_root(
888 display_name.clone(),
895 CrateOrigin::CratesIo {
897 name: display_name.map(|n| n.canonical_name().to_string()),
899 target_layout.clone(),
902 public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate);
907 fn handle_rustc_crates(
908 crate_graph: &mut CrateGraph,
909 pkg_to_lib_crate: &mut FxHashMap<Package, CrateId>,
910 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
911 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
912 rustc_workspace: &CargoWorkspace,
913 cargo: &CargoWorkspace,
914 public_deps: &SysrootPublicDeps,
915 libproc_macro: Option<CrateId>,
916 pkg_crates: &FxHashMap<Package, Vec<(CrateId, TargetKind)>>,
917 cfg_options: &CfgOptions,
918 override_cfg: &CfgOverrides,
919 build_scripts: &WorkspaceBuildScripts,
920 target_layout: Option<Arc<str>>,
922 let mut rustc_pkg_crates = FxHashMap::default();
923 // The root package of the rustc-dev component is rustc_driver, so we match that
925 rustc_workspace.packages().find(|&package| rustc_workspace[package].name == "rustc_driver");
926 // The rustc workspace might be incomplete (such as if rustc-dev is not
927 // installed for the current toolchain) and `rustc_source` is set to discover.
928 if let Some(root_pkg) = root_pkg {
929 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
930 let mut queue = VecDeque::new();
931 queue.push_back(root_pkg);
932 while let Some(pkg) = queue.pop_front() {
933 // Don't duplicate packages if they are dependent on a diamond pattern
934 // N.B. if this line is omitted, we try to analyze over 4_800_000 crates
935 // which is not ideal
936 if rustc_pkg_crates.contains_key(&pkg) {
939 for dep in &rustc_workspace[pkg].dependencies {
940 queue.push_back(dep.pkg);
943 let mut cfg_options = cfg_options.clone();
945 let overrides = match override_cfg {
946 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
947 CfgOverrides::Selective(cfg_overrides) => {
948 cfg_overrides.get(&rustc_workspace[pkg].name)
952 if let Some(overrides) = overrides {
953 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
954 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
955 // working on rust-lang/rust as that's the only time it appears outside sysroot).
957 // A more ideal solution might be to reanalyze crates based on where the cursor is and
958 // figure out the set of cfgs that would have to apply to make it active.
960 cfg_options.apply_diff(overrides.clone());
963 for &tgt in rustc_workspace[pkg].targets.iter() {
964 if rustc_workspace[tgt].kind != TargetKind::Lib {
967 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
968 let crate_id = add_target_crate_root(
970 &rustc_workspace[pkg],
971 build_scripts.get_output(pkg),
973 &mut |path| load_proc_macro(&rustc_workspace[tgt].name, path),
975 &rustc_workspace[tgt].name,
976 rustc_workspace[tgt].is_proc_macro,
977 target_layout.clone(),
979 pkg_to_lib_crate.insert(pkg, crate_id);
980 // Add dependencies on core / std / alloc for this crate
981 public_deps.add_to_crate_graph(crate_graph, crate_id);
982 if let Some(proc_macro) = libproc_macro {
983 add_dep_with_prelude(
986 CrateName::new("proc_macro").unwrap(),
988 rustc_workspace[tgt].is_proc_macro,
991 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
996 // Now add a dep edge from all targets of upstream to the lib
997 // target of downstream.
998 for pkg in rustc_pkg_crates.keys().copied() {
999 for dep in rustc_workspace[pkg].dependencies.iter() {
1000 let name = CrateName::new(&dep.name).unwrap();
1001 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
1002 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
1003 add_dep(crate_graph, from, name.clone(), to);
1008 // Add a dependency on the rustc_private crates for all targets of each package
1010 for dep in rustc_workspace.packages() {
1011 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
1013 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
1014 for pkg in cargo.packages() {
1015 let package = &cargo[pkg];
1016 if !package.metadata.rustc_private {
1019 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
1020 // Avoid creating duplicate dependencies
1021 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
1022 // `rust_analyzer` thinks that it should use the one from the `rustc_source`
1023 // instead of the one from `crates.io`
1024 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
1025 add_dep(crate_graph, *from, name.clone(), to);
1033 fn add_target_crate_root(
1034 crate_graph: &mut CrateGraph,
1036 build_data: Option<&BuildScriptOutput>,
1037 cfg_options: CfgOptions,
1038 load_proc_macro: &mut dyn FnMut(&AbsPath) -> ProcMacroLoadResult,
1041 is_proc_macro: bool,
1042 target_layout: Option<Arc<str>>,
1044 let edition = pkg.edition;
1045 let mut potential_cfg_options = cfg_options.clone();
1046 potential_cfg_options.extend(
1049 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
1052 let mut opts = cfg_options;
1053 for feature in pkg.active_features.iter() {
1054 opts.insert_key_value("feature".into(), feature.into());
1056 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
1057 opts.extend(cfgs.iter().cloned());
1062 let mut env = Env::default();
1063 inject_cargo_env(pkg, &mut env);
1065 if let Some(envs) = build_data.map(|it| &it.envs) {
1066 for (k, v) in envs {
1067 env.set(k, v.clone());
1071 let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) {
1072 Some(Some(it)) => load_proc_macro(it),
1073 Some(None) => Err("no proc macro dylib present".into()),
1074 None => Err("crate has not (yet) been built".into()),
1077 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
1078 crate_graph.add_crate_root(
1082 Some(pkg.version.to_string()),
1084 potential_cfg_options,
1088 CrateOrigin::CratesIo { repo: pkg.repository.clone(), name: Some(pkg.name.clone()) },
1094 struct SysrootPublicDeps {
1095 deps: Vec<(CrateName, CrateId, bool)>,
1098 impl SysrootPublicDeps {
1099 /// Makes `from` depend on the public sysroot crates.
1100 fn add_to_crate_graph(&self, crate_graph: &mut CrateGraph, from: CrateId) {
1101 for (name, krate, prelude) in &self.deps {
1102 add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude);
1107 fn sysroot_to_crate_graph(
1108 crate_graph: &mut CrateGraph,
1110 rustc_cfg: Vec<CfgFlag>,
1111 target_layout: Option<Arc<str>>,
1112 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
1113 ) -> (SysrootPublicDeps, Option<CrateId>) {
1114 let _p = profile::span("sysroot_to_crate_graph");
1115 let mut cfg_options = CfgOptions::default();
1116 cfg_options.extend(rustc_cfg);
1117 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
1119 .filter_map(|krate| {
1120 let file_id = load(&sysroot[krate].root)?;
1122 let env = Env::default();
1123 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
1124 let crate_id = crate_graph.add_crate_root(
1129 cfg_options.clone(),
1130 cfg_options.clone(),
1132 Err("no proc macro loaded for sysroot crate".into()),
1134 CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
1135 target_layout.clone(),
1137 Some((krate, crate_id))
1141 for from in sysroot.crates() {
1142 for &to in sysroot[from].deps.iter() {
1143 let name = CrateName::new(&sysroot[to].name).unwrap();
1144 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
1145 add_dep(crate_graph, from, name, to);
1150 let public_deps = SysrootPublicDeps {
1153 .map(|(name, idx, prelude)| {
1154 (CrateName::new(name).unwrap(), sysroot_crates[&idx], prelude)
1156 .collect::<Vec<_>>(),
1159 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
1160 (public_deps, libproc_macro)
1163 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
1164 add_dep_inner(graph, from, Dependency::new(name, to))
1167 fn add_dep_with_prelude(
1168 graph: &mut CrateGraph,
1174 add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude))
1177 fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
1178 if let Err(err) = graph.add_dep(from, dep) {
1179 tracing::error!("{}", err)
1183 /// Recreates the compile-time environment variables that Cargo sets.
1185 /// Should be synced with
1186 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
1188 /// FIXME: ask Cargo to provide this data instead of re-deriving.
1189 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
1190 // FIXME: Missing variables:
1191 // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
1193 let manifest_dir = package.manifest.parent();
1194 env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned());
1196 // Not always right, but works for common cases.
1197 env.set("CARGO", "cargo".into());
1199 env.set("CARGO_PKG_VERSION", package.version.to_string());
1200 env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
1201 env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
1202 env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
1203 env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
1205 env.set("CARGO_PKG_AUTHORS", String::new());
1207 env.set("CARGO_PKG_NAME", package.name.clone());
1208 // FIXME: This isn't really correct (a package can have many crates with different names), but
1209 // it's better than leaving the variable unset.
1210 env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string());
1211 env.set("CARGO_PKG_DESCRIPTION", String::new());
1212 env.set("CARGO_PKG_HOMEPAGE", String::new());
1213 env.set("CARGO_PKG_REPOSITORY", String::new());
1214 env.set("CARGO_PKG_LICENSE", String::new());
1216 env.set("CARGO_PKG_LICENSE_FILE", String::new());