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};
8 use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
9 use cargo_workspace::DepKind;
10 use cfg::{CfgDiff, CfgOptions};
11 use paths::{AbsPath, AbsPathBuf};
12 use proc_macro_api::ProcMacroClient;
13 use rustc_hash::{FxHashMap, FxHashSet};
16 build_data::{BuildDataResult, PackageBuildData, WorkspaceBuildData},
20 sysroot::SysrootCrate,
21 utf8_stdout, BuildDataCollector, CargoConfig, CargoWorkspace, ProjectJson, ProjectManifest,
25 pub type CfgOverrides = FxHashMap<String, CfgDiff>;
27 /// `PackageRoot` describes a package root folder.
28 /// Which may be an external dependency, or a member of
29 /// the current workspace.
30 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
31 pub struct PackageRoot {
32 /// Is a member of the current workspace
34 pub include: Vec<AbsPathBuf>,
35 pub exclude: Vec<AbsPathBuf>,
38 #[derive(Clone, Eq, PartialEq)]
39 pub enum ProjectWorkspace {
40 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
42 cargo: CargoWorkspace,
44 rustc: Option<CargoWorkspace>,
45 /// Holds cfg flags for the current target. We get those by running
46 /// `rustc --print cfg`.
48 /// FIXME: make this a per-crate map, as, eg, build.rs might have a
50 rustc_cfg: Vec<CfgFlag>,
51 cfg_overrides: CfgOverrides,
53 /// Project workspace was manually specified using a `rust-project.json` file.
54 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
56 // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
57 // That's not the end user experience we should strive for.
58 // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
59 // That needs some changes on the salsa-level though.
60 // 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).
61 // 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.
62 // 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.
64 /// Project with a set of disjoint files, not belonging to any particular workspace.
65 /// Backed by basic sysroot crates for basic completion and highlighting.
66 DetachedFiles { files: Vec<AbsPathBuf>, sysroot: Sysroot, rustc_cfg: Vec<CfgFlag> },
69 impl fmt::Debug for ProjectWorkspace {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 // Make sure this isn't too verbose.
73 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg, cfg_overrides } => f
74 .debug_struct("Cargo")
75 .field("root", &cargo.workspace_root().file_name())
76 .field("n_packages", &cargo.packages().len())
77 .field("n_sysroot_crates", &sysroot.crates().len())
79 "n_rustc_compiler_crates",
80 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
82 .field("n_rustc_cfg", &rustc_cfg.len())
83 .field("n_cfg_overrides", &cfg_overrides.len())
85 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
86 let mut debug_struct = f.debug_struct("Json");
87 debug_struct.field("n_crates", &project.n_crates());
88 if let Some(sysroot) = sysroot {
89 debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
91 debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
94 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
95 .debug_struct("DetachedFiles")
96 .field("n_files", &files.len())
97 .field("n_sysroot_crates", &sysroot.crates().len())
98 .field("n_rustc_cfg", &rustc_cfg.len())
104 impl ProjectWorkspace {
106 manifest: ProjectManifest,
107 config: &CargoConfig,
108 progress: &dyn Fn(String),
109 ) -> Result<ProjectWorkspace> {
110 let res = match manifest {
111 ProjectManifest::ProjectJson(project_json) => {
112 let file = fs::read_to_string(&project_json).with_context(|| {
113 format!("Failed to read json file {}", project_json.display())
115 let data = serde_json::from_str(&file).with_context(|| {
116 format!("Failed to deserialize json file {}", project_json.display())
118 let project_location = project_json.parent().unwrap().to_path_buf();
119 let project_json = ProjectJson::new(&project_location, data);
120 ProjectWorkspace::load_inline(project_json, config.target.as_deref())?
122 ProjectManifest::CargoToml(cargo_toml) => {
123 let cargo_version = utf8_stdout({
124 let mut cmd = Command::new(toolchain::cargo());
125 cmd.arg("--version");
129 let meta = CargoWorkspace::fetch_metadata(&cargo_toml, config, progress)
132 "Failed to read Cargo metadata from Cargo.toml file {}, {}",
133 cargo_toml.display(),
137 let cargo = CargoWorkspace::new(&cargo_toml, config, meta);
139 let sysroot = if config.no_sysroot {
142 Sysroot::discover(&cargo_toml).with_context(|| {
144 "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
150 let rustc_dir = if let Some(rustc_source) = &config.rustc_source {
151 use cargo_workspace::RustcSource;
153 RustcSource::Path(path) => Some(path.clone()),
154 RustcSource::Discover => Sysroot::discover_rustc(&cargo_toml),
160 let rustc = match rustc_dir {
161 Some(rustc_dir) => Some({
162 let meta = CargoWorkspace::fetch_metadata(&rustc_dir, config, progress)
164 format!("Failed to read Cargo metadata for Rust sources")
166 CargoWorkspace::new(&rustc_dir, config, meta)
171 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
173 let cfg_overrides = config.cfg_overrides();
174 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg, cfg_overrides }
182 project_json: ProjectJson,
183 target: Option<&str>,
184 ) -> Result<ProjectWorkspace> {
185 let sysroot = match &project_json.sysroot_src {
186 Some(path) => Some(Sysroot::load(path)?),
189 let rustc_cfg = rustc_cfg::get(None, target);
190 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
193 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
194 let sysroot = Sysroot::discover(
195 detached_files.first().ok_or_else(|| format_err!("No detached files to load"))?,
197 let rustc_cfg = rustc_cfg::get(None, None);
198 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
201 /// Returns the roots for the current `ProjectWorkspace`
202 /// The return type contains the path and whether or not
203 /// the root is a member of the current workspace
204 pub fn to_roots(&self, build_data: Option<&BuildDataResult>) -> Vec<PackageRoot> {
206 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
208 .map(|(_, krate)| PackageRoot {
209 is_member: krate.is_workspace_member,
210 include: krate.include.clone(),
211 exclude: krate.exclude.clone(),
213 .collect::<FxHashSet<_>>()
215 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
216 sysroot.crates().map(move |krate| PackageRoot {
218 include: vec![sysroot[krate].root_dir().to_path_buf()],
222 .collect::<Vec<_>>(),
223 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg: _, cfg_overrides: _ } => {
227 let is_member = cargo[pkg].is_member;
228 let pkg_root = cargo[pkg].root().to_path_buf();
230 let mut include = vec![pkg_root.clone()];
233 .and_then(|it| it.get(cargo.workspace_root()))
234 .and_then(|map| map.get(&cargo[pkg].id))
235 .and_then(|it| it.out_dir.clone()),
238 // In case target's path is manually set in Cargo.toml to be
239 // outside the package root, add its parent as an extra include.
240 // An example of this situation would look like this:
244 // path = "../../src/lib.rs"
246 let extra_targets = cargo[pkg]
249 .filter(|&&tgt| cargo[tgt].kind == TargetKind::Lib)
250 .filter_map(|&tgt| cargo[tgt].root.parent())
251 .map(|tgt| tgt.normalize().to_path_buf())
252 .filter(|path| !path.starts_with(&pkg_root));
253 include.extend(extra_targets);
255 let mut exclude = vec![pkg_root.join(".git")];
257 exclude.push(pkg_root.join("target"));
259 exclude.push(pkg_root.join("tests"));
260 exclude.push(pkg_root.join("examples"));
261 exclude.push(pkg_root.join("benches"));
263 PackageRoot { is_member, include, exclude }
265 .chain(sysroot.crates().map(|krate| PackageRoot {
267 include: vec![sysroot[krate].root_dir().to_path_buf()],
270 .chain(rustc.into_iter().flat_map(|rustc| {
271 rustc.packages().map(move |krate| PackageRoot {
273 include: vec![rustc[krate].root().to_path_buf()],
279 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
281 .map(|detached_file| PackageRoot {
283 include: vec![detached_file.clone()],
286 .chain(sysroot.crates().map(|krate| PackageRoot {
288 include: vec![sysroot[krate].root_dir().to_path_buf()],
295 pub fn n_packages(&self) -> usize {
297 ProjectWorkspace::Json { project, .. } => project.n_crates(),
298 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
299 let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len());
300 cargo.packages().len() + sysroot.crates().len() + rustc_package_len
302 ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
303 sysroot.crates().len() + files.len()
308 pub fn to_crate_graph(
310 build_data: Option<&BuildDataResult>,
311 proc_macro_client: Option<&ProcMacroClient>,
312 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
314 let _p = profile::span("ProjectWorkspace::to_crate_graph");
315 let proc_macro_loader = |path: &AbsPath| match proc_macro_client {
316 Some(client) => client.by_dylib_path(path),
320 let mut crate_graph = match self {
321 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
328 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg, cfg_overrides } => {
329 cargo_to_crate_graph(
335 build_data.and_then(|it| it.get(cargo.workspace_root())),
341 .and_then(|(it, map)| map.get(it.workspace_root())),
344 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
345 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
348 if crate_graph.patch_cfg_if() {
349 log::debug!("Patched std to depend on cfg-if")
351 log::debug!("Did not patch std to depend on cfg-if")
356 pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) {
358 ProjectWorkspace::Cargo { cargo, .. } => {
359 collector.add_config(cargo.workspace_root(), cargo.build_data_config().clone());
366 fn project_json_to_crate_graph(
367 rustc_cfg: Vec<CfgFlag>,
368 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
369 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
370 project: &ProjectJson,
371 sysroot: &Option<Sysroot>,
373 let mut crate_graph = CrateGraph::default();
374 let sysroot_deps = sysroot
376 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
378 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
379 let crates: FxHashMap<CrateId, CrateId> = project
381 .filter_map(|(crate_id, krate)| {
382 let file_path = &krate.root_module;
383 let file_id = load(file_path)?;
384 Some((crate_id, krate, file_id))
386 .map(|(crate_id, krate, file_id)| {
387 let env = krate.env.clone().into_iter().collect();
388 let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| proc_macro_loader(&it));
390 let target_cfgs = match krate.target.as_deref() {
392 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
397 let mut cfg_options = CfgOptions::default();
398 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
401 crate_graph.add_crate_root(
404 krate.display_name.clone(),
408 proc_macro.unwrap_or_default(),
414 for (from, krate) in project.crates() {
415 if let Some(&from) = crates.get(&from) {
416 if let Some((public_deps, _proc_macro)) = &sysroot_deps {
417 for (name, to) in public_deps.iter() {
418 add_dep(&mut crate_graph, from, name.clone(), *to)
422 for dep in &krate.deps {
423 if let Some(&to) = crates.get(&dep.crate_id) {
424 add_dep(&mut crate_graph, from, dep.name.clone(), to)
432 fn cargo_to_crate_graph(
433 rustc_cfg: Vec<CfgFlag>,
434 override_cfg: &CfgOverrides,
435 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
436 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
437 cargo: &CargoWorkspace,
438 build_data_map: Option<&WorkspaceBuildData>,
440 rustc: &Option<CargoWorkspace>,
441 rustc_build_data_map: Option<&WorkspaceBuildData>,
443 let _p = profile::span("cargo_to_crate_graph");
444 let mut crate_graph = CrateGraph::default();
445 let (public_deps, libproc_macro) =
446 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
448 let mut cfg_options = CfgOptions::default();
449 cfg_options.extend(rustc_cfg);
451 let mut pkg_to_lib_crate = FxHashMap::default();
453 // Add test cfg for non-sysroot crates
454 cfg_options.insert_atom("test".into());
455 cfg_options.insert_atom("debug_assertions".into());
457 let mut pkg_crates = FxHashMap::default();
458 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
459 let mut has_private = false;
460 // Next, create crates for each package, target pair
461 for pkg in cargo.packages() {
462 let mut cfg_options = &cfg_options;
463 let mut replaced_cfg_options;
464 if let Some(overrides) = override_cfg.get(&cargo[pkg].name) {
465 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
466 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
467 // working on rust-lang/rust as that's the only time it appears outside sysroot).
469 // A more ideal solution might be to reanalyze crates based on where the cursor is and
470 // figure out the set of cfgs that would have to apply to make it active.
472 replaced_cfg_options = cfg_options.clone();
473 replaced_cfg_options.apply_diff(overrides.clone());
474 cfg_options = &replaced_cfg_options;
477 has_private |= cargo[pkg].metadata.rustc_private;
478 let mut lib_tgt = None;
479 for &tgt in cargo[pkg].targets.iter() {
480 if let Some(file_id) = load(&cargo[tgt].root) {
481 let crate_id = add_target_crate_root(
484 build_data_map.and_then(|it| it.get(&cargo[pkg].id)),
490 if cargo[tgt].kind == TargetKind::Lib {
491 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
492 pkg_to_lib_crate.insert(pkg, crate_id);
494 if cargo[tgt].is_proc_macro {
495 if let Some(proc_macro) = libproc_macro {
499 CrateName::new("proc_macro").unwrap(),
505 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
509 // Set deps to the core, std and to the lib target of the current package
510 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
511 if let Some((to, name)) = lib_tgt.clone() {
512 if to != *from && *kind != TargetKind::BuildScript {
513 // (build script can not depend on its library target)
515 // For root projects with dashes in their name,
516 // cargo metadata does not do any normalization,
517 // so we do it ourselves currently
518 let name = CrateName::normalize_dashes(&name);
519 add_dep(&mut crate_graph, *from, name, to);
522 for (name, krate) in public_deps.iter() {
523 add_dep(&mut crate_graph, *from, name.clone(), *krate);
528 // Now add a dep edge from all targets of upstream to the lib
529 // target of downstream.
530 for pkg in cargo.packages() {
531 for dep in cargo[pkg].dependencies.iter() {
532 let name = CrateName::new(&dep.name).unwrap();
533 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
534 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
535 if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
536 // Only build scripts may depend on build dependencies.
539 if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
540 // Build scripts may only depend on build dependencies.
544 add_dep(&mut crate_graph, *from, name.clone(), to)
551 // If the user provided a path to rustc sources, we add all the rustc_private crates
552 // and create dependencies on them for the crates which opt-in to that
553 if let Some(rustc_workspace) = rustc {
558 rustc_build_data_map,
561 &mut pkg_to_lib_crate,
571 fn detached_files_to_crate_graph(
572 rustc_cfg: Vec<CfgFlag>,
573 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
574 detached_files: &[AbsPathBuf],
577 let _p = profile::span("detached_files_to_crate_graph");
578 let mut crate_graph = CrateGraph::default();
579 let (public_deps, _libproc_macro) =
580 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
582 let mut cfg_options = CfgOptions::default();
583 cfg_options.extend(rustc_cfg);
585 for detached_file in detached_files {
586 let file_id = match load(detached_file) {
587 Some(file_id) => file_id,
589 log::error!("Failed to load detached file {:?}", detached_file);
593 let display_name = detached_file
595 .and_then(|os_str| os_str.to_str())
596 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
597 let detached_file_crate = crate_graph.add_crate_root(
607 for (name, krate) in public_deps.iter() {
608 add_dep(&mut crate_graph, detached_file_crate, name.clone(), *krate);
614 fn handle_rustc_crates(
615 rustc_workspace: &CargoWorkspace,
616 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
617 crate_graph: &mut CrateGraph,
618 rustc_build_data_map: Option<&WorkspaceBuildData>,
619 cfg_options: &CfgOptions,
620 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
621 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
622 public_deps: &[(CrateName, CrateId)],
623 cargo: &CargoWorkspace,
624 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
626 let mut rustc_pkg_crates = FxHashMap::default();
627 // The root package of the rustc-dev component is rustc_driver, so we match that
629 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
630 // The rustc workspace might be incomplete (such as if rustc-dev is not
631 // installed for the current toolchain) and `rustcSource` is set to discover.
632 if let Some(root_pkg) = root_pkg {
633 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
634 let mut queue = VecDeque::new();
635 queue.push_back(root_pkg);
636 while let Some(pkg) = queue.pop_front() {
637 // Don't duplicate packages if they are dependended on a diamond pattern
638 // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
639 // which is not ideal
640 if rustc_pkg_crates.contains_key(&pkg) {
643 for dep in &rustc_workspace[pkg].dependencies {
644 queue.push_back(dep.pkg);
646 for &tgt in rustc_workspace[pkg].targets.iter() {
647 if rustc_workspace[tgt].kind != TargetKind::Lib {
650 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
651 let crate_id = add_target_crate_root(
653 &rustc_workspace[pkg],
654 rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)),
658 &rustc_workspace[tgt].name,
660 pkg_to_lib_crate.insert(pkg, crate_id);
661 // Add dependencies on core / std / alloc for this crate
662 for (name, krate) in public_deps.iter() {
663 add_dep(crate_graph, crate_id, name.clone(), *krate);
665 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
670 // Now add a dep edge from all targets of upstream to the lib
671 // target of downstream.
672 for pkg in rustc_pkg_crates.keys().copied() {
673 for dep in rustc_workspace[pkg].dependencies.iter() {
674 let name = CrateName::new(&dep.name).unwrap();
675 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
676 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
677 add_dep(crate_graph, from, name.clone(), to);
682 // Add a dependency on the rustc_private crates for all targets of each package
684 for dep in rustc_workspace.packages() {
685 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
687 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
688 for pkg in cargo.packages() {
689 let package = &cargo[pkg];
690 if !package.metadata.rustc_private {
693 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
694 // Avoid creating duplicate dependencies
695 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
696 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
697 // instead of the one from `crates.io`
698 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
699 add_dep(crate_graph, *from, name.clone(), to);
707 fn add_target_crate_root(
708 crate_graph: &mut CrateGraph,
709 pkg: &cargo_workspace::PackageData,
710 build_data: Option<&PackageBuildData>,
711 cfg_options: &CfgOptions,
712 proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
716 let edition = pkg.edition;
718 let mut opts = cfg_options.clone();
719 for feature in pkg.active_features.iter() {
720 opts.insert_key_value("feature".into(), feature.into());
722 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
723 opts.extend(cfgs.iter().cloned());
728 let mut env = Env::default();
729 if let Some(envs) = build_data.map(|it| &it.envs) {
731 env.set(k, v.clone());
735 let proc_macro = build_data
737 .and_then(|it| it.proc_macro_dylib_path.as_ref())
738 .map(|it| proc_macro_loader(it))
739 .unwrap_or_default();
741 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
742 let mut potential_cfg_options = cfg_options.clone();
743 potential_cfg_options.extend(
746 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
749 let crate_id = crate_graph.add_crate_root(
754 potential_cfg_options,
762 fn sysroot_to_crate_graph(
763 crate_graph: &mut CrateGraph,
765 rustc_cfg: Vec<CfgFlag>,
766 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
767 ) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
768 let _p = profile::span("sysroot_to_crate_graph");
769 let mut cfg_options = CfgOptions::default();
770 cfg_options.extend(rustc_cfg);
771 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
773 .filter_map(|krate| {
774 let file_id = load(&sysroot[krate].root)?;
776 let env = Env::default();
777 let proc_macro = vec![];
778 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
779 let crate_id = crate_graph.add_crate_root(
788 Some((krate, crate_id))
792 for from in sysroot.crates() {
793 for &to in sysroot[from].deps.iter() {
794 let name = CrateName::new(&sysroot[to].name).unwrap();
795 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
796 add_dep(crate_graph, from, name, to);
801 let public_deps = sysroot
803 .map(|(name, idx)| (CrateName::new(name).unwrap(), sysroot_crates[&idx]))
804 .collect::<Vec<_>>();
806 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
807 (public_deps, libproc_macro)
810 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
811 if let Err(err) = graph.add_dep(from, name, to) {
812 log::error!("{}", err)