]> git.lizzy.rs Git - rust.git/blob - crates/project_model/src/workspace.rs
Merge #9080
[rust.git] / crates / project_model / src / workspace.rs
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`.
4
5 use std::{collections::VecDeque, fmt, fs, path::Path, process::Command};
6
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};
14
15 use crate::{
16     build_data::{BuildDataResult, PackageBuildData, WorkspaceBuildData},
17     cargo_workspace,
18     cfg_flag::CfgFlag,
19     rustc_cfg,
20     sysroot::SysrootCrate,
21     utf8_stdout, BuildDataCollector, CargoConfig, CargoWorkspace, ProjectJson, ProjectManifest,
22     Sysroot, TargetKind,
23 };
24
25 pub type CfgOverrides = FxHashMap<String, CfgDiff>;
26
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
33     pub is_member: bool,
34     pub include: Vec<AbsPathBuf>,
35     pub exclude: Vec<AbsPathBuf>,
36 }
37
38 #[derive(Clone, Eq, PartialEq)]
39 pub enum ProjectWorkspace {
40     /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
41     Cargo {
42         cargo: CargoWorkspace,
43         sysroot: Sysroot,
44         rustc: Option<CargoWorkspace>,
45         /// Holds cfg flags for the current target. We get those by running
46         /// `rustc --print cfg`.
47         ///
48         /// FIXME: make this a per-crate map, as, eg, build.rs might have a
49         /// different target.
50         rustc_cfg: Vec<CfgFlag>,
51         cfg_overrides: CfgOverrides,
52     },
53     /// Project workspace was manually specified using a `rust-project.json` file.
54     Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
55
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.
63     // //
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> },
67 }
68
69 impl fmt::Debug for ProjectWorkspace {
70     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71         // Make sure this isn't too verbose.
72         match self {
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())
78                 .field(
79                     "n_rustc_compiler_crates",
80                     &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
81                 )
82                 .field("n_rustc_cfg", &rustc_cfg.len())
83                 .field("n_cfg_overrides", &cfg_overrides.len())
84                 .finish(),
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());
90                 }
91                 debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
92                 debug_struct.finish()
93             }
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())
99                 .finish(),
100         }
101     }
102 }
103
104 impl ProjectWorkspace {
105     pub fn load(
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())
114                 })?;
115                 let data = serde_json::from_str(&file).with_context(|| {
116                     format!("Failed to deserialize json file {}", project_json.display())
117                 })?;
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())?
121             }
122             ProjectManifest::CargoToml(cargo_toml) => {
123                 let cargo_version = utf8_stdout({
124                     let mut cmd = Command::new(toolchain::cargo());
125                     cmd.arg("--version");
126                     cmd
127                 })?;
128
129                 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, config, progress)
130                     .with_context(|| {
131                         format!(
132                             "Failed to read Cargo metadata from Cargo.toml file {}, {}",
133                             cargo_toml.display(),
134                             cargo_version
135                         )
136                     })?;
137
138                 let sysroot = if config.no_sysroot {
139                     Sysroot::default()
140                 } else {
141                     Sysroot::discover(&cargo_toml).with_context(|| {
142                         format!(
143                             "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
144                             cargo_toml.display()
145                         )
146                     })?
147                 };
148
149                 let rustc_dir = if let Some(rustc_source) = &config.rustc_source {
150                     use cargo_workspace::RustcSource;
151                     match rustc_source {
152                         RustcSource::Path(path) => Some(path.clone()),
153                         RustcSource::Discover => Sysroot::discover_rustc(&cargo_toml),
154                     }
155                 } else {
156                     None
157                 };
158
159                 let rustc = if let Some(rustc_dir) = rustc_dir {
160                     Some(
161                         CargoWorkspace::from_cargo_metadata(&rustc_dir, config, progress)
162                             .with_context(|| {
163                                 format!("Failed to read Cargo metadata for Rust sources")
164                             })?,
165                     )
166                 } else {
167                     None
168                 };
169
170                 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
171
172                 let cfg_overrides = config.cfg_overrides();
173                 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg, cfg_overrides }
174             }
175         };
176
177         Ok(res)
178     }
179
180     pub fn load_inline(
181         project_json: ProjectJson,
182         target: Option<&str>,
183     ) -> Result<ProjectWorkspace> {
184         let sysroot = match &project_json.sysroot_src {
185             Some(path) => Some(Sysroot::load(path)?),
186             None => None,
187         };
188         let rustc_cfg = rustc_cfg::get(None, target);
189         Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
190     }
191
192     pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
193         let sysroot = Sysroot::discover(
194             detached_files.first().ok_or_else(|| format_err!("No detached files to load"))?,
195         )?;
196         let rustc_cfg = rustc_cfg::get(None, None);
197         Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
198     }
199
200     /// Returns the roots for the current `ProjectWorkspace`
201     /// The return type contains the path and whether or not
202     /// the root is a member of the current workspace
203     pub fn to_roots(&self, build_data: Option<&BuildDataResult>) -> Vec<PackageRoot> {
204         match self {
205             ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
206                 .crates()
207                 .map(|(_, krate)| PackageRoot {
208                     is_member: krate.is_workspace_member,
209                     include: krate.include.clone(),
210                     exclude: krate.exclude.clone(),
211                 })
212                 .collect::<FxHashSet<_>>()
213                 .into_iter()
214                 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
215                     sysroot.crates().map(move |krate| PackageRoot {
216                         is_member: false,
217                         include: vec![sysroot[krate].root_dir().to_path_buf()],
218                         exclude: Vec::new(),
219                     })
220                 }))
221                 .collect::<Vec<_>>(),
222             ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg: _, cfg_overrides: _ } => {
223                 cargo
224                     .packages()
225                     .map(|pkg| {
226                         let is_member = cargo[pkg].is_member;
227                         let pkg_root = cargo[pkg].root().to_path_buf();
228
229                         let mut include = vec![pkg_root.clone()];
230                         include.extend(
231                             build_data
232                                 .and_then(|it| it.get(cargo.workspace_root()))
233                                 .and_then(|map| map.get(&cargo[pkg].id))
234                                 .and_then(|it| it.out_dir.clone()),
235                         );
236
237                         let mut exclude = vec![pkg_root.join(".git")];
238                         if is_member {
239                             exclude.push(pkg_root.join("target"));
240                         } else {
241                             exclude.push(pkg_root.join("tests"));
242                             exclude.push(pkg_root.join("examples"));
243                             exclude.push(pkg_root.join("benches"));
244                         }
245                         PackageRoot { is_member, include, exclude }
246                     })
247                     .chain(sysroot.crates().map(|krate| PackageRoot {
248                         is_member: false,
249                         include: vec![sysroot[krate].root_dir().to_path_buf()],
250                         exclude: Vec::new(),
251                     }))
252                     .chain(rustc.into_iter().flat_map(|rustc| {
253                         rustc.packages().map(move |krate| PackageRoot {
254                             is_member: false,
255                             include: vec![rustc[krate].root().to_path_buf()],
256                             exclude: Vec::new(),
257                         })
258                     }))
259                     .collect()
260             }
261             ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
262                 .into_iter()
263                 .map(|detached_file| PackageRoot {
264                     is_member: true,
265                     include: vec![detached_file.clone()],
266                     exclude: Vec::new(),
267                 })
268                 .chain(sysroot.crates().map(|krate| PackageRoot {
269                     is_member: false,
270                     include: vec![sysroot[krate].root_dir().to_path_buf()],
271                     exclude: Vec::new(),
272                 }))
273                 .collect(),
274         }
275     }
276
277     pub fn n_packages(&self) -> usize {
278         match self {
279             ProjectWorkspace::Json { project, .. } => project.n_crates(),
280             ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
281                 let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len());
282                 cargo.packages().len() + sysroot.crates().len() + rustc_package_len
283             }
284             ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
285                 sysroot.crates().len() + files.len()
286             }
287         }
288     }
289
290     pub fn to_crate_graph(
291         &self,
292         build_data: Option<&BuildDataResult>,
293         proc_macro_client: Option<&ProcMacroClient>,
294         load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
295     ) -> CrateGraph {
296         let _p = profile::span("ProjectWorkspace::to_crate_graph");
297         let proc_macro_loader = |path: &Path| match proc_macro_client {
298             Some(client) => client.by_dylib_path(path),
299             None => Vec::new(),
300         };
301
302         let mut crate_graph = match self {
303             ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
304                 rustc_cfg.clone(),
305                 &proc_macro_loader,
306                 load,
307                 project,
308                 sysroot,
309             ),
310             ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg, cfg_overrides } => {
311                 cargo_to_crate_graph(
312                     rustc_cfg.clone(),
313                     cfg_overrides,
314                     &proc_macro_loader,
315                     load,
316                     cargo,
317                     build_data.and_then(|it| it.get(cargo.workspace_root())),
318                     sysroot,
319                     rustc,
320                     rustc
321                         .as_ref()
322                         .zip(build_data)
323                         .and_then(|(it, map)| map.get(it.workspace_root())),
324                 )
325             }
326             ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
327                 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
328             }
329         };
330         if crate_graph.patch_cfg_if() {
331             log::debug!("Patched std to depend on cfg-if")
332         } else {
333             log::debug!("Did not patch std to depend on cfg-if")
334         }
335         crate_graph
336     }
337
338     pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) {
339         match self {
340             ProjectWorkspace::Cargo { cargo, .. } => {
341                 collector.add_config(cargo.workspace_root(), cargo.build_data_config().clone());
342             }
343             _ => {}
344         }
345     }
346 }
347
348 fn project_json_to_crate_graph(
349     rustc_cfg: Vec<CfgFlag>,
350     proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
351     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
352     project: &ProjectJson,
353     sysroot: &Option<Sysroot>,
354 ) -> CrateGraph {
355     let mut crate_graph = CrateGraph::default();
356     let sysroot_deps = sysroot
357         .as_ref()
358         .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
359
360     let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
361     let crates: FxHashMap<CrateId, CrateId> = project
362         .crates()
363         .filter_map(|(crate_id, krate)| {
364             let file_path = &krate.root_module;
365             let file_id = load(file_path)?;
366             Some((crate_id, krate, file_id))
367         })
368         .map(|(crate_id, krate, file_id)| {
369             let env = krate.env.clone().into_iter().collect();
370             let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| proc_macro_loader(&it));
371
372             let target_cfgs = match krate.target.as_deref() {
373                 Some(target) => {
374                     cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
375                 }
376                 None => &rustc_cfg,
377             };
378
379             let mut cfg_options = CfgOptions::default();
380             cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
381             (
382                 crate_id,
383                 crate_graph.add_crate_root(
384                     file_id,
385                     krate.edition,
386                     krate.display_name.clone(),
387                     cfg_options.clone(),
388                     cfg_options,
389                     env,
390                     proc_macro.unwrap_or_default(),
391                 ),
392             )
393         })
394         .collect();
395
396     for (from, krate) in project.crates() {
397         if let Some(&from) = crates.get(&from) {
398             if let Some((public_deps, _proc_macro)) = &sysroot_deps {
399                 for (name, to) in public_deps.iter() {
400                     add_dep(&mut crate_graph, from, name.clone(), *to)
401                 }
402             }
403
404             for dep in &krate.deps {
405                 if let Some(&to) = crates.get(&dep.crate_id) {
406                     add_dep(&mut crate_graph, from, dep.name.clone(), to)
407                 }
408             }
409         }
410     }
411     crate_graph
412 }
413
414 fn cargo_to_crate_graph(
415     rustc_cfg: Vec<CfgFlag>,
416     override_cfg: &CfgOverrides,
417     proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
418     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
419     cargo: &CargoWorkspace,
420     build_data_map: Option<&WorkspaceBuildData>,
421     sysroot: &Sysroot,
422     rustc: &Option<CargoWorkspace>,
423     rustc_build_data_map: Option<&WorkspaceBuildData>,
424 ) -> CrateGraph {
425     let _p = profile::span("cargo_to_crate_graph");
426     let mut crate_graph = CrateGraph::default();
427     let (public_deps, libproc_macro) =
428         sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
429
430     let mut cfg_options = CfgOptions::default();
431     cfg_options.extend(rustc_cfg);
432
433     let mut pkg_to_lib_crate = FxHashMap::default();
434
435     // Add test cfg for non-sysroot crates
436     cfg_options.insert_atom("test".into());
437     cfg_options.insert_atom("debug_assertions".into());
438
439     let mut pkg_crates = FxHashMap::default();
440     // Does any crate signal to rust-analyzer that they need the rustc_private crates?
441     let mut has_private = false;
442     // Next, create crates for each package, target pair
443     for pkg in cargo.packages() {
444         let mut cfg_options = &cfg_options;
445         let mut replaced_cfg_options;
446         if let Some(overrides) = override_cfg.get(&cargo[pkg].name) {
447             // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
448             // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
449             // working on rust-lang/rust as that's the only time it appears outside sysroot).
450             //
451             // A more ideal solution might be to reanalyze crates based on where the cursor is and
452             // figure out the set of cfgs that would have to apply to make it active.
453
454             replaced_cfg_options = cfg_options.clone();
455             replaced_cfg_options.apply_diff(overrides.clone());
456             cfg_options = &replaced_cfg_options;
457         };
458
459         has_private |= cargo[pkg].metadata.rustc_private;
460         let mut lib_tgt = None;
461         for &tgt in cargo[pkg].targets.iter() {
462             if let Some(file_id) = load(&cargo[tgt].root) {
463                 let crate_id = add_target_crate_root(
464                     &mut crate_graph,
465                     &cargo[pkg],
466                     build_data_map.and_then(|it| it.get(&cargo[pkg].id)),
467                     &cfg_options,
468                     proc_macro_loader,
469                     file_id,
470                     &cargo[tgt].name,
471                 );
472                 if cargo[tgt].kind == TargetKind::Lib {
473                     lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
474                     pkg_to_lib_crate.insert(pkg, crate_id);
475                 }
476                 if cargo[tgt].is_proc_macro {
477                     if let Some(proc_macro) = libproc_macro {
478                         add_dep(
479                             &mut crate_graph,
480                             crate_id,
481                             CrateName::new("proc_macro").unwrap(),
482                             proc_macro,
483                         );
484                     }
485                 }
486
487                 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
488             }
489         }
490
491         // Set deps to the core, std and to the lib target of the current package
492         for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
493             if let Some((to, name)) = lib_tgt.clone() {
494                 if to != *from && *kind != TargetKind::BuildScript {
495                     // (build script can not depend on its library target)
496
497                     // For root projects with dashes in their name,
498                     // cargo metadata does not do any normalization,
499                     // so we do it ourselves currently
500                     let name = CrateName::normalize_dashes(&name);
501                     add_dep(&mut crate_graph, *from, name, to);
502                 }
503             }
504             for (name, krate) in public_deps.iter() {
505                 add_dep(&mut crate_graph, *from, name.clone(), *krate);
506             }
507         }
508     }
509
510     // Now add a dep edge from all targets of upstream to the lib
511     // target of downstream.
512     for pkg in cargo.packages() {
513         for dep in cargo[pkg].dependencies.iter() {
514             let name = CrateName::new(&dep.name).unwrap();
515             if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
516                 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
517                     if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
518                         // Only build scripts may depend on build dependencies.
519                         continue;
520                     }
521                     if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
522                         // Build scripts may only depend on build dependencies.
523                         continue;
524                     }
525
526                     add_dep(&mut crate_graph, *from, name.clone(), to)
527                 }
528             }
529         }
530     }
531
532     if has_private {
533         // If the user provided a path to rustc sources, we add all the rustc_private crates
534         // and create dependencies on them for the crates which opt-in to that
535         if let Some(rustc_workspace) = rustc {
536             handle_rustc_crates(
537                 rustc_workspace,
538                 load,
539                 &mut crate_graph,
540                 rustc_build_data_map,
541                 &cfg_options,
542                 proc_macro_loader,
543                 &mut pkg_to_lib_crate,
544                 &public_deps,
545                 cargo,
546                 &pkg_crates,
547             );
548         }
549     }
550     crate_graph
551 }
552
553 fn detached_files_to_crate_graph(
554     rustc_cfg: Vec<CfgFlag>,
555     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
556     detached_files: &[AbsPathBuf],
557     sysroot: &Sysroot,
558 ) -> CrateGraph {
559     let _p = profile::span("detached_files_to_crate_graph");
560     let mut crate_graph = CrateGraph::default();
561     let (public_deps, _libproc_macro) =
562         sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
563
564     let mut cfg_options = CfgOptions::default();
565     cfg_options.extend(rustc_cfg);
566
567     for detached_file in detached_files {
568         let file_id = match load(detached_file) {
569             Some(file_id) => file_id,
570             None => {
571                 log::error!("Failed to load detached file {:?}", detached_file);
572                 continue;
573             }
574         };
575         let display_name = detached_file
576             .file_stem()
577             .and_then(|os_str| os_str.to_str())
578             .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
579         let detached_file_crate = crate_graph.add_crate_root(
580             file_id,
581             Edition::Edition2018,
582             display_name,
583             cfg_options.clone(),
584             cfg_options.clone(),
585             Env::default(),
586             Vec::new(),
587         );
588
589         for (name, krate) in public_deps.iter() {
590             add_dep(&mut crate_graph, detached_file_crate, name.clone(), *krate);
591         }
592     }
593     crate_graph
594 }
595
596 fn handle_rustc_crates(
597     rustc_workspace: &CargoWorkspace,
598     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
599     crate_graph: &mut CrateGraph,
600     rustc_build_data_map: Option<&WorkspaceBuildData>,
601     cfg_options: &CfgOptions,
602     proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
603     pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
604     public_deps: &[(CrateName, CrateId)],
605     cargo: &CargoWorkspace,
606     pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
607 ) {
608     let mut rustc_pkg_crates = FxHashMap::default();
609     // The root package of the rustc-dev component is rustc_driver, so we match that
610     let root_pkg =
611         rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
612     // The rustc workspace might be incomplete (such as if rustc-dev is not
613     // installed for the current toolchain) and `rustcSource` is set to discover.
614     if let Some(root_pkg) = root_pkg {
615         // Iterate through every crate in the dependency subtree of rustc_driver using BFS
616         let mut queue = VecDeque::new();
617         queue.push_back(root_pkg);
618         while let Some(pkg) = queue.pop_front() {
619             // Don't duplicate packages if they are dependended on a diamond pattern
620             // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
621             // which is not ideal
622             if rustc_pkg_crates.contains_key(&pkg) {
623                 continue;
624             }
625             for dep in &rustc_workspace[pkg].dependencies {
626                 queue.push_back(dep.pkg);
627             }
628             for &tgt in rustc_workspace[pkg].targets.iter() {
629                 if rustc_workspace[tgt].kind != TargetKind::Lib {
630                     continue;
631                 }
632                 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
633                     let crate_id = add_target_crate_root(
634                         crate_graph,
635                         &rustc_workspace[pkg],
636                         rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)),
637                         cfg_options,
638                         proc_macro_loader,
639                         file_id,
640                         &rustc_workspace[tgt].name,
641                     );
642                     pkg_to_lib_crate.insert(pkg, crate_id);
643                     // Add dependencies on core / std / alloc for this crate
644                     for (name, krate) in public_deps.iter() {
645                         add_dep(crate_graph, crate_id, name.clone(), *krate);
646                     }
647                     rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
648                 }
649             }
650         }
651     }
652     // Now add a dep edge from all targets of upstream to the lib
653     // target of downstream.
654     for pkg in rustc_pkg_crates.keys().copied() {
655         for dep in rustc_workspace[pkg].dependencies.iter() {
656             let name = CrateName::new(&dep.name).unwrap();
657             if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
658                 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
659                     add_dep(crate_graph, from, name.clone(), to);
660                 }
661             }
662         }
663     }
664     // Add a dependency on the rustc_private crates for all targets of each package
665     // which opts in
666     for dep in rustc_workspace.packages() {
667         let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
668
669         if let Some(&to) = pkg_to_lib_crate.get(&dep) {
670             for pkg in cargo.packages() {
671                 let package = &cargo[pkg];
672                 if !package.metadata.rustc_private {
673                     continue;
674                 }
675                 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
676                     // Avoid creating duplicate dependencies
677                     // This avoids the situation where `from` depends on e.g. `arrayvec`, but
678                     // `rust_analyzer` thinks that it should use the one from the `rustcSource`
679                     // instead of the one from `crates.io`
680                     if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
681                         add_dep(crate_graph, *from, name.clone(), to);
682                     }
683                 }
684             }
685         }
686     }
687 }
688
689 fn add_target_crate_root(
690     crate_graph: &mut CrateGraph,
691     pkg: &cargo_workspace::PackageData,
692     build_data: Option<&PackageBuildData>,
693     cfg_options: &CfgOptions,
694     proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
695     file_id: FileId,
696     cargo_name: &str,
697 ) -> CrateId {
698     let edition = pkg.edition;
699     let cfg_options = {
700         let mut opts = cfg_options.clone();
701         for feature in pkg.active_features.iter() {
702             opts.insert_key_value("feature".into(), feature.into());
703         }
704         if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
705             opts.extend(cfgs.iter().cloned());
706         }
707         opts
708     };
709
710     let mut env = Env::default();
711     if let Some(envs) = build_data.map(|it| &it.envs) {
712         for (k, v) in envs {
713             env.set(k, v.clone());
714         }
715     }
716
717     let proc_macro = build_data
718         .as_ref()
719         .and_then(|it| it.proc_macro_dylib_path.as_ref())
720         .map(|it| proc_macro_loader(it))
721         .unwrap_or_default();
722
723     let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
724     let mut potential_cfg_options = cfg_options.clone();
725     potential_cfg_options.extend(
726         pkg.features
727             .iter()
728             .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
729     );
730
731     let crate_id = crate_graph.add_crate_root(
732         file_id,
733         edition,
734         Some(display_name),
735         cfg_options,
736         potential_cfg_options,
737         env,
738         proc_macro,
739     );
740
741     crate_id
742 }
743
744 fn sysroot_to_crate_graph(
745     crate_graph: &mut CrateGraph,
746     sysroot: &Sysroot,
747     rustc_cfg: Vec<CfgFlag>,
748     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
749 ) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
750     let _p = profile::span("sysroot_to_crate_graph");
751     let mut cfg_options = CfgOptions::default();
752     cfg_options.extend(rustc_cfg);
753     let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
754         .crates()
755         .filter_map(|krate| {
756             let file_id = load(&sysroot[krate].root)?;
757
758             let env = Env::default();
759             let proc_macro = vec![];
760             let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
761             let crate_id = crate_graph.add_crate_root(
762                 file_id,
763                 Edition::Edition2018,
764                 Some(display_name),
765                 cfg_options.clone(),
766                 cfg_options.clone(),
767                 env,
768                 proc_macro,
769             );
770             Some((krate, crate_id))
771         })
772         .collect();
773
774     for from in sysroot.crates() {
775         for &to in sysroot[from].deps.iter() {
776             let name = CrateName::new(&sysroot[to].name).unwrap();
777             if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
778                 add_dep(crate_graph, from, name, to);
779             }
780         }
781     }
782
783     let public_deps = sysroot
784         .public_deps()
785         .map(|(name, idx)| (CrateName::new(name).unwrap(), sysroot_crates[&idx]))
786         .collect::<Vec<_>>();
787
788     let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
789     (public_deps, libproc_macro)
790 }
791
792 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
793     if let Err(err) = graph.add_dep(from, name, to) {
794         log::error!("{}", err)
795     }
796 }