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,
12 use cfg::{CfgDiff, CfgOptions};
13 use paths::{AbsPath, AbsPathBuf};
14 use rustc_hash::{FxHashMap, FxHashSet};
18 build_scripts::BuildScriptOutput,
19 cargo_workspace::{DepKind, PackageData, RustcSource},
22 sysroot::SysrootCrate,
23 utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, ProjectJson, ProjectManifest, Sysroot,
24 TargetKind, WorkspaceBuildScripts,
27 /// A set of cfg-overrides per crate.
29 /// `Wildcard(..)` is useful e.g. disabling `#[cfg(test)]` on all crates,
30 /// without having to first obtain a list of all crates.
31 #[derive(Debug, Clone, Eq, PartialEq)]
32 pub enum CfgOverrides {
33 /// A single global set of overrides matching all crates.
35 /// A set of overrides matching specific crates.
36 Selective(FxHashMap<String, CfgDiff>),
39 impl Default for CfgOverrides {
40 fn default() -> Self {
41 Self::Selective(FxHashMap::default())
46 pub fn len(&self) -> usize {
48 CfgOverrides::Wildcard(_) => 1,
49 CfgOverrides::Selective(hash_map) => hash_map.len(),
54 /// `PackageRoot` describes a package root folder.
55 /// Which may be an external dependency, or a member of
56 /// the current workspace.
57 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
58 pub struct PackageRoot {
59 /// Is from the local filesystem and may be edited
61 pub include: Vec<AbsPathBuf>,
62 pub exclude: Vec<AbsPathBuf>,
65 #[derive(Clone, Eq, PartialEq)]
66 pub enum ProjectWorkspace {
67 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
69 cargo: CargoWorkspace,
70 build_scripts: WorkspaceBuildScripts,
71 sysroot: Option<Sysroot>,
72 rustc: Option<CargoWorkspace>,
73 /// Holds cfg flags for the current target. We get those by running
74 /// `rustc --print cfg`.
76 /// FIXME: make this a per-crate map, as, eg, build.rs might have a
78 rustc_cfg: Vec<CfgFlag>,
79 cfg_overrides: CfgOverrides,
81 /// Project workspace was manually specified using a `rust-project.json` file.
82 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
84 // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
85 // That's not the end user experience we should strive for.
86 // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
87 // That needs some changes on the salsa-level though.
88 // 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).
89 // 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.
90 // 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.
92 /// Project with a set of disjoint files, not belonging to any particular workspace.
93 /// Backed by basic sysroot crates for basic completion and highlighting.
94 DetachedFiles { files: Vec<AbsPathBuf>, sysroot: Sysroot, rustc_cfg: Vec<CfgFlag> },
97 impl fmt::Debug for ProjectWorkspace {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 // Make sure this isn't too verbose.
101 ProjectWorkspace::Cargo {
109 .debug_struct("Cargo")
110 .field("root", &cargo.workspace_root().file_name())
111 .field("n_packages", &cargo.packages().len())
112 .field("sysroot", &sysroot.is_some())
114 "n_rustc_compiler_crates",
115 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
117 .field("n_rustc_cfg", &rustc_cfg.len())
118 .field("n_cfg_overrides", &cfg_overrides.len())
120 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
121 let mut debug_struct = f.debug_struct("Json");
122 debug_struct.field("n_crates", &project.n_crates());
123 if let Some(sysroot) = sysroot {
124 debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
126 debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
127 debug_struct.finish()
129 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
130 .debug_struct("DetachedFiles")
131 .field("n_files", &files.len())
132 .field("n_sysroot_crates", &sysroot.crates().len())
133 .field("n_rustc_cfg", &rustc_cfg.len())
139 impl ProjectWorkspace {
141 manifest: ProjectManifest,
142 config: &CargoConfig,
143 progress: &dyn Fn(String),
144 ) -> Result<ProjectWorkspace> {
145 let res = match manifest {
146 ProjectManifest::ProjectJson(project_json) => {
147 let file = fs::read_to_string(&project_json).with_context(|| {
148 format!("Failed to read json file {}", project_json.display())
150 let data = serde_json::from_str(&file).with_context(|| {
151 format!("Failed to deserialize json file {}", project_json.display())
153 let project_location = project_json.parent().to_path_buf();
154 let project_json = ProjectJson::new(&project_location, data);
155 ProjectWorkspace::load_inline(project_json, config.target.as_deref())?
157 ProjectManifest::CargoToml(cargo_toml) => {
158 let cargo_version = utf8_stdout({
159 let mut cmd = Command::new(toolchain::cargo());
160 cmd.arg("--version");
164 let meta = CargoWorkspace::fetch_metadata(
172 "Failed to read Cargo metadata from Cargo.toml file {}, {}",
173 cargo_toml.display(),
177 let cargo = CargoWorkspace::new(meta);
179 let sysroot = if config.no_sysroot {
182 Some(Sysroot::discover(cargo_toml.parent()).with_context(|| {
184 "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
190 let rustc_dir = match &config.rustc_source {
191 Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
192 Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml),
196 let rustc = match rustc_dir {
197 Some(rustc_dir) => Some({
198 let meta = CargoWorkspace::fetch_metadata(
205 "Failed to read Cargo metadata for Rust sources".to_string()
207 CargoWorkspace::new(meta)
212 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
214 let cfg_overrides = config.cfg_overrides();
215 ProjectWorkspace::Cargo {
217 build_scripts: WorkspaceBuildScripts::default(),
230 project_json: ProjectJson,
231 target: Option<&str>,
232 ) -> Result<ProjectWorkspace> {
233 let sysroot = match &project_json.sysroot_src {
234 Some(path) => Some(Sysroot::load(path.clone())?),
237 let rustc_cfg = rustc_cfg::get(None, target);
238 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
241 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
242 let sysroot = Sysroot::discover(
245 .and_then(|it| it.parent())
246 .ok_or_else(|| format_err!("No detached files to load"))?,
248 let rustc_cfg = rustc_cfg::get(None, None);
249 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
252 pub fn run_build_scripts(
254 config: &CargoConfig,
255 progress: &dyn Fn(String),
256 ) -> Result<WorkspaceBuildScripts> {
258 ProjectWorkspace::Cargo { cargo, .. } => {
259 WorkspaceBuildScripts::run(config, cargo, progress)
261 ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
262 Ok(WorkspaceBuildScripts::default())
267 pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
269 ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
271 always!(bs == WorkspaceBuildScripts::default());
276 /// Returns the roots for the current `ProjectWorkspace`
277 /// The return type contains the path and whether or not
278 /// the root is a member of the current workspace
279 pub fn to_roots(&self) -> Vec<PackageRoot> {
281 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
283 .map(|(_, krate)| PackageRoot {
284 is_local: krate.is_workspace_member,
285 include: krate.include.clone(),
286 exclude: krate.exclude.clone(),
288 .collect::<FxHashSet<_>>()
290 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
291 sysroot.crates().map(move |krate| PackageRoot {
293 include: vec![sysroot[krate].root.parent().to_path_buf()],
297 .collect::<Vec<_>>(),
298 ProjectWorkspace::Cargo {
309 let is_local = cargo[pkg].is_local;
310 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
312 let mut include = vec![pkg_root.clone()];
314 build_scripts.outputs.get(pkg).and_then(|it| it.out_dir.clone()),
317 // In case target's path is manually set in Cargo.toml to be
318 // outside the package root, add its parent as an extra include.
319 // An example of this situation would look like this:
323 // path = "../../src/lib.rs"
325 let extra_targets = cargo[pkg]
328 .filter(|&&tgt| cargo[tgt].kind == TargetKind::Lib)
329 .filter_map(|&tgt| cargo[tgt].root.parent())
330 .map(|tgt| tgt.normalize().to_path_buf())
331 .filter(|path| !path.starts_with(&pkg_root));
332 include.extend(extra_targets);
334 let mut exclude = vec![pkg_root.join(".git")];
336 exclude.push(pkg_root.join("target"));
338 exclude.push(pkg_root.join("tests"));
339 exclude.push(pkg_root.join("examples"));
340 exclude.push(pkg_root.join("benches"));
342 PackageRoot { is_local, include, exclude }
344 .chain(sysroot.iter().map(|sysroot| PackageRoot {
346 include: vec![sysroot.root().to_path_buf()],
349 .chain(rustc.iter().flat_map(|rustc| {
350 rustc.packages().map(move |krate| PackageRoot {
352 include: vec![rustc[krate].manifest.parent().to_path_buf()],
358 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
360 .map(|detached_file| PackageRoot {
362 include: vec![detached_file.clone()],
365 .chain(sysroot.crates().map(|krate| PackageRoot {
367 include: vec![sysroot[krate].root.parent().to_path_buf()],
374 pub fn n_packages(&self) -> usize {
376 ProjectWorkspace::Json { project, .. } => project.n_crates(),
377 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
378 let rustc_package_len = rustc.as_ref().map_or(0, |it| it.packages().len());
379 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
380 cargo.packages().len() + sysroot_package_len + rustc_package_len
382 ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
383 sysroot.crates().len() + files.len()
388 pub fn to_crate_graph(
390 dummy_replace: &FxHashMap<Box<str>, Box<[Box<str>]>>,
391 load_proc_macro: &mut dyn FnMut(&AbsPath, &[Box<str>]) -> Vec<ProcMacro>,
392 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
394 let _p = profile::span("ProjectWorkspace::to_crate_graph");
395 let load_proc_macro = &mut |crate_name: &_, path: &_| {
396 load_proc_macro(path, dummy_replace.get(crate_name).map(|it| &**it).unwrap_or_default())
399 let mut crate_graph = match self {
400 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
407 ProjectWorkspace::Cargo {
414 } => cargo_to_crate_graph(
424 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
425 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
428 if crate_graph.patch_cfg_if() {
429 tracing::debug!("Patched std to depend on cfg-if")
431 tracing::debug!("Did not patch std to depend on cfg-if")
437 fn project_json_to_crate_graph(
438 rustc_cfg: Vec<CfgFlag>,
439 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> Vec<ProcMacro>,
440 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
441 project: &ProjectJson,
442 sysroot: &Option<Sysroot>,
444 let mut crate_graph = CrateGraph::default();
445 let sysroot_deps = sysroot
447 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
449 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
450 let crates: FxHashMap<CrateId, CrateId> = project
452 .filter_map(|(crate_id, krate)| {
453 let file_path = &krate.root_module;
454 let file_id = load(file_path)?;
455 Some((crate_id, krate, file_id))
457 .map(|(crate_id, krate, file_id)| {
458 let env = krate.env.clone().into_iter().collect();
459 let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| {
461 krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""),
466 let target_cfgs = match krate.target.as_deref() {
468 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
473 let mut cfg_options = CfgOptions::default();
474 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
477 crate_graph.add_crate_root(
480 krate.display_name.clone(),
481 krate.version.clone(),
485 proc_macro.unwrap_or_default(),
486 if krate.display_name.is_some() {
487 CrateOrigin::CratesIo { repo: krate.repository.clone() }
496 for (from, krate) in project.crates() {
497 if let Some(&from) = crates.get(&from) {
498 if let Some((public_deps, libproc_macro)) = &sysroot_deps {
499 public_deps.add(from, &mut crate_graph);
500 if krate.is_proc_macro {
501 if let Some(proc_macro) = libproc_macro {
505 CrateName::new("proc_macro").unwrap(),
512 for dep in &krate.deps {
513 if let Some(&to) = crates.get(&dep.crate_id) {
514 add_dep(&mut crate_graph, from, dep.name.clone(), to)
522 fn cargo_to_crate_graph(
523 rustc_cfg: Vec<CfgFlag>,
524 override_cfg: &CfgOverrides,
525 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> Vec<ProcMacro>,
526 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
527 cargo: &CargoWorkspace,
528 build_scripts: &WorkspaceBuildScripts,
529 sysroot: Option<&Sysroot>,
530 rustc: &Option<CargoWorkspace>,
532 let _p = profile::span("cargo_to_crate_graph");
533 let mut crate_graph = CrateGraph::default();
534 let (public_deps, libproc_macro) = match sysroot {
535 Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load),
536 None => (SysrootPublicDeps::default(), None),
539 let mut cfg_options = CfgOptions::default();
540 cfg_options.extend(rustc_cfg);
542 let mut pkg_to_lib_crate = FxHashMap::default();
544 // Add test cfg for non-sysroot crates
545 cfg_options.insert_atom("test".into());
546 cfg_options.insert_atom("debug_assertions".into());
548 let mut pkg_crates = FxHashMap::default();
549 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
550 let mut has_private = false;
551 // Next, create crates for each package, target pair
552 for pkg in cargo.packages() {
553 let mut cfg_options = &cfg_options;
554 let mut replaced_cfg_options;
556 let overrides = match override_cfg {
557 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
558 CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
561 if let Some(overrides) = overrides {
562 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
563 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
564 // working on rust-lang/rust as that's the only time it appears outside sysroot).
566 // A more ideal solution might be to reanalyze crates based on where the cursor is and
567 // figure out the set of cfgs that would have to apply to make it active.
569 replaced_cfg_options = cfg_options.clone();
570 replaced_cfg_options.apply_diff(overrides.clone());
571 cfg_options = &replaced_cfg_options;
574 has_private |= cargo[pkg].metadata.rustc_private;
575 let mut lib_tgt = None;
576 for &tgt in cargo[pkg].targets.iter() {
577 if cargo[tgt].kind != TargetKind::Lib && !cargo[pkg].is_member {
578 // For non-workspace-members, Cargo does not resolve dev-dependencies, so we don't
579 // add any targets except the library target, since those will not work correctly if
580 // they use dev-dependencies.
581 // In fact, they can break quite badly if multiple client workspaces get merged:
582 // https://github.com/rust-analyzer/rust-analyzer/issues/11300
586 if let Some(file_id) = load(&cargo[tgt].root) {
587 let crate_id = add_target_crate_root(
590 build_scripts.outputs.get(pkg),
592 &mut |path| load_proc_macro(&cargo[tgt].name, path),
596 if cargo[tgt].kind == TargetKind::Lib {
597 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
598 pkg_to_lib_crate.insert(pkg, crate_id);
600 if let Some(proc_macro) = libproc_macro {
601 add_dep_with_prelude(
604 CrateName::new("proc_macro").unwrap(),
606 cargo[tgt].is_proc_macro,
610 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
614 // Set deps to the core, std and to the lib target of the current package
615 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
616 // Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
617 public_deps.add(*from, &mut crate_graph);
619 if let Some((to, name)) = lib_tgt.clone() {
620 if to != *from && *kind != TargetKind::BuildScript {
621 // (build script can not depend on its library target)
623 // For root projects with dashes in their name,
624 // cargo metadata does not do any normalization,
625 // so we do it ourselves currently
626 let name = CrateName::normalize_dashes(&name);
627 add_dep(&mut crate_graph, *from, name, to);
633 // Now add a dep edge from all targets of upstream to the lib
634 // target of downstream.
635 for pkg in cargo.packages() {
636 for dep in cargo[pkg].dependencies.iter() {
637 let name = CrateName::new(&dep.name).unwrap();
638 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
639 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
640 if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
641 // Only build scripts may depend on build dependencies.
644 if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
645 // Build scripts may only depend on build dependencies.
649 add_dep(&mut crate_graph, *from, name.clone(), to)
656 // If the user provided a path to rustc sources, we add all the rustc_private crates
657 // and create dependencies on them for the crates which opt-in to that
658 if let Some(rustc_workspace) = rustc {
665 &mut pkg_to_lib_crate,
675 fn detached_files_to_crate_graph(
676 rustc_cfg: Vec<CfgFlag>,
677 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
678 detached_files: &[AbsPathBuf],
681 let _p = profile::span("detached_files_to_crate_graph");
682 let mut crate_graph = CrateGraph::default();
683 let (public_deps, _libproc_macro) =
684 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
686 let mut cfg_options = CfgOptions::default();
687 cfg_options.extend(rustc_cfg);
689 for detached_file in detached_files {
690 let file_id = match load(detached_file) {
691 Some(file_id) => file_id,
693 tracing::error!("Failed to load detached file {:?}", detached_file);
697 let display_name = detached_file
699 .and_then(|os_str| os_str.to_str())
700 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
701 let detached_file_crate = crate_graph.add_crate_root(
710 CrateOrigin::Unknown,
713 public_deps.add(detached_file_crate, &mut crate_graph);
718 fn handle_rustc_crates(
719 rustc_workspace: &CargoWorkspace,
720 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
721 crate_graph: &mut CrateGraph,
722 cfg_options: &CfgOptions,
723 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> Vec<ProcMacro>,
724 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
725 public_deps: &SysrootPublicDeps,
726 cargo: &CargoWorkspace,
727 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
729 let mut rustc_pkg_crates = FxHashMap::default();
730 // The root package of the rustc-dev component is rustc_driver, so we match that
732 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
733 // The rustc workspace might be incomplete (such as if rustc-dev is not
734 // installed for the current toolchain) and `rustcSource` is set to discover.
735 if let Some(root_pkg) = root_pkg {
736 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
737 let mut queue = VecDeque::new();
738 queue.push_back(root_pkg);
739 while let Some(pkg) = queue.pop_front() {
740 // Don't duplicate packages if they are dependended on a diamond pattern
741 // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
742 // which is not ideal
743 if rustc_pkg_crates.contains_key(&pkg) {
746 for dep in &rustc_workspace[pkg].dependencies {
747 queue.push_back(dep.pkg);
749 for &tgt in rustc_workspace[pkg].targets.iter() {
750 if rustc_workspace[tgt].kind != TargetKind::Lib {
753 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
754 let crate_id = add_target_crate_root(
756 &rustc_workspace[pkg],
759 &mut |path| load_proc_macro(&rustc_workspace[tgt].name, path),
761 &rustc_workspace[tgt].name,
763 pkg_to_lib_crate.insert(pkg, crate_id);
764 // Add dependencies on core / std / alloc for this crate
765 public_deps.add(crate_id, crate_graph);
766 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
771 // Now add a dep edge from all targets of upstream to the lib
772 // target of downstream.
773 for pkg in rustc_pkg_crates.keys().copied() {
774 for dep in rustc_workspace[pkg].dependencies.iter() {
775 let name = CrateName::new(&dep.name).unwrap();
776 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
777 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
778 add_dep(crate_graph, from, name.clone(), to);
783 // Add a dependency on the rustc_private crates for all targets of each package
785 for dep in rustc_workspace.packages() {
786 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
788 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
789 for pkg in cargo.packages() {
790 let package = &cargo[pkg];
791 if !package.metadata.rustc_private {
794 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
795 // Avoid creating duplicate dependencies
796 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
797 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
798 // instead of the one from `crates.io`
799 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
800 add_dep(crate_graph, *from, name.clone(), to);
808 fn add_target_crate_root(
809 crate_graph: &mut CrateGraph,
811 build_data: Option<&BuildScriptOutput>,
812 cfg_options: &CfgOptions,
813 load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
817 let edition = pkg.edition;
819 let mut opts = cfg_options.clone();
820 for feature in pkg.active_features.iter() {
821 opts.insert_key_value("feature".into(), feature.into());
823 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
824 opts.extend(cfgs.iter().cloned());
829 let mut env = Env::default();
830 inject_cargo_env(pkg, &mut env);
832 if let Some(envs) = build_data.map(|it| &it.envs) {
834 env.set(k, v.clone());
838 let proc_macro = build_data
840 .and_then(|it| it.proc_macro_dylib_path.as_ref())
841 .map(|it| load_proc_macro(it))
842 .unwrap_or_default();
844 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
845 let mut potential_cfg_options = cfg_options.clone();
846 potential_cfg_options.extend(
849 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
851 crate_graph.add_crate_root(
855 Some(pkg.version.to_string()),
857 potential_cfg_options,
860 CrateOrigin::CratesIo { repo: pkg.repository.clone() },
865 struct SysrootPublicDeps {
866 deps: Vec<(CrateName, CrateId, bool)>,
869 impl SysrootPublicDeps {
870 /// Makes `from` depend on the public sysroot crates.
871 fn add(&self, from: CrateId, crate_graph: &mut CrateGraph) {
872 for (name, krate, prelude) in &self.deps {
873 add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude);
878 fn sysroot_to_crate_graph(
879 crate_graph: &mut CrateGraph,
881 rustc_cfg: Vec<CfgFlag>,
882 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
883 ) -> (SysrootPublicDeps, Option<CrateId>) {
884 let _p = profile::span("sysroot_to_crate_graph");
885 let mut cfg_options = CfgOptions::default();
886 cfg_options.extend(rustc_cfg);
887 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
889 .filter_map(|krate| {
890 let file_id = load(&sysroot[krate].root)?;
892 let env = Env::default();
893 let proc_macro = vec![];
894 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
895 let crate_id = crate_graph.add_crate_root(
906 Some((krate, crate_id))
910 for from in sysroot.crates() {
911 for &to in sysroot[from].deps.iter() {
912 let name = CrateName::new(&sysroot[to].name).unwrap();
913 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
914 add_dep(crate_graph, from, name, to);
919 let public_deps = SysrootPublicDeps {
922 .map(|(name, idx, prelude)| {
923 (CrateName::new(name).unwrap(), sysroot_crates[&idx], prelude)
925 .collect::<Vec<_>>(),
928 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
929 (public_deps, libproc_macro)
932 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
933 add_dep_inner(graph, from, Dependency::new(name, to))
936 fn add_dep_with_prelude(
937 graph: &mut CrateGraph,
943 add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude))
946 fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
947 if let Err(err) = graph.add_dep(from, dep) {
948 tracing::error!("{}", err)
952 /// Recreates the compile-time environment variables that Cargo sets.
954 /// Should be synced with
955 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
957 /// FIXME: ask Cargo to provide this data instead of re-deriving.
958 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
959 // FIXME: Missing variables:
960 // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
962 let manifest_dir = package.manifest.parent();
963 env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned());
965 // Not always right, but works for common cases.
966 env.set("CARGO", "cargo".into());
968 env.set("CARGO_PKG_VERSION", package.version.to_string());
969 env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
970 env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
971 env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
972 env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
974 env.set("CARGO_PKG_AUTHORS", String::new());
976 env.set("CARGO_PKG_NAME", package.name.clone());
977 // FIXME: This isn't really correct (a package can have many crates with different names), but
978 // it's better than leaving the variable unset.
979 env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string());
980 env.set("CARGO_PKG_DESCRIPTION", String::new());
981 env.set("CARGO_PKG_HOMEPAGE", String::new());
982 env.set("CARGO_PKG_REPOSITORY", String::new());
983 env.set("CARGO_PKG_LICENSE", String::new());
985 env.set("CARGO_PKG_LICENSE_FILE", String::new());