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 utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath, Package,
25 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>,
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(
161 config.target.as_deref(),
165 ProjectManifest::CargoToml(cargo_toml) => {
166 let cargo_version = utf8_stdout({
167 let mut cmd = Command::new(toolchain::cargo());
168 cmd.envs(&config.extra_env);
169 cmd.arg("--version");
172 let toolchain = cargo_version
173 .get("cargo ".len()..)
174 .and_then(|it| Version::parse(it.split_whitespace().next()?).ok());
176 let meta = CargoWorkspace::fetch_metadata(
184 "Failed to read Cargo metadata from Cargo.toml file {}, {:?}",
185 cargo_toml.display(),
189 let cargo = CargoWorkspace::new(meta);
191 let sysroot = match &config.sysroot {
192 Some(RustcSource::Path(path)) => {
193 Some(Sysroot::with_sysroot_dir(path.clone()).with_context(|| {
195 "Failed to find sysroot for Cargo.toml file {}.",
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 ProjectWorkspace::Cargo {
249 build_scripts: WorkspaceBuildScripts::default(),
263 project_json: ProjectJson,
264 target: Option<&str>,
265 extra_env: &FxHashMap<String, String>,
266 ) -> Result<ProjectWorkspace> {
267 let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
268 (Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)?),
269 (Some(sysroot), None) => {
270 // assume sysroot is structured like rustup's and guess `sysroot_src`
272 sysroot.join("lib").join("rustlib").join("src").join("rust").join("library");
274 Some(Sysroot::load(sysroot, sysroot_src)?)
276 (None, Some(sysroot_src)) => {
277 // assume sysroot is structured like rustup's and guess `sysroot`
278 let mut sysroot = sysroot_src.clone();
282 Some(Sysroot::load(sysroot, sysroot_src)?)
284 (None, None) => None,
286 if let Some(sysroot) = &sysroot {
287 tracing::info!(src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot");
290 let rustc_cfg = rustc_cfg::get(None, target, extra_env);
291 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
294 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
295 let sysroot = Sysroot::discover(
298 .and_then(|it| it.parent())
299 .ok_or_else(|| format_err!("No detached files to load"))?,
302 let rustc_cfg = rustc_cfg::get(None, None, &Default::default());
303 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
306 /// Runs the build scripts for this [`ProjectWorkspace`].
307 pub fn run_build_scripts(
309 config: &CargoConfig,
310 progress: &dyn Fn(String),
311 ) -> Result<WorkspaceBuildScripts> {
313 ProjectWorkspace::Cargo { cargo, toolchain, .. } => {
314 WorkspaceBuildScripts::run_for_workspace(config, cargo, progress, toolchain)
317 "Failed to run build scripts for {}",
318 &cargo.workspace_root().display()
322 ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
323 Ok(WorkspaceBuildScripts::default())
328 /// Runs the build scripts for the given [`ProjectWorkspace`]s. Depending on the invocation
329 /// strategy this may run a single build process for all project workspaces.
330 pub fn run_all_build_scripts(
331 workspaces: &[ProjectWorkspace],
332 config: &CargoConfig,
333 progress: &dyn Fn(String),
334 ) -> Vec<Result<WorkspaceBuildScripts>> {
335 if matches!(config.invocation_strategy, InvocationStrategy::PerWorkspace)
336 || config.run_build_script_command.is_none()
338 return workspaces.iter().map(|it| it.run_build_scripts(config, progress)).collect();
341 let cargo_ws: Vec<_> = workspaces
343 .filter_map(|it| match it {
344 ProjectWorkspace::Cargo { cargo, .. } => Some(cargo),
348 let ref mut outputs = match WorkspaceBuildScripts::run_once(config, &cargo_ws, progress) {
349 Ok(it) => Ok(it.into_iter()),
350 // io::Error is not Clone?
351 Err(e) => Err(Arc::new(e)),
357 ProjectWorkspace::Cargo { cargo, .. } => match outputs {
358 Ok(outputs) => Ok(outputs.next().unwrap()),
359 Err(e) => Err(e.clone()).with_context(|| {
361 "Failed to run build scripts for {}",
362 &cargo.workspace_root().display()
366 _ => Ok(WorkspaceBuildScripts::default()),
371 pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
373 ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
375 always!(bs == WorkspaceBuildScripts::default());
380 /// Returns the roots for the current `ProjectWorkspace`
381 /// The return type contains the path and whether or not
382 /// the root is a member of the current workspace
383 pub fn to_roots(&self) -> Vec<PackageRoot> {
384 let mk_sysroot = |sysroot: Option<&Sysroot>| {
385 sysroot.map(|sysroot| PackageRoot {
387 include: vec![sysroot.src_root().to_path_buf()],
392 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
394 .map(|(_, krate)| PackageRoot {
395 is_local: krate.is_workspace_member,
396 include: krate.include.clone(),
397 exclude: krate.exclude.clone(),
399 .collect::<FxHashSet<_>>()
401 .chain(mk_sysroot(sysroot.as_ref()))
402 .collect::<Vec<_>>(),
403 ProjectWorkspace::Cargo {
415 let is_local = cargo[pkg].is_local;
416 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
418 let mut include = vec![pkg_root.clone()];
420 build_scripts.get_output(pkg).and_then(|it| it.out_dir.clone());
421 include.extend(out_dir);
423 // In case target's path is manually set in Cargo.toml to be
424 // outside the package root, add its parent as an extra include.
425 // An example of this situation would look like this:
429 // path = "../../src/lib.rs"
431 let extra_targets = cargo[pkg]
434 .filter(|&&tgt| cargo[tgt].kind == TargetKind::Lib)
435 .filter_map(|&tgt| cargo[tgt].root.parent())
436 .map(|tgt| tgt.normalize().to_path_buf())
437 .filter(|path| !path.starts_with(&pkg_root));
438 include.extend(extra_targets);
440 let mut exclude = vec![pkg_root.join(".git")];
442 exclude.push(pkg_root.join("target"));
444 exclude.push(pkg_root.join("tests"));
445 exclude.push(pkg_root.join("examples"));
446 exclude.push(pkg_root.join("benches"));
448 PackageRoot { is_local, include, exclude }
450 .chain(mk_sysroot(sysroot.as_ref()))
451 .chain(rustc.iter().flat_map(|rustc| {
452 rustc.packages().map(move |krate| PackageRoot {
454 include: vec![rustc[krate].manifest.parent().to_path_buf()],
460 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
462 .map(|detached_file| PackageRoot {
464 include: vec![detached_file.clone()],
467 .chain(mk_sysroot(Some(sysroot)))
472 pub fn n_packages(&self) -> usize {
474 ProjectWorkspace::Json { project, .. } => project.n_crates(),
475 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
476 let rustc_package_len = rustc.as_ref().map_or(0, |it| it.packages().len());
477 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
478 cargo.packages().len() + sysroot_package_len + rustc_package_len
480 ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
481 sysroot.crates().len() + files.len()
486 pub fn to_crate_graph(
488 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
489 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
490 extra_env: &FxHashMap<String, String>,
492 let _p = profile::span("ProjectWorkspace::to_crate_graph");
494 let mut crate_graph = match self {
495 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
503 ProjectWorkspace::Cargo {
511 } => cargo_to_crate_graph(
521 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
522 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
525 if crate_graph.patch_cfg_if() {
526 tracing::debug!("Patched std to depend on cfg-if")
528 tracing::debug!("Did not patch std to depend on cfg-if")
534 fn project_json_to_crate_graph(
535 rustc_cfg: Vec<CfgFlag>,
536 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
537 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
538 project: &ProjectJson,
539 sysroot: &Option<Sysroot>,
540 extra_env: &FxHashMap<String, String>,
542 let mut crate_graph = CrateGraph::default();
543 let sysroot_deps = sysroot
545 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
547 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
548 let crates: NoHashHashMap<CrateId, CrateId> = project
550 .filter_map(|(crate_id, krate)| {
551 let file_path = &krate.root_module;
552 let file_id = load(file_path)?;
553 Some((crate_id, krate, file_id))
555 .map(|(crate_id, krate, file_id)| {
556 let env = krate.env.clone().into_iter().collect();
557 let proc_macro = match krate.proc_macro_dylib_path.clone() {
558 Some(it) => load_proc_macro(
559 krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""),
562 None => Err("no proc macro dylib present".into()),
565 let target_cfgs = match krate.target.as_deref() {
566 Some(target) => cfg_cache
568 .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)),
572 let mut cfg_options = CfgOptions::default();
573 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
576 crate_graph.add_crate_root(
579 krate.display_name.clone(),
580 krate.version.clone(),
586 if krate.display_name.is_some() {
587 CrateOrigin::CratesIo {
588 repo: krate.repository.clone(),
592 .map(|n| n.canonical_name().to_string()),
595 CrateOrigin::CratesIo { repo: None, name: None }
602 for (from, krate) in project.crates() {
603 if let Some(&from) = crates.get(&from) {
604 if let Some((public_deps, libproc_macro)) = &sysroot_deps {
605 public_deps.add(from, &mut crate_graph);
606 if krate.is_proc_macro {
607 if let Some(proc_macro) = libproc_macro {
611 CrateName::new("proc_macro").unwrap(),
618 for dep in &krate.deps {
619 if let Some(&to) = crates.get(&dep.crate_id) {
620 add_dep(&mut crate_graph, from, dep.name.clone(), to)
628 fn cargo_to_crate_graph(
629 rustc_cfg: Vec<CfgFlag>,
630 override_cfg: &CfgOverrides,
631 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
632 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
633 cargo: &CargoWorkspace,
634 build_scripts: &WorkspaceBuildScripts,
635 sysroot: Option<&Sysroot>,
636 rustc: &Option<CargoWorkspace>,
638 let _p = profile::span("cargo_to_crate_graph");
639 let mut crate_graph = CrateGraph::default();
640 let (public_deps, libproc_macro) = match sysroot {
641 Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load),
642 None => (SysrootPublicDeps::default(), None),
645 let mut cfg_options = CfgOptions::default();
646 cfg_options.extend(rustc_cfg);
648 let mut pkg_to_lib_crate = FxHashMap::default();
650 cfg_options.insert_atom("debug_assertions".into());
652 let mut pkg_crates = FxHashMap::default();
653 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
654 let mut has_private = false;
655 // Next, create crates for each package, target pair
656 for pkg in cargo.packages() {
657 let mut cfg_options = cfg_options.clone();
659 let overrides = match override_cfg {
660 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
661 CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
664 // Add test cfg for local crates
665 if cargo[pkg].is_local {
666 cfg_options.insert_atom("test".into());
669 if let Some(overrides) = overrides {
670 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
671 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
672 // working on rust-lang/rust as that's the only time it appears outside sysroot).
674 // A more ideal solution might be to reanalyze crates based on where the cursor is and
675 // figure out the set of cfgs that would have to apply to make it active.
677 cfg_options.apply_diff(overrides.clone());
680 has_private |= cargo[pkg].metadata.rustc_private;
681 let mut lib_tgt = None;
682 for &tgt in cargo[pkg].targets.iter() {
683 if cargo[tgt].kind != TargetKind::Lib && !cargo[pkg].is_member {
684 // For non-workspace-members, Cargo does not resolve dev-dependencies, so we don't
685 // add any targets except the library target, since those will not work correctly if
686 // they use dev-dependencies.
687 // In fact, they can break quite badly if multiple client workspaces get merged:
688 // https://github.com/rust-lang/rust-analyzer/issues/11300
692 if let Some(file_id) = load(&cargo[tgt].root) {
693 let crate_id = add_target_crate_root(
696 build_scripts.get_output(pkg),
698 &mut |path| load_proc_macro(&cargo[tgt].name, path),
701 cargo[tgt].is_proc_macro,
703 if cargo[tgt].kind == TargetKind::Lib {
704 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
705 pkg_to_lib_crate.insert(pkg, crate_id);
707 // Even crates that don't set proc-macro = true are allowed to depend on proc_macro
708 // (just none of the APIs work when called outside of a proc macro).
709 if let Some(proc_macro) = libproc_macro {
710 add_dep_with_prelude(
713 CrateName::new("proc_macro").unwrap(),
715 cargo[tgt].is_proc_macro,
719 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
723 // Set deps to the core, std and to the lib target of the current package
724 for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
725 // Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
726 public_deps.add(from, &mut crate_graph);
728 if let Some((to, name)) = lib_tgt.clone() {
729 if to != from && kind != TargetKind::BuildScript {
730 // (build script can not depend on its library target)
732 // For root projects with dashes in their name,
733 // cargo metadata does not do any normalization,
734 // so we do it ourselves currently
735 let name = CrateName::normalize_dashes(&name);
736 add_dep(&mut crate_graph, from, name, to);
742 // Now add a dep edge from all targets of upstream to the lib
743 // target of downstream.
744 for pkg in cargo.packages() {
745 for dep in cargo[pkg].dependencies.iter() {
746 let name = CrateName::new(&dep.name).unwrap();
747 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
748 for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
749 if dep.kind == DepKind::Build && kind != TargetKind::BuildScript {
750 // Only build scripts may depend on build dependencies.
753 if dep.kind != DepKind::Build && kind == TargetKind::BuildScript {
754 // Build scripts may only depend on build dependencies.
758 add_dep(&mut crate_graph, from, name.clone(), to)
765 // If the user provided a path to rustc sources, we add all the rustc_private crates
766 // and create dependencies on them for the crates which opt-in to that
767 if let Some(rustc_workspace) = rustc {
775 &mut pkg_to_lib_crate,
786 fn detached_files_to_crate_graph(
787 rustc_cfg: Vec<CfgFlag>,
788 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
789 detached_files: &[AbsPathBuf],
792 let _p = profile::span("detached_files_to_crate_graph");
793 let mut crate_graph = CrateGraph::default();
794 let (public_deps, _libproc_macro) =
795 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
797 let mut cfg_options = CfgOptions::default();
798 cfg_options.extend(rustc_cfg);
800 for detached_file in detached_files {
801 let file_id = match load(detached_file) {
802 Some(file_id) => file_id,
804 tracing::error!("Failed to load detached file {:?}", detached_file);
808 let display_name = detached_file
810 .and_then(|os_str| os_str.to_str())
811 .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
812 let detached_file_crate = crate_graph.add_crate_root(
815 display_name.clone(),
822 CrateOrigin::CratesIo {
824 name: display_name.map(|n| n.canonical_name().to_string()),
828 public_deps.add(detached_file_crate, &mut crate_graph);
833 fn handle_rustc_crates(
834 crate_graph: &mut CrateGraph,
835 rustc_workspace: &CargoWorkspace,
836 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
837 cfg_options: &CfgOptions,
838 override_cfg: &CfgOverrides,
839 load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
840 pkg_to_lib_crate: &mut FxHashMap<Package, CrateId>,
841 public_deps: &SysrootPublicDeps,
842 cargo: &CargoWorkspace,
843 pkg_crates: &FxHashMap<Package, Vec<(CrateId, TargetKind)>>,
844 build_scripts: &WorkspaceBuildScripts,
846 let mut rustc_pkg_crates = FxHashMap::default();
847 // The root package of the rustc-dev component is rustc_driver, so we match that
849 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
850 // The rustc workspace might be incomplete (such as if rustc-dev is not
851 // installed for the current toolchain) and `rustc_source` is set to discover.
852 if let Some(root_pkg) = root_pkg {
853 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
854 let mut queue = VecDeque::new();
855 queue.push_back(root_pkg);
856 while let Some(pkg) = queue.pop_front() {
857 // Don't duplicate packages if they are dependent on a diamond pattern
858 // N.B. if this line is omitted, we try to analyze over 4_800_000 crates
859 // which is not ideal
860 if rustc_pkg_crates.contains_key(&pkg) {
863 for dep in &rustc_workspace[pkg].dependencies {
864 queue.push_back(dep.pkg);
867 let mut cfg_options = cfg_options.clone();
869 let overrides = match override_cfg {
870 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
871 CfgOverrides::Selective(cfg_overrides) => {
872 cfg_overrides.get(&rustc_workspace[pkg].name)
876 if let Some(overrides) = overrides {
877 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
878 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
879 // working on rust-lang/rust as that's the only time it appears outside sysroot).
881 // A more ideal solution might be to reanalyze crates based on where the cursor is and
882 // figure out the set of cfgs that would have to apply to make it active.
884 cfg_options.apply_diff(overrides.clone());
887 for &tgt in rustc_workspace[pkg].targets.iter() {
888 if rustc_workspace[tgt].kind != TargetKind::Lib {
891 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
892 let crate_id = add_target_crate_root(
894 &rustc_workspace[pkg],
895 build_scripts.get_output(pkg),
897 &mut |path| load_proc_macro(&rustc_workspace[tgt].name, path),
899 &rustc_workspace[tgt].name,
900 rustc_workspace[tgt].is_proc_macro,
902 pkg_to_lib_crate.insert(pkg, crate_id);
903 // Add dependencies on core / std / alloc for this crate
904 public_deps.add(crate_id, crate_graph);
905 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
910 // Now add a dep edge from all targets of upstream to the lib
911 // target of downstream.
912 for pkg in rustc_pkg_crates.keys().copied() {
913 for dep in rustc_workspace[pkg].dependencies.iter() {
914 let name = CrateName::new(&dep.name).unwrap();
915 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
916 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
917 add_dep(crate_graph, from, name.clone(), to);
922 // Add a dependency on the rustc_private crates for all targets of each package
924 for dep in rustc_workspace.packages() {
925 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
927 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
928 for pkg in cargo.packages() {
929 let package = &cargo[pkg];
930 if !package.metadata.rustc_private {
933 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
934 // Avoid creating duplicate dependencies
935 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
936 // `rust_analyzer` thinks that it should use the one from the `rustc_source`
937 // instead of the one from `crates.io`
938 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
939 add_dep(crate_graph, *from, name.clone(), to);
947 fn add_target_crate_root(
948 crate_graph: &mut CrateGraph,
950 build_data: Option<&BuildScriptOutput>,
951 cfg_options: CfgOptions,
952 load_proc_macro: &mut dyn FnMut(&AbsPath) -> ProcMacroLoadResult,
957 let edition = pkg.edition;
958 let mut potential_cfg_options = cfg_options.clone();
959 potential_cfg_options.extend(
962 .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
965 let mut opts = cfg_options;
966 for feature in pkg.active_features.iter() {
967 opts.insert_key_value("feature".into(), feature.into());
969 if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
970 opts.extend(cfgs.iter().cloned());
975 let mut env = Env::default();
976 inject_cargo_env(pkg, &mut env);
978 if let Some(envs) = build_data.map(|it| &it.envs) {
980 env.set(k, v.clone());
984 let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) {
985 Some(Some(it)) => load_proc_macro(it),
986 Some(None) => Err("no proc macro dylib present".into()),
987 None => Err("crate has not (yet) been built".into()),
990 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
991 crate_graph.add_crate_root(
995 Some(pkg.version.to_string()),
997 potential_cfg_options,
1001 CrateOrigin::CratesIo { repo: pkg.repository.clone(), name: Some(pkg.name.clone()) },
1006 struct SysrootPublicDeps {
1007 deps: Vec<(CrateName, CrateId, bool)>,
1010 impl SysrootPublicDeps {
1011 /// Makes `from` depend on the public sysroot crates.
1012 fn add(&self, from: CrateId, crate_graph: &mut CrateGraph) {
1013 for (name, krate, prelude) in &self.deps {
1014 add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude);
1019 fn sysroot_to_crate_graph(
1020 crate_graph: &mut CrateGraph,
1022 rustc_cfg: Vec<CfgFlag>,
1023 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
1024 ) -> (SysrootPublicDeps, Option<CrateId>) {
1025 let _p = profile::span("sysroot_to_crate_graph");
1026 let mut cfg_options = CfgOptions::default();
1027 cfg_options.extend(rustc_cfg);
1028 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
1030 .filter_map(|krate| {
1031 let file_id = load(&sysroot[krate].root)?;
1033 let env = Env::default();
1034 let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
1035 let crate_id = crate_graph.add_crate_root(
1040 cfg_options.clone(),
1041 cfg_options.clone(),
1043 Err("no proc macro loaded for sysroot crate".into()),
1045 CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
1047 Some((krate, crate_id))
1051 for from in sysroot.crates() {
1052 for &to in sysroot[from].deps.iter() {
1053 let name = CrateName::new(&sysroot[to].name).unwrap();
1054 if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
1055 add_dep(crate_graph, from, name, to);
1060 let public_deps = SysrootPublicDeps {
1063 .map(|(name, idx, prelude)| {
1064 (CrateName::new(name).unwrap(), sysroot_crates[&idx], prelude)
1066 .collect::<Vec<_>>(),
1069 let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
1070 (public_deps, libproc_macro)
1073 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
1074 add_dep_inner(graph, from, Dependency::new(name, to))
1077 fn add_dep_with_prelude(
1078 graph: &mut CrateGraph,
1084 add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude))
1087 fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
1088 if let Err(err) = graph.add_dep(from, dep) {
1089 tracing::error!("{}", err)
1093 /// Recreates the compile-time environment variables that Cargo sets.
1095 /// Should be synced with
1096 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
1098 /// FIXME: ask Cargo to provide this data instead of re-deriving.
1099 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
1100 // FIXME: Missing variables:
1101 // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
1103 let manifest_dir = package.manifest.parent();
1104 env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned());
1106 // Not always right, but works for common cases.
1107 env.set("CARGO", "cargo".into());
1109 env.set("CARGO_PKG_VERSION", package.version.to_string());
1110 env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
1111 env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
1112 env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
1113 env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
1115 env.set("CARGO_PKG_AUTHORS", String::new());
1117 env.set("CARGO_PKG_NAME", package.name.clone());
1118 // FIXME: This isn't really correct (a package can have many crates with different names), but
1119 // it's better than leaving the variable unset.
1120 env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string());
1121 env.set("CARGO_PKG_DESCRIPTION", String::new());
1122 env.set("CARGO_PKG_HOMEPAGE", String::new());
1123 env.set("CARGO_PKG_REPOSITORY", String::new());
1124 env.set("CARGO_PKG_LICENSE", String::new());
1126 env.set("CARGO_PKG_LICENSE_FILE", String::new());